mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-10-29 17:58:36 +08:00
support hcs
This commit is contained in:
@@ -38,6 +38,7 @@ func init() {
|
||||
cmd.CreateWithKeyword("create-openstack", &options.SOpenStackCloudAccountCreateOptions{})
|
||||
cmd.CreateWithKeyword("create-huawei", &options.SHuaweiCloudAccountCreateOptions{})
|
||||
cmd.CreateWithKeyword("create-hcso", &options.SHCSOAccountCreateOptions{})
|
||||
cmd.CreateWithKeyword("create-hcs", &options.SHCSAccountCreateOptions{})
|
||||
cmd.CreateWithKeyword("create-ucloud", &options.SUcloudCloudAccountCreateOptions{})
|
||||
cmd.CreateWithKeyword("create-zstack", &options.SZStackCloudAccountCreateOptions{})
|
||||
cmd.CreateWithKeyword("create-s3", &options.SS3CloudAccountCreateOptions{})
|
||||
@@ -63,6 +64,7 @@ func init() {
|
||||
cmd.UpdateWithKeyword("update-openstack", &options.SOpenStackCloudAccountUpdateOptions{})
|
||||
cmd.UpdateWithKeyword("update-huawei", &options.SHuaweiCloudAccountUpdateOptions{})
|
||||
cmd.UpdateWithKeyword("update-hcso", &options.SHCSOAccountUpdateOptions{})
|
||||
cmd.UpdateWithKeyword("update-hcs", &options.SHCSOAccountUpdateOptions{})
|
||||
cmd.UpdateWithKeyword("update-ucloud", &options.SUcloudCloudAccountUpdateOptions{})
|
||||
cmd.UpdateWithKeyword("update-zstack", &options.SZStackCloudAccountUpdateOptions{})
|
||||
cmd.UpdateWithKeyword("update-s3", &options.SS3CloudAccountUpdateOptions{})
|
||||
@@ -86,6 +88,7 @@ func init() {
|
||||
cmd.PerformWithKeyword("update-credential-huawei", "update-credential", &options.SHuaweiCloudAccountUpdateCredentialOptions{})
|
||||
cmd.PerformWithKeyword("update-credential-apsara", "update-credential", &options.SApsaraCloudAccountUpdateCredentialOptions{})
|
||||
cmd.PerformWithKeyword("update-credential-hcso", "update-credential", &options.SHCSOAccountUpdateCredentialOptions{})
|
||||
cmd.PerformWithKeyword("update-credential-hcs", "update-credential", &options.SHCSOAccountUpdateCredentialOptions{})
|
||||
cmd.PerformWithKeyword("update-credential-ucloud", "update-credential", &options.SUcloudCloudAccountUpdateCredentialOptions{})
|
||||
cmd.PerformWithKeyword("update-credential-zstack", "update-credential", &options.SZStackCloudAccountUpdateCredentialOptions{})
|
||||
cmd.PerformWithKeyword("update-credential-s3", "update-credential", &options.SS3CloudAccountUpdateCredentialOptions{})
|
||||
|
||||
@@ -47,6 +47,7 @@ func init() {
|
||||
"apsara",
|
||||
"cloudpods",
|
||||
"hcso",
|
||||
"hcs",
|
||||
"nutanix",
|
||||
"s3",
|
||||
"ceph",
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"golang.org/x/net/http/httpproxy"
|
||||
|
||||
"yunion.io/x/structarg"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud/hcs"
|
||||
_ "yunion.io/x/onecloud/pkg/multicloud/hcs/shell"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
type BaseOptions struct {
|
||||
Debug bool `help:"debug mode"`
|
||||
AuthUrl string `help:"Auth url" default:"$HCS_AUTH_URL"`
|
||||
AccessKey string `help:"Access key" default:"$HCS_ACCESS_KEY" metavar:"HCS_ACCESS_KEY"`
|
||||
Secret string `help:"Secret" default:"$HCS_SECRET" metavar:"HCS_SECRET"`
|
||||
RegionId string `help:"RegionId" default:"$HCS_REGION" metavar:"HCS_REGION"`
|
||||
ProjectId string `help:"RegionId" default:"$HCS_PROJECT_ID" metavar:"HCS_PROJECT_ID"`
|
||||
SUBCOMMAND string `help:"hcscli subcommand" subcommand:"true"`
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParserWithHelp(&BaseOptions{},
|
||||
"hcscli",
|
||||
"Command-line interface to hcs API.",
|
||||
`See "hcscli COMMAND --help" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
subcmd := parse.GetSubcommand()
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
}
|
||||
return parse, nil
|
||||
}
|
||||
|
||||
func showErrorAndExit(e error) {
|
||||
fmt.Fprintf(os.Stderr, "%s", e)
|
||||
fmt.Fprintln(os.Stderr)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func newClient(options *BaseOptions) (*hcs.SRegion, error) {
|
||||
if len(options.AccessKey) == 0 {
|
||||
return nil, fmt.Errorf("Missing accessKey")
|
||||
}
|
||||
|
||||
if len(options.Secret) == 0 {
|
||||
return nil, fmt.Errorf("Missing secret")
|
||||
}
|
||||
|
||||
if len(options.AuthUrl) == 0 {
|
||||
return nil, fmt.Errorf("Missing authUrl")
|
||||
}
|
||||
|
||||
if len(options.RegionId) == 0 {
|
||||
return nil, fmt.Errorf("Missing regionId")
|
||||
}
|
||||
|
||||
cfg := &httpproxy.Config{
|
||||
HTTPProxy: os.Getenv("HTTP_PROXY"),
|
||||
HTTPSProxy: os.Getenv("HTTPS_PROXY"),
|
||||
NoProxy: os.Getenv("NO_PROXY"),
|
||||
}
|
||||
cfgProxyFunc := cfg.ProxyFunc()
|
||||
proxyFunc := func(req *http.Request) (*url.URL, error) {
|
||||
return cfgProxyFunc(req.URL)
|
||||
}
|
||||
|
||||
cli, err := hcs.NewHcsClient(
|
||||
hcs.NewHcsConfig(
|
||||
options.AccessKey,
|
||||
options.Secret,
|
||||
options.ProjectId,
|
||||
options.AuthUrl,
|
||||
).Debug(options.Debug).
|
||||
CloudproviderConfig(
|
||||
cloudprovider.ProviderConfig{
|
||||
ProxyFunc: proxyFunc,
|
||||
},
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cli.GetRegion(options.RegionId)
|
||||
}
|
||||
|
||||
func main() {
|
||||
parser, e := getSubcommandParser()
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
options := parser.Options().(*BaseOptions)
|
||||
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
return
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
return
|
||||
}
|
||||
suboptions := subparser.Options()
|
||||
if subparser.IsHelpSet() {
|
||||
fmt.Print(subparser.HelpString())
|
||||
return
|
||||
}
|
||||
var region *hcs.SRegion
|
||||
region, e = newClient(options)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = subcmd.Invoke(region, suboptions)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,7 @@ require (
|
||||
github.com/gosuri/uitable v0.0.0-20160404203958-36ee7e946282
|
||||
github.com/hako/durafmt v0.0.0-20180520121703-7b7ae1e72ead
|
||||
github.com/huaweicloud/huaweicloud-sdk-go v1.0.26
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.21.12+incompatible
|
||||
github.com/jaypipes/ghw v0.9.1
|
||||
github.com/jdcloud-api/jdcloud-sdk-go v1.55.0
|
||||
github.com/koding/websocketproxy v0.0.0-20181220232114-7ed82d81a28c
|
||||
@@ -88,7 +89,7 @@ require (
|
||||
golang.org/x/text v0.3.7
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220916014741-473347a5e6e3
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20191008142428-8d021180e987
|
||||
google.golang.org/grpc v1.38.0
|
||||
google.golang.org/protobuf v1.27.1
|
||||
gopkg.in/fatih/set.v0 v0.2.1
|
||||
@@ -175,6 +176,7 @@ require (
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
|
||||
github.com/google/btree v1.0.0 // indirect
|
||||
github.com/google/go-cmp v0.5.7 // indirect
|
||||
github.com/google/go-querystring v1.0.0 // indirect
|
||||
github.com/google/gofuzz v1.1.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.0.5 // indirect
|
||||
|
||||
@@ -418,6 +418,8 @@ github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0
|
||||
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
|
||||
github.com/huaweicloud/huaweicloud-sdk-go v1.0.26 h1:aWTl4Ng9lZUW1DupYyqYiqkTwuTKMmqUZBKLmX0iWxo=
|
||||
github.com/huaweicloud/huaweicloud-sdk-go v1.0.26/go.mod h1:YHXxw/bm7AohI4jTY8Z43JYw+DPCDLqjxoLTqmcyja4=
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.21.12+incompatible h1:tANYIteuFrosKbRYUk1Yo/OGJjbt4x3OVg211Qc60M0=
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.21.12+incompatible/go.mod h1:l7VUhRbTKCzdOacdT4oWCwATKyvZqUOlOqr0Ous3k4s=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
|
||||
@@ -434,6 +436,7 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
@@ -514,6 +517,10 @@ github.com/mdlayher/arp v0.0.0-20190313224443-98a83c8a2717/go.mod h1:eOj1DDj3NAZ
|
||||
github.com/mdlayher/ethernet v0.0.0-20190313224307-5b5fc417d966/go.mod h1:5s5p/sMJ6sNsFl6uCh85lkFGV8kLuIYJCRJLavVJwvg=
|
||||
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7 h1:lez6TS6aAau+8wXUP3G9I3TGlmPFEq2CTxBaRqY6AGE=
|
||||
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=
|
||||
github.com/mdlayher/genetlink v0.0.0-20191004171646-5cf585d3b847/go.mod h1:LNhNWFVJapYK8zEjVHUIle4gy+Oahfc3UtcaqZ8Dz98=
|
||||
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
|
||||
github.com/mdlayher/netlink v0.0.0-20191004170026-3c8695cb0643/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
|
||||
github.com/mdlayher/netlink v0.0.0-20191008140946-2a17fd90af51/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
|
||||
github.com/mdlayher/raw v0.0.0-20190313224157-43dbcdd7739d/go.mod h1:r1fbeITl2xL/zLbVnNHFyOzQJTgr/3fpf1lJX/cjzR8=
|
||||
github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
|
||||
github.com/mdlayher/raw v0.0.0-20190606144222-a54781e5f38f h1:Xjvm7UTnKTwrlhbs+8qA6I0v1iX77rY3QxftSgvOVRk=
|
||||
@@ -524,6 +531,7 @@ github.com/mholt/caddy v0.10.11 h1:s8X+R8DuBbrrMuUTcWSxlDe567B0s5EDmiDBKSYsioY=
|
||||
github.com/mholt/caddy v0.10.11/go.mod h1:Wb1PlT4DAYSqOEd03MsqkdkXnTxA8v9pKjdpxbqM1kY=
|
||||
github.com/miekg/dns v1.1.25 h1:dFwPR6SfLtrSwgDcIq2bcU/gVutB4sNApq2HBdqcakg=
|
||||
github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
|
||||
github.com/minio/cli v1.20.0/go.mod h1:bYxnK0uS629N3Bq+AOZZ+6lwF77Sodk4+UL9vNuXhOY=
|
||||
github.com/minio/minio-go v6.0.14+incompatible h1:fnV+GD28LeqdN6vT2XdGKW8Qe/IfjJDswNVuni6km9o=
|
||||
github.com/minio/minio-go v6.0.14+incompatible/go.mod h1:7guKYtitv8dktvNUGrhzmNlA5wrAABTQXCoesZdFQO8=
|
||||
@@ -739,7 +747,9 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
@@ -800,7 +810,10 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -851,6 +864,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -860,11 +874,15 @@ golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190830023255-19e00faab6ad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191003212358-c178f38b412c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -975,8 +993,9 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220916014741-473347a5e6e3 h1:ARxNdT6I+00ZyY5yRT/ZECkQti4iGrMZX9dvG/ao/LY=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220916014741-473347a5e6e3/go.mod h1:yp4gl6zOlnDGOZeWeDfMwQcsdOIQnMdhuPx9mwwWBL4=
|
||||
golang.zx2c4.com/wireguard v0.0.20190908/go.mod h1:LhfXh5z6bLC2lW2ve6BzYZFwnnsXK3OQjySR0Yh2dO8=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20191008142428-8d021180e987 h1:26OAgqBTufVr8WKonCEhhjO1oKsYhHv0iM5Dg92G1TM=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20191008142428-8d021180e987/go.mod h1:7hq1rEDsx7/FWl8IEEnfH2Xhs6M2MNnjUfN0PeI8Rm0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
|
||||
@@ -46,6 +46,7 @@ const (
|
||||
CLOUD_PROVIDER_AWS = "Aws"
|
||||
CLOUD_PROVIDER_HUAWEI = "Huawei"
|
||||
CLOUD_PROVIDER_HCSO = "HCSO"
|
||||
CLOUD_PROVIDER_HCS = "HCS"
|
||||
CLOUD_PROVIDER_OPENSTACK = "OpenStack"
|
||||
CLOUD_PROVIDER_UCLOUD = "Ucloud"
|
||||
CLOUD_PROVIDER_ZSTACK = "ZStack"
|
||||
@@ -103,7 +104,8 @@ const (
|
||||
var (
|
||||
CLOUD_PROVIDER_VALID_STATUS = []string{CLOUD_PROVIDER_CONNECTED}
|
||||
CLOUD_PROVIDER_VALID_HEALTH_STATUS = []string{CLOUD_PROVIDER_HEALTH_NORMAL, CLOUD_PROVIDER_HEALTH_NO_PERMISSION}
|
||||
PRIVATE_CLOUD_PROVIDERS = []string{CLOUD_PROVIDER_ZSTACK, CLOUD_PROVIDER_OPENSTACK, CLOUD_PROVIDER_APSARA, CLOUD_PROVIDER_HCSO, CLOUD_PROVIDER_INCLOUD_SPHERE, CLOUD_PROVIDER_PROXMOX, CLOUD_PROVIDER_REMOTEFILE}
|
||||
PRIVATE_CLOUD_PROVIDERS = []string{CLOUD_PROVIDER_ZSTACK, CLOUD_PROVIDER_OPENSTACK, CLOUD_PROVIDER_APSARA,
|
||||
CLOUD_PROVIDER_HCSO, CLOUD_PROVIDER_HCS, CLOUD_PROVIDER_INCLOUD_SPHERE, CLOUD_PROVIDER_PROXMOX, CLOUD_PROVIDER_REMOTEFILE}
|
||||
|
||||
CLOUD_PROVIDERS = []string{
|
||||
CLOUD_PROVIDER_ONECLOUD,
|
||||
@@ -115,6 +117,7 @@ var (
|
||||
CLOUD_PROVIDER_AWS,
|
||||
CLOUD_PROVIDER_HUAWEI,
|
||||
CLOUD_PROVIDER_HCSO,
|
||||
CLOUD_PROVIDER_HCS,
|
||||
CLOUD_PROVIDER_OPENSTACK,
|
||||
CLOUD_PROVIDER_UCLOUD,
|
||||
CLOUD_PROVIDER_ZSTACK,
|
||||
@@ -160,6 +163,9 @@ var (
|
||||
CLOUD_PROVIDER_HCSO: {
|
||||
HOST_TYPE_HCSO,
|
||||
},
|
||||
CLOUD_PROVIDER_HCS: {
|
||||
HOST_TYPE_HCS,
|
||||
},
|
||||
CLOUD_PROVIDER_OPENSTACK: {
|
||||
HOST_TYPE_OPENSTACK,
|
||||
},
|
||||
|
||||
@@ -179,17 +179,23 @@ type ElasticcacheCreateInput struct {
|
||||
ElasticcacheSecgroupsInput
|
||||
|
||||
// 主可用区名称或Id
|
||||
Zone string `json:"zone"`
|
||||
ZoneId string `json:"zone_id"`
|
||||
|
||||
// 备可用区名称或Id列表
|
||||
// swagger: ignore
|
||||
Zone string `json:"zone" yunion-deprecated-by:"zone_id"`
|
||||
|
||||
// 备可用区名称或Id列表, split by: ,
|
||||
// 默认副本与主可用区一致
|
||||
// 支持此参数的云厂商: 腾讯云
|
||||
// required: false
|
||||
SlaveZones []string `json:"slave_zones"`
|
||||
SlaveZones string `json:"slave_zones"`
|
||||
|
||||
// Ip子网名称或Id,建议使用Id
|
||||
// required: true
|
||||
Network string `json:"network"`
|
||||
NetworkId string `json:"network_id"`
|
||||
|
||||
// swagger: ignore
|
||||
Network string `json:"network" yunion-deprecated-by:"network_id"`
|
||||
|
||||
// 网络类型
|
||||
// enum: vpc, cLassic
|
||||
@@ -209,13 +215,20 @@ type ElasticcacheCreateInput struct {
|
||||
// required: false
|
||||
InstanceType string `json:"instance_type"`
|
||||
|
||||
NodeType string `json:"node_type"`
|
||||
|
||||
// swagger: ignore
|
||||
MemorySizeMb int `json:"memory_size_mb" yunion-deprecated-by:"capacity_mb"`
|
||||
|
||||
// 初始密码
|
||||
// required: false
|
||||
Password string `json:"password"`
|
||||
|
||||
ResetPassword bool `json:"reset_password"`
|
||||
|
||||
// 安全组名称或Id
|
||||
// default: default
|
||||
Secgroup string `json:"secgroup"`
|
||||
SecgroupIds []string `json:"secgroup_ids"`
|
||||
|
||||
// 内网IP
|
||||
// 阿里云、华为云此参数可选,其它公有云该参数无效
|
||||
@@ -249,4 +262,10 @@ type ElasticcacheCreateInput struct {
|
||||
// enum: 22:00:00, 02:00:00, 06:00:00, 10:00:00, 14:00:00, 18:00:00
|
||||
// required: false
|
||||
MaintainStartTime string `json:"maintain_start_time"`
|
||||
|
||||
LocalCategory string `json:"local_category"`
|
||||
|
||||
CapacityMb int `json:"capacity_mb"`
|
||||
|
||||
CloudregionId string `json:"cloudregion_id"`
|
||||
}
|
||||
|
||||
@@ -174,6 +174,7 @@ const (
|
||||
HYPERVISOR_AZURE = "azure"
|
||||
HYPERVISOR_AWS = "aws"
|
||||
HYPERVISOR_HUAWEI = "huawei"
|
||||
HYPERVISOR_HCS = "hcs"
|
||||
HYPERVISOR_HCSO = "hcso"
|
||||
HYPERVISOR_OPENSTACK = "openstack"
|
||||
HYPERVISOR_UCLOUD = "ucloud"
|
||||
@@ -240,6 +241,7 @@ var HYPERVISORS = []string{
|
||||
HYPERVISOR_QCLOUD,
|
||||
HYPERVISOR_HUAWEI,
|
||||
HYPERVISOR_HCSO,
|
||||
HYPERVISOR_HCS,
|
||||
HYPERVISOR_OPENSTACK,
|
||||
HYPERVISOR_UCLOUD,
|
||||
HYPERVISOR_ZSTACK,
|
||||
@@ -280,6 +282,7 @@ var PRIVATE_CLOUD_HYPERVISORS = []string{
|
||||
HYPERVISOR_APSARA,
|
||||
HYPERVISOR_CLOUDPODS,
|
||||
HYPERVISOR_HCSO,
|
||||
HYPERVISOR_HCS,
|
||||
HYPERVISOR_NUTANIX,
|
||||
HYPERVISOR_BINGO_CLOUD,
|
||||
HYPERVISOR_INCLOUD_SPHERE,
|
||||
@@ -301,6 +304,7 @@ var HYPERVISOR_HOSTTYPE = map[string]string{
|
||||
HYPERVISOR_QCLOUD: HOST_TYPE_QCLOUD,
|
||||
HYPERVISOR_HUAWEI: HOST_TYPE_HUAWEI,
|
||||
HYPERVISOR_HCSO: HOST_TYPE_HCSO,
|
||||
HYPERVISOR_HCS: HOST_TYPE_HCS,
|
||||
HYPERVISOR_OPENSTACK: HOST_TYPE_OPENSTACK,
|
||||
HYPERVISOR_UCLOUD: HOST_TYPE_UCLOUD,
|
||||
HYPERVISOR_ZSTACK: HOST_TYPE_ZSTACK,
|
||||
@@ -328,6 +332,7 @@ var HOSTTYPE_HYPERVISOR = map[string]string{
|
||||
HOST_TYPE_QCLOUD: HYPERVISOR_QCLOUD,
|
||||
HOST_TYPE_HUAWEI: HYPERVISOR_HUAWEI,
|
||||
HOST_TYPE_HCSO: HYPERVISOR_HCSO,
|
||||
HOST_TYPE_HCS: HYPERVISOR_HCS,
|
||||
HOST_TYPE_OPENSTACK: HYPERVISOR_OPENSTACK,
|
||||
HOST_TYPE_UCLOUD: HYPERVISOR_UCLOUD,
|
||||
HOST_TYPE_ZSTACK: HYPERVISOR_ZSTACK,
|
||||
|
||||
@@ -30,6 +30,7 @@ const (
|
||||
HOST_TYPE_AZURE = "azure"
|
||||
HOST_TYPE_HUAWEI = "huawei"
|
||||
HOST_TYPE_HCSO = "hcso"
|
||||
HOST_TYPE_HCS = "hcs"
|
||||
HOST_TYPE_OPENSTACK = "openstack"
|
||||
HOST_TYPE_UCLOUD = "ucloud"
|
||||
HOST_TYPE_ZSTACK = "zstack"
|
||||
@@ -118,6 +119,7 @@ var HOST_TYPES = []string{
|
||||
HOST_TYPE_QCLOUD,
|
||||
HOST_TYPE_HUAWEI,
|
||||
HOST_TYPE_HCSO,
|
||||
HOST_TYPE_HCS,
|
||||
HOST_TYPE_OPENSTACK,
|
||||
HOST_TYPE_UCLOUD,
|
||||
HOST_TYPE_ZSTACK,
|
||||
|
||||
@@ -64,6 +64,7 @@ var (
|
||||
REGIONAL_NETWORK_PROVIDERS = []string{
|
||||
CLOUD_PROVIDER_HUAWEI,
|
||||
CLOUD_PROVIDER_HCSO,
|
||||
CLOUD_PROVIDER_HCS,
|
||||
CLOUD_PROVIDER_CTYUN,
|
||||
CLOUD_PROVIDER_UCLOUD,
|
||||
CLOUD_PROVIDER_GOOGLE,
|
||||
|
||||
@@ -55,6 +55,9 @@ const (
|
||||
SYS_CLOUD_TAG_PREFIX = "sys:"
|
||||
// Such tags have inherited and isolated properties
|
||||
CLASS_TAT_PREFIX = "cls:"
|
||||
|
||||
SKU_STATUS_AVAILABLE = "available"
|
||||
SKU_STATUS_SOLDOUT = "soldout"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package providerdriver
|
||||
|
||||
import (
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
)
|
||||
|
||||
type HcsCollect struct {
|
||||
SCollectByResourceIdDriver
|
||||
}
|
||||
|
||||
func (self *HcsCollect) GetProvider() string {
|
||||
return api.CLOUD_PROVIDER_HCS
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register(&HcsCollect{})
|
||||
}
|
||||
|
||||
func (self *HcsCollect) IsSupportMetrics() bool {
|
||||
return true
|
||||
}
|
||||
@@ -19,7 +19,6 @@ import "yunion.io/x/onecloud/pkg/util/billing"
|
||||
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423019.html
|
||||
// https://help.aliyun.com/document_detail/60873.html?spm=a2c4g.11174283.6.715.7412dce0qSYemb
|
||||
type SCloudElasticCacheInput struct {
|
||||
RegionId string // 地域
|
||||
InstanceType string // 实例规格 redis.master.small.default
|
||||
CapacityGB int64 // 缓存容量 华为云此项参数必选
|
||||
InstanceName string // 实例名称
|
||||
@@ -38,7 +37,7 @@ type SCloudElasticCacheInput struct {
|
||||
EipId string // 绑定弹性IP
|
||||
MaintainBegin string // 维护时间窗开始时间,格式为HH:mm:ss
|
||||
MaintainEnd string // 维护时间窗结束时间,格式为HH:mm:ss
|
||||
BC *billing.SBillingCycle // 包年包月
|
||||
BillingCycle *billing.SBillingCycle // 包年包月
|
||||
ProjectId string
|
||||
Tags map[string]string
|
||||
}
|
||||
|
||||
@@ -150,6 +150,7 @@ type ICloudRegion interface {
|
||||
CreateIDBInstance(desc *SManagedDBInstanceCreateConfig) (ICloudDBInstance, error)
|
||||
|
||||
GetIElasticcaches() ([]ICloudElasticcache, error)
|
||||
GetIElasticcacheSkus() ([]ICloudElasticcacheSku, error)
|
||||
GetIElasticcacheById(id string) (ICloudElasticcache, error)
|
||||
CreateIElasticcaches(ec *SCloudElasticCacheInput) (ICloudElasticcache, error)
|
||||
|
||||
@@ -1052,6 +1053,34 @@ type ICloudDBInstanceAccountPrivilege interface {
|
||||
GetDBName() string
|
||||
}
|
||||
|
||||
type ICloudElasticcacheSku interface {
|
||||
GetName() string
|
||||
GetGlobalId() string
|
||||
GetZoneId() string
|
||||
GetSlaveZoneId() string
|
||||
GetEngineArch() string
|
||||
GetLocalCategory() string
|
||||
GetPrepaidStatus() string
|
||||
GetPostpaidStatus() string
|
||||
GetEngine() string
|
||||
GetEngineVersion() string
|
||||
GetCpuArch() string
|
||||
GetStorageType() string
|
||||
GetMemorySizeMb() int
|
||||
GetPerformanceType() string
|
||||
GetNodeType() string
|
||||
GetDiskSizeGb() int
|
||||
GetShardNum() int
|
||||
GetMaxShardNum() int
|
||||
GetReplicasNum() int
|
||||
GetMaxReplicasNum() int
|
||||
GetMaxClients() int
|
||||
GetMaxConnections() int
|
||||
GetMaxInBandwidthMb() int
|
||||
GetMaxMemoryMb() int
|
||||
GetQps() int
|
||||
}
|
||||
|
||||
type ICloudElasticcache interface {
|
||||
IVirtualResource
|
||||
IBillingResource
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
// 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 guestdrivers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/utils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/billing"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
)
|
||||
|
||||
type SHCSGuestDriver struct {
|
||||
SManagedVirtualizedGuestDriver
|
||||
}
|
||||
|
||||
func init() {
|
||||
driver := SHCSGuestDriver{}
|
||||
models.RegisterGuestDriver(&driver)
|
||||
}
|
||||
|
||||
func (self *SHCSGuestDriver) GetHypervisor() string {
|
||||
return api.HYPERVISOR_HCS
|
||||
}
|
||||
|
||||
func (self *SHCSGuestDriver) GetProvider() string {
|
||||
return api.CLOUD_PROVIDER_HCS
|
||||
}
|
||||
|
||||
func (self *SHCSGuestDriver) DoScheduleSKUFilter() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SHCSGuestDriver) DoScheduleStorageFilter() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SHCSGuestDriver) GetComputeQuotaKeys(scope rbacutils.TRbacScope, ownerId mcclient.IIdentityProvider, brand string) models.SComputeResourceKeys {
|
||||
keys := models.SComputeResourceKeys{}
|
||||
keys.SBaseProjectQuotaKeys = quotas.OwnerIdProjectQuotaKeys(scope, ownerId)
|
||||
keys.CloudEnv = api.CLOUD_ENV_PRIVATE_CLOUD
|
||||
keys.Provider = api.CLOUD_PROVIDER_HCS
|
||||
keys.Brand = api.CLOUD_PROVIDER_HCS
|
||||
keys.Hypervisor = api.HYPERVISOR_HCS
|
||||
return keys
|
||||
}
|
||||
|
||||
func (self *SHCSGuestDriver) GetDefaultSysDiskBackend() string {
|
||||
return api.STORAGE_HUAWEI_SAS
|
||||
}
|
||||
|
||||
func (self *SHCSGuestDriver) GetMinimalSysDiskSizeGb() int {
|
||||
return 40
|
||||
}
|
||||
|
||||
func (self *SHCSGuestDriver) GetStorageTypes() []string {
|
||||
storages, _ := models.StorageManager.GetStorageTypesByHostType(api.HYPERVISOR_HOSTTYPE[self.GetHypervisor()])
|
||||
return storages
|
||||
}
|
||||
|
||||
func (self *SHCSGuestDriver) ChooseHostStorage(host *models.SHost, guest *models.SGuest, diskConfig *api.DiskConfig, storageIds []string) (*models.SStorage, error) {
|
||||
return self.chooseHostStorage(self, host, diskConfig.Backend, storageIds), nil
|
||||
}
|
||||
|
||||
func (self *SHCSGuestDriver) GetDetachDiskStatus() ([]string, error) {
|
||||
return []string{api.VM_READY, api.VM_RUNNING}, nil
|
||||
}
|
||||
|
||||
func (self *SHCSGuestDriver) GetAttachDiskStatus() ([]string, error) {
|
||||
return []string{api.VM_READY, api.VM_RUNNING}, nil
|
||||
}
|
||||
|
||||
func (self *SHCSGuestDriver) GetRebuildRootStatus() ([]string, error) {
|
||||
return []string{api.VM_READY, api.VM_RUNNING}, nil
|
||||
}
|
||||
|
||||
func (self *SHCSGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
|
||||
return []string{api.VM_READY}, nil
|
||||
}
|
||||
|
||||
func (self *SHCSGuestDriver) GetDeployStatus() ([]string, error) {
|
||||
return []string{api.VM_READY, api.VM_RUNNING}, nil
|
||||
}
|
||||
|
||||
func (self *SHCSGuestDriver) ValidateResizeDisk(guest *models.SGuest, disk *models.SDisk, storage *models.SStorage) error {
|
||||
if !utils.IsInStringArray(guest.Status, []string{api.VM_RUNNING, api.VM_READY}) {
|
||||
return fmt.Errorf("Cannot resize disk when guest in status %s", guest.Status)
|
||||
}
|
||||
if !utils.IsInStringArray(storage.StorageType, []string{api.STORAGE_HUAWEI_SATA, api.STORAGE_HUAWEI_SAS, api.STORAGE_HUAWEI_SSD}) {
|
||||
return fmt.Errorf("Cannot resize disk with unsupported volumes type %s", storage.StorageType)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SHCSGuestDriver) GetGuestInitialStateAfterCreate() string {
|
||||
return api.VM_RUNNING
|
||||
}
|
||||
|
||||
func (self *SHCSGuestDriver) GetGuestInitialStateAfterRebuild() string {
|
||||
return api.VM_RUNNING
|
||||
}
|
||||
|
||||
func (self *SHCSGuestDriver) GetInstanceCapability() cloudprovider.SInstanceCapability {
|
||||
return cloudprovider.SInstanceCapability{
|
||||
Hypervisor: self.GetHypervisor(),
|
||||
Provider: self.GetProvider(),
|
||||
DefaultAccount: cloudprovider.SDefaultAccount{
|
||||
Linux: cloudprovider.SOsDefaultAccount{
|
||||
DefaultAccount: api.VM_DEFAULT_LINUX_LOGIN_USER,
|
||||
},
|
||||
Windows: cloudprovider.SOsDefaultAccount{
|
||||
DefaultAccount: api.VM_DEFAULT_WINDOWS_LOGIN_USER,
|
||||
},
|
||||
},
|
||||
Storages: cloudprovider.Storage{
|
||||
DataDisk: []cloudprovider.StorageInfo{
|
||||
cloudprovider.StorageInfo{StorageType: api.STORAGE_HUAWEI_SSD, MaxSizeGb: 32768, MinSizeGb: 10, StepSizeGb: 1, Resizable: true},
|
||||
cloudprovider.StorageInfo{StorageType: api.STORAGE_HUAWEI_SATA, MaxSizeGb: 32768, MinSizeGb: 10, StepSizeGb: 1, Resizable: true},
|
||||
cloudprovider.StorageInfo{StorageType: api.STORAGE_HUAWEI_SAS, MaxSizeGb: 32768, MinSizeGb: 10, StepSizeGb: 1, Resizable: true},
|
||||
cloudprovider.StorageInfo{StorageType: api.STORAGE_HUAWEI_GPSSD, MaxSizeGb: 32768, MinSizeGb: 10, StepSizeGb: 1, Resizable: true},
|
||||
},
|
||||
SysDisk: []cloudprovider.StorageInfo{
|
||||
cloudprovider.StorageInfo{StorageType: api.STORAGE_HUAWEI_SSD, MaxSizeGb: 1024, MinSizeGb: 40, StepSizeGb: 1, Resizable: false},
|
||||
cloudprovider.StorageInfo{StorageType: api.STORAGE_HUAWEI_SATA, MaxSizeGb: 1024, MinSizeGb: 40, StepSizeGb: 1, Resizable: false},
|
||||
cloudprovider.StorageInfo{StorageType: api.STORAGE_HUAWEI_SAS, MaxSizeGb: 1024, MinSizeGb: 40, StepSizeGb: 1, Resizable: false},
|
||||
cloudprovider.StorageInfo{StorageType: api.STORAGE_HUAWEI_GPSSD, MaxSizeGb: 1024, MinSizeGb: 40, StepSizeGb: 1, Resizable: false},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SHCSGuestDriver) RemoteDeployGuestSyncHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, iVM cloudprovider.ICloudVM) (cloudprovider.ICloudHost, error) {
|
||||
if hostId := iVM.GetIHostId(); len(hostId) > 0 {
|
||||
nh, err := db.FetchByExternalIdAndManagerId(models.HostManager, hostId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
|
||||
return q.Equals("manager_id", host.ManagerId)
|
||||
})
|
||||
if err != nil {
|
||||
log.Debugf("failed to found new hostId(%s) for ivm %s(%s) error: %v", hostId, guest.Name, guest.Id, err)
|
||||
if errors.Cause(err) != sql.ErrNoRows {
|
||||
return nil, errors.Wrap(err, "FetchByExternalIdAndManagerId")
|
||||
}
|
||||
|
||||
// HYPERVISOR_HCS VM被部署到一台全新的宿主机
|
||||
zone, err := host.GetZone()
|
||||
if err != nil {
|
||||
log.Warningf("host %s GetZone: %s", host.GetId(), err)
|
||||
} else {
|
||||
_host, err := models.HostManager.NewFromCloudHost(ctx, userCred, iVM.GetIHost(), host.GetCloudprovider(), zone)
|
||||
if err != nil {
|
||||
log.Warningf("NewFromCloudHost %s: %s", iVM.GetIHostId(), err)
|
||||
} else {
|
||||
host = _host
|
||||
}
|
||||
}
|
||||
} else {
|
||||
host = nh.(*models.SHost)
|
||||
}
|
||||
}
|
||||
|
||||
if host.GetId() != guest.HostId {
|
||||
guest.OnScheduleToHost(ctx, userCred, host.GetId())
|
||||
}
|
||||
|
||||
return host.GetIHost(ctx)
|
||||
}
|
||||
|
||||
func (self *SHCSGuestDriver) IsSupportedBillingCycle(bc billing.SBillingCycle) bool {
|
||||
months := bc.GetMonths()
|
||||
if (months >= 1 && months <= 9) || (months == 12) || (months == 24) || (months == 36) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SHCSGuestDriver) IsNeedInjectPasswordByCloudInit() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SHCSGuestDriver) IsSupportSetAutoRenew() bool {
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// 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 hostdrivers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
)
|
||||
|
||||
type SHCSHostDriver struct {
|
||||
SManagedVirtualizationHostDriver
|
||||
}
|
||||
|
||||
func init() {
|
||||
driver := SHCSHostDriver{}
|
||||
models.RegisterHostDriver(&driver)
|
||||
}
|
||||
|
||||
func (self *SHCSHostDriver) GetHostType() string {
|
||||
return api.HOST_TYPE_HCS
|
||||
}
|
||||
|
||||
func (self *SHCSHostDriver) GetHypervisor() string {
|
||||
return api.HYPERVISOR_HCS
|
||||
}
|
||||
|
||||
// 系统盘必须至少40G
|
||||
func (self *SHCSHostDriver) ValidateDiskSize(storage *models.SStorage, sizeGb int) error {
|
||||
if sizeGb < 1 || sizeGb > 65536 {
|
||||
return fmt.Errorf("The %s disk size must be in the range of 1G ~ 65536GB", storage.StorageType)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SHCSHostDriver) ValidateResetDisk(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, snapshot *models.SSnapshot, guests []models.SGuest, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
if len(guests) >= 1 {
|
||||
if disk.DiskType == api.DISK_TYPE_SYS {
|
||||
for _, g := range guests {
|
||||
if g.Status != api.VM_READY {
|
||||
return nil, httperrors.NewBadRequestError("Server %s must in status ready", g.GetName())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return nil, httperrors.NewBadRequestError("Disk must be detached")
|
||||
}
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
@@ -52,7 +52,7 @@ func (self *SManagedVirtualizationHostDriver) CheckAndSetCacheImage(ctx context.
|
||||
}
|
||||
|
||||
providerName := storageCache.GetProviderName()
|
||||
if utils.IsInStringArray(providerName, []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_UCLOUD}) {
|
||||
if utils.IsInStringArray(providerName, []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HCS, api.CLOUD_PROVIDER_UCLOUD}) {
|
||||
image.OsVersion = input.OsFullVersion
|
||||
}
|
||||
|
||||
|
||||
@@ -1130,6 +1130,33 @@ func syncNATSkus(ctx context.Context, userCred mcclient.TokenCredential, syncRes
|
||||
}
|
||||
}
|
||||
|
||||
func syncCacheSkus(ctx context.Context, userCred mcclient.TokenCredential, syncResults SSyncResultSet, provider *SCloudprovider, localRegion *SCloudregion, remoteRegion cloudprovider.ICloudRegion, syncRange *SSyncRange) {
|
||||
skus, err := func() ([]cloudprovider.ICloudElasticcacheSku, error) {
|
||||
defer syncResults.AddRequestCost(ElasticcacheSkuManager)()
|
||||
return remoteRegion.GetIElasticcacheSkus()
|
||||
}()
|
||||
if err != nil {
|
||||
if errors.Cause(err) == cloudprovider.ErrNotImplemented {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("GetIElasticcacheSkus for region %s failed %s", remoteRegion.GetName(), err)
|
||||
log.Errorf(msg)
|
||||
return
|
||||
}
|
||||
result := func() compare.SyncResult {
|
||||
defer syncResults.AddSqlCost(ElasticcacheSkuManager)()
|
||||
return localRegion.SyncPrivateCloudCacheSkus(ctx, userCred, skus)
|
||||
}()
|
||||
|
||||
syncResults.Add(NasSkuManager, result)
|
||||
|
||||
msg := result.Result()
|
||||
log.Infof("SyncRedisSkus for region %s result: %s", localRegion.Name, msg)
|
||||
if result.IsError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func syncDBInstanceResource(ctx context.Context, userCred mcclient.TokenCredential, syncResults SSyncResultSet, localInstance *SDBInstance, remoteInstance cloudprovider.ICloudDBInstance) {
|
||||
err := syncDBInstanceNetwork(ctx, userCred, syncResults, localInstance, remoteInstance)
|
||||
if err != nil {
|
||||
@@ -1767,6 +1794,9 @@ func syncPublicCloudProviderInfo(
|
||||
if cloudprovider.IsSupportNAT(driver) && syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_NAT) {
|
||||
syncNATSkus(ctx, userCred, syncResults, provider, localRegion, remoteRegion, syncRange)
|
||||
}
|
||||
if cloudprovider.IsSupportElasticCache(driver) && syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_CACHE) {
|
||||
syncCacheSkus(ctx, userCred, syncResults, provider, localRegion, remoteRegion, syncRange)
|
||||
}
|
||||
}
|
||||
|
||||
// no need to lock public cloud region as cloud region for public cloud is readonly
|
||||
|
||||
@@ -606,11 +606,13 @@ func (manager *SDiskManager) validateDiskOnStorage(diskConfig *api.DiskConfig, s
|
||||
}
|
||||
}
|
||||
|
||||
var guestdriver IGuestDriver = nil
|
||||
if host, _ := storage.GetMasterHost(); host != nil {
|
||||
//公有云磁盘大小检查。
|
||||
if err := host.GetHostDriver().ValidateDiskSize(storage, diskConfig.SizeMb>>10); err != nil {
|
||||
return httperrors.NewInputParameterError("%v", err)
|
||||
}
|
||||
guestdriver = GetDriver(api.HOSTTYPE_HYPERVISOR[host.HostType])
|
||||
}
|
||||
hoststorages := HoststorageManager.Query().SubQuery()
|
||||
hoststorage := make([]SHoststorage, 0)
|
||||
@@ -620,8 +622,10 @@ func (manager *SDiskManager) validateDiskOnStorage(diskConfig *api.DiskConfig, s
|
||||
if len(hoststorage) == 0 {
|
||||
return httperrors.NewInputParameterError("Storage[%s] must attach to a host", storage.Name)
|
||||
}
|
||||
if int64(diskConfig.SizeMb) > storage.GetFreeCapacity() && !storage.IsEmulated {
|
||||
return httperrors.NewInputParameterError("Not enough free space")
|
||||
if guestdriver == nil || guestdriver.DoScheduleStorageFilter() {
|
||||
if int64(diskConfig.SizeMb) > storage.GetFreeCapacity() && !storage.IsEmulated {
|
||||
return httperrors.NewInputParameterError("Not enough free space")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -2191,7 +2195,7 @@ func (self *SDisk) PerformPurge(ctx context.Context, userCred mcclient.TokenCred
|
||||
}
|
||||
|
||||
provider := self.GetCloudprovider()
|
||||
if provider != nil && utils.IsInStringArray(provider.Provider, []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO}) {
|
||||
if provider != nil && utils.IsInStringArray(provider.Provider, []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HCS}) {
|
||||
cnt, err := self.GetSnapshotCount()
|
||||
if err != nil {
|
||||
return nil, httperrors.NewInternalServerError("GetSnapshotCount fail %s", err)
|
||||
@@ -2206,7 +2210,7 @@ func (self *SDisk) PerformPurge(ctx context.Context, userCred mcclient.TokenCred
|
||||
|
||||
func (self *SDisk) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
|
||||
if !jsonutils.QueryBoolean(query, "delete_snapshots", false) {
|
||||
if provider := self.GetCloudprovider(); provider != nil && utils.IsInStringArray(provider.Provider, []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO}) {
|
||||
if provider := self.GetCloudprovider(); provider != nil && utils.IsInStringArray(provider.Provider, []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HCS}) {
|
||||
cnt, err := self.GetSnapshotCount()
|
||||
if err != nil {
|
||||
return httperrors.NewInternalServerError("GetSnapshotCount fail %s", err)
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/tristate"
|
||||
"yunion.io/x/pkg/util/compare"
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
"yunion.io/x/pkg/utils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
@@ -44,6 +45,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/compute/options"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/billing"
|
||||
bc "yunion.io/x/onecloud/pkg/util/billing"
|
||||
"yunion.io/x/onecloud/pkg/util/choices"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
@@ -122,9 +124,6 @@ type SElasticcache struct {
|
||||
// 带宽
|
||||
Bandwidth int `nullable:"true" list:"user" create:"optional"`
|
||||
|
||||
// 安全组
|
||||
SecurityGroupId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"optional" json:"security_group_id"`
|
||||
|
||||
// 内网DNS
|
||||
PrivateDNS string `width:"256" charset:"ascii" nullable:"true" list:"user" create:"optional" json:"private_dns"`
|
||||
|
||||
@@ -779,48 +778,86 @@ func (manager *SElasticcacheManager) getElasticcachesByProviderId(providerId str
|
||||
return instances, nil
|
||||
}
|
||||
|
||||
func (manager *SElasticcacheManager) BatchCreateValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
_data := api.ElasticcacheCreateInput{}
|
||||
err := data.Unmarshal(&_data)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "ElasticcacheCreateInput.Unmarshal")
|
||||
}
|
||||
input, err := manager.validateCreateData(ctx, userCred, ownerId, query, _data)
|
||||
func (manager *SElasticcacheManager) BatchCreateValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input *api.ElasticcacheCreateInput) (*api.ElasticcacheCreateInput, error) {
|
||||
var err error
|
||||
input, err = manager.validateCreateData(ctx, userCred, ownerId, query, input)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "validateCreateData")
|
||||
}
|
||||
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (manager *SElasticcacheManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.ElasticcacheCreateInput) (*jsonutils.JSONDict, error) {
|
||||
func (manager *SElasticcacheManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input *api.ElasticcacheCreateInput) (*api.ElasticcacheCreateInput, error) {
|
||||
return manager.validateCreateData(ctx, userCred, ownerId, query, input)
|
||||
}
|
||||
|
||||
func (manager *SElasticcacheManager) validateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.ElasticcacheCreateInput) (*jsonutils.JSONDict, error) {
|
||||
var region *SCloudregion
|
||||
var provider *SCloudprovider
|
||||
if len(input.Network) > 0 {
|
||||
network, err := db.FetchByIdOrName(NetworkManager, userCred, strings.Split(input.Network, ",")[0])
|
||||
func (manager *SElasticcacheManager) validateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input *api.ElasticcacheCreateInput) (*api.ElasticcacheCreateInput, error) {
|
||||
if len(input.NetworkId) == 0 {
|
||||
return nil, httperrors.NewMissingParameterError("network_id")
|
||||
}
|
||||
networkObj, err := validators.ValidateModel(userCred, NetworkManager, &input.NetworkId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting network failed")
|
||||
}
|
||||
network := networkObj.(*SNetwork)
|
||||
wire, err := network.GetWire()
|
||||
if err != nil {
|
||||
return nil, httperrors.NewGeneralError(errors.Wrapf(err, "GetWire"))
|
||||
}
|
||||
if len(wire.ZoneId) > 0 {
|
||||
input.ZoneId = wire.ZoneId
|
||||
}
|
||||
_, err = validators.ValidateModel(userCred, ZoneManager, &input.ZoneId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vpc, err := network.GetVpc()
|
||||
if err != nil {
|
||||
return nil, httperrors.NewGeneralError(errors.Wrapf(err, "GetVpc"))
|
||||
}
|
||||
input.VpcId = vpc.Id
|
||||
region, err := vpc.GetRegion()
|
||||
if err != nil {
|
||||
return nil, httperrors.NewGeneralError(errors.Wrapf(err, "GetRegion"))
|
||||
}
|
||||
input.CloudregionId = region.Id
|
||||
provider := vpc.GetCloudprovider()
|
||||
input.ManagerId = provider.Id
|
||||
skuObj, err := validators.ValidateModel(userCred, ElasticcacheSkuManager, &input.InstanceType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sku := skuObj.(*SElasticcacheSku)
|
||||
input.Engine = sku.Engine
|
||||
input.EngineVersion = sku.EngineVersion
|
||||
input.InstanceType = sku.InstanceSpec
|
||||
input.NodeType = sku.NodeType
|
||||
input.LocalCategory = sku.LocalCategory
|
||||
|
||||
if len(input.PrivateIp) > 0 {
|
||||
_, err = netutils.NewIPV4Addr(input.PrivateIp)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting network failed")
|
||||
return nil, httperrors.NewInputParameterError("invalid private ip %s", input.PrivateIp)
|
||||
}
|
||||
region, _ = network.(*SNetwork).GetRegion()
|
||||
vpc, _ := network.(*SNetwork).GetVpc()
|
||||
provider = vpc.GetCloudprovider()
|
||||
}
|
||||
|
||||
if region == nil {
|
||||
return nil, fmt.Errorf("getting region failed")
|
||||
if len(input.SecgroupIds) == 0 {
|
||||
input.SecgroupIds = []string{api.SECGROUP_DEFAULT_ID}
|
||||
}
|
||||
|
||||
for i := range input.SecgroupIds {
|
||||
_, err = validators.ValidateModel(userCred, SecurityGroupManager, &input.SecgroupIds[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// postpiad billing cycle
|
||||
if input.BillingType == billing_api.BILLING_TYPE_POSTPAID {
|
||||
_cycle := input.Duration
|
||||
if len(_cycle) > 0 {
|
||||
cycle, err := bc.ParseBillingCycle(_cycle)
|
||||
if len(input.Duration) > 0 {
|
||||
cycle, err := bc.ParseBillingCycle(input.Duration)
|
||||
if err != nil {
|
||||
return nil, httperrors.NewInputParameterError("invalid billing_cycle %s", _cycle)
|
||||
return nil, httperrors.NewInputParameterError("invalid duration %s", input.Duration)
|
||||
}
|
||||
|
||||
tm := time.Time{}
|
||||
@@ -828,9 +865,24 @@ func (manager *SElasticcacheManager) validateCreateData(ctx context.Context, use
|
||||
// .Format("2006-01-02 15:04:05")
|
||||
input.ExpiredAt = cycle.EndAt(tm)
|
||||
}
|
||||
} else if input.BillingType == billing_api.BILLING_TYPE_PREPAID {
|
||||
cycle, err := billing.ParseBillingCycle(input.BillingCycle)
|
||||
if err != nil {
|
||||
return nil, httperrors.NewInputParameterError("invalid billing_cycle %s", input.BillingCycle)
|
||||
}
|
||||
input.BillingCycle = cycle.String()
|
||||
}
|
||||
|
||||
// validate password
|
||||
if len(input.Password) > 0 {
|
||||
err := seclib2.ValidatePassword(input.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if input.ResetPassword {
|
||||
input.Password = seclib2.RandomPassword2(12)
|
||||
}
|
||||
|
||||
var err error
|
||||
input.VirtualResourceCreateInput, err = manager.SVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.VirtualResourceCreateInput)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ValidateCreateData")
|
||||
@@ -861,335 +913,37 @@ func (self *SElasticcache) PostCreate(ctx context.Context, userCred mcclient.Tok
|
||||
log.Errorf("CancelPendingUsage error %s", err)
|
||||
}
|
||||
|
||||
password, _ := data.GetString("password")
|
||||
if reset, _ := data.Bool("reset_password"); reset && len(password) == 0 {
|
||||
password = seclib2.RandomPassword2(12)
|
||||
}
|
||||
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("password", jsonutils.NewString(password))
|
||||
secgroupIds, err := data.Get("secgroup_ids")
|
||||
if err == nil {
|
||||
params.Set("secgroup_ids", secgroupIds)
|
||||
}
|
||||
self.SetStatus(userCred, api.ELASTIC_CACHE_STATUS_DEPLOYING, "")
|
||||
if err := self.StartElasticcacheCreateTask(ctx, userCred, params, ""); err != nil {
|
||||
log.Errorf("Failed to create elastic cache error: %v", err)
|
||||
}
|
||||
input := &api.ElasticcacheCreateInput{}
|
||||
data.Unmarshal(input)
|
||||
self.StartElasticcacheCreateTask(ctx, userCred, input, "")
|
||||
}
|
||||
|
||||
func (self *SElasticcache) StartElasticcacheCreateTask(ctx context.Context, userCred mcclient.TokenCredential, params *jsonutils.JSONDict, parentTaskId string) error {
|
||||
task, err := taskman.TaskManager.NewTask(ctx, "ElasticcacheCreateTask", self, userCred, params, parentTaskId, "", nil)
|
||||
func (self *SElasticcache) StartElasticcacheCreateTask(ctx context.Context, userCred mcclient.TokenCredential, input *api.ElasticcacheCreateInput, parentTaskId string) {
|
||||
self.SetStatus(userCred, api.ELASTIC_CACHE_STATUS_DEPLOYING, "")
|
||||
params := jsonutils.Marshal(input).(*jsonutils.JSONDict)
|
||||
err := func() error {
|
||||
task, err := taskman.TaskManager.NewTask(ctx, "ElasticcacheCreateTask", self, userCred, params, parentTaskId, "", nil)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "NewTask")
|
||||
}
|
||||
return task.ScheduleRun(nil)
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
self.SetStatus(userCred, api.ELASTIC_CACHE_STATUS_CREATE_FAILED, err.Error())
|
||||
}
|
||||
task.ScheduleRun(nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetSlaveZones() ([]SZone, error) {
|
||||
zones := []SZone{}
|
||||
if len(self.SlaveZones) > 0 {
|
||||
zones := []SZone{}
|
||||
sz := strings.Split(self.SlaveZones, ",")
|
||||
err := ZoneManager.Query().In("id", sz).All(&zones)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetZones")
|
||||
}
|
||||
|
||||
zoneMap := map[string]SZone{}
|
||||
for i := range zones {
|
||||
zoneMap[zones[i].GetId()] = zones[i]
|
||||
}
|
||||
|
||||
ret := make([]SZone, len(sz))
|
||||
for i := range sz {
|
||||
z, ok := zoneMap[sz[i]]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("zone %s is not found", sz[i])
|
||||
}
|
||||
|
||||
ret[i] = z
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
return zones, nil
|
||||
}
|
||||
|
||||
return []SZone{}, nil
|
||||
}
|
||||
|
||||
/*func (self *SElasticcache) GetIRegion() (cloudprovider.ICloudRegion, error) {
|
||||
provider, err := self.GetDriver()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("No cloudprovider for elastic cache %s: %s", self.Name, err)
|
||||
}
|
||||
region := self.GetRegion()
|
||||
if region == nil {
|
||||
return nil, fmt.Errorf("failed to find region for elastic cache %s", self.Name)
|
||||
}
|
||||
return provider.GetIRegionById(region.ExternalId)
|
||||
}*/
|
||||
|
||||
func (self *SElasticcache) GetCreateAliyunElasticcacheParams(ctx context.Context, data *jsonutils.JSONDict) (*cloudprovider.SCloudElasticCacheInput, error) {
|
||||
input := &cloudprovider.SCloudElasticCacheInput{}
|
||||
iregion, err := self.GetIRegion(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("elastic cache %s(%s) region not found", self.Name, self.Id)
|
||||
} else {
|
||||
input.RegionId = iregion.GetId()
|
||||
}
|
||||
|
||||
input.InstanceType = self.InstanceType
|
||||
input.InstanceName = self.GetName()
|
||||
|
||||
if password, _ := data.GetString("password"); len(password) > 0 {
|
||||
input.Password = password
|
||||
}
|
||||
|
||||
input.Engine = strings.Title(self.Engine)
|
||||
input.EngineVersion = self.EngineVersion
|
||||
input.PrivateIpAddress = self.PrivateIpAddr
|
||||
|
||||
zone, _ := self.GetZone()
|
||||
if zone != nil {
|
||||
izone, err := iregion.GetIZoneById(zone.ExternalId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "elasticcache.GetCreateAliyunElasticcacheParams.Zone")
|
||||
}
|
||||
input.ZoneIds = []string{izone.GetId()}
|
||||
}
|
||||
|
||||
switch self.BillingType {
|
||||
case billing_api.BILLING_TYPE_PREPAID:
|
||||
input.ChargeType = "PrePaid"
|
||||
billingCycle, err := bc.ParseBillingCycle(self.BillingCycle)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "elasticcache.GetCreateAliyunElasticcacheParams.BillingCycle")
|
||||
}
|
||||
billingCycle.AutoRenew = self.AutoRenew
|
||||
input.BC = &billingCycle
|
||||
default:
|
||||
input.ChargeType = "PostPaid"
|
||||
}
|
||||
|
||||
// todo: fix me
|
||||
if len(self.NodeType) > 0 {
|
||||
switch self.NodeType {
|
||||
case "single":
|
||||
input.NodeType = "STAND_ALONE"
|
||||
case "double":
|
||||
input.NodeType = "MASTER_SLAVE"
|
||||
default:
|
||||
input.NodeType = ""
|
||||
}
|
||||
}
|
||||
|
||||
switch self.NetworkType {
|
||||
case api.LB_NETWORK_TYPE_CLASSIC:
|
||||
input.NetworkType = "CLASSIC"
|
||||
default:
|
||||
input.NetworkType = "VPC"
|
||||
}
|
||||
|
||||
if ivpc, err := db.FetchById(VpcManager, self.VpcId); err != nil {
|
||||
return nil, errors.Wrap(err, "elasticcache.GetCreateAliyunElasticcacheParams.Vpc")
|
||||
} else {
|
||||
if ivpc != nil {
|
||||
vpc := ivpc.(*SVpc)
|
||||
input.VpcId = vpc.ExternalId
|
||||
}
|
||||
}
|
||||
|
||||
if inetwork, err := db.FetchById(NetworkManager, self.NetworkId); err != nil {
|
||||
return nil, errors.Wrap(err, "elasticcache.GetCreateAliyunElasticcacheParams.Network")
|
||||
} else {
|
||||
if inetwork != nil {
|
||||
network := inetwork.(*SNetwork)
|
||||
input.NetworkId = network.ExternalId
|
||||
}
|
||||
}
|
||||
input.Tags, _ = self.GetAllUserMetadata()
|
||||
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetCreateHuaweiElasticcacheParams(ctx context.Context, data *jsonutils.JSONDict) (*cloudprovider.SCloudElasticCacheInput, error) {
|
||||
input := &cloudprovider.SCloudElasticCacheInput{}
|
||||
iregion, err := self.GetIRegion(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("elastic cache %s(%s) region not found", self.Name, self.Id)
|
||||
} else {
|
||||
input.RegionId = iregion.GetId()
|
||||
}
|
||||
|
||||
if self.CapacityMB > 0 {
|
||||
input.CapacityGB = int64(self.CapacityMB / 1024)
|
||||
}
|
||||
|
||||
input.InstanceType = self.InstanceType
|
||||
input.InstanceName = self.GetName()
|
||||
|
||||
if password, _ := data.GetString("password"); len(password) > 0 {
|
||||
input.Password = password
|
||||
}
|
||||
|
||||
switch self.Engine {
|
||||
case "redis":
|
||||
input.Engine = "Redis"
|
||||
case "memcache":
|
||||
input.Engine = "Memcached"
|
||||
}
|
||||
input.EngineVersion = self.EngineVersion
|
||||
input.PrivateIpAddress = self.PrivateIpAddr
|
||||
|
||||
zone, _ := self.GetZone()
|
||||
if zone != nil {
|
||||
izone, err := iregion.GetIZoneById(zone.ExternalId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "elasticcache.GetCreateHuaweiElasticcacheParams.Zone")
|
||||
}
|
||||
input.ZoneIds = []string{izone.GetId()}
|
||||
}
|
||||
|
||||
switch self.BillingType {
|
||||
case billing_api.BILLING_TYPE_PREPAID:
|
||||
input.ChargeType = "PrePaid"
|
||||
billingCycle, err := bc.ParseBillingCycle(self.BillingCycle)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "elasticcache.GetCreateHuaweiElasticcacheParams.BillingCycle")
|
||||
}
|
||||
billingCycle.AutoRenew = self.AutoRenew
|
||||
input.BC = &billingCycle
|
||||
default:
|
||||
input.ChargeType = "PostPaid"
|
||||
}
|
||||
|
||||
if len(self.NodeType) > 0 {
|
||||
input.NodeType = self.NodeType
|
||||
}
|
||||
|
||||
switch self.NetworkType {
|
||||
case api.LB_NETWORK_TYPE_CLASSIC:
|
||||
input.NetworkType = "CLASSIC"
|
||||
default:
|
||||
input.NetworkType = "VPC"
|
||||
}
|
||||
|
||||
if ivpc, err := db.FetchById(VpcManager, self.VpcId); err != nil {
|
||||
return nil, errors.Wrap(err, "elasticcache.GetCreateHuaweiElasticcacheParams.Vpc")
|
||||
} else {
|
||||
if ivpc != nil {
|
||||
vpc := ivpc.(*SVpc)
|
||||
input.VpcId = vpc.ExternalId
|
||||
}
|
||||
}
|
||||
|
||||
if inetwork, err := db.FetchById(NetworkManager, self.NetworkId); err != nil {
|
||||
return nil, errors.Wrap(err, "elasticcache.GetCreateHuaweiElasticcacheParams.Network")
|
||||
} else {
|
||||
if inetwork != nil {
|
||||
network := inetwork.(*SNetwork)
|
||||
input.NetworkId = network.ExternalId
|
||||
}
|
||||
}
|
||||
|
||||
// fill security group here
|
||||
if len(self.SecurityGroupId) > 0 {
|
||||
region, _ := self.GetRegion()
|
||||
sgCache, err := SecurityGroupCacheManager.GetSecgroupCache(context.Background(), nil, self.SecurityGroupId, self.VpcId, region.Id, self.GetCloudprovider().Id, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "elasticcache.GetCreateHuaweiElasticcacheParams.SecurityGroup")
|
||||
}
|
||||
|
||||
if sgCache == nil {
|
||||
return nil, errors.Wrap(fmt.Errorf("cached security group not found"), "elasticcache.GetCreateHuaweiElasticcacheParams.SecurityGroup")
|
||||
}
|
||||
|
||||
input.SecurityGroupIds = []string{sgCache.GetExternalId()}
|
||||
}
|
||||
|
||||
if len(self.MaintainEndTime) > 0 {
|
||||
input.MaintainBegin = self.MaintainStartTime
|
||||
input.MaintainEnd = self.MaintainEndTime
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetCreateQCloudElasticcacheParams(ctx context.Context, data *jsonutils.JSONDict) (*cloudprovider.SCloudElasticCacheInput, error) {
|
||||
input := &cloudprovider.SCloudElasticCacheInput{}
|
||||
iregion, err := self.GetIRegion(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("elastic cache %s(%s) region not found", self.Name, self.Id)
|
||||
} else {
|
||||
input.RegionId = iregion.GetId()
|
||||
}
|
||||
|
||||
input.InstanceType = self.InstanceType
|
||||
input.InstanceName = self.GetName()
|
||||
|
||||
if password, _ := data.GetString("password"); len(password) > 0 {
|
||||
input.Password = password
|
||||
}
|
||||
|
||||
zone, _ := self.GetZone()
|
||||
if zone != nil {
|
||||
zones := []SZone{*zone}
|
||||
// slave zones
|
||||
sz, err := self.GetSlaveZones()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetSlaveZones")
|
||||
}
|
||||
|
||||
zones = append(zones, sz...)
|
||||
for i := range zones {
|
||||
izone, err := iregion.GetIZoneById(zones[i].ExternalId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "elasticcache.GetCreateHuaweiElasticcacheParams.Zone")
|
||||
}
|
||||
input.ZoneIds = append(input.ZoneIds, izone.GetId())
|
||||
}
|
||||
}
|
||||
|
||||
switch self.BillingType {
|
||||
case billing_api.BILLING_TYPE_PREPAID:
|
||||
input.ChargeType = "PrePaid"
|
||||
billingCycle, err := bc.ParseBillingCycle(self.BillingCycle)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "elasticcache.GetCreateHuaweiElasticcacheParams.BillingCycle")
|
||||
}
|
||||
billingCycle.AutoRenew = self.AutoRenew
|
||||
input.BC = &billingCycle
|
||||
default:
|
||||
input.ChargeType = "PostPaid"
|
||||
}
|
||||
|
||||
switch self.NetworkType {
|
||||
case api.LB_NETWORK_TYPE_CLASSIC:
|
||||
input.NetworkType = "CLASSIC"
|
||||
default:
|
||||
input.NetworkType = "VPC"
|
||||
}
|
||||
|
||||
if ivpc, err := db.FetchById(VpcManager, self.VpcId); err != nil {
|
||||
return nil, errors.Wrap(err, "elasticcache.GetCreateHuaweiElasticcacheParams.Vpc")
|
||||
} else {
|
||||
if ivpc != nil {
|
||||
vpc := ivpc.(*SVpc)
|
||||
input.VpcId = vpc.ExternalId
|
||||
}
|
||||
}
|
||||
|
||||
if inetwork, err := db.FetchById(NetworkManager, self.NetworkId); err != nil {
|
||||
return nil, errors.Wrap(err, "elasticcache.GetCreateHuaweiElasticcacheParams.Network")
|
||||
} else {
|
||||
if inetwork != nil {
|
||||
network := inetwork.(*SNetwork)
|
||||
input.NetworkId = network.ExternalId
|
||||
}
|
||||
}
|
||||
|
||||
input.Tags, _ = self.GetAllUserMetadata()
|
||||
|
||||
return input, nil
|
||||
return zones, nil
|
||||
}
|
||||
|
||||
func (self *SElasticcache) PerformRestart(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
@@ -1983,30 +1737,14 @@ func (self *SElasticcache) validateSecgroupInput(secgroups []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func CheckingSecgroupIds(ctx context.Context, userCred mcclient.TokenCredential, secgroupIds []string) ([]string, error) {
|
||||
secgroupNames := []string{}
|
||||
for _, secgroupId := range secgroupIds {
|
||||
secgrp, err := SecurityGroupManager.FetchByIdOrName(userCred, secgroupId)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == sql.ErrNoRows {
|
||||
return nil, httperrors.NewResourceNotFoundError2("secgroup", secgroupId)
|
||||
}
|
||||
return nil, httperrors.NewGeneralError(errors.Wrapf(err, "FetchByIdOrName(%s)", secgroupId))
|
||||
}
|
||||
|
||||
err = SecurityGroupManager.ValidateName(secgrp.GetName())
|
||||
if err != nil {
|
||||
return nil, httperrors.NewInputParameterError("The secgroup name %s does not meet the requirements, please change the name", secgrp.GetName())
|
||||
}
|
||||
|
||||
secgroupNames = append(secgroupNames, secgrp.GetName())
|
||||
}
|
||||
|
||||
return secgroupNames, nil
|
||||
}
|
||||
|
||||
func (self *SElasticcache) checkingSecgroupIds(ctx context.Context, userCred mcclient.TokenCredential, secgroupIds []string) ([]string, error) {
|
||||
return CheckingSecgroupIds(ctx, userCred, secgroupIds)
|
||||
for i := range secgroupIds {
|
||||
_, err := validators.ValidateModel(userCred, SecurityGroupManager, &secgroupIds[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return secgroupIds, nil
|
||||
}
|
||||
|
||||
// 返回 本次更新后secgroup id的全集、本次更增的secgroup id, 本次删除的secgroup id, error
|
||||
|
||||
@@ -25,12 +25,14 @@ import (
|
||||
"yunion.io/x/pkg/util/compare"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
"yunion.io/x/onecloud/pkg/apis/billing"
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/policy"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/validators"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
@@ -683,3 +685,94 @@ func (manager *SElasticcacheSkuManager) PerformSyncSkus(ctx context.Context, use
|
||||
func (manager *SElasticcacheSkuManager) GetPropertySyncTasks(ctx context.Context, userCred mcclient.TokenCredential, query api.SkuTaskQueryInput) (jsonutils.JSONObject, error) {
|
||||
return GetPropertySkusSyncTasks(ctx, userCred, query)
|
||||
}
|
||||
|
||||
func (self *SCloudregion) SyncPrivateCloudCacheSkus(ctx context.Context, userCred mcclient.TokenCredential, iskus []cloudprovider.ICloudElasticcacheSku) compare.SyncResult {
|
||||
lockman.LockRawObject(ctx, self.Id, ElasticcacheSkuManager.Keyword())
|
||||
defer lockman.ReleaseRawObject(ctx, self.Id, ElasticcacheSkuManager.Keyword())
|
||||
|
||||
result := compare.SyncResult{}
|
||||
|
||||
dbSkus, err := self.GetElasticcacheSkus()
|
||||
if err != nil {
|
||||
result.Error(err)
|
||||
return result
|
||||
}
|
||||
|
||||
removed := make([]SElasticcacheSku, 0)
|
||||
commondb := make([]SElasticcacheSku, 0)
|
||||
commonext := make([]cloudprovider.ICloudElasticcacheSku, 0)
|
||||
added := make([]cloudprovider.ICloudElasticcacheSku, 0)
|
||||
|
||||
err = compare.CompareSets(dbSkus, iskus, &removed, &commondb, &commonext, &added)
|
||||
if err != nil {
|
||||
result.Error(err)
|
||||
return result
|
||||
}
|
||||
|
||||
for i := 0; i < len(removed); i += 1 {
|
||||
err = removed[i].Delete(ctx, userCred)
|
||||
if err != nil {
|
||||
result.DeleteError(err)
|
||||
continue
|
||||
}
|
||||
result.Delete()
|
||||
}
|
||||
for i := 0; i < len(added); i += 1 {
|
||||
err = self.newFromCloudElasticcacheSku(ctx, userCred, added[i])
|
||||
if err != nil {
|
||||
result.AddError(err)
|
||||
} else {
|
||||
result.Add()
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (self *SCloudregion) newFromCloudElasticcacheSku(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudElasticcacheSku) error {
|
||||
sku := &SElasticcacheSku{}
|
||||
sku.SetModelManager(ElasticcacheSkuManager, sku)
|
||||
sku.Name = ext.GetName()
|
||||
sku.InstanceSpec = ext.GetName()
|
||||
sku.Status = apis.SKU_STATUS_AVAILABLE
|
||||
sku.CloudregionId = self.Id
|
||||
sku.ExternalId = ext.GetGlobalId()
|
||||
sku.Provider = self.Provider
|
||||
sku.EngineArch = ext.GetEngineArch()
|
||||
sku.LocalCategory = ext.GetLocalCategory()
|
||||
sku.PrepaidStatus = ext.GetPrepaidStatus()
|
||||
sku.PostpaidStatus = ext.GetPostpaidStatus()
|
||||
sku.Engine = ext.GetEngine()
|
||||
sku.EngineVersion = ext.GetEngineVersion()
|
||||
sku.CpuArch = ext.GetCpuArch()
|
||||
sku.StorageType = ext.GetStorageType()
|
||||
sku.MemorySizeMB = ext.GetMemorySizeMb()
|
||||
sku.PerformanceType = ext.GetPerformanceType()
|
||||
sku.NodeType = ext.GetNodeType()
|
||||
sku.DiskSizeGB = ext.GetDiskSizeGb()
|
||||
sku.ShardNum = ext.GetShardNum()
|
||||
sku.MaxShardNum = ext.GetMaxShardNum()
|
||||
sku.ReplicasNum = ext.GetReplicasNum()
|
||||
sku.MaxReplicasNum = ext.GetMaxReplicasNum()
|
||||
sku.MaxClients = ext.GetMaxClients()
|
||||
sku.MaxConnections = ext.GetMaxConnections()
|
||||
sku.MaxInBandwidthMb = ext.GetMaxInBandwidthMb()
|
||||
sku.MaxMemoryMB = ext.GetMaxMemoryMb()
|
||||
sku.QPS = ext.GetQps()
|
||||
|
||||
zones, err := self.GetZones()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetZones")
|
||||
}
|
||||
zoneId := ext.GetZoneId()
|
||||
slaveZoneId := ext.GetSlaveZoneId()
|
||||
for i := range zones {
|
||||
if len(zoneId) > 0 && strings.HasSuffix(zones[i].ExternalId, zoneId) {
|
||||
sku.ZoneId = zones[i].Id
|
||||
}
|
||||
if len(slaveZoneId) > 0 && strings.HasSuffix(zones[i].ExternalId, slaveZoneId) {
|
||||
sku.SlaveZoneId = zones[i].Id
|
||||
}
|
||||
}
|
||||
|
||||
return ElasticcacheSkuManager.TableSpec().Insert(ctx, sku)
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ func (man *SCachedLoadbalancerAclManager) ValidateCreateData(ctx context.Context
|
||||
}
|
||||
}
|
||||
|
||||
if utils.IsInStringArray(providerV.Model.(*SCloudprovider).Provider, []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO}) {
|
||||
if utils.IsInStringArray(providerV.Model.(*SCloudprovider).Provider, []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HCS}) {
|
||||
listenerV := validators.NewModelIdOrNameValidator("listener", "loadbalancerlistener", ownerId)
|
||||
if err := listenerV.Validate(data); err != nil {
|
||||
return nil, err
|
||||
@@ -307,7 +307,7 @@ func (self *SCachedLoadbalancerAcl) syncRemoveCloudLoadbalanceAcl(ctx context.Co
|
||||
func (acl *SCachedLoadbalancerAcl) SyncWithCloudLoadbalancerAcl(ctx context.Context, userCred mcclient.TokenCredential, extAcl cloudprovider.ICloudLoadbalancerAcl, projectId mcclient.IIdentityProvider) error {
|
||||
diff, err := db.UpdateWithLock(ctx, acl, func() error {
|
||||
// todo: 华为云acl没有name字段应此不需要同步名称
|
||||
if !utils.IsInStringArray(acl.GetProviderName(), []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO}) {
|
||||
if !utils.IsInStringArray(acl.GetProviderName(), []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HCS}) {
|
||||
acl.Name = extAcl.GetName()
|
||||
} else {
|
||||
ext_listener_id := extAcl.GetAclListenerID()
|
||||
@@ -343,7 +343,7 @@ func (man *SCachedLoadbalancerAclManager) GetOrCreateCachedAcl(ctx context.Conte
|
||||
defer lockman.ReleaseClass(ctx, man, ownerProjId)
|
||||
|
||||
listenerId := ""
|
||||
if utils.IsInStringArray(lblis.GetProviderName(), []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO}) {
|
||||
if utils.IsInStringArray(lblis.GetProviderName(), []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HCS}) {
|
||||
listenerId = lblis.Id
|
||||
}
|
||||
if lblis.GetProviderName() == api.CLOUD_PROVIDER_OPENSTACK {
|
||||
|
||||
@@ -724,7 +724,7 @@ func (lbr *SLoadbalancerListenerRule) constructFieldsFromCloudListenerRule(userC
|
||||
}
|
||||
|
||||
if groupId := extRule.GetBackendGroupId(); len(groupId) > 0 {
|
||||
if utils.IsInStringArray(lbr.GetProviderName(), []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO}) {
|
||||
if utils.IsInStringArray(lbr.GetProviderName(), []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HCS}) {
|
||||
group, err := db.FetchByExternalId(HuaweiCachedLbbgManager, groupId)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
@@ -769,7 +769,7 @@ func (lbr *SLoadbalancerListenerRule) updateCachedLoadbalancerBackendGroupAssoci
|
||||
}
|
||||
|
||||
switch lbr.GetProviderName() {
|
||||
case api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO:
|
||||
case api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HCS:
|
||||
_group, err := db.FetchByExternalId(HuaweiCachedLbbgManager, exteralLbbgId)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
|
||||
@@ -386,7 +386,7 @@ func (man *SLoadbalancerListenerManager) ValidateAcl(aclStatusV *validators.Vali
|
||||
return httperrors.NewMissingParameterError("acl_type")
|
||||
}
|
||||
} else {
|
||||
if !utils.IsInStringArray(providerName, []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO}) {
|
||||
if !utils.IsInStringArray(providerName, []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HCS}) {
|
||||
data.Set("acl_id", jsonutils.NewString(""))
|
||||
data.Set("cached_acl_id", jsonutils.NewString(""))
|
||||
}
|
||||
@@ -1014,7 +1014,7 @@ func (lblis *SLoadbalancerListener) constructFieldsFromCloudListener(userCred mc
|
||||
|
||||
switch lblis.ListenerType {
|
||||
case api.LB_LISTENER_TYPE_UDP:
|
||||
if !utils.IsInStringArray(lblis.GetProviderName(), []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO}) {
|
||||
if !utils.IsInStringArray(lblis.GetProviderName(), []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HCS}) {
|
||||
lblis.HealthCheckExp = extListener.GetHealthCheckExp()
|
||||
lblis.HealthCheckReq = extListener.GetHealthCheckReq()
|
||||
}
|
||||
@@ -1057,7 +1057,7 @@ func (lblis *SLoadbalancerListener) constructFieldsFromCloudListener(userCred mc
|
||||
|
||||
groupId := extListener.GetBackendGroupId()
|
||||
switch lblis.GetProviderName() {
|
||||
case api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO:
|
||||
case api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HCS:
|
||||
if len(groupId) > 0 {
|
||||
group, err := db.FetchByExternalIdAndManagerId(HuaweiCachedLbbgManager, groupId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
|
||||
return q.Equals("manager_id", lb.ManagerId)
|
||||
@@ -1150,7 +1150,7 @@ func (lblis *SLoadbalancerListener) updateCachedLoadbalancerBackendGroupAssociat
|
||||
}
|
||||
|
||||
switch lblis.GetProviderName() {
|
||||
case api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO:
|
||||
case api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HCS:
|
||||
_group, err := db.FetchByExternalIdAndManagerId(HuaweiCachedLbbgManager, exteralLbbgId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
|
||||
return q.Equals("manager_id", managerId)
|
||||
})
|
||||
|
||||
@@ -391,6 +391,8 @@ func (man *SLoadbalancerManager) ValidateCreateData(
|
||||
}
|
||||
vpc := _vpc.(*SVpc)
|
||||
region, _ = vpc.GetRegion()
|
||||
input.ManagerId = vpc.ManagerId
|
||||
input.CloudproviderId = vpc.ManagerId
|
||||
} else if len(input.ZoneId) > 0 {
|
||||
var zone *SZone
|
||||
zone, input.ZoneResourceInput, err = ValidateZoneResourceInput(userCred, input.ZoneResourceInput)
|
||||
@@ -594,7 +596,7 @@ func (lb *SLoadbalancer) GetCreateLoadbalancerParams(ctx context.Context, iRegio
|
||||
params.EgressMbps = lb.EgressMbps
|
||||
}
|
||||
|
||||
if lb.AddressType == api.LB_ADDR_TYPE_INTRANET || utils.IsInStringArray(lb.SManagedResourceBase.GetProviderName(), []string{api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_AWS, api.CLOUD_PROVIDER_QCLOUD}) {
|
||||
if lb.AddressType == api.LB_ADDR_TYPE_INTRANET || utils.IsInStringArray(lb.SManagedResourceBase.GetProviderName(), []string{api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCS, api.CLOUD_PROVIDER_AWS, api.CLOUD_PROVIDER_QCLOUD}) {
|
||||
vpc, err := lb.GetVpc()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -606,7 +608,7 @@ func (lb *SLoadbalancer) GetCreateLoadbalancerParams(ctx context.Context, iRegio
|
||||
params.VpcID = iVpc.GetId()
|
||||
}
|
||||
|
||||
if lb.AddressType == api.LB_ADDR_TYPE_INTRANET || utils.IsInStringArray(lb.SManagedResourceBase.GetProviderName(), []string{api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_AWS}) {
|
||||
if lb.AddressType == api.LB_ADDR_TYPE_INTRANET || utils.IsInStringArray(lb.SManagedResourceBase.GetProviderName(), []string{api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HCS, api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_AWS}) {
|
||||
networks, err := lb.GetNetworks()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find network for lb %s: %s", lb.Name, err)
|
||||
|
||||
@@ -342,7 +342,7 @@ func (self *SMountTarget) ValidateDeleteCondition(ctx context.Context, info json
|
||||
if err != nil {
|
||||
return httperrors.NewGeneralError(errors.Wrapf(err, "GetRegion"))
|
||||
}
|
||||
if utils.IsInStringArray(region.Provider, []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO}) {
|
||||
if utils.IsInStringArray(region.Provider, []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HCS}) {
|
||||
return httperrors.NewNotSupportedError("not allow to delete")
|
||||
}
|
||||
return self.SStatusStandaloneResourceBase.ValidateDeleteCondition(ctx, nil)
|
||||
|
||||
@@ -187,6 +187,16 @@ func (self *SCloudregion) GetNatSkus() ([]SNatSku, error) {
|
||||
return skus, nil
|
||||
}
|
||||
|
||||
func (self *SCloudregion) GetElasticcacheSkus() ([]SElasticcacheSku, error) {
|
||||
skus := []SElasticcacheSku{}
|
||||
q := ElasticcacheSkuManager.Query().Equals("cloudregion_id", self.Id)
|
||||
err := db.FetchModelObjects(NatSkuManager, q, &skus)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "db.FetchModelObjects")
|
||||
}
|
||||
return skus, nil
|
||||
}
|
||||
|
||||
func (self SNatSku) GetGlobalId() string {
|
||||
return self.ExternalId
|
||||
}
|
||||
|
||||
@@ -557,7 +557,7 @@ func (lbbg *SLoadbalancerBackendGroup) purgeCachedlbbg(ctx context.Context, user
|
||||
switch lbbg.GetProviderName() {
|
||||
case api.CLOUD_PROVIDER_AWS:
|
||||
return lbbg.purgeAwsCachedlbbg(ctx, userCred)
|
||||
case api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO:
|
||||
case api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HCS:
|
||||
return lbbg.purgeHuaweiCachedlbbg(ctx, userCred)
|
||||
}
|
||||
|
||||
|
||||
@@ -226,7 +226,7 @@ type IElasticcacheDriver interface {
|
||||
|
||||
AllowCreateElasticcacheBackup(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, elasticcache *SElasticcache) error
|
||||
AllowUpdateElasticcacheAuthMode(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, elasticcache *SElasticcache) error
|
||||
ValidateCreateElasticcacheData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, input api.ElasticcacheCreateInput) (*jsonutils.JSONDict, error)
|
||||
ValidateCreateElasticcacheData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, input *api.ElasticcacheCreateInput) (*api.ElasticcacheCreateInput, error)
|
||||
ValidateCreateElasticcacheAccountData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error)
|
||||
ValidateCreateElasticcacheAclData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error)
|
||||
ValidateCreateElasticcacheBackupData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error)
|
||||
|
||||
@@ -615,7 +615,7 @@ func (self *SSecurityGroupCache) StartSecurityGroupCacheDeleteTask(ctx context.C
|
||||
}
|
||||
|
||||
func (manager *SSecurityGroupCacheManager) InitializeData() error {
|
||||
providerIds := CloudproviderManager.Query("id").In("provider", []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_CTYUN, api.CLOUD_PROVIDER_QCLOUD}).SubQuery()
|
||||
providerIds := CloudproviderManager.Query("id").In("provider", []string{api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HCS, api.CLOUD_PROVIDER_CTYUN, api.CLOUD_PROVIDER_QCLOUD}).SubQuery()
|
||||
|
||||
deprecatedSecgroups := []SSecurityGroupCache{}
|
||||
q := manager.Query().In("manager_id", providerIds).NotEquals("vpc_id", api.NORMAL_VPC_ID)
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
"unicode"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/regutils"
|
||||
"yunion.io/x/pkg/util/secrules"
|
||||
@@ -1066,178 +1065,11 @@ func (self *SAliyunRegionDriver) ValidateDBInstanceAccountPrivilege(ctx context.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SAliyunRegionDriver) ValidateCreateElasticcacheData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, input api.ElasticcacheCreateInput) (*jsonutils.JSONDict, error) {
|
||||
data := input.JSON(input)
|
||||
zoneV := validators.NewModelIdOrNameValidator("zone", "zone", ownerId)
|
||||
networkV := validators.NewModelIdOrNameValidator("network", "network", ownerId)
|
||||
|
||||
// billing cycle
|
||||
billingTypeV := validators.NewStringChoicesValidator("billing_type", choices.NewChoices(billing_api.BILLING_TYPE_PREPAID, billing_api.BILLING_TYPE_POSTPAID))
|
||||
networkTypeV := validators.NewStringChoicesValidator("network_type", choices.NewChoices(api.LB_NETWORK_TYPE_VPC, api.LB_NETWORK_TYPE_CLASSIC)).Default(api.LB_NETWORK_TYPE_VPC).Optional(true)
|
||||
engineV := validators.NewStringChoicesValidator("engine", choices.NewChoices("redis", "memcache"))
|
||||
engineVersionV := validators.NewStringChoicesValidator("engine_version", choices.NewChoices("2.8", "4.0", "5.0"))
|
||||
privateIpV := validators.NewIPv4AddrValidator("private_ip").Optional(true)
|
||||
|
||||
keyV := map[string]validators.IValidator{
|
||||
"zone": zoneV,
|
||||
"billing_type": billingTypeV,
|
||||
"network_type": networkTypeV,
|
||||
"network": networkV,
|
||||
"engine": engineV,
|
||||
"engine_version": engineVersionV,
|
||||
"private_ip": privateIpV,
|
||||
func (self *SAliyunRegionDriver) ValidateCreateElasticcacheData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, input *api.ElasticcacheCreateInput) (*api.ElasticcacheCreateInput, error) {
|
||||
if !utils.IsInStringArray(input.Engine, []string{"redis", "memcache"}) {
|
||||
return nil, httperrors.NewInputParameterError("invalid engine %s", input.Engine)
|
||||
}
|
||||
if err := RunValidators(keyV, data, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// validate password
|
||||
if password, _ := data.GetString("password"); len(password) > 0 {
|
||||
err := seclib2.ValidatePassword(password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// validate sku
|
||||
billingType, _ := data.GetString("billing_type")
|
||||
zone := zoneV.Model.(*models.SZone)
|
||||
network := networkV.Model.(*models.SNetwork)
|
||||
if sku, err := data.GetString("instance_type"); err != nil || len(sku) == 0 {
|
||||
return nil, httperrors.NewMissingParameterError("instance_type")
|
||||
} else {
|
||||
_skuModel, err := db.FetchByIdOrName(models.ElasticcacheSkuManager, userCred, sku)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
skuModel := _skuModel.(*models.SElasticcacheSku)
|
||||
if err := ValidateElasticcacheSku(zone.Id, billingType, skuModel, network); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
data.Set("instance_type", jsonutils.NewString(skuModel.InstanceSpec))
|
||||
data.Set("node_type", jsonutils.NewString(skuModel.NodeType))
|
||||
data.Set("local_category", jsonutils.NewString(skuModel.LocalCategory))
|
||||
}
|
||||
}
|
||||
|
||||
// billing cycle
|
||||
if billingType == billing_api.BILLING_TYPE_PREPAID {
|
||||
billingCycle, err := data.GetString("billing_cycle")
|
||||
if err != nil {
|
||||
return nil, httperrors.NewMissingParameterError("billing_cycle")
|
||||
}
|
||||
|
||||
cycle, err := billing.ParseBillingCycle(billingCycle)
|
||||
if err != nil {
|
||||
return nil, httperrors.NewInputParameterError("invalid billing_cycle %s", billingCycle)
|
||||
}
|
||||
|
||||
data.Set("billing_cycle", jsonutils.NewString(cycle.String()))
|
||||
}
|
||||
|
||||
vpc, _ := network.GetVpc()
|
||||
if vpc == nil {
|
||||
return nil, httperrors.NewNotFoundError("network %s related vpc not found", network.GetId())
|
||||
}
|
||||
data.Set("vpc_id", jsonutils.NewString(vpc.Id))
|
||||
data.Set("manager_id", jsonutils.NewString(vpc.ManagerId))
|
||||
data.Set("cloudregion_id", jsonutils.NewString(zone.GetCloudRegionId()))
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (self *SAliyunRegionDriver) RequestCreateElasticcache(ctx context.Context, userCred mcclient.TokenCredential, ec *models.SElasticcache, task taskman.ITask, data *jsonutils.JSONDict) error {
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
iRegion, err := ec.GetIRegion(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "aliyunRegionDriver.CreateElasticcache.GetIRegion")
|
||||
}
|
||||
|
||||
iprovider, err := db.FetchById(models.CloudproviderManager, ec.GetCloudproviderId())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "aliyunRegionDriver.CreateElasticcache.GetProvider")
|
||||
}
|
||||
|
||||
params, err := ec.GetCreateAliyunElasticcacheParams(ctx, task.GetParams())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "aliyunRegionDriver.CreateElasticcache.GetCreateAliyunElasticcacheParams")
|
||||
}
|
||||
|
||||
provider := iprovider.(*models.SCloudprovider)
|
||||
params.ProjectId, err = provider.SyncProject(ctx, userCred, ec.ProjectId)
|
||||
if err != nil {
|
||||
log.Errorf("failed to sync project %s for create %s elastic cache %s error: %v", ec.ProjectId, provider.Provider, ec.Name, err)
|
||||
}
|
||||
|
||||
iec, err := iRegion.CreateIElasticcaches(params)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "aliyunRegionDriver.CreateElasticcache.CreateIElasticcaches")
|
||||
}
|
||||
|
||||
err = cloudprovider.WaitStatusWithDelay(iec, api.ELASTIC_CACHE_STATUS_RUNNING, 30*time.Second, 15*time.Second, 600*time.Second)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "aliyunRegionDriver.CreateElasticcache.WaitStatusWithDelay")
|
||||
}
|
||||
|
||||
ec.SetModelManager(models.ElasticcacheManager, ec)
|
||||
if err := db.SetExternalId(ec, userCred, iec.GetGlobalId()); err != nil {
|
||||
return nil, errors.Wrap(err, "aliyunRegionDriver.CreateElasticcache.SetExternalId")
|
||||
}
|
||||
|
||||
{
|
||||
// todo: 开启外网访问
|
||||
}
|
||||
|
||||
if err := ec.SyncWithCloudElasticcache(ctx, userCred, provider, iec); err != nil {
|
||||
return nil, errors.Wrap(err, "aliyunRegionDriver.CreateElasticcache.SyncWithCloudElasticcache")
|
||||
}
|
||||
|
||||
// sync accounts
|
||||
{
|
||||
iaccounts, err := iec.GetICloudElasticcacheAccounts()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "aliyunRegionDriver.CreateElasticcache.GetICloudElasticcacheAccounts")
|
||||
}
|
||||
|
||||
result := models.ElasticcacheAccountManager.SyncElasticcacheAccounts(ctx, userCred, ec, iaccounts)
|
||||
log.Debugf("aliyunRegionDriver.CreateElasticcache.SyncElasticcacheAccounts %s", result.Result())
|
||||
|
||||
account, err := ec.GetAdminAccount()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "aliyunRegionDriver.CreateElasticcache.GetAdminAccount")
|
||||
}
|
||||
|
||||
err = account.SavePassword(params.Password)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "aliyunRegionDriver.CreateElasticcache.SavePassword")
|
||||
}
|
||||
}
|
||||
|
||||
// sync acl
|
||||
{
|
||||
iacls, err := iec.GetICloudElasticcacheAcls()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "aliyunRegionDriver.CreateElasticcache.GetICloudElasticcacheAcls")
|
||||
}
|
||||
|
||||
result := models.ElasticcacheAclManager.SyncElasticcacheAcls(ctx, userCred, ec, iacls)
|
||||
log.Debugf("aliyunRegionDriver.CreateElasticcache.SyncElasticcacheAcls %s", result.Result())
|
||||
}
|
||||
|
||||
// sync parameters
|
||||
{
|
||||
iparams, err := iec.GetICloudElasticcacheParameters()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "aliyunRegionDriver.CreateElasticcache.GetICloudElasticcacheParameters")
|
||||
}
|
||||
|
||||
result := models.ElasticcacheParameterManager.SyncElasticcacheParameters(ctx, userCred, ec, iparams)
|
||||
log.Debugf("aliyunRegionDriver.CreateElasticcache.SyncElasticcacheParameters %s", result.Result())
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
return nil
|
||||
return self.SManagedVirtualizationRegionDriver.ValidateCreateElasticcacheData(ctx, userCred, ownerId, input)
|
||||
}
|
||||
|
||||
func (self *SAliyunRegionDriver) ValidateCreateElasticcacheAccountData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
|
||||
@@ -396,6 +396,10 @@ func (self *SBaseRegionDriver) RequestSyncDBInstanceBackupStatus(ctx context.Con
|
||||
return fmt.Errorf("Not Implement RequestSyncDBInstanceBackupStatus")
|
||||
}
|
||||
|
||||
func (self *SBaseRegionDriver) RequestCreateElasticcache(ctx context.Context, userCred mcclient.TokenCredential, ec *models.SElasticcache, task taskman.ITask, data *jsonutils.JSONDict) error {
|
||||
return errors.Wrapf(cloudprovider.ErrNotImplemented, "RequestCreateElasticcache")
|
||||
}
|
||||
|
||||
func (self *SBaseRegionDriver) RequestSyncElasticcacheStatus(ctx context.Context, userCred mcclient.TokenCredential, elasticcache *models.SElasticcache, task taskman.ITask) error {
|
||||
return fmt.Errorf("Not Implement RequestSyncElasticcacheStatus")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package regiondrivers
|
||||
|
||||
import (
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
)
|
||||
|
||||
type SHCSRegionDriver struct {
|
||||
SHuaWeiRegionDriver
|
||||
}
|
||||
|
||||
func init() {
|
||||
driver := SHCSRegionDriver{}
|
||||
models.RegisterRegionDriver(&driver)
|
||||
}
|
||||
|
||||
func (self *SHCSRegionDriver) GetProvider() string {
|
||||
return api.CLOUD_PROVIDER_HCS
|
||||
}
|
||||
|
||||
func (self *SHCSRegionDriver) IsSupportedElasticcache() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SHCSRegionDriver) IsSecurityGroupBelongVpc() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SHCSRegionDriver) IsSupportedElasticcacheSecgroup() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SHCSRegionDriver) GetMaxElasticcacheSecurityGroupCount() int {
|
||||
return 1
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
@@ -29,7 +28,6 @@ import (
|
||||
"yunion.io/x/pkg/utils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
billing_api "yunion.io/x/onecloud/pkg/apis/billing"
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
|
||||
@@ -40,9 +38,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/billing"
|
||||
"yunion.io/x/onecloud/pkg/util/choices"
|
||||
"yunion.io/x/onecloud/pkg/util/rand"
|
||||
"yunion.io/x/onecloud/pkg/util/seclib2"
|
||||
)
|
||||
|
||||
type SHuaWeiRegionDriver struct {
|
||||
@@ -149,6 +145,7 @@ func (self *SHuaWeiRegionDriver) ValidateCreateLoadbalancerData(ctx context.Cont
|
||||
data.Set("network_type", jsonutils.NewString(api.LB_NETWORK_TYPE_VPC))
|
||||
data.Set("cloudregion_id", jsonutils.NewString(region.GetId()))
|
||||
data.Set("vpc_id", jsonutils.NewString(vpc.GetId()))
|
||||
data.Set("manager_id", jsonutils.NewString(vpc.ManagerId))
|
||||
return self.SManagedVirtualizationRegionDriver.ValidateCreateLoadbalancerData(ctx, userCred, ownerId, data)
|
||||
}
|
||||
|
||||
@@ -2345,225 +2342,19 @@ func validatorSlaveZones(ownerId mcclient.IIdentityProvider, regionId string, da
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidateElasticcacheSku(zoneId string, chargeType string, sku *models.SElasticcacheSku, network *models.SNetwork) error {
|
||||
if sku.ZoneId != zoneId {
|
||||
return httperrors.NewResourceNotFoundError("zone mismatch, elastic cache sku zone %s != %s", sku.ZoneId, zoneId)
|
||||
func (self *SHuaWeiRegionDriver) ValidateCreateElasticcacheData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, input *api.ElasticcacheCreateInput) (*api.ElasticcacheCreateInput, error) {
|
||||
if !utils.IsInStringArray(input.Engine, []string{"redis", "memcache"}) {
|
||||
return nil, httperrors.NewInputParameterError("invalid engine %s", input.Engine)
|
||||
}
|
||||
if len(input.MaintainStartTime) > 0 && !utils.IsInStringArray(input.MaintainStartTime, []string{"22:00:00", "02:00:00", "06:00:00", "10:00:00", "14:00:00", "18:00:00"}) {
|
||||
return nil, httperrors.NewInputParameterError("invalid maintain_start_time %s", input.MaintainStartTime)
|
||||
}
|
||||
|
||||
if network != nil {
|
||||
if zone, _ := network.GetZone(); zone != nil && zone.Id != sku.ZoneId {
|
||||
return httperrors.NewResourceNotFoundError("elastic cache sku zone (%s) and subnet zone (%s) mismatch", sku.ZoneId, zone.Id)
|
||||
}
|
||||
if input.CapacityMb == 0 {
|
||||
return nil, httperrors.NewMissingParameterError("capacity_mb")
|
||||
}
|
||||
|
||||
if chargeType == billing_api.BILLING_TYPE_PREPAID {
|
||||
if sku.PrepaidStatus != api.SkuStatusAvailable {
|
||||
return httperrors.NewOutOfResourceError("sku %s is soldout", sku.Name)
|
||||
}
|
||||
} else {
|
||||
if sku.PostpaidStatus != api.SkuStatusAvailable {
|
||||
return httperrors.NewOutOfResourceError("sku %s is soldout", sku.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SHuaWeiRegionDriver) ValidateCreateElasticcacheData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, input api.ElasticcacheCreateInput) (*jsonutils.JSONDict, error) {
|
||||
data := input.JSON(input)
|
||||
zoneV := validators.NewModelIdOrNameValidator("zone", "zone", ownerId)
|
||||
networkV := validators.NewModelIdOrNameValidator("network", "network", ownerId)
|
||||
// secgroupV := validators.NewModelIdOrNameValidator("security_group", "secgroup", ownerId)
|
||||
|
||||
// todo: fix me
|
||||
instanceTypeV := validators.NewModelIdOrNameValidator("instance_type", "elasticcachesku", ownerId)
|
||||
chargeTypeV := validators.NewStringChoicesValidator("billing_type", choices.NewChoices(billing_api.BILLING_TYPE_PREPAID, billing_api.BILLING_TYPE_POSTPAID))
|
||||
networkTypeV := validators.NewStringChoicesValidator("network_type", choices.NewChoices(api.LB_NETWORK_TYPE_VPC, api.LB_NETWORK_TYPE_CLASSIC)).Default(api.LB_NETWORK_TYPE_VPC).Optional(true)
|
||||
engineV := validators.NewStringChoicesValidator("engine", choices.NewChoices("redis", "memcache"))
|
||||
engineVersionV := validators.NewStringChoicesValidator("engine_version", choices.NewChoices("3.0", "4.0", "5.0"))
|
||||
privateIpV := validators.NewIPv4AddrValidator("private_ip").Optional(true)
|
||||
maintainStartTimeV := validators.NewStringChoicesValidator("maintain_start_time", choices.NewChoices("22:00:00", "02:00:00", "06:00:00", "10:00:00", "14:00:00", "18:00:00")).Default("02:00:00").Optional(true)
|
||||
|
||||
keyV := map[string]validators.IValidator{
|
||||
"zone": zoneV,
|
||||
"billing_type": chargeTypeV,
|
||||
"network_type": networkTypeV,
|
||||
"network": networkV,
|
||||
"instance_type": instanceTypeV,
|
||||
// "security_group": secgroupV,
|
||||
"engine": engineV,
|
||||
"engine_version": engineVersionV,
|
||||
"private_ip": privateIpV,
|
||||
"mantain_start_time": maintainStartTimeV,
|
||||
}
|
||||
if err := RunValidators(keyV, data, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// validate password
|
||||
if password, _ := data.GetString("password"); len(password) > 0 {
|
||||
err := seclib2.ValidatePassword(password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// validate sku
|
||||
sku := instanceTypeV.Model.(*models.SElasticcacheSku)
|
||||
zoneId, _ := data.GetString("zone_id")
|
||||
billingType, _ := data.GetString("billing_type")
|
||||
network := networkV.Model.(*models.SNetwork)
|
||||
if err := ValidateElasticcacheSku(zoneId, billingType, sku, network); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
data.Set("instance_type", jsonutils.NewString(sku.InstanceSpec))
|
||||
data.Set("node_type", jsonutils.NewString(sku.NodeType))
|
||||
data.Set("local_category", jsonutils.NewString(sku.LocalCategory))
|
||||
data.Set("capacity_mb", jsonutils.NewInt(int64(sku.MemorySizeMB)))
|
||||
}
|
||||
|
||||
// validate slave zones
|
||||
if len(input.SlaveZones) > 0 {
|
||||
data.Set("slave_zones", jsonutils.NewString(strings.Join(input.SlaveZones, ",")))
|
||||
}
|
||||
if err := validatorSlaveZones(ownerId, zoneV.Model.(*models.SZone).GetCloudRegionId(), data, true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// validate capacity
|
||||
if capacityMB, err := data.Int("capacity_mb"); err != nil {
|
||||
return nil, errors.Wrap(err, "invalid parameter capacity_mb")
|
||||
} else {
|
||||
// todo: fix me
|
||||
// Redis引擎:单机和主备类型实例取值:2、4、8、16、32、64。集群实例规格支持64G、128G、256G、512G和1024G。
|
||||
data.Set("capacity_mb", jsonutils.NewInt(capacityMB))
|
||||
}
|
||||
|
||||
// MaintainEndTime
|
||||
// 开始时间必须为22:00:00、02:00:00、06:00:00、10:00:00、14:00:00和18:00:00。
|
||||
data.Remove("mantain_end_time")
|
||||
if startTime, err := data.GetString("maintain_start_time"); err == nil {
|
||||
maintainTimes := []string{"22:00:00", "02:00:00", "06:00:00", "10:00:00", "14:00:00", "18:00:00", "22:00:00"}
|
||||
|
||||
for i := range maintainTimes {
|
||||
if maintainTimes[i] == startTime {
|
||||
data.Add(jsonutils.NewString(maintainTimes[i+1]), "mantain_end_time")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// billing cycle
|
||||
if billingType == billing_api.BILLING_TYPE_PREPAID {
|
||||
billingCycle, err := data.GetString("billing_cycle")
|
||||
if err != nil {
|
||||
return nil, httperrors.NewMissingParameterError("billing_cycle")
|
||||
}
|
||||
|
||||
cycle, err := billing.ParseBillingCycle(billingCycle)
|
||||
if err != nil {
|
||||
return nil, httperrors.NewInputParameterError("invalid billing_cycle %s", billingCycle)
|
||||
}
|
||||
|
||||
data.Set("billing_cycle", jsonutils.NewString(cycle.String()))
|
||||
}
|
||||
|
||||
vpc, _ := network.GetVpc()
|
||||
if vpc == nil {
|
||||
return nil, httperrors.NewNotFoundError("network %s related vpc not found", network.GetId())
|
||||
}
|
||||
data.Set("vpc_id", jsonutils.NewString(vpc.Id))
|
||||
data.Set("manager_id", jsonutils.NewString(vpc.ManagerId))
|
||||
data.Set("cloudregion_id", jsonutils.NewString(sku.CloudregionId))
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (self *SHuaWeiRegionDriver) RequestCreateElasticcache(ctx context.Context, userCred mcclient.TokenCredential, ec *models.SElasticcache, task taskman.ITask, data *jsonutils.JSONDict) error {
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
iRegion, err := ec.GetIRegion(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "huaweiRegionDriver.CreateElasticcache.GetIRegion")
|
||||
}
|
||||
|
||||
provider := ec.GetCloudprovider()
|
||||
if provider == nil {
|
||||
return nil, errors.Wrap(httperrors.ErrInvalidStatus, "huaweiRegionDriver.CreateElasticcache.GetProvider")
|
||||
}
|
||||
|
||||
params, err := ec.GetCreateHuaweiElasticcacheParams(ctx, task.GetParams())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "huaweiRegionDriver.CreateElasticcache.GetCreateHuaweiElasticcacheParams")
|
||||
}
|
||||
|
||||
iec, err := iRegion.CreateIElasticcaches(params)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "huaweiRegionDriver.CreateElasticcache.CreateIElasticcaches")
|
||||
}
|
||||
|
||||
err = cloudprovider.WaitStatusWithDelay(iec, api.ELASTIC_CACHE_STATUS_RUNNING, 30*time.Second, 15*time.Second, 600*time.Second)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "huaweiRegionDriver.CreateElasticcache.WaitStatusWithDelay")
|
||||
}
|
||||
|
||||
ec.SetModelManager(models.ElasticcacheManager, ec)
|
||||
if err := db.SetExternalId(ec, userCred, iec.GetGlobalId()); err != nil {
|
||||
return nil, errors.Wrap(err, "huaweiRegionDriver.CreateElasticcache.SetExternalId")
|
||||
}
|
||||
|
||||
{
|
||||
// todo: 开启外网访问
|
||||
}
|
||||
|
||||
if err := ec.SyncWithCloudElasticcache(ctx, userCred, provider, iec); err != nil {
|
||||
return nil, errors.Wrap(err, "huaweiRegionDriver.CreateElasticcache.SyncWithCloudElasticcache")
|
||||
}
|
||||
|
||||
// sync accounts
|
||||
{
|
||||
iaccounts, err := iec.GetICloudElasticcacheAccounts()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "huaweiRegionDriver.CreateElasticcache.GetICloudElasticcacheAccounts")
|
||||
}
|
||||
|
||||
result := models.ElasticcacheAccountManager.SyncElasticcacheAccounts(ctx, userCred, ec, iaccounts)
|
||||
log.Debugf("huaweiRegionDriver.CreateElasticcache.SyncElasticcacheAccounts %s", result.Result())
|
||||
|
||||
account, err := ec.GetAdminAccount()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "huaweiRegionDriver.CreateElasticcache.GetAdminAccount")
|
||||
}
|
||||
|
||||
err = account.SavePassword(params.Password)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "huaweiRegionDriver.CreateElasticcache.SavePassword")
|
||||
}
|
||||
}
|
||||
|
||||
// sync acl
|
||||
{
|
||||
iacls, err := iec.GetICloudElasticcacheAcls()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "huaweiRegionDriver.CreateElasticcache.GetICloudElasticcacheAcls")
|
||||
}
|
||||
|
||||
result := models.ElasticcacheAclManager.SyncElasticcacheAcls(ctx, userCred, ec, iacls)
|
||||
log.Debugf("huaweiRegionDriver.CreateElasticcache.SyncElasticcacheAcls %s", result.Result())
|
||||
}
|
||||
|
||||
// sync parameters
|
||||
{
|
||||
iparams, err := iec.GetICloudElasticcacheParameters()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "huaweiRegionDriver.CreateElasticcache.GetICloudElasticcacheParameters")
|
||||
}
|
||||
|
||||
result := models.ElasticcacheParameterManager.SyncElasticcacheParameters(ctx, userCred, ec, iparams)
|
||||
log.Debugf("huaweiRegionDriver.CreateElasticcache.SyncElasticcacheParameters %s", result.Result())
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
return nil
|
||||
return self.SManagedVirtualizationRegionDriver.ValidateCreateElasticcacheData(ctx, userCred, ownerId, input)
|
||||
}
|
||||
|
||||
func (self *SHuaWeiRegionDriver) ValidateCreateElasticcacheAccountData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
|
||||
@@ -1720,12 +1720,8 @@ func (self *SKVMRegionDriver) ValidateCacheSecgroup(ctx context.Context, userCre
|
||||
return fmt.Errorf("No need to cache secgroup for onecloud region")
|
||||
}
|
||||
|
||||
func (self *SKVMRegionDriver) RequestCreateElasticcache(ctx context.Context, userCred mcclient.TokenCredential, elasticcache *models.SElasticcache, task taskman.ITask, data *jsonutils.JSONDict) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SKVMRegionDriver) ValidateCreateElasticcacheData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, input api.ElasticcacheCreateInput) (*jsonutils.JSONDict, error) {
|
||||
return input.JSON(input), nil
|
||||
func (self *SKVMRegionDriver) ValidateCreateElasticcacheData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, input *api.ElasticcacheCreateInput) (*api.ElasticcacheCreateInput, error) {
|
||||
return input, httperrors.NewNotSupportedError("Not support create elasticcache")
|
||||
}
|
||||
|
||||
func (self *SKVMRegionDriver) RequestRestartElasticcache(ctx context.Context, userCred mcclient.TokenCredential, elasticcache *models.SElasticcache, task taskman.ITask) error {
|
||||
|
||||
@@ -1874,20 +1874,158 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateDBInstanceFromBacku
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SManagedVirtualizationRegionDriver) RequestCreateElasticcache(ctx context.Context, userCred mcclient.TokenCredential, elasticcache *models.SElasticcache, task taskman.ITask, data *jsonutils.JSONDict) error {
|
||||
task.ScheduleRun(nil)
|
||||
func (self *SManagedVirtualizationRegionDriver) RequestCreateElasticcache(ctx context.Context, userCred mcclient.TokenCredential, ec *models.SElasticcache, task taskman.ITask, data *jsonutils.JSONDict) error {
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
iRegion, err := ec.GetIRegion(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetIRegion")
|
||||
}
|
||||
|
||||
iprovider, err := db.FetchById(models.CloudproviderManager, ec.GetCloudproviderId())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetProvider")
|
||||
}
|
||||
vpc, err := ec.GetVpc()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetVpc")
|
||||
}
|
||||
|
||||
networkObj, err := db.FetchById(models.NetworkManager, ec.NetworkId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetNetwork")
|
||||
}
|
||||
network := networkObj.(*models.SNetwork)
|
||||
|
||||
params := &cloudprovider.SCloudElasticCacheInput{
|
||||
InstanceType: ec.InstanceType,
|
||||
InstanceName: ec.Name,
|
||||
Engine: ec.Engine,
|
||||
EngineVersion: ec.EngineVersion,
|
||||
PrivateIpAddress: ec.PrivateIpAddr,
|
||||
CapacityGB: int64(ec.CapacityMB / 1024),
|
||||
NodeType: ec.NodeType,
|
||||
NetworkType: ec.NetworkType,
|
||||
VpcId: vpc.ExternalId,
|
||||
NetworkId: network.ExternalId,
|
||||
MaintainBegin: ec.MaintainStartTime,
|
||||
MaintainEnd: ec.MaintainEndTime,
|
||||
}
|
||||
if ec.BillingType == billing_api.BILLING_TYPE_PREPAID {
|
||||
bc, err := billing.ParseBillingCycle(ec.BillingCycle)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "ParseBillingCycle(%s)", ec.BillingCycle)
|
||||
}
|
||||
bc.AutoRenew = ec.AutoRenew
|
||||
params.BillingCycle = &bc
|
||||
}
|
||||
params.Tags, _ = ec.GetAllUserMetadata()
|
||||
|
||||
zone, err := ec.GetZone()
|
||||
if zone != nil {
|
||||
izone, err := iRegion.GetIZoneById(zone.ExternalId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetIZoneById(%s)", zone.ExternalId)
|
||||
}
|
||||
params.ZoneIds = []string{izone.GetId()}
|
||||
}
|
||||
slaveZones, err := ec.GetSlaveZones()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetSlaveZones")
|
||||
}
|
||||
for i := range slaveZones {
|
||||
izone, err := iRegion.GetIZoneById(slaveZones[i].ExternalId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetSlaveZoneBy(%s)", slaveZones[i].ExternalId)
|
||||
}
|
||||
if !utils.IsInStringArray(izone.GetId(), params.ZoneIds) {
|
||||
params.ZoneIds = append(params.ZoneIds, izone.GetId())
|
||||
}
|
||||
}
|
||||
|
||||
params.Password, _ = data.GetString("password")
|
||||
data.Unmarshal(¶ms.SecurityGroupIds, "ext_secgroup_ids")
|
||||
|
||||
provider := iprovider.(*models.SCloudprovider)
|
||||
params.ProjectId, err = provider.SyncProject(ctx, userCred, ec.ProjectId)
|
||||
if err != nil {
|
||||
log.Errorf("failed to sync project %s for create %s elastic cache %s error: %v", ec.ProjectId, provider.Provider, ec.Name, err)
|
||||
}
|
||||
|
||||
iec, err := iRegion.CreateIElasticcaches(params)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "CreateIElasticcaches")
|
||||
}
|
||||
|
||||
err = db.SetExternalId(ec, userCred, iec.GetGlobalId())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SetExternalId")
|
||||
}
|
||||
|
||||
err = cloudprovider.WaitStatusWithDelay(iec, api.ELASTIC_CACHE_STATUS_RUNNING, 30*time.Second, 15*time.Second, 10*time.Minute)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "WaitStatusWithDelay")
|
||||
}
|
||||
|
||||
err = ec.SyncWithCloudElasticcache(ctx, userCred, provider, iec)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SyncWithCloudElasticcache")
|
||||
}
|
||||
|
||||
// sync accounts
|
||||
{
|
||||
iaccounts, err := iec.GetICloudElasticcacheAccounts()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetICloudElasticcacheAccounts")
|
||||
}
|
||||
|
||||
result := models.ElasticcacheAccountManager.SyncElasticcacheAccounts(ctx, userCred, ec, iaccounts)
|
||||
log.Infof("SyncElasticcacheAccounts %s", result.Result())
|
||||
|
||||
account, err := ec.GetAdminAccount()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetAdminAccount")
|
||||
}
|
||||
|
||||
err = account.SavePassword(params.Password)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SavePassword")
|
||||
}
|
||||
}
|
||||
|
||||
// sync acl
|
||||
{
|
||||
iacls, err := iec.GetICloudElasticcacheAcls()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetICloudElasticcacheAcls")
|
||||
}
|
||||
|
||||
result := models.ElasticcacheAclManager.SyncElasticcacheAcls(ctx, userCred, ec, iacls)
|
||||
log.Infof("SyncElasticcacheAcls %s", result.Result())
|
||||
}
|
||||
|
||||
// sync parameters
|
||||
{
|
||||
iparams, err := iec.GetICloudElasticcacheParameters()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetICloudElasticcacheParameters")
|
||||
}
|
||||
|
||||
result := models.ElasticcacheParameterManager.SyncElasticcacheParameters(ctx, userCred, ec, iparams)
|
||||
log.Infof("SyncElasticcacheParameters %s", result.Result())
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SManagedVirtualizationRegionDriver) ValidateCreateElasticcacheData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, input api.ElasticcacheCreateInput) (*jsonutils.JSONDict, error) {
|
||||
m := jsonutils.NewDict()
|
||||
m.Set("manager_id", jsonutils.NewString(input.ManagerId))
|
||||
_, err := self.ValidateManagerId(ctx, userCred, m)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "ValidateManagerId")
|
||||
func (self *SManagedVirtualizationRegionDriver) ValidateCreateElasticcacheData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, input *api.ElasticcacheCreateInput) (*api.ElasticcacheCreateInput, error) {
|
||||
if len(input.ManagerId) == 0 {
|
||||
return nil, httperrors.NewMissingParameterError("manager_id")
|
||||
}
|
||||
|
||||
return input.JSON(input), nil
|
||||
if len(input.NetworkType) == 0 {
|
||||
input.NetworkType = api.LB_NETWORK_TYPE_VPC
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (self *SManagedVirtualizationRegionDriver) RequestRestartElasticcache(ctx context.Context, userCred mcclient.TokenCredential, ec *models.SElasticcache, task taskman.ITask) error {
|
||||
@@ -2994,7 +3132,47 @@ func (self *SManagedVirtualizationRegionDriver) RequestRemoteUpdateElasticcache(
|
||||
}
|
||||
|
||||
func (self *SManagedVirtualizationRegionDriver) RequestSyncSecgroupsForElasticcache(ctx context.Context, userCred mcclient.TokenCredential, ec *models.SElasticcache, task taskman.ITask) error {
|
||||
return fmt.Errorf("Not Implement RequestSyncSecgroupsForElasticcache")
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
// sync secgroups to cloud
|
||||
secgroupExternalIds := []string{}
|
||||
{
|
||||
ess, err := ec.GetElasticcacheSecgroups()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetElasticcacheSecgroups")
|
||||
}
|
||||
|
||||
provider := ec.GetCloudprovider()
|
||||
if provider == nil {
|
||||
return nil, errors.Wrap(httperrors.ErrInvalidStatus, "GetCloudprovider")
|
||||
}
|
||||
|
||||
vpc, err := ec.GetVpc()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetVpc")
|
||||
}
|
||||
region, err := vpc.GetRegion()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetRegion")
|
||||
}
|
||||
vpcId, err := self.GetSecurityGroupVpcId(ctx, userCred, region, nil, vpc, false)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetSecurityGroupVpcId")
|
||||
}
|
||||
|
||||
for i := range ess {
|
||||
externalId, err := self.RequestSyncSecurityGroup(ctx, task.GetUserCred(), vpcId, vpc, ess[i].GetSecGroup(), "", "redis", true)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "RequestSyncSecurityGroup")
|
||||
}
|
||||
secgroupExternalIds = append(secgroupExternalIds, externalId)
|
||||
}
|
||||
}
|
||||
|
||||
ret := jsonutils.NewDict()
|
||||
ret.Set("ext_secgroup_ids", jsonutils.NewStringArray(secgroupExternalIds))
|
||||
return ret, nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SManagedVirtualizationRegionDriver) RequestRenewElasticcache(ctx context.Context, userCred mcclient.TokenCredential, ec *models.SElasticcache, bc billing.SBillingCycle) (time.Time, error) {
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
@@ -29,7 +28,6 @@ import (
|
||||
"yunion.io/x/pkg/utils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
billing_api "yunion.io/x/onecloud/pkg/apis/billing"
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
@@ -1481,203 +1479,18 @@ func (self *SQcloudRegionDriver) GetMaxElasticcacheSecurityGroupCount() int {
|
||||
return 10
|
||||
}
|
||||
|
||||
func (self *SQcloudRegionDriver) ValidateCreateElasticcacheData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, input api.ElasticcacheCreateInput) (*jsonutils.JSONDict, error) {
|
||||
data := input.JSON(input)
|
||||
zoneV := validators.NewModelIdOrNameValidator("zone", "zone", ownerId)
|
||||
networkV := validators.NewModelIdOrNameValidator("network", "network", ownerId)
|
||||
instanceTypeV := validators.NewModelIdOrNameValidator("instance_type", "elasticcachesku", ownerId)
|
||||
chargeTypeV := validators.NewStringChoicesValidator("billing_type", choices.NewChoices(billing_api.BILLING_TYPE_PREPAID, billing_api.BILLING_TYPE_POSTPAID))
|
||||
networkTypeV := validators.NewStringChoicesValidator("network_type", choices.NewChoices(api.LB_NETWORK_TYPE_VPC)).Default(api.LB_NETWORK_TYPE_VPC).Optional(true)
|
||||
engineVersionV := validators.NewStringChoicesValidator("engine_version", choices.NewChoices("2.8", "3.0", "3.2", "4.0", "5.0"))
|
||||
|
||||
keyV := map[string]validators.IValidator{
|
||||
"zone": zoneV,
|
||||
"billing_type": chargeTypeV,
|
||||
"network_type": networkTypeV,
|
||||
"network": networkV,
|
||||
"instance_type": instanceTypeV,
|
||||
"engine_version": engineVersionV,
|
||||
func (self *SQcloudRegionDriver) ValidateCreateElasticcacheData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, input *api.ElasticcacheCreateInput) (*api.ElasticcacheCreateInput, error) {
|
||||
if len(input.NetworkType) == 0 {
|
||||
input.NetworkType = api.LB_NETWORK_TYPE_VPC
|
||||
}
|
||||
if err := RunValidators(keyV, data, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// validate password
|
||||
if password, _ := data.GetString("password"); len(password) > 0 {
|
||||
err := seclib2.ValidatePassword(password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
zoneId, _ := data.GetString("zone_id")
|
||||
billingType, _ := data.GetString("billing_type")
|
||||
// validate sku
|
||||
sku := instanceTypeV.Model.(*models.SElasticcacheSku)
|
||||
network := networkV.Model.(*models.SNetwork)
|
||||
if err := ValidateElasticcacheSku(zoneId, billingType, sku, network); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
data.Set("instance_type", jsonutils.NewString(sku.InstanceSpec))
|
||||
data.Set("node_type", jsonutils.NewString(sku.NodeType))
|
||||
data.Set("local_category", jsonutils.NewString(sku.LocalCategory))
|
||||
data.Set("capacity_mb", jsonutils.NewInt(int64(sku.MemorySizeMB)))
|
||||
}
|
||||
|
||||
// validate slave zones
|
||||
if len(input.SlaveZones) > 0 {
|
||||
if len(input.SlaveZones) < sku.ReplicasNum {
|
||||
padding := make([]string, sku.ReplicasNum-len(input.SlaveZones))
|
||||
for i := range padding {
|
||||
padding[i] = zoneId
|
||||
}
|
||||
|
||||
input.SlaveZones = append(input.SlaveZones, padding...)
|
||||
}
|
||||
|
||||
data.Set("slave_zones", jsonutils.NewString(strings.Join(input.SlaveZones, ",")))
|
||||
}
|
||||
if err := validatorSlaveZones(ownerId, zoneV.Model.(*models.SZone).GetCloudRegionId(), data, true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sz, _ := data.GetString("slave_zones")
|
||||
if len(strings.Split(sz, ",")) > sku.ReplicasNum {
|
||||
return nil, fmt.Errorf("the number of slave zones can not beyond redis replicas number")
|
||||
}
|
||||
|
||||
// validate secgroups
|
||||
secgroups := []string{}
|
||||
err := data.Unmarshal(&secgroups, "secgroup_ids")
|
||||
if err != nil {
|
||||
log.Debugf("Unmarshal.security_groups %s", err)
|
||||
data.Set("secgroup_ids", jsonutils.NewArray(jsonutils.NewString(api.SECGROUP_DEFAULT_ID)))
|
||||
secgroups = []string{api.SECGROUP_DEFAULT_ID}
|
||||
}
|
||||
|
||||
if len(secgroups) == 0 || len(secgroups) > 10 {
|
||||
return nil, errors.Wrap(err, "secgroups id quantity should between 1 and 10.")
|
||||
}
|
||||
|
||||
_, err = models.CheckingSecgroupIds(ctx, userCred, secgroups)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "CheckingSecgroupIds")
|
||||
}
|
||||
|
||||
// billing cycle
|
||||
if billingType == billing_api.BILLING_TYPE_PREPAID {
|
||||
billingCycle, err := data.GetString("billing_cycle")
|
||||
if err != nil {
|
||||
return nil, httperrors.NewMissingParameterError("billing_cycle")
|
||||
}
|
||||
|
||||
cycle, err := billing.ParseBillingCycle(billingCycle)
|
||||
if err != nil {
|
||||
return nil, httperrors.NewInputParameterError("invalid billing_cycle %s", billingCycle)
|
||||
}
|
||||
|
||||
data.Set("billing_cycle", jsonutils.NewString(cycle.String()))
|
||||
}
|
||||
|
||||
vpc, _ := network.GetVpc()
|
||||
if vpc == nil {
|
||||
return nil, httperrors.NewNotFoundError("network %s related vpc not found", network.GetId())
|
||||
}
|
||||
data.Set("engine", jsonutils.NewString("redis"))
|
||||
data.Set("vpc_id", jsonutils.NewString(vpc.Id))
|
||||
data.Set("manager_id", jsonutils.NewString(vpc.ManagerId))
|
||||
data.Set("cloudregion_id", jsonutils.NewString(vpc.CloudregionId))
|
||||
return data, nil
|
||||
input.Engine = "redis"
|
||||
return self.SManagedVirtualizationRegionDriver.ValidateCreateElasticcacheData(ctx, userCred, ownerId, input)
|
||||
}
|
||||
|
||||
func (self *SQcloudRegionDriver) IsSupportedElasticcache() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SQcloudRegionDriver) RequestCreateElasticcache(ctx context.Context, userCred mcclient.TokenCredential, ec *models.SElasticcache, task taskman.ITask, data *jsonutils.JSONDict) error {
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
iRegion, err := ec.GetIRegion(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "qcloudRegionDriver.CreateElasticcache.GetIRegion")
|
||||
}
|
||||
|
||||
provider := ec.GetCloudprovider()
|
||||
if provider == nil {
|
||||
return nil, errors.Wrap(httperrors.ErrInvalidStatus, "qcloudRegionDriver.CreateElasticcache.GetProvider")
|
||||
}
|
||||
|
||||
secgroups := []string{}
|
||||
err = data.Unmarshal(&secgroups, "ext_secgroup_ids")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Unmarshal.ext_secgroup_ids")
|
||||
}
|
||||
|
||||
params, err := ec.GetCreateQCloudElasticcacheParams(ctx, task.GetParams())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "qcloudRegionDriver.CreateElasticcache.GetCreateHuaweiElasticcacheParams")
|
||||
}
|
||||
params.SecurityGroupIds = secgroups
|
||||
params.ProjectId, err = provider.SyncProject(ctx, userCred, ec.ProjectId)
|
||||
if err != nil {
|
||||
log.Errorf("failed to sync project %s for create %s elastic cache %s error: %v", ec.ProjectId, provider.Provider, ec.Name, err)
|
||||
}
|
||||
|
||||
iec, err := iRegion.CreateIElasticcaches(params)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "qcloudRegionDriver.CreateElasticcache.CreateIElasticcaches")
|
||||
}
|
||||
|
||||
err = cloudprovider.WaitStatusWithDelay(iec, api.ELASTIC_CACHE_STATUS_RUNNING, 30*time.Second, 15*time.Second, 900*time.Second)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "qcloudRegionDriver.CreateElasticcache.WaitStatusWithDelay")
|
||||
}
|
||||
|
||||
ec.SetModelManager(models.ElasticcacheManager, ec)
|
||||
if err := db.SetExternalId(ec, userCred, iec.GetGlobalId()); err != nil {
|
||||
return nil, errors.Wrap(err, "qcloudRegionDriver.CreateElasticcache.SetExternalId")
|
||||
}
|
||||
|
||||
if err := ec.SyncWithCloudElasticcache(ctx, userCred, provider, iec); err != nil {
|
||||
return nil, errors.Wrap(err, "qcloudRegionDriver.CreateElasticcache.SyncWithCloudElasticcache")
|
||||
}
|
||||
|
||||
// sync accounts
|
||||
{
|
||||
iaccounts, err := iec.GetICloudElasticcacheAccounts()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "qcloudRegionDriver.CreateElasticcache.GetICloudElasticcacheAccounts")
|
||||
}
|
||||
|
||||
result := models.ElasticcacheAccountManager.SyncElasticcacheAccounts(ctx, userCred, ec, iaccounts)
|
||||
log.Debugf("qcloudRegionDriver.CreateElasticcache.SyncElasticcacheAccounts %s", result.Result())
|
||||
|
||||
account, err := ec.GetAdminAccount()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "qcloudRegionDriver.CreateElasticcache.GetAdminAccount")
|
||||
}
|
||||
|
||||
err = account.SavePassword(params.Password)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "qcloudRegionDriver.CreateElasticcache.SavePassword")
|
||||
}
|
||||
}
|
||||
|
||||
// sync parameters
|
||||
{
|
||||
iparams, err := iec.GetICloudElasticcacheParameters()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "qcloudRegionDriver.CreateElasticcache.GetICloudElasticcacheParameters")
|
||||
}
|
||||
|
||||
result := models.ElasticcacheParameterManager.SyncElasticcacheParameters(ctx, userCred, ec, iparams)
|
||||
log.Debugf("qcloudRegionDriver.CreateElasticcache.SyncElasticcacheParameters %s", result.Result())
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SQcloudRegionDriver) GetSecurityGroupPublicScope(service string) rbacutils.TRbacScope {
|
||||
if service == "redis" {
|
||||
return rbacutils.ScopeProject
|
||||
@@ -1685,49 +1498,6 @@ func (self *SQcloudRegionDriver) GetSecurityGroupPublicScope(service string) rba
|
||||
return rbacutils.ScopeSystem
|
||||
}
|
||||
|
||||
func (self *SQcloudRegionDriver) RequestSyncSecgroupsForElasticcache(ctx context.Context, userCred mcclient.TokenCredential, ec *models.SElasticcache, task taskman.ITask) error {
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
// sync secgroups to cloud
|
||||
secgroupExternalIds := []string{}
|
||||
{
|
||||
ess, err := ec.GetElasticcacheSecgroups()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "qcloudRegionDriver.GetElasticcacheSecgroups")
|
||||
}
|
||||
|
||||
provider := ec.GetCloudprovider()
|
||||
if provider == nil {
|
||||
return nil, errors.Wrap(httperrors.ErrInvalidStatus, "qcloudRegionDriver.GetCloudprovider")
|
||||
}
|
||||
|
||||
extProjectId, err := provider.SyncProject(ctx, userCred, ec.ProjectId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to sync project %s for create %s elastic cache %s error: %v", ec.ProjectId, provider.Provider, ec.Name, err)
|
||||
}
|
||||
|
||||
vpc, _ := ec.GetVpc()
|
||||
region, _ := vpc.GetRegion()
|
||||
vpcId, err := self.GetSecurityGroupVpcId(ctx, userCred, region, nil, vpc, false)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetSecurityGroupVpcId")
|
||||
}
|
||||
|
||||
for i := range ess {
|
||||
externalId, err := self.RequestSyncSecurityGroup(ctx, task.GetUserCred(), vpcId, vpc, ess[i].GetSecGroup(), extProjectId, "redis", true)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "RequestSyncSecurityGroup")
|
||||
}
|
||||
secgroupExternalIds = append(secgroupExternalIds, externalId)
|
||||
}
|
||||
}
|
||||
|
||||
ret := jsonutils.NewDict()
|
||||
ret.Set("ext_secgroup_ids", jsonutils.NewStringArray(secgroupExternalIds))
|
||||
return ret, nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SQcloudRegionDriver) ValidateCreateElasticcacheAccountData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
|
||||
elasticCacheV := validators.NewModelIdOrNameValidator("elasticcache", "elasticcache", ownerId)
|
||||
accountTypeV := validators.NewStringChoicesValidator("account_type", choices.NewChoices("normal")).Default("normal")
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
@@ -36,23 +37,23 @@ func init() {
|
||||
taskman.RegisterTask(ElasticcacheCreateTask{})
|
||||
}
|
||||
|
||||
func (self *ElasticcacheCreateTask) taskFail(ctx context.Context, elasticcache *models.SElasticcache, reason jsonutils.JSONObject) {
|
||||
elasticcache.SetStatus(self.GetUserCred(), api.ELASTIC_CACHE_STATUS_CREATE_FAILED, reason.String())
|
||||
db.OpsLog.LogEvent(elasticcache, db.ACT_ALLOCATE_FAIL, reason, self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, elasticcache, logclient.ACT_CREATE, reason, self.UserCred, false)
|
||||
func (self *ElasticcacheCreateTask) taskFail(ctx context.Context, elasticcache *models.SElasticcache, err error) {
|
||||
elasticcache.SetStatus(self.GetUserCred(), api.ELASTIC_CACHE_STATUS_CREATE_FAILED, err.Error())
|
||||
db.OpsLog.LogEvent(elasticcache, db.ACT_ALLOCATE_FAIL, err, self.UserCred)
|
||||
logclient.AddActionLogWithStartable(self, elasticcache, logclient.ACT_CREATE, err, self.UserCred, false)
|
||||
notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{
|
||||
Obj: elasticcache,
|
||||
Action: notifyclient.ActionCreate,
|
||||
IsFail: true,
|
||||
})
|
||||
self.SetStageFailed(ctx, reason)
|
||||
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
|
||||
}
|
||||
|
||||
func (self *ElasticcacheCreateTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
|
||||
elasticcache := obj.(*models.SElasticcache)
|
||||
region, _ := elasticcache.GetRegion()
|
||||
if region == nil {
|
||||
self.taskFail(ctx, elasticcache, jsonutils.NewString(fmt.Sprintf("failed to find region for elastic cache %s", elasticcache.GetName())))
|
||||
region, err := elasticcache.GetRegion()
|
||||
if err != nil {
|
||||
self.taskFail(ctx, elasticcache, errors.Wrapf(err, "GetRegion"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -60,22 +61,16 @@ func (self *ElasticcacheCreateTask) OnInit(ctx context.Context, obj db.IStandalo
|
||||
self.SetStage("OnSyncSecurityGroupComplete", data.(*jsonutils.JSONDict))
|
||||
if region.GetDriver().IsSupportedElasticcacheSecgroup() {
|
||||
secgroups := []string{}
|
||||
err := self.GetParams().Unmarshal(&secgroups, "secgroup_ids")
|
||||
if err != nil {
|
||||
notes := fmt.Sprintf("Unmarshal.secgroup_ids %s", err)
|
||||
self.taskFail(ctx, elasticcache, jsonutils.NewString(notes))
|
||||
return
|
||||
}
|
||||
self.GetParams().Unmarshal(&secgroups, "secgroup_ids")
|
||||
secgroupInput := api.ElasticcacheSecgroupsInput{SecgroupIds: secgroups}
|
||||
_, err = elasticcache.ProcessElasticcacheSecgroupsInput(ctx, self.UserCred, "set", &secgroupInput)
|
||||
if err != nil {
|
||||
notes := fmt.Sprintf("ProcessElasticcacheSecgroupsInput %s", err)
|
||||
self.taskFail(ctx, elasticcache, jsonutils.NewString(notes))
|
||||
self.taskFail(ctx, elasticcache, errors.Wrapf(err, "ProcessElasticcacheSecgroupsInput"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := region.GetDriver().RequestSyncSecgroupsForElasticcache(ctx, self.UserCred, elasticcache, self); err != nil {
|
||||
self.taskFail(ctx, elasticcache, jsonutils.NewString(err.Error()))
|
||||
self.taskFail(ctx, elasticcache, errors.Wrapf(err, "RequestSyncSecgroupsForElasticcache"))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
@@ -84,22 +79,26 @@ func (self *ElasticcacheCreateTask) OnInit(ctx context.Context, obj db.IStandalo
|
||||
}
|
||||
|
||||
func (self *ElasticcacheCreateTask) OnSyncSecurityGroupComplete(ctx context.Context, elasticcache *models.SElasticcache, data jsonutils.JSONObject) {
|
||||
region, _ := elasticcache.GetRegion()
|
||||
region, err := elasticcache.GetRegion()
|
||||
if err != nil {
|
||||
self.taskFail(ctx, elasticcache, errors.Wrapf(err, "GetRegion"))
|
||||
return
|
||||
}
|
||||
self.SetStage("OnElasticcacheCreateComplete", nil)
|
||||
if err := region.GetDriver().RequestCreateElasticcache(ctx, self.GetUserCred(), elasticcache, self, data.(*jsonutils.JSONDict)); err != nil {
|
||||
self.taskFail(ctx, elasticcache, jsonutils.NewString(err.Error()))
|
||||
err = region.GetDriver().RequestCreateElasticcache(ctx, self.GetUserCred(), elasticcache, self, data.(*jsonutils.JSONDict))
|
||||
if err != nil {
|
||||
self.taskFail(ctx, elasticcache, errors.Wrapf(err, "RequestCreateElasticcache"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (self *ElasticcacheCreateTask) OnSyncSecurityGroupCompleteFailed(ctx context.Context, elasticcache *models.SElasticcache, reason jsonutils.JSONObject) {
|
||||
self.taskFail(ctx, elasticcache, reason)
|
||||
self.taskFail(ctx, elasticcache, fmt.Errorf(reason.String()))
|
||||
}
|
||||
|
||||
func (self *ElasticcacheCreateTask) OnElasticcacheCreateComplete(ctx context.Context, elasticcache *models.SElasticcache, data jsonutils.JSONObject) {
|
||||
elasticcache.SetStatus(self.GetUserCred(), api.ELASTIC_CACHE_STATUS_RUNNING, "")
|
||||
logclient.AddActionLogWithStartable(self, elasticcache, logclient.ACT_CREATE, "", self.UserCred, true)
|
||||
// notifyclient.NotifyWebhook(ctx, self.UserCred, elasticcache, notifyclient.ActionCreate)
|
||||
notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{
|
||||
Obj: elasticcache,
|
||||
Action: notifyclient.ActionCreate,
|
||||
@@ -108,5 +107,5 @@ func (self *ElasticcacheCreateTask) OnElasticcacheCreateComplete(ctx context.Con
|
||||
}
|
||||
|
||||
func (self *ElasticcacheCreateTask) OnElasticcacheCreateCompleteFailed(ctx context.Context, elasticcache *models.SElasticcache, reason jsonutils.JSONObject) {
|
||||
self.taskFail(ctx, elasticcache, reason)
|
||||
self.taskFail(ctx, elasticcache, fmt.Errorf(reason.String()))
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
@@ -35,9 +36,9 @@ func init() {
|
||||
taskman.RegisterTask(ElasticcacheSyncsecgroupsTask{})
|
||||
}
|
||||
|
||||
func (self *ElasticcacheSyncsecgroupsTask) taskFailed(ctx context.Context, cache *models.SElasticcache, err jsonutils.JSONObject) {
|
||||
cache.SetStatus(self.GetUserCred(), api.ELASTIC_CACHE_STATUS_SYNC_FAILED, err.String())
|
||||
self.SetStageFailed(ctx, err)
|
||||
func (self *ElasticcacheSyncsecgroupsTask) taskFailed(ctx context.Context, cache *models.SElasticcache, err error) {
|
||||
cache.SetStatus(self.GetUserCred(), api.ELASTIC_CACHE_STATUS_SYNC_FAILED, err.Error())
|
||||
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
|
||||
db.OpsLog.LogEvent(cache, db.ACT_SYNC_CONF, cache.GetShortDesc(ctx), self.GetUserCred())
|
||||
logclient.AddActionLogWithContext(ctx, cache, logclient.ACT_SYNC_CONF, err, self.UserCred, false)
|
||||
}
|
||||
@@ -45,16 +46,16 @@ func (self *ElasticcacheSyncsecgroupsTask) taskFailed(ctx context.Context, cache
|
||||
func (self *ElasticcacheSyncsecgroupsTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
|
||||
cache := obj.(*models.SElasticcache)
|
||||
|
||||
region, _ := cache.GetRegion()
|
||||
if region == nil {
|
||||
self.taskFailed(ctx, cache, jsonutils.NewString(fmt.Sprintf("failed to found cloudregion for elasticcache %s(%s)", cache.Name, cache.Id)))
|
||||
region, err := cache.GetRegion()
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, cache, errors.Wrapf(err, "GetRegion"))
|
||||
return
|
||||
}
|
||||
|
||||
self.SetStage("OnElasticcacheSyncSecgroupsComplete", nil)
|
||||
err := region.GetDriver().RequestSyncSecgroupsForElasticcache(ctx, self.GetUserCred(), cache, self)
|
||||
err = region.GetDriver().RequestSyncSecgroupsForElasticcache(ctx, self.GetUserCred(), cache, self)
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, cache, jsonutils.NewString(err.Error()))
|
||||
self.taskFailed(ctx, cache, errors.Wrapf(err, "RequestSyncSecgroupsForElasticcache"))
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -65,25 +66,25 @@ func (self *ElasticcacheSyncsecgroupsTask) OnElasticcacheSyncSecgroupsComplete(c
|
||||
secgroups := []string{}
|
||||
err := data.Unmarshal(&secgroups, "ext_secgroup_ids")
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, cache, jsonutils.NewString(err.Error()))
|
||||
self.taskFailed(ctx, cache, errors.Wrapf(err, "get ext_secgroup_ids"))
|
||||
return
|
||||
}
|
||||
|
||||
iregion, err := cache.GetIRegion(ctx)
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, cache, jsonutils.NewString(err.Error()))
|
||||
self.taskFailed(ctx, cache, errors.Wrapf(err, "GetIRegion"))
|
||||
return
|
||||
}
|
||||
|
||||
iec, err := iregion.GetIElasticcacheById(cache.GetExternalId())
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, cache, jsonutils.NewString(err.Error()))
|
||||
self.taskFailed(ctx, cache, errors.Wrapf(err, "GetIElasticcacheById"))
|
||||
return
|
||||
}
|
||||
|
||||
err = iec.UpdateSecurityGroups(secgroups)
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, cache, jsonutils.NewString(err.Error()))
|
||||
self.taskFailed(ctx, cache, errors.Wrapf(err, "UpdateSecurityGroups"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -92,5 +93,5 @@ func (self *ElasticcacheSyncsecgroupsTask) OnElasticcacheSyncSecgroupsComplete(c
|
||||
}
|
||||
|
||||
func (self *ElasticcacheSyncsecgroupsTask) OnElasticcacheSyncSecgroupsCompleteFailed(ctx context.Context, cache *models.SElasticcache, data jsonutils.JSONObject) {
|
||||
self.taskFailed(ctx, cache, data)
|
||||
self.taskFailed(ctx, cache, fmt.Errorf(data.String()))
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ func init() {
|
||||
|
||||
func getOnLoadbalancerListenerCreateCompleteFunc(provider string) func(ctx context.Context, lblis *models.SLoadbalancerListener, data jsonutils.JSONObject, self *LoadbalancerListenerCreateTask) {
|
||||
switch provider {
|
||||
case api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO:
|
||||
case api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HCS:
|
||||
return onHuaweiLoadbalancerListenerCreateComplete
|
||||
case api.CLOUD_PROVIDER_OPENSTACK:
|
||||
return onOpenstackLoadbalancerListenerCreateComplete
|
||||
|
||||
@@ -37,7 +37,7 @@ func init() {
|
||||
|
||||
func getOnPrepareLoadbalancerBackendgroupFunc(provider string) func(ctx context.Context, region *models.SCloudregion, lbr *models.SLoadbalancerListenerRule, data jsonutils.JSONObject, self *LoadbalancerListenerRuleCreateTask) {
|
||||
switch provider {
|
||||
case api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO:
|
||||
case api.CLOUD_PROVIDER_HUAWEI, api.CLOUD_PROVIDER_HCSO, api.CLOUD_PROVIDER_HCS:
|
||||
return onHuaiweiPrepareLoadbalancerBackendgroup
|
||||
case api.CLOUD_PROVIDER_AWS:
|
||||
return onAwsPrepareLoadbalancerBackendgroup
|
||||
|
||||
@@ -306,6 +306,18 @@ func (opts *SHCSOAccountCreateOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return params, nil
|
||||
}
|
||||
|
||||
type SHCSAccountCreateOptions struct {
|
||||
SCloudAccountCreateBaseOptions
|
||||
AuthURL string `help:"Hcs auth_url" positional:"true" json:"auth_url"`
|
||||
SAccessKeyCredential
|
||||
}
|
||||
|
||||
func (opts *SHCSAccountCreateOptions) Params() (jsonutils.JSONObject, error) {
|
||||
params := jsonutils.Marshal(opts)
|
||||
params.(*jsonutils.JSONDict).Add(jsonutils.NewString("HCS"), "provider")
|
||||
return params, nil
|
||||
}
|
||||
|
||||
type SUcloudCloudAccountCreateOptions struct {
|
||||
SCloudAccountCreateBaseOptions
|
||||
SAccessKeyCredential
|
||||
@@ -505,6 +517,15 @@ func (opts *SHCSOAccountUpdateCredentialOptions) Params() (jsonutils.JSONObject,
|
||||
return jsonutils.Marshal(opts), nil
|
||||
}
|
||||
|
||||
type SHCSAccountUpdateCredentialOptions struct {
|
||||
SCloudAccountIdOptions
|
||||
SAccessKeyCredential
|
||||
}
|
||||
|
||||
func (opts *SHCSAccountUpdateCredentialOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.Marshal(opts), nil
|
||||
}
|
||||
|
||||
type SUcloudCloudAccountUpdateCredentialOptions struct {
|
||||
SCloudAccountIdOptions
|
||||
SAccessKeyCredential
|
||||
@@ -854,6 +875,14 @@ func (opts *SHCSOAccountUpdateOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.Marshal(opts), nil
|
||||
}
|
||||
|
||||
type SHCSAccountUpdateOptions struct {
|
||||
SCloudAccountUpdateBaseOptions
|
||||
}
|
||||
|
||||
func (opts *SHCSAccountUpdateOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.Marshal(opts), nil
|
||||
}
|
||||
|
||||
type SUcloudCloudAccountUpdateOptions struct {
|
||||
SCloudAccountUpdateBaseOptions
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
|
||||
billingapi "yunion.io/x/onecloud/pkg/apis/billing"
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
@@ -642,7 +641,12 @@ func (self *SRegion) CreateIElasticcaches(ec *cloudprovider.SCloudElasticCacheIn
|
||||
}
|
||||
|
||||
if len(ec.NodeType) > 0 {
|
||||
params["NodeType"] = ec.NodeType
|
||||
switch ec.NodeType {
|
||||
case "single":
|
||||
params["NodeType"] = "STAND_ALONE"
|
||||
case "double":
|
||||
params["NodeType"] = "MASTER_SLAVE"
|
||||
}
|
||||
}
|
||||
|
||||
if len(ec.ProjectId) > 0 {
|
||||
@@ -652,25 +656,24 @@ func (self *SRegion) CreateIElasticcaches(ec *cloudprovider.SCloudElasticCacheIn
|
||||
params["NetworkType"] = ec.NetworkType
|
||||
params["VpcId"] = ec.VpcId
|
||||
params["VSwitchId"] = ec.NetworkId
|
||||
params["ChargeType"] = ec.ChargeType
|
||||
if strings.ToLower(ec.ChargeType) == billingapi.BILLING_TYPE_PREPAID && ec.BC != nil {
|
||||
if ec.BC.GetMonths() >= 1 && ec.BC.GetMonths() <= 9 {
|
||||
params["Period"] = strconv.Itoa(ec.BC.GetMonths())
|
||||
} else if ec.BC.GetMonths() == 12 || ec.BC.GetMonths() == 24 || ec.BC.GetMonths() == 36 {
|
||||
params["Period"] = strconv.Itoa(ec.BC.GetMonths())
|
||||
params["ChargeType"] = "PostPaid"
|
||||
if ec.BillingCycle != nil {
|
||||
params["ChargeType"] = "PrePaid"
|
||||
if ec.BillingCycle.GetMonths() >= 1 && ec.BillingCycle.GetMonths() <= 9 {
|
||||
params["Period"] = strconv.Itoa(ec.BillingCycle.GetMonths())
|
||||
} else if ec.BillingCycle.GetMonths() == 12 || ec.BillingCycle.GetMonths() == 24 || ec.BillingCycle.GetMonths() == 36 {
|
||||
params["Period"] = strconv.Itoa(ec.BillingCycle.GetMonths())
|
||||
} else {
|
||||
return nil, fmt.Errorf("region.CreateIElasticcaches invalid billing cycle.reqired month (1~9) or year(1~3)")
|
||||
}
|
||||
}
|
||||
|
||||
ret := &SElasticcache{}
|
||||
ret := &SElasticcache{region: self}
|
||||
err := DoAction(self.kvsRequest, "CreateInstance", params, []string{}, ret)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "region.CreateIElasticcaches")
|
||||
}
|
||||
self.SetResourceTags(ALIYUN_SERVICE_KVS, "INSTANCE", ret.InstanceID, ec.Tags, true)
|
||||
|
||||
ret.region = self
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -639,11 +639,11 @@ func (self *SRegion) CreateIElasticcaches(ec *cloudprovider.SCloudElasticCacheIn
|
||||
params["VpcId"] = ec.VpcId
|
||||
params["VSwitchId"] = ec.NetworkId
|
||||
params["ChargeType"] = ec.ChargeType
|
||||
if strings.ToLower(ec.ChargeType) == billingapi.BILLING_TYPE_PREPAID && ec.BC != nil {
|
||||
if ec.BC.GetMonths() >= 1 && ec.BC.GetMonths() <= 9 {
|
||||
params["Period"] = strconv.Itoa(ec.BC.GetMonths())
|
||||
} else if ec.BC.GetMonths() == 12 || ec.BC.GetMonths() == 24 || ec.BC.GetMonths() == 36 {
|
||||
params["Period"] = strconv.Itoa(ec.BC.GetMonths())
|
||||
if strings.ToLower(ec.ChargeType) == billingapi.BILLING_TYPE_PREPAID && ec.BillingCycle != nil {
|
||||
if ec.BillingCycle.GetMonths() >= 1 && ec.BillingCycle.GetMonths() <= 9 {
|
||||
params["Period"] = strconv.Itoa(ec.BillingCycle.GetMonths())
|
||||
} else if ec.BillingCycle.GetMonths() == 12 || ec.BillingCycle.GetMonths() == 24 || ec.BillingCycle.GetMonths() == 36 {
|
||||
params["Period"] = strconv.Itoa(ec.BillingCycle.GetMonths())
|
||||
} else {
|
||||
return nil, fmt.Errorf("region.CreateIElasticcaches invalid billing cycle.reqired month (1~9) or year(1~3)")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,803 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/huaweicloud/huaweicloud-sdk-go-obs/obs"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/utils"
|
||||
"yunion.io/x/s3cli"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
type SBucket struct {
|
||||
multicloud.SBaseBucket
|
||||
multicloud.HuaweiTags
|
||||
|
||||
region *SRegion
|
||||
|
||||
Name string
|
||||
Location string
|
||||
CreationDate time.Time
|
||||
}
|
||||
|
||||
func (b *SBucket) GetProjectId() string {
|
||||
resp, err := b.region.HeadBucket(b.Name)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
epid, _ := resp.ResponseHeaders["epid"]
|
||||
if len(epid) > 0 {
|
||||
return epid[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (b *SBucket) GetGlobalId() string {
|
||||
return b.Name
|
||||
}
|
||||
|
||||
func (b *SBucket) GetName() string {
|
||||
return b.Name
|
||||
}
|
||||
|
||||
func (b *SBucket) GetLocation() string {
|
||||
return b.Location
|
||||
}
|
||||
|
||||
func (b *SBucket) GetIRegion() cloudprovider.ICloudRegion {
|
||||
return b.region
|
||||
}
|
||||
|
||||
func (b *SBucket) GetCreateAt() time.Time {
|
||||
return b.CreationDate
|
||||
}
|
||||
|
||||
func (b *SBucket) GetStorageClass() string {
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
log.Errorf("b.region.getOBSClient error %s", err)
|
||||
return ""
|
||||
}
|
||||
output, err := obscli.GetBucketStoragePolicy(b.Name)
|
||||
if err != nil {
|
||||
log.Errorf("obscli.GetBucketStoragePolicy error %s", err)
|
||||
return ""
|
||||
}
|
||||
return output.StorageClass
|
||||
}
|
||||
|
||||
func obsAcl2CannedAcl(acls []obs.Grant) cloudprovider.TBucketACLType {
|
||||
switch {
|
||||
case len(acls) == 1:
|
||||
if acls[0].Grantee.URI == "" && acls[0].Permission == s3cli.PERMISSION_FULL_CONTROL {
|
||||
return cloudprovider.ACLPrivate
|
||||
}
|
||||
case len(acls) == 2:
|
||||
for _, g := range acls {
|
||||
if g.Grantee.URI == s3cli.GRANTEE_GROUP_URI_AUTH_USERS && g.Permission == s3cli.PERMISSION_READ {
|
||||
return cloudprovider.ACLAuthRead
|
||||
}
|
||||
if g.Grantee.URI == s3cli.GRANTEE_GROUP_URI_ALL_USERS && g.Permission == s3cli.PERMISSION_READ {
|
||||
return cloudprovider.ACLPublicRead
|
||||
}
|
||||
}
|
||||
case len(acls) == 3:
|
||||
for _, g := range acls {
|
||||
if g.Grantee.URI == s3cli.GRANTEE_GROUP_URI_ALL_USERS && g.Permission == s3cli.PERMISSION_WRITE {
|
||||
return cloudprovider.ACLPublicReadWrite
|
||||
}
|
||||
}
|
||||
}
|
||||
return cloudprovider.ACLUnknown
|
||||
}
|
||||
|
||||
func (b *SBucket) GetAcl() cloudprovider.TBucketACLType {
|
||||
acl := cloudprovider.ACLPrivate
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
log.Errorf("b.region.getOBSClient error %s", err)
|
||||
return acl
|
||||
}
|
||||
output, err := obscli.GetBucketAcl(b.Name)
|
||||
if err != nil {
|
||||
log.Errorf("obscli.GetBucketAcl error %s", err)
|
||||
return acl
|
||||
}
|
||||
acl = obsAcl2CannedAcl(output.Grants)
|
||||
return acl
|
||||
}
|
||||
|
||||
func (b *SBucket) SetAcl(acl cloudprovider.TBucketACLType) error {
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "b.region.getOBSClient")
|
||||
}
|
||||
input := &obs.SetBucketAclInput{}
|
||||
input.Bucket = b.Name
|
||||
input.ACL = obs.AclType(string(acl))
|
||||
_, err = obscli.SetBucketAcl(input)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "obscli.SetBucketAcl")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *SBucket) GetAccessUrls() []cloudprovider.SBucketAccessUrl {
|
||||
return []cloudprovider.SBucketAccessUrl{
|
||||
{
|
||||
Url: fmt.Sprintf("https://%s.%s", b.Name, b.region.getOBSEndpoint()),
|
||||
Description: "bucket url",
|
||||
Primary: true,
|
||||
},
|
||||
{
|
||||
Url: fmt.Sprintf("https://%s/%s", b.region.getOBSEndpoint(), b.Name),
|
||||
Description: "obs url",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (b *SBucket) GetStats() cloudprovider.SBucketStats {
|
||||
stats := cloudprovider.SBucketStats{}
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
log.Errorf("b.region.getOBSClient error %s", err)
|
||||
stats.SizeBytes = -1
|
||||
stats.ObjectCount = -1
|
||||
return stats
|
||||
}
|
||||
output, err := obscli.GetBucketStorageInfo(b.Name)
|
||||
if err != nil {
|
||||
log.Errorf("obscli.GetBucketStorageInfo error %s", err)
|
||||
stats.SizeBytes = -1
|
||||
stats.ObjectCount = -1
|
||||
return stats
|
||||
}
|
||||
stats.SizeBytes = output.Size
|
||||
stats.ObjectCount = output.ObjectNumber
|
||||
return stats
|
||||
}
|
||||
|
||||
func (b *SBucket) ListObjects(prefix string, marker string, delimiter string, maxCount int) (cloudprovider.SListObjectResult, error) {
|
||||
result := cloudprovider.SListObjectResult{}
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
return result, errors.Wrap(err, "GetOBSClient")
|
||||
}
|
||||
input := &obs.ListObjectsInput{}
|
||||
input.Bucket = b.Name
|
||||
if len(prefix) > 0 {
|
||||
input.Prefix = prefix
|
||||
}
|
||||
if len(marker) > 0 {
|
||||
input.Marker = marker
|
||||
}
|
||||
if len(delimiter) > 0 {
|
||||
input.Delimiter = delimiter
|
||||
}
|
||||
if maxCount > 0 {
|
||||
input.MaxKeys = maxCount
|
||||
}
|
||||
oResult, err := obscli.ListObjects(input)
|
||||
if err != nil {
|
||||
return result, errors.Wrap(err, "ListObjects")
|
||||
}
|
||||
result.Objects = make([]cloudprovider.ICloudObject, 0)
|
||||
for _, object := range oResult.Contents {
|
||||
obj := &SObject{
|
||||
bucket: b,
|
||||
SBaseCloudObject: cloudprovider.SBaseCloudObject{
|
||||
StorageClass: string(object.StorageClass),
|
||||
Key: object.Key,
|
||||
SizeBytes: object.Size,
|
||||
ETag: object.ETag,
|
||||
LastModified: object.LastModified,
|
||||
},
|
||||
}
|
||||
result.Objects = append(result.Objects, obj)
|
||||
}
|
||||
if oResult.CommonPrefixes != nil {
|
||||
result.CommonPrefixes = make([]cloudprovider.ICloudObject, 0)
|
||||
for _, commonPrefix := range oResult.CommonPrefixes {
|
||||
obj := &SObject{
|
||||
bucket: b,
|
||||
SBaseCloudObject: cloudprovider.SBaseCloudObject{
|
||||
Key: commonPrefix,
|
||||
},
|
||||
}
|
||||
result.CommonPrefixes = append(result.CommonPrefixes, obj)
|
||||
}
|
||||
}
|
||||
result.IsTruncated = oResult.IsTruncated
|
||||
result.NextMarker = oResult.NextMarker
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (b *SBucket) PutObject(ctx context.Context, key string, reader io.Reader, sizeBytes int64, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) error {
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetOBSClient")
|
||||
}
|
||||
input := &obs.PutObjectInput{}
|
||||
input.Bucket = b.Name
|
||||
input.Key = key
|
||||
input.Body = reader
|
||||
|
||||
if sizeBytes > 0 {
|
||||
input.ContentLength = sizeBytes
|
||||
}
|
||||
if len(storageClassStr) > 0 {
|
||||
input.StorageClass, err = str2StorageClass(storageClassStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(cannedAcl) == 0 {
|
||||
cannedAcl = b.GetAcl()
|
||||
}
|
||||
input.ACL = obs.AclType(string(cannedAcl))
|
||||
if meta != nil {
|
||||
val := meta.Get(cloudprovider.META_HEADER_CONTENT_TYPE)
|
||||
if len(val) > 0 {
|
||||
input.ContentType = val
|
||||
}
|
||||
val = meta.Get(cloudprovider.META_HEADER_CONTENT_MD5)
|
||||
if len(val) > 0 {
|
||||
input.ContentMD5 = val
|
||||
}
|
||||
extraMeta := make(map[string]string)
|
||||
for k, v := range meta {
|
||||
if utils.IsInStringArray(k, []string{
|
||||
cloudprovider.META_HEADER_CONTENT_TYPE,
|
||||
cloudprovider.META_HEADER_CONTENT_MD5,
|
||||
}) {
|
||||
continue
|
||||
}
|
||||
if len(v[0]) > 0 {
|
||||
extraMeta[k] = v[0]
|
||||
}
|
||||
}
|
||||
input.Metadata = extraMeta
|
||||
}
|
||||
_, err = obscli.PutObject(input)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "PutObject")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *SBucket) NewMultipartUpload(ctx context.Context, key string, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) (string, error) {
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "GetOBSClient")
|
||||
}
|
||||
|
||||
input := &obs.InitiateMultipartUploadInput{}
|
||||
input.Bucket = b.Name
|
||||
input.Key = key
|
||||
if meta != nil {
|
||||
val := meta.Get(cloudprovider.META_HEADER_CONTENT_TYPE)
|
||||
if len(val) > 0 {
|
||||
input.ContentType = val
|
||||
}
|
||||
extraMeta := make(map[string]string)
|
||||
for k, v := range meta {
|
||||
if utils.IsInStringArray(k, []string{
|
||||
cloudprovider.META_HEADER_CONTENT_TYPE,
|
||||
}) {
|
||||
continue
|
||||
}
|
||||
if len(v[0]) > 0 {
|
||||
extraMeta[k] = v[0]
|
||||
}
|
||||
}
|
||||
input.Metadata = extraMeta
|
||||
}
|
||||
if len(cannedAcl) == 0 {
|
||||
cannedAcl = b.GetAcl()
|
||||
}
|
||||
input.ACL = obs.AclType(string(cannedAcl))
|
||||
if len(storageClassStr) > 0 {
|
||||
input.StorageClass, err = str2StorageClass(storageClassStr)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "str2StorageClass")
|
||||
}
|
||||
}
|
||||
output, err := obscli.InitiateMultipartUpload(input)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "InitiateMultipartUpload")
|
||||
}
|
||||
|
||||
return output.UploadId, nil
|
||||
}
|
||||
|
||||
func (b *SBucket) UploadPart(ctx context.Context, key string, uploadId string, partIndex int, part io.Reader, partSize int64, offset, totalSize int64) (string, error) {
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "GetOBSClient")
|
||||
}
|
||||
|
||||
input := &obs.UploadPartInput{}
|
||||
input.Bucket = b.Name
|
||||
input.Key = key
|
||||
input.UploadId = uploadId
|
||||
input.PartNumber = partIndex
|
||||
input.PartSize = partSize
|
||||
input.Body = part
|
||||
output, err := obscli.UploadPart(input)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "UploadPart")
|
||||
}
|
||||
|
||||
return output.ETag, nil
|
||||
}
|
||||
|
||||
func (b *SBucket) CompleteMultipartUpload(ctx context.Context, key string, uploadId string, partEtags []string) error {
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetOBSClient")
|
||||
}
|
||||
input := &obs.CompleteMultipartUploadInput{}
|
||||
input.Bucket = b.Name
|
||||
input.Key = key
|
||||
input.UploadId = uploadId
|
||||
parts := make([]obs.Part, len(partEtags))
|
||||
for i := range partEtags {
|
||||
parts[i] = obs.Part{
|
||||
PartNumber: i + 1,
|
||||
ETag: partEtags[i],
|
||||
}
|
||||
}
|
||||
input.Parts = parts
|
||||
_, err = obscli.CompleteMultipartUpload(input)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "CompleteMultipartUpload")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *SBucket) AbortMultipartUpload(ctx context.Context, key string, uploadId string) error {
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetOBSClient")
|
||||
}
|
||||
|
||||
input := &obs.AbortMultipartUploadInput{}
|
||||
input.Bucket = b.Name
|
||||
input.Key = key
|
||||
input.UploadId = uploadId
|
||||
|
||||
_, err = obscli.AbortMultipartUpload(input)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "AbortMultipartUpload")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *SBucket) DeleteObject(ctx context.Context, key string) error {
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetOBSClient")
|
||||
}
|
||||
input := &obs.DeleteObjectInput{}
|
||||
input.Bucket = b.Name
|
||||
input.Key = key
|
||||
_, err = obscli.DeleteObject(input)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "DeleteObject")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *SBucket) GetTempUrl(method string, key string, expire time.Duration) (string, error) {
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "GetOBSClient")
|
||||
}
|
||||
input := obs.CreateSignedUrlInput{}
|
||||
input.Bucket = b.Name
|
||||
input.Key = key
|
||||
input.Expires = int(expire / time.Second)
|
||||
switch method {
|
||||
case "GET":
|
||||
input.Method = obs.HttpMethodGet
|
||||
case "PUT":
|
||||
input.Method = obs.HttpMethodPut
|
||||
case "DELETE":
|
||||
input.Method = obs.HttpMethodDelete
|
||||
default:
|
||||
return "", errors.Error("unsupported method")
|
||||
}
|
||||
output, err := obscli.CreateSignedUrl(&input)
|
||||
return output.SignedUrl, nil
|
||||
}
|
||||
|
||||
func (b *SBucket) LimitSupport() cloudprovider.SBucketStats {
|
||||
return cloudprovider.SBucketStats{
|
||||
SizeBytes: 1,
|
||||
ObjectCount: -1,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *SBucket) GetLimit() cloudprovider.SBucketStats {
|
||||
stats := cloudprovider.SBucketStats{}
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
log.Errorf("getOBSClient error %s", err)
|
||||
return stats
|
||||
}
|
||||
output, err := obscli.GetBucketQuota(b.Name)
|
||||
if err != nil {
|
||||
return stats
|
||||
}
|
||||
stats.SizeBytes = output.Quota
|
||||
return stats
|
||||
}
|
||||
|
||||
func (b *SBucket) SetLimit(limit cloudprovider.SBucketStats) error {
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "getOBSClient")
|
||||
}
|
||||
input := &obs.SetBucketQuotaInput{}
|
||||
input.Bucket = b.Name
|
||||
input.Quota = limit.SizeBytes
|
||||
_, err = obscli.SetBucketQuota(input)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "SetBucketQuota")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *SBucket) CopyObject(ctx context.Context, destKey string, srcBucket, srcKey string, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) error {
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetOBSClient")
|
||||
}
|
||||
input := &obs.CopyObjectInput{}
|
||||
input.Bucket = b.Name
|
||||
input.Key = destKey
|
||||
input.CopySourceBucket = srcBucket
|
||||
input.CopySourceKey = srcKey
|
||||
if len(storageClassStr) > 0 {
|
||||
input.StorageClass, err = str2StorageClass(storageClassStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(cannedAcl) == 0 {
|
||||
cannedAcl = b.GetAcl()
|
||||
}
|
||||
input.ACL = obs.AclType(string(cannedAcl))
|
||||
if meta != nil {
|
||||
val := meta.Get(cloudprovider.META_HEADER_CONTENT_TYPE)
|
||||
if len(val) > 0 {
|
||||
input.ContentType = val
|
||||
}
|
||||
extraMeta := make(map[string]string)
|
||||
for k, v := range meta {
|
||||
if utils.IsInStringArray(k, []string{
|
||||
cloudprovider.META_HEADER_CONTENT_TYPE,
|
||||
}) {
|
||||
continue
|
||||
}
|
||||
if len(v[0]) > 0 {
|
||||
extraMeta[k] = v[0]
|
||||
}
|
||||
}
|
||||
input.Metadata = extraMeta
|
||||
input.MetadataDirective = obs.ReplaceMetadata
|
||||
} else {
|
||||
input.MetadataDirective = obs.CopyMetadata
|
||||
}
|
||||
_, err = obscli.CopyObject(input)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "obscli.CopyObject")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *SBucket) GetObject(ctx context.Context, key string, rangeOpt *cloudprovider.SGetObjectRange) (io.ReadCloser, error) {
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetOBSClient")
|
||||
}
|
||||
input := &obs.GetObjectInput{}
|
||||
input.Bucket = b.Name
|
||||
input.Key = key
|
||||
if rangeOpt != nil {
|
||||
input.RangeStart = rangeOpt.Start
|
||||
input.RangeEnd = rangeOpt.End
|
||||
}
|
||||
output, err := obscli.GetObject(input)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "obscli.GetObject")
|
||||
}
|
||||
return output.Body, nil
|
||||
}
|
||||
|
||||
func (b *SBucket) CopyPart(ctx context.Context, key string, uploadId string, partIndex int, srcBucket string, srcKey string, srcOffset int64, srcLength int64) (string, error) {
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "GetOBSClient")
|
||||
}
|
||||
input := &obs.CopyPartInput{}
|
||||
input.Bucket = b.Name
|
||||
input.Key = key
|
||||
input.UploadId = uploadId
|
||||
input.PartNumber = partIndex
|
||||
input.CopySourceBucket = srcBucket
|
||||
input.CopySourceKey = srcKey
|
||||
input.CopySourceRangeStart = srcOffset
|
||||
input.CopySourceRangeEnd = srcOffset + srcLength - 1
|
||||
output, err := obscli.CopyPart(input)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "CopyPart")
|
||||
}
|
||||
return output.ETag, nil
|
||||
}
|
||||
|
||||
func (b *SBucket) SetWebsite(websitConf cloudprovider.SBucketWebsiteConf) error {
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetOBSClient")
|
||||
}
|
||||
|
||||
obsWebConf := obs.SetBucketWebsiteConfigurationInput{}
|
||||
obsWebConf.Bucket = b.Name
|
||||
obsWebConf.BucketWebsiteConfiguration = obs.BucketWebsiteConfiguration{
|
||||
IndexDocument: obs.IndexDocument{Suffix: websitConf.Index},
|
||||
ErrorDocument: obs.ErrorDocument{Key: websitConf.ErrorDocument},
|
||||
}
|
||||
_, err = obscli.SetBucketWebsiteConfiguration(&obsWebConf)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "obscli.SetBucketWebsiteConfiguration(&obsWebConf)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *SBucket) GetWebsiteConf() (cloudprovider.SBucketWebsiteConf, error) {
|
||||
result := cloudprovider.SBucketWebsiteConf{}
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
return result, errors.Wrap(err, "GetOBSClient")
|
||||
}
|
||||
out, err := obscli.GetBucketWebsiteConfiguration(b.Name)
|
||||
if out == nil {
|
||||
return result, nil
|
||||
}
|
||||
result.Index = out.IndexDocument.Suffix
|
||||
result.ErrorDocument = out.ErrorDocument.Key
|
||||
result.Url = fmt.Sprintf("https://%s.obs-website.%s.myhuaweicloud.com", b.Name, b.region.GetId())
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (b *SBucket) DeleteWebSiteConf() error {
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetOBSClient")
|
||||
}
|
||||
_, err = obscli.DeleteBucketWebsiteConfiguration(b.Name)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "obscli.DeleteBucketWebsiteConfiguration(%s)", b.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *SBucket) SetCORS(rules []cloudprovider.SBucketCORSRule) error {
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetOBSClient")
|
||||
}
|
||||
opts := []obs.CorsRule{}
|
||||
for i := range rules {
|
||||
opts = append(opts, obs.CorsRule{
|
||||
AllowedOrigin: rules[i].AllowedOrigins,
|
||||
AllowedMethod: rules[i].AllowedMethods,
|
||||
AllowedHeader: rules[i].AllowedHeaders,
|
||||
MaxAgeSeconds: rules[i].MaxAgeSeconds,
|
||||
ExposeHeader: rules[i].ExposeHeaders,
|
||||
})
|
||||
}
|
||||
|
||||
input := obs.SetBucketCorsInput{}
|
||||
input.Bucket = b.Name
|
||||
input.BucketCors.CorsRules = opts
|
||||
_, err = obscli.SetBucketCors(&input)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "obscli.SetBucketCors(%s)", jsonutils.Marshal(input).String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *SBucket) GetCORSRules() ([]cloudprovider.SBucketCORSRule, error) {
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetOBSClient")
|
||||
}
|
||||
conf, err := obscli.GetBucketCors(b.Name)
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), "NoSuchCORSConfiguration") {
|
||||
return nil, errors.Wrapf(err, "obscli.GetBucketCors(%s)", b.Name)
|
||||
}
|
||||
}
|
||||
if conf == nil {
|
||||
return nil, nil
|
||||
}
|
||||
result := []cloudprovider.SBucketCORSRule{}
|
||||
for i := range conf.CorsRules {
|
||||
result = append(result, cloudprovider.SBucketCORSRule{
|
||||
AllowedOrigins: conf.CorsRules[i].AllowedOrigin,
|
||||
AllowedMethods: conf.CorsRules[i].AllowedMethod,
|
||||
AllowedHeaders: conf.CorsRules[i].AllowedHeader,
|
||||
MaxAgeSeconds: conf.CorsRules[i].MaxAgeSeconds,
|
||||
ExposeHeaders: conf.CorsRules[i].ExposeHeader,
|
||||
Id: strconv.Itoa(i),
|
||||
})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (b *SBucket) DeleteCORS() error {
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetOBSClient")
|
||||
}
|
||||
|
||||
_, err = obscli.DeleteBucketCors(b.Name)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "obscli.DeleteBucketCors(%s)", b.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *SBucket) GetTags() (map[string]string, error) {
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetOBSClient")
|
||||
}
|
||||
tagresult, err := obscli.GetBucketTagging(b.Name)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "404") {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.Wrapf(err, "osscli.GetBucketTagging(%s)", b.Name)
|
||||
}
|
||||
result := map[string]string{}
|
||||
for i := range tagresult.Tags {
|
||||
result[tagresult.Tags[i].Key] = tagresult.Tags[i].Value
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (b *SBucket) SetTags(tags map[string]string, replace bool) error {
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetOBSClient")
|
||||
}
|
||||
|
||||
_, err = obscli.DeleteBucketTagging(b.Name)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "DeleteBucketTagging")
|
||||
}
|
||||
|
||||
if len(tags) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
input := obs.SetBucketTaggingInput{BucketTagging: obs.BucketTagging{}}
|
||||
input.Bucket = b.Name
|
||||
for k, v := range tags {
|
||||
input.BucketTagging.Tags = append(input.BucketTagging.Tags, obs.Tag{Key: k, Value: v})
|
||||
}
|
||||
|
||||
_, err = obscli.SetBucketTagging(&input)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "obscli.SetBucketTagging(%s)", jsonutils.Marshal(input).String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *SBucket) ListMultipartUploads() ([]cloudprovider.SBucketMultipartUploads, error) {
|
||||
obscli, err := b.region.getOBSClient()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetOBSClient")
|
||||
}
|
||||
result := []cloudprovider.SBucketMultipartUploads{}
|
||||
|
||||
input := obs.ListMultipartUploadsInput{Bucket: b.Name}
|
||||
keyMarker := ""
|
||||
uploadIDMarker := ""
|
||||
for {
|
||||
if len(keyMarker) > 0 {
|
||||
input.KeyMarker = keyMarker
|
||||
}
|
||||
if len(uploadIDMarker) > 0 {
|
||||
input.UploadIdMarker = uploadIDMarker
|
||||
}
|
||||
|
||||
output, err := obscli.ListMultipartUploads(&input)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, " coscli.Bucket.ListMultipartUploads(context.Background(), &input)")
|
||||
}
|
||||
for i := range output.Uploads {
|
||||
temp := cloudprovider.SBucketMultipartUploads{
|
||||
ObjectName: output.Uploads[i].Key,
|
||||
UploadID: output.Uploads[i].UploadId,
|
||||
Initiator: output.Uploads[i].Initiator.DisplayName,
|
||||
Initiated: output.Uploads[i].Initiated,
|
||||
}
|
||||
result = append(result, temp)
|
||||
}
|
||||
keyMarker = output.NextKeyMarker
|
||||
uploadIDMarker = output.NextUploadIdMarker
|
||||
if !output.IsTruncated {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateIBucket(name string, storageClassStr string, aclStr string) error {
|
||||
return self.CreateBucket(name, storageClassStr, aclStr)
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateBucket(name string, storageClassStr string, aclStr string) error {
|
||||
obsClient, err := self.getOBSClient()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "self.getOBSClient")
|
||||
}
|
||||
input := &obs.CreateBucketInput{}
|
||||
input.Bucket = name
|
||||
input.Location = self.GetId()
|
||||
if len(aclStr) > 0 {
|
||||
if strings.EqualFold(aclStr, string(obs.AclPrivate)) {
|
||||
input.ACL = obs.AclPrivate
|
||||
} else if strings.EqualFold(aclStr, string(obs.AclPublicRead)) {
|
||||
input.ACL = obs.AclPublicRead
|
||||
} else if strings.EqualFold(aclStr, string(obs.AclPublicReadWrite)) {
|
||||
input.ACL = obs.AclPublicReadWrite
|
||||
} else {
|
||||
return errors.Error("unsupported acl")
|
||||
}
|
||||
}
|
||||
if len(storageClassStr) > 0 {
|
||||
input.StorageClass, err = str2StorageClass(storageClassStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err = obsClient.CreateBucket(input)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "obsClient.CreateBucket")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,706 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
billing_api "yunion.io/x/onecloud/pkg/apis/billing"
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
"yunion.io/x/onecloud/pkg/util/billing"
|
||||
"yunion.io/x/onecloud/pkg/util/httputils"
|
||||
)
|
||||
|
||||
type SBackupStrategy struct {
|
||||
KeepDays int
|
||||
StartTime string
|
||||
}
|
||||
|
||||
type SDatastore struct {
|
||||
Type string
|
||||
Version string
|
||||
}
|
||||
|
||||
type SHa struct {
|
||||
ReplicationMode string
|
||||
}
|
||||
|
||||
type SNonde struct {
|
||||
AvailabilityZone string
|
||||
Id string
|
||||
Name string
|
||||
Role string
|
||||
Staus string
|
||||
}
|
||||
|
||||
type SVolume struct {
|
||||
Size int
|
||||
Type string
|
||||
}
|
||||
|
||||
type SRelatedInstance struct {
|
||||
Id string
|
||||
Type string
|
||||
}
|
||||
|
||||
type SDBInstance struct {
|
||||
multicloud.SDBInstanceBase
|
||||
multicloud.HuaweiTags
|
||||
region *SRegion
|
||||
|
||||
flavorCache []SDBInstanceFlavor
|
||||
|
||||
BackupStrategy SBackupStrategy
|
||||
Created string //time.Time
|
||||
Datastore SDatastore
|
||||
DbUserName string
|
||||
DIskEncryptionId string
|
||||
FlavorRef string
|
||||
Ha SHa
|
||||
Id string
|
||||
MaintenanceWindow string
|
||||
Name string
|
||||
Nodes []SNonde
|
||||
Port int
|
||||
PrivateIps []string
|
||||
PublicIps []string
|
||||
Region string
|
||||
RelatedInstance []SRelatedInstance
|
||||
SecurityGroupId string
|
||||
Status string
|
||||
SubnetId string
|
||||
SwitchStrategy string
|
||||
TimeZone string
|
||||
Type string
|
||||
Updated string //time.Time
|
||||
Volume SVolume
|
||||
VpcId string
|
||||
EnterpriseProjectId string
|
||||
}
|
||||
|
||||
func (region *SRegion) GetDBInstances() ([]SDBInstance, error) {
|
||||
dbinstances := []SDBInstance{}
|
||||
err := region.rdsList("instances", nil, nil)
|
||||
return dbinstances, err
|
||||
}
|
||||
|
||||
func (region *SRegion) GetDBInstance(instanceId string) (*SDBInstance, error) {
|
||||
if len(instanceId) == 0 {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
instance := SDBInstance{region: region}
|
||||
res := &SDBInstance{}
|
||||
err := region.rdsGet(fmt.Sprintf("instance/%s", instanceId), res)
|
||||
return &instance, err
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetName() string {
|
||||
return rds.Name
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetId() string {
|
||||
return rds.Id
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetGlobalId() string {
|
||||
return rds.GetId()
|
||||
}
|
||||
|
||||
// 值为“BUILD”,表示实例正在创建。
|
||||
// 值为“ACTIVE”,表示实例正常。
|
||||
// 值为“FAILED”,表示实例异常。
|
||||
// 值为“FROZEN”,表示实例冻结。
|
||||
// 值为“MODIFYING”,表示实例正在扩容。
|
||||
// 值为“REBOOTING”,表示实例正在重启。
|
||||
// 值为“RESTORING”,表示实例正在恢复。
|
||||
// 值为“MODIFYING INSTANCE TYPE”,表示实例正在转主备。
|
||||
// 值为“SWITCHOVER”,表示实例正在主备切换。
|
||||
// 值为“MIGRATING”,表示实例正在迁移。
|
||||
// 值为“BACKING UP”,表示实例正在进行备份。
|
||||
// 值为“MODIFYING DATABASE PORT”,表示实例正在修改数据库端口。
|
||||
// 值为“STORAGE FULL”,表示实例磁盘空间满。
|
||||
|
||||
func (rds *SDBInstance) GetStatus() string {
|
||||
switch rds.Status {
|
||||
case "BUILD", "MODIFYING", "MODIFYING INSTANCE TYPE", "SWITCHOVER", "MODIFYING DATABASE PORT":
|
||||
return api.DBINSTANCE_DEPLOYING
|
||||
case "ACTIVE":
|
||||
return api.DBINSTANCE_RUNNING
|
||||
case "FAILED", "FROZEN", "STORAGE FULL":
|
||||
return api.DBINSTANCE_UNKNOWN
|
||||
case "REBOOTING":
|
||||
return api.DBINSTANCE_REBOOTING
|
||||
case "RESTORING":
|
||||
return api.DBINSTANCE_RESTORING
|
||||
case "MIGRATING":
|
||||
return api.DBINSTANCE_MIGRATING
|
||||
case "BACKING UP":
|
||||
return api.DBINSTANCE_BACKING_UP
|
||||
}
|
||||
return rds.Status
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetBillingType() string {
|
||||
return billing_api.BILLING_TYPE_POSTPAID
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetSecurityGroupIds() ([]string, error) {
|
||||
return []string{rds.SecurityGroupId}, nil
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) fetchFlavor() error {
|
||||
if len(rds.flavorCache) > 0 {
|
||||
return nil
|
||||
}
|
||||
flavors, err := rds.region.GetDBInstanceFlavors(rds.Datastore.Type, rds.Datastore.Version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rds.flavorCache = flavors
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetExpiredAt() time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetStorageType() string {
|
||||
return rds.Volume.Type
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetCreatedAt() time.Time {
|
||||
t, err := time.Parse("2006-01-02T15:04:05Z0700", rds.Created)
|
||||
if err != nil {
|
||||
return time.Time{}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetEngine() string {
|
||||
return rds.Datastore.Type
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetEngineVersion() string {
|
||||
return rds.Datastore.Version
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetInstanceType() string {
|
||||
return rds.FlavorRef
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetCategory() string {
|
||||
switch rds.Type {
|
||||
case "Single":
|
||||
return api.HUAWEI_DBINSTANCE_CATEGORY_SINGLE
|
||||
case "Ha":
|
||||
return api.HUAWEI_DBINSTANCE_CATEGORY_HA
|
||||
case "Replica":
|
||||
return api.HUAWEI_DBINSTANCE_CATEGORY_REPLICA
|
||||
}
|
||||
return rds.Type
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetVcpuCount() int {
|
||||
err := rds.fetchFlavor()
|
||||
if err != nil {
|
||||
log.Errorf("failed to fetch flavors: %v", err)
|
||||
return 0
|
||||
}
|
||||
for _, flavor := range rds.flavorCache {
|
||||
if flavor.SpecCode == rds.FlavorRef {
|
||||
return flavor.Vcpus
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetVmemSizeMB() int {
|
||||
err := rds.fetchFlavor()
|
||||
if err != nil {
|
||||
log.Errorf("failed to fetch flavors: %v", err)
|
||||
return 0
|
||||
}
|
||||
for _, flavor := range rds.flavorCache {
|
||||
if flavor.SpecCode == rds.FlavorRef {
|
||||
return flavor.Ram * 1024
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetDiskSizeGB() int {
|
||||
return rds.Volume.Size
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetPort() int {
|
||||
return rds.Port
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetMaintainTime() string {
|
||||
return rds.MaintenanceWindow
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetIVpcId() string {
|
||||
return rds.VpcId
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetProjectId() string {
|
||||
return rds.EnterpriseProjectId
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) Refresh() error {
|
||||
instance, err := rds.region.GetDBInstance(rds.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(rds, instance)
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetZone1Id() string {
|
||||
return rds.GetZoneIdByRole("master")
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetZoneIdByRole(role string) string {
|
||||
// for _, node := range rds.Nodes {
|
||||
// if node.Role == role {
|
||||
// zone, err := rds.region.getZoneById(node.AvailabilityZone)
|
||||
// if err != nil {
|
||||
// log.Errorf("failed to found zone %s for rds %s error: %v", node.AvailabilityZone, rds.Name, err)
|
||||
// return ""
|
||||
// }
|
||||
// return zone.GetGlobalId()
|
||||
// }
|
||||
// }
|
||||
return ""
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetZone2Id() string {
|
||||
return rds.GetZoneIdByRole("slave")
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetZone3Id() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type SRdsNetwork struct {
|
||||
SubnetId string
|
||||
IP string
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetDBNetworks() ([]cloudprovider.SDBInstanceNetwork, error) {
|
||||
ret := []cloudprovider.SDBInstanceNetwork{}
|
||||
for _, ip := range rds.PrivateIps {
|
||||
network := cloudprovider.SDBInstanceNetwork{
|
||||
IP: ip,
|
||||
NetworkId: rds.SubnetId,
|
||||
}
|
||||
ret = append(ret, network)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetInternalConnectionStr() string {
|
||||
for _, ip := range rds.PrivateIps {
|
||||
return ip
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetConnectionStr() string {
|
||||
for _, ip := range rds.PublicIps {
|
||||
return ip
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (region *SRegion) GetIDBInstanceById(instanceId string) (cloudprovider.ICloudDBInstance, error) {
|
||||
dbinstance, err := region.GetDBInstance(instanceId)
|
||||
if err != nil {
|
||||
log.Errorf("failed to get dbinstance by id %s error: %v", instanceId, err)
|
||||
}
|
||||
return dbinstance, err
|
||||
}
|
||||
|
||||
func (region *SRegion) GetIDBInstances() ([]cloudprovider.ICloudDBInstance, error) {
|
||||
instances, err := region.GetDBInstances()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "region.GetDBInstances()")
|
||||
}
|
||||
idbinstances := []cloudprovider.ICloudDBInstance{}
|
||||
for i := 0; i < len(instances); i++ {
|
||||
instances[i].region = region
|
||||
idbinstances = append(idbinstances, &instances[i])
|
||||
}
|
||||
return idbinstances, nil
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetIDBInstanceParameters() ([]cloudprovider.ICloudDBInstanceParameter, error) {
|
||||
parameters, err := rds.region.GetDBInstanceParameters(rds.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iparameters := []cloudprovider.ICloudDBInstanceParameter{}
|
||||
for i := 0; i < len(parameters); i++ {
|
||||
iparameters = append(iparameters, ¶meters[i])
|
||||
}
|
||||
return iparameters, nil
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetIDBInstanceDatabases() ([]cloudprovider.ICloudDBInstanceDatabase, error) {
|
||||
databases, err := rds.region.GetDBInstanceDatabases(rds.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "rds.region.GetDBInstanceDatabases(rds.Id)")
|
||||
}
|
||||
|
||||
idatabase := []cloudprovider.ICloudDBInstanceDatabase{}
|
||||
for i := 0; i < len(databases); i++ {
|
||||
databases[i].instance = rds
|
||||
idatabase = append(idatabase, &databases[i])
|
||||
}
|
||||
return idatabase, nil
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetIDBInstanceAccounts() ([]cloudprovider.ICloudDBInstanceAccount, error) {
|
||||
accounts, err := rds.region.GetDBInstanceAccounts(rds.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "rds.region.GetDBInstanceAccounts(rds.Id)")
|
||||
}
|
||||
|
||||
user := "root"
|
||||
if rds.GetEngine() == api.DBINSTANCE_TYPE_SQLSERVER {
|
||||
user = "rduser"
|
||||
}
|
||||
|
||||
accounts = append(accounts, SDBInstanceAccount{
|
||||
Name: user,
|
||||
instance: rds,
|
||||
})
|
||||
|
||||
iaccounts := []cloudprovider.ICloudDBInstanceAccount{}
|
||||
for i := 0; i < len(accounts); i++ {
|
||||
accounts[i].instance = rds
|
||||
iaccounts = append(iaccounts, &accounts[i])
|
||||
}
|
||||
return iaccounts, nil
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) Delete() error {
|
||||
return rds.region.DeleteDBInstance(rds.Id)
|
||||
}
|
||||
|
||||
func (region *SRegion) DeleteDBInstance(instanceId string) error {
|
||||
err := region.client.rdsDelete(region.Id, instanceId)
|
||||
return err
|
||||
}
|
||||
|
||||
func (region *SRegion) CreateIDBInstance(desc *cloudprovider.SManagedDBInstanceCreateConfig) (cloudprovider.ICloudDBInstance, error) {
|
||||
zoneIds := []string{}
|
||||
zones, err := region.GetIZones()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, zone := range zones {
|
||||
zoneIds = append(zoneIds, zone.GetId())
|
||||
}
|
||||
|
||||
if len(desc.SecgroupIds) == 0 {
|
||||
return nil, fmt.Errorf("Missing secgroupId")
|
||||
}
|
||||
|
||||
params := map[string]interface{}{
|
||||
"region": region.Id,
|
||||
"name": desc.Name,
|
||||
"datastore": map[string]string{
|
||||
"type": desc.Engine,
|
||||
"version": desc.EngineVersion,
|
||||
},
|
||||
"password": desc.Password,
|
||||
"volume": map[string]interface{}{
|
||||
"type": desc.StorageType,
|
||||
"size": desc.DiskSizeGB,
|
||||
},
|
||||
"vpc_id": desc.VpcId,
|
||||
"subnet_id": desc.NetworkId,
|
||||
"security_group_id": desc.SecgroupIds[0],
|
||||
}
|
||||
|
||||
if len(desc.ProjectId) > 0 {
|
||||
params["enterprise_project_id"] = desc.ProjectId
|
||||
}
|
||||
|
||||
if len(desc.MasterInstanceId) > 0 {
|
||||
params["replica_of_id"] = desc.MasterInstanceId
|
||||
delete(params, "security_group_id")
|
||||
}
|
||||
|
||||
if len(desc.RdsId) > 0 && len(desc.BackupId) > 0 {
|
||||
params["restore_point"] = map[string]interface{}{
|
||||
"backup_id": desc.BackupId,
|
||||
"instance_id": desc.RdsId,
|
||||
"type": "backup",
|
||||
}
|
||||
}
|
||||
|
||||
switch desc.Category {
|
||||
case api.HUAWEI_DBINSTANCE_CATEGORY_HA:
|
||||
switch desc.Engine {
|
||||
case api.DBINSTANCE_TYPE_MYSQL, api.DBINSTANCE_TYPE_POSTGRESQL:
|
||||
params["ha"] = map[string]string{
|
||||
"mode": "Ha",
|
||||
"replication_mode": "async",
|
||||
}
|
||||
case api.DBINSTANCE_TYPE_SQLSERVER:
|
||||
params["ha"] = map[string]string{
|
||||
"mode": "Ha",
|
||||
"replication_mode": "sync",
|
||||
}
|
||||
}
|
||||
case api.HUAWEI_DBINSTANCE_CATEGORY_SINGLE:
|
||||
case api.HUAWEI_DBINSTANCE_CATEGORY_REPLICA:
|
||||
}
|
||||
|
||||
if desc.BillingCycle != nil {
|
||||
periodType := "month"
|
||||
periodNum := desc.BillingCycle.GetMonths()
|
||||
if desc.BillingCycle.GetYears() > 0 {
|
||||
periodType = "year"
|
||||
periodNum = desc.BillingCycle.GetYears()
|
||||
}
|
||||
params["charge_info"] = map[string]interface{}{
|
||||
"charge_mode": "prePaid",
|
||||
"period_type": periodType,
|
||||
"period_num": periodNum,
|
||||
"is_auto_renew": false,
|
||||
}
|
||||
}
|
||||
params["flavor_ref"] = desc.InstanceType
|
||||
params["availability_zone"] = desc.ZoneId
|
||||
resp, err := region.client.request(httputils.POST, region.client._url("instance", "v3", region.Id, "instance"), nil, params)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Create")
|
||||
}
|
||||
|
||||
instance := &SDBInstance{region: region}
|
||||
err = resp.Unmarshal(instance, "instance")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, `resp.Unmarshal(&instance, "instance")`)
|
||||
}
|
||||
if jobId, _ := resp.GetString("job_id"); len(jobId) > 0 {
|
||||
err = cloudprovider.Wait(10*time.Second, 20*time.Minute, func() (bool, error) {
|
||||
|
||||
job, err := region.client.request(httputils.POST, region.client._url(fmt.Sprintf("instance/%s", jobId), "v3", region.Id, "instance"), nil, params)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
status, _ := job.GetString("status")
|
||||
process, _ := job.GetString("process")
|
||||
log.Debugf("create dbinstance job %s status: %s process: %s", jobId, status, process)
|
||||
if status == "Completed" {
|
||||
return true, nil
|
||||
}
|
||||
if status == "Failed" {
|
||||
return false, fmt.Errorf("create failed")
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
}
|
||||
return instance, err
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) Reboot() error {
|
||||
// return rds.region.RebootDBInstance(rds.Id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) OpenPublicConnection() error {
|
||||
return fmt.Errorf("Huawei current not support this operation")
|
||||
//return rds.region.PublicConnectionAction(rds.Id, "openRC")
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) ClosePublicConnection() error {
|
||||
return fmt.Errorf("Huawei current not support this operation")
|
||||
//return rds.region.PublicConnectionAction(rds.Id, "closeRC")
|
||||
}
|
||||
|
||||
func (region *SRegion) PublicConnectionAction(instanceId string, action string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
/*resp, err := region.ecsClient.DBInstance.PerformAction2(action, instanceId, nil, "")
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "rds.%s", action)
|
||||
}
|
||||
|
||||
if jobId, _ := resp.GetString("job_id"); len(jobId) > 0 {
|
||||
err = cloudprovider.WaitCreated(10*time.Second, 20*time.Minute, func() bool {
|
||||
job := struct {
|
||||
Status string
|
||||
Process string
|
||||
}{}
|
||||
query := url.Values{}
|
||||
query.Add("id", jobId)
|
||||
err := region.rdsJobGet("jobs", query, job)
|
||||
if err != nil {
|
||||
log.Errorf("failed to get job %s info error: %v", jobId, err)
|
||||
return false
|
||||
}
|
||||
if job.Status == "Completed" {
|
||||
return true
|
||||
}
|
||||
log.Debugf("%s dbinstance job %s status: %s process: %s", action, jobId, job.Status, job.Process)
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
*/
|
||||
}
|
||||
|
||||
func (region *SRegion) RebootDBInstance(instanceId string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
/*params := jsonutils.Marshal(map[string]interface{}{
|
||||
"restart": map[string]string{},
|
||||
})
|
||||
resp, err := region.ecsClient.DBInstance.PerformAction2("action", instanceId, params, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if jobId, _ := resp.GetString("job_id"); len(jobId) > 0 {
|
||||
err = cloudprovider.WaitCreated(10*time.Second, 20*time.Minute, func() bool {
|
||||
job := struct {
|
||||
Status string
|
||||
Process string
|
||||
}{}
|
||||
query := url.Values{}
|
||||
query.Add("id", jobId)
|
||||
err := region.rdsJobGet("jobs", query, job)
|
||||
if err != nil {
|
||||
log.Errorf("failed to get job %s info error: %v", jobId, err)
|
||||
return false
|
||||
}
|
||||
if job.Status == "Completed" {
|
||||
return true
|
||||
}
|
||||
log.Debugf("%s dbinstance job %s status: %s process: %s", action, jobId, job.Status, job.Process)
|
||||
return false
|
||||
})
|
||||
}
|
||||
return err*/
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) CreateAccount(conf *cloudprovider.SDBInstanceAccountCreateConfig) error {
|
||||
return rds.region.CreateDBInstanceAccount(rds.Id, conf.Name, conf.Password)
|
||||
}
|
||||
|
||||
func (region *SRegion) CreateDBInstanceAccount(instanceId, account, password string) error {
|
||||
params := map[string]interface{}{
|
||||
"name": account,
|
||||
"password": password,
|
||||
}
|
||||
|
||||
err := region.rdsCreate(fmt.Sprintf("instances/%s/db_user", instanceId), params, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) CreateDatabase(conf *cloudprovider.SDBInstanceDatabaseCreateConfig) error {
|
||||
return rds.region.CreateDBInstanceDatabase(rds.Id, conf.Name, conf.CharacterSet)
|
||||
}
|
||||
|
||||
func (region *SRegion) CreateDBInstanceDatabase(instanceId, database, characterSet string) error {
|
||||
params := map[string]interface{}{
|
||||
"name": database,
|
||||
"character_set": characterSet,
|
||||
}
|
||||
err := region.rdsCreate(fmt.Sprintf("instances/%s/database", instanceId), params, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) ChangeConfig(cxt context.Context, desc *cloudprovider.SManagedDBInstanceChangeConfig) error {
|
||||
return rds.region.ChangeDBInstanceConfig(rds.Id, desc.InstanceType, desc.DiskSizeGB)
|
||||
}
|
||||
|
||||
func (region *SRegion) ChangeDBInstanceConfig(instanceId string, instanceType string, diskSizeGb int) error {
|
||||
instance, err := region.GetIDBInstanceById(instanceId)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "region.GetIDBInstanceById(%s)", instanceId)
|
||||
}
|
||||
|
||||
if len(instanceType) > 0 {
|
||||
params := map[string]interface{}{
|
||||
"resize_flavor": map[string]string{
|
||||
"spec_code": instanceType,
|
||||
},
|
||||
}
|
||||
err := region.rdsPerform(fmt.Sprintf("instances/%s", instanceId), "action", params, nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "resize_flavor")
|
||||
}
|
||||
cloudprovider.WaitStatus(instance, api.DBINSTANCE_RUNNING, time.Second*5, time.Minute*30)
|
||||
}
|
||||
if diskSizeGb > 0 {
|
||||
params := map[string]interface{}{
|
||||
"enlarge_volume": map[string]int{
|
||||
"size": diskSizeGb,
|
||||
},
|
||||
}
|
||||
err := region.rdsPerform(fmt.Sprintf("instances/%s", instanceId), "action", params, nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "enlarge_volume")
|
||||
}
|
||||
cloudprovider.WaitStatus(instance, api.DBINSTANCE_RUNNING, time.Second*5, time.Minute*30)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) RecoveryFromBackup(conf *cloudprovider.SDBInstanceRecoveryConfig) error {
|
||||
if len(conf.OriginDBInstanceExternalId) == 0 {
|
||||
conf.OriginDBInstanceExternalId = rds.Id
|
||||
}
|
||||
return rds.region.RecoveryDBInstanceFromBackup(rds.Id, conf.OriginDBInstanceExternalId, conf.BackupId, conf.Databases)
|
||||
}
|
||||
|
||||
func (region *SRegion) RecoveryDBInstanceFromBackup(target, origin string, backupId string, databases map[string]string) error {
|
||||
source := map[string]interface{}{
|
||||
"type": "backup",
|
||||
"backup_id": backupId,
|
||||
}
|
||||
if len(origin) > 0 {
|
||||
source["instance_id"] = origin
|
||||
}
|
||||
if len(databases) > 0 {
|
||||
source["database_name"] = databases
|
||||
}
|
||||
params := map[string]interface{}{
|
||||
"source": source,
|
||||
"target": map[string]string{
|
||||
"instance_id": target,
|
||||
},
|
||||
}
|
||||
err := region.rdsPerform("instance", "recovery", params, nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "dbinstance.recovery")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) Renew(bc billing.SBillingCycle) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
type SDBInstanceAccount struct {
|
||||
multicloud.SDBInstanceAccountBase
|
||||
multicloud.HuaweiTags
|
||||
instance *SDBInstance
|
||||
Name string
|
||||
}
|
||||
|
||||
func (account *SDBInstanceAccount) GetName() string {
|
||||
return account.Name
|
||||
}
|
||||
|
||||
func (account *SDBInstanceAccount) Delete() error {
|
||||
return account.instance.region.rdsDelete(fmt.Sprintf("%s/db_user/%s", account.instance.Id, account.Name))
|
||||
}
|
||||
|
||||
func (account *SDBInstanceAccount) GetIDBInstanceAccountPrivileges() ([]cloudprovider.ICloudDBInstanceAccountPrivilege, error) {
|
||||
privileges, err := account.instance.region.GetDBInstancePrivileges(account.instance.Id, account.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iprivileves := []cloudprovider.ICloudDBInstanceAccountPrivilege{}
|
||||
for i := 0; i < len(privileges); i++ {
|
||||
privileges[i].account = account
|
||||
iprivileves = append(iprivileves, &privileges[i])
|
||||
}
|
||||
return iprivileves, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) GetDBInstanceAccounts(instanceId string) ([]SDBInstanceAccount, error) {
|
||||
accounts := []SDBInstanceAccount{}
|
||||
err := region.rdsList(fmt.Sprintf("instances/%s/db_user/detail", instanceId), nil, accounts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return accounts, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) GetDBInstancePrivileges(instanceId string, username string) ([]SDatabasePrivilege, error) {
|
||||
query := url.Values{}
|
||||
query.Add("iser-name", username)
|
||||
privileges := []SDatabasePrivilege{}
|
||||
err := region.rdsList(fmt.Sprintf("instances/%s/db_user/database", instanceId), query, privileges)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return privileges, nil
|
||||
}
|
||||
|
||||
func (account *SDBInstanceAccount) RevokePrivilege(database string) error {
|
||||
return account.instance.region.RevokeDBInstancePrivilege(account.instance.Id, account.Name, database)
|
||||
}
|
||||
|
||||
func (region *SRegion) RevokeDBInstancePrivilege(instanceId string, account, database string) error {
|
||||
params := map[string]interface{}{
|
||||
"db_name": database,
|
||||
"users": []map[string]interface{}{
|
||||
map[string]interface{}{
|
||||
"name": account,
|
||||
},
|
||||
},
|
||||
}
|
||||
return region.rdsDBPrivilegesDelete(fmt.Sprintf("instances/%s/db_privilege", instanceId), params)
|
||||
}
|
||||
|
||||
func (account *SDBInstanceAccount) GrantPrivilege(database, privilege string) error {
|
||||
return account.instance.region.GrantDBInstancePrivilege(account.instance.Id, account.Name, database, privilege)
|
||||
}
|
||||
|
||||
func (region *SRegion) GrantDBInstancePrivilege(instanceId string, account, database string, privilege string) error {
|
||||
readonly := false
|
||||
switch privilege {
|
||||
case api.DATABASE_PRIVILEGE_R:
|
||||
readonly = true
|
||||
case api.DATABASE_PRIVILEGE_RW:
|
||||
default:
|
||||
return fmt.Errorf("Unknown privilege %s", privilege)
|
||||
}
|
||||
params := map[string]interface{}{
|
||||
"db_name": database,
|
||||
"users": []map[string]interface{}{
|
||||
map[string]interface{}{
|
||||
"name": account,
|
||||
"readonly": readonly,
|
||||
},
|
||||
},
|
||||
}
|
||||
resp := &struct {
|
||||
Resp string
|
||||
}{}
|
||||
err := region.rdsDBPrivilegesGrant(fmt.Sprintf("instances/%s/db_privilege", instanceId), params, resp)
|
||||
return err
|
||||
}
|
||||
|
||||
func (account *SDBInstanceAccount) ResetPassword(password string) error {
|
||||
return account.instance.region.ResetDBInstanceAccountPassword(account.instance.Id, account.Name, password)
|
||||
}
|
||||
|
||||
func (region *SRegion) ResetDBInstanceAccountPassword(instanceId, account, password string) error {
|
||||
return fmt.Errorf("The API does not exist or has not been published in the environment")
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
type SDBInstanceBackup struct {
|
||||
multicloud.SDBInstanceBackupBase
|
||||
multicloud.HuaweiTags
|
||||
region *SRegion
|
||||
|
||||
BeginTime string
|
||||
Datastore SDatastore
|
||||
EndTime string
|
||||
Id string
|
||||
InstanceId string
|
||||
Name string
|
||||
Size int
|
||||
Status string
|
||||
Type string
|
||||
}
|
||||
|
||||
func (backup *SDBInstanceBackup) GetId() string {
|
||||
return backup.Id
|
||||
}
|
||||
|
||||
func (backup *SDBInstanceBackup) GetGlobalId() string {
|
||||
return backup.Id
|
||||
}
|
||||
|
||||
func (backup *SDBInstanceBackup) GetName() string {
|
||||
return backup.Name
|
||||
}
|
||||
|
||||
func (backup *SDBInstanceBackup) GetEngine() string {
|
||||
return backup.Datastore.Type
|
||||
}
|
||||
|
||||
func (backup *SDBInstanceBackup) GetEngineVersion() string {
|
||||
return backup.Datastore.Version
|
||||
}
|
||||
|
||||
func (backup *SDBInstanceBackup) GetStartTime() time.Time {
|
||||
//2019-08-05T08:00:02+0000
|
||||
t, err := time.Parse("2006-01-02T15:04:05Z0700", backup.BeginTime)
|
||||
if err != nil {
|
||||
return time.Time{}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (backup *SDBInstanceBackup) GetEndTime() time.Time {
|
||||
t, err := time.Parse("2006-01-02T15:04:05Z0700", backup.EndTime)
|
||||
if err != nil {
|
||||
return time.Time{}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (backup *SDBInstanceBackup) GetBackupMode() string {
|
||||
switch backup.Type {
|
||||
case "manual":
|
||||
return api.BACKUP_MODE_MANUAL
|
||||
default:
|
||||
return api.BACKUP_MODE_AUTOMATED
|
||||
}
|
||||
}
|
||||
|
||||
func (backup *SDBInstanceBackup) GetStatus() string {
|
||||
switch backup.Status {
|
||||
case "COMPLETED":
|
||||
return api.DBINSTANCE_BACKUP_READY
|
||||
case "FAILED":
|
||||
return api.DBINSTANCE_BACKUP_FAILED
|
||||
case "BUILDING":
|
||||
return api.DBINSTANCE_BACKUP_CREATING
|
||||
case "DELETING":
|
||||
return api.DBINSTANCE_BACKUP_DELETING
|
||||
default:
|
||||
return api.DBINSTANCE_BACKUP_UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
func (backup *SDBInstanceBackup) GetBackupSizeMb() int {
|
||||
return backup.Size / 1024
|
||||
}
|
||||
|
||||
func (backup *SDBInstanceBackup) GetDBNames() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (backup *SDBInstanceBackup) Delete() error {
|
||||
return backup.region.DeleteDBInstanceBackup(backup.Id)
|
||||
}
|
||||
|
||||
func (region *SRegion) DeleteDBInstanceBackup(backupId string) error {
|
||||
err := region.rdsDelete(fmt.Sprintf("backups/%s", backupId))
|
||||
return err
|
||||
}
|
||||
|
||||
func (backup *SDBInstanceBackup) GetDBInstanceId() string {
|
||||
return backup.InstanceId
|
||||
}
|
||||
|
||||
func (region *SRegion) GetDBInstanceBackups(instanceId, backupId string) ([]SDBInstanceBackup, error) {
|
||||
query := url.Values{}
|
||||
query.Add("instance_id", instanceId)
|
||||
if len(backupId) > 0 {
|
||||
query.Add("backup_id", backupId)
|
||||
}
|
||||
backups := []SDBInstanceBackup{}
|
||||
err := region.rdsList("offsite-backups", query, backups)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return backups, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) GetIDBInstanceBackups() ([]cloudprovider.ICloudDBInstanceBackup, error) {
|
||||
dbinstnaces, err := region.GetIDBInstances()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ibackups := []cloudprovider.ICloudDBInstanceBackup{}
|
||||
for i := 0; i < len(dbinstnaces); i++ {
|
||||
_dbinstance := dbinstnaces[i].(*SDBInstance)
|
||||
_ibackup, err := _dbinstance.GetIDBInstanceBackups()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "_dbinstance(%v).GetIDBInstanceBackups", _dbinstance)
|
||||
}
|
||||
ibackups = append(ibackups, _ibackup...)
|
||||
}
|
||||
return ibackups, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) GetIDBInstanceBackupById(backupId string) (cloudprovider.ICloudDBInstanceBackup, error) {
|
||||
backups, err := region.GetIDBInstanceBackups()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "region.GetIDBInstanceBackups")
|
||||
}
|
||||
for _, backup := range backups {
|
||||
if backup.GetGlobalId() == backupId {
|
||||
return backup, nil
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) GetIDBInstanceBackups() ([]cloudprovider.ICloudDBInstanceBackup, error) {
|
||||
backups, err := rds.region.GetDBInstanceBackups(rds.Id, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ibackups := []cloudprovider.ICloudDBInstanceBackup{}
|
||||
for i := 0; i < len(backups); i++ {
|
||||
backups[i].region = rds.region
|
||||
ibackups = append(ibackups, &backups[i])
|
||||
}
|
||||
return ibackups, nil
|
||||
}
|
||||
|
||||
func (backup *SDBInstanceBackup) Refresh() error {
|
||||
backups, err := backup.region.GetDBInstanceBackups(backup.InstanceId, backup.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(backups) == 0 {
|
||||
return cloudprovider.ErrNotFound
|
||||
}
|
||||
return jsonutils.Update(backup, backups[0])
|
||||
}
|
||||
|
||||
func (rds *SDBInstance) CreateIBackup(conf *cloudprovider.SDBInstanceBackupCreateConfig) (string, error) {
|
||||
backupId, err := rds.region.CreateDBInstanceBackup(rds.Id, conf.Name, conf.Description, conf.Databases)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
backup, err := rds.region.GetIDBInstanceBackupById(backupId)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "region.GetIDBInstanceBackupById")
|
||||
}
|
||||
cloudprovider.WaitStatus(backup, api.DBINSTANCE_BACKUP_READY, time.Second*3, time.Minute*30)
|
||||
return backupId, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) CreateDBInstanceBackup(instanceId string, name string, descrition string, databases []string) (string, error) {
|
||||
params := map[string]interface{}{
|
||||
"instance_id": instanceId,
|
||||
"name": name,
|
||||
"description": descrition,
|
||||
}
|
||||
if len(databases) > 0 {
|
||||
dbs := []map[string]string{}
|
||||
for _, database := range databases {
|
||||
dbs = append(dbs, map[string]string{"name": database})
|
||||
}
|
||||
params["databases"] = dbs
|
||||
}
|
||||
backup := SDBInstanceBackup{}
|
||||
err := region.rdsCreate("backups", params, backup)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "DBInstanceBackup.Create")
|
||||
}
|
||||
backupId := backup.Id
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "resp.GetBackupId")
|
||||
}
|
||||
return backupId, nil
|
||||
}
|
||||
|
||||
func (self *SDBInstanceBackup) CreateICloudDBInstance(opts *cloudprovider.SManagedDBInstanceCreateConfig) (cloudprovider.ICloudDBInstance, error) {
|
||||
opts.BackupId = self.Id
|
||||
return self.region.CreateIDBInstance(opts)
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
type SDBInstanceDatabase struct {
|
||||
instance *SDBInstance
|
||||
multicloud.SDBInstanceDatabaseBase
|
||||
multicloud.HuaweiTags
|
||||
|
||||
Name string
|
||||
CharacterSet string
|
||||
}
|
||||
|
||||
func (database *SDBInstanceDatabase) GetId() string {
|
||||
return database.Name
|
||||
}
|
||||
|
||||
func (database *SDBInstanceDatabase) GetGlobalId() string {
|
||||
return database.Name
|
||||
}
|
||||
|
||||
func (database *SDBInstanceDatabase) GetName() string {
|
||||
return database.Name
|
||||
}
|
||||
|
||||
func (database *SDBInstanceDatabase) GetStatus() string {
|
||||
return api.DBINSTANCE_DATABASE_RUNNING
|
||||
}
|
||||
|
||||
func (database *SDBInstanceDatabase) GetCharacterSet() string {
|
||||
return database.CharacterSet
|
||||
}
|
||||
|
||||
func (database *SDBInstanceDatabase) Delete() error {
|
||||
return database.instance.region.DeleteDBInstanceDatabase(database.instance.Id, database.Name)
|
||||
}
|
||||
|
||||
func (region *SRegion) DeleteDBInstanceDatabase(instanceId, database string) error {
|
||||
resource := fmt.Sprintf("instances/%s/database/%s", instanceId, database)
|
||||
err := region.rdsDelete(resource)
|
||||
return err
|
||||
}
|
||||
|
||||
// rds查询数据库列表
|
||||
func (region *SRegion) GetDBInstanceDatabases(instanceId string) ([]SDBInstanceDatabase, error) {
|
||||
resource := fmt.Sprintf("%s/database/detail", instanceId)
|
||||
databases := []SDBInstanceDatabase{}
|
||||
err := region.rdsList(resource, nil, databases)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return databases, nil
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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 hcs
|
||||
|
||||
type SDBInstanceParameter struct {
|
||||
instance *SDBInstance
|
||||
|
||||
Name string
|
||||
Value string
|
||||
RestartRequired bool
|
||||
Readonly bool
|
||||
ValueRange string
|
||||
Type string
|
||||
Description string
|
||||
}
|
||||
|
||||
func (region *SRegion) GetDBInstanceParameters(dbinstanceId string) ([]SDBInstanceParameter, error) {
|
||||
paramters := []SDBInstanceParameter{}
|
||||
err := region.rdsList("configurations", nil, paramters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return paramters, nil
|
||||
}
|
||||
|
||||
func (param *SDBInstanceParameter) GetGlobalId() string {
|
||||
return param.Name
|
||||
}
|
||||
|
||||
func (param *SDBInstanceParameter) GetKey() string {
|
||||
return param.Name
|
||||
}
|
||||
|
||||
func (param *SDBInstanceParameter) GetValue() string {
|
||||
return param.Value
|
||||
}
|
||||
|
||||
func (param *SDBInstanceParameter) GetDescription() string {
|
||||
return param.Description
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
)
|
||||
|
||||
type SDatabasePrivilege struct {
|
||||
account *SDBInstanceAccount
|
||||
|
||||
Name string
|
||||
Readonly bool
|
||||
}
|
||||
|
||||
func (privilege *SDatabasePrivilege) GetGlobalId() string {
|
||||
return fmt.Sprintf("%s/%s", privilege.account.Name, privilege.Name)
|
||||
}
|
||||
|
||||
func (privilege *SDatabasePrivilege) GetPrivilege() string {
|
||||
if privilege.Readonly {
|
||||
return api.DATABASE_PRIVILEGE_R
|
||||
}
|
||||
return api.DATABASE_PRIVILEGE_RW
|
||||
}
|
||||
|
||||
func (privilege *SDatabasePrivilege) GetDBName() string {
|
||||
return privilege.Name
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
)
|
||||
|
||||
type SDBInstanceSku struct {
|
||||
Vcpus int
|
||||
Ram int //单位GB
|
||||
SpecCode string
|
||||
InstanceMode string //实例模型
|
||||
|
||||
StorageType string
|
||||
Az1 string
|
||||
Az2 string
|
||||
Engine string
|
||||
EngineVersion string
|
||||
}
|
||||
|
||||
func (self *SDBInstanceSku) GetName() string {
|
||||
return self.SpecCode
|
||||
}
|
||||
|
||||
func (self *SDBInstanceSku) GetGlobalId() string {
|
||||
return fmt.Sprintf("%s-%s-%s-%s-%s-%s", self.Engine, self.EngineVersion, self.InstanceMode, self.SpecCode, self.StorageType, self.GetZoneId())
|
||||
}
|
||||
|
||||
func (self *SDBInstanceSku) GetCategory() string {
|
||||
return self.InstanceMode
|
||||
}
|
||||
|
||||
func (self *SDBInstanceSku) GetDiskSizeStep() int {
|
||||
return 10
|
||||
}
|
||||
|
||||
func (self *SDBInstanceSku) GetIOPS() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SDBInstanceSku) GetTPS() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SDBInstanceSku) GetZone1Id() string {
|
||||
return self.Az1
|
||||
}
|
||||
|
||||
func (self *SDBInstanceSku) GetZone2Id() string {
|
||||
return self.Az2
|
||||
}
|
||||
|
||||
func (self *SDBInstanceSku) GetZone3Id() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SDBInstanceSku) GetZoneId() string {
|
||||
zones := []string{}
|
||||
for _, zone := range []string{self.Az1, self.Az2} {
|
||||
if len(zone) > 0 {
|
||||
zones = append(zones, zone)
|
||||
}
|
||||
}
|
||||
return strings.Join(zones, ",")
|
||||
}
|
||||
|
||||
func (self *SDBInstanceSku) GetMaxConnections() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SDBInstanceSku) GetMaxDiskSizeGb() int {
|
||||
return 4000
|
||||
}
|
||||
|
||||
func (self *SDBInstanceSku) GetMinDiskSizeGb() int {
|
||||
return 40
|
||||
}
|
||||
|
||||
func (self *SDBInstanceSku) GetVcpuCount() int {
|
||||
return self.Vcpus
|
||||
}
|
||||
|
||||
func (self *SDBInstanceSku) GetVmemSizeMb() int {
|
||||
return self.Ram * 1024
|
||||
}
|
||||
|
||||
func (self *SDBInstanceSku) GetStorageType() string {
|
||||
return self.StorageType
|
||||
}
|
||||
|
||||
func (sku *SDBInstanceSku) GetEngine() string {
|
||||
return sku.Engine
|
||||
}
|
||||
|
||||
func (sku *SDBInstanceSku) GetEngineVersion() string {
|
||||
return sku.EngineVersion
|
||||
}
|
||||
|
||||
func (self *SDBInstanceSku) GetStatus() string {
|
||||
return api.DBINSTANCE_SKU_AVAILABLE
|
||||
}
|
||||
|
||||
func (sku *SDBInstanceSku) GetQPS() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
type SDBInstanceFlavor struct {
|
||||
Vcpus int
|
||||
Ram int //单位GB
|
||||
SpecCode string
|
||||
InstanceMode string //实例模型
|
||||
|
||||
StorageType string
|
||||
AzStatus map[string]string
|
||||
Engine string
|
||||
EngineVersion string
|
||||
}
|
||||
|
||||
func (self *SDBInstanceFlavor) GetISkus() []cloudprovider.ICloudDBInstanceSku {
|
||||
ret := []cloudprovider.ICloudDBInstanceSku{}
|
||||
switch self.InstanceMode {
|
||||
case api.HUAWEI_DBINSTANCE_CATEGORY_HA:
|
||||
for az1, status1 := range self.AzStatus {
|
||||
if status1 != "normal" {
|
||||
continue
|
||||
}
|
||||
for az2, status2 := range self.AzStatus {
|
||||
if status2 != "normal" {
|
||||
continue
|
||||
}
|
||||
sku := &SDBInstanceSku{
|
||||
Vcpus: self.Vcpus,
|
||||
Ram: self.Ram,
|
||||
SpecCode: self.SpecCode,
|
||||
InstanceMode: self.InstanceMode,
|
||||
StorageType: self.StorageType,
|
||||
Az1: az1,
|
||||
Az2: az2,
|
||||
Engine: self.Engine,
|
||||
EngineVersion: self.EngineVersion,
|
||||
}
|
||||
ret = append(ret, sku)
|
||||
}
|
||||
}
|
||||
case api.HUAWEI_DBINSTANCE_CATEGORY_SINGLE:
|
||||
for az, status := range self.AzStatus {
|
||||
if status != "normal" {
|
||||
continue
|
||||
}
|
||||
sku := &SDBInstanceSku{
|
||||
Vcpus: self.Vcpus,
|
||||
Ram: self.Ram,
|
||||
SpecCode: self.SpecCode,
|
||||
InstanceMode: self.InstanceMode,
|
||||
StorageType: self.StorageType,
|
||||
Az1: az,
|
||||
Engine: self.Engine,
|
||||
EngineVersion: self.EngineVersion,
|
||||
}
|
||||
ret = append(ret, sku)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIDBInstanceSkus() ([]cloudprovider.ICloudDBInstanceSku, error) {
|
||||
skus, err := self.GetDBInstanceSkus()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := []cloudprovider.ICloudDBInstanceSku{}
|
||||
for i := range skus {
|
||||
ret = append(ret, skus[i].GetISkus()...)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
type SDBInstanceDatastore struct {
|
||||
Id string
|
||||
Name string
|
||||
}
|
||||
|
||||
func (self *SRegion) GetDBInstanceSkus() ([]SDBInstanceFlavor, error) {
|
||||
skus := []SDBInstanceFlavor{}
|
||||
for _, engine := range []string{api.DBINSTANCE_TYPE_MYSQL, api.DBINSTANCE_TYPE_POSTGRESQL, api.DBINSTANCE_TYPE_SQLSERVER} {
|
||||
stores, err := self.GetDBInstanceDatastores(engine)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, version := range stores {
|
||||
storages, err := self.GetDBInstanceStorages(engine, version.Name)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetDBInstanceStorages(%s,%s)", engine, version.Name)
|
||||
}
|
||||
flavors, err := self.GetDBInstanceFlavors(engine, version.Name)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetDBInstanceFlavors(%s, %s)", engine, version.Name)
|
||||
}
|
||||
for i := range flavors {
|
||||
flavors[i].Engine = engine
|
||||
flavors[i].EngineVersion = version.Name
|
||||
for j := range storages {
|
||||
flavors[i].StorageType = storages[j].Name
|
||||
flavors[i].AzStatus = storages[j].AzStatus
|
||||
skus = append(skus, flavors[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return skus, nil
|
||||
}
|
||||
|
||||
// rds规格信息
|
||||
func (region *SRegion) GetDBInstanceFlavors(engine string, version string) ([]SDBInstanceFlavor, error) {
|
||||
flavors := []SDBInstanceFlavor{}
|
||||
query := url.Values{}
|
||||
query.Add("version_name", version)
|
||||
resource := fmt.Sprintf("%s/%s", "flavor", engine)
|
||||
err := region.rdsList(resource, query, flavors)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return flavors, nil
|
||||
}
|
||||
|
||||
// rds数据库引擎版本信息
|
||||
func (region *SRegion) GetDBInstanceDatastores(engine string) ([]SDBInstanceDatastore, error) {
|
||||
stores := []SDBInstanceDatastore{}
|
||||
resource := fmt.Sprintf("%s/%s", "datastores", engine)
|
||||
err := region.rdsList(resource, nil, stores)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return stores, nil
|
||||
}
|
||||
|
||||
type SDBInstanceStorage struct {
|
||||
Name string
|
||||
AzStatus map[string]string
|
||||
}
|
||||
|
||||
// rds数据库磁盘类型
|
||||
func (region *SRegion) GetDBInstanceStorages(engine, engineVersion string) ([]SDBInstanceStorage, error) {
|
||||
storages := []SDBInstanceStorage{}
|
||||
resource := fmt.Sprintf("%s/%s", "storage-type", engine)
|
||||
query := url.Values{}
|
||||
query.Add("version_name", engineVersion)
|
||||
err := region.rdsList(resource, query, storages)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return storages, nil
|
||||
}
|
||||
@@ -0,0 +1,438 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
billing_api "yunion.io/x/onecloud/pkg/apis/billing"
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
/*
|
||||
华为云云硬盘
|
||||
======创建==========
|
||||
1.磁盘只能挂载到同一可用区的云服务器内,创建后不支持更换可用区
|
||||
2.计费模式 包年包月/按需计费
|
||||
3.*支持自动备份
|
||||
|
||||
|
||||
共享盘 和 普通盘:https://support.huaweicloud.com/productdesc-evs/zh-cn_topic_0032860759.html
|
||||
根据是否支持挂载至多台云服务器可以将云硬盘分为非共享云硬盘和共享云硬盘。
|
||||
一个非共享云硬盘只能挂载至一台云服务器,而一个共享云硬盘可以同时挂载至多台云服务器。
|
||||
单个共享云硬盘最多可同时挂载给16个云服务器。目前,共享云硬盘只适用于数据盘,不支持系统盘。
|
||||
*/
|
||||
|
||||
type Attachment struct {
|
||||
ServerId string `json:"server_id"`
|
||||
AttachmentId string `json:"attachment_id"`
|
||||
AttachedAt string `json:"attached_at"`
|
||||
HostName string `json:"host_name"`
|
||||
VolumeId string `json:"volume_id"`
|
||||
Device string `json:"device"`
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
type DiskMeta struct {
|
||||
ResourceSpecCode string `json:"resourceSpecCode"`
|
||||
Billing string `json:"billing"`
|
||||
ResourceType string `json:"resourceType"`
|
||||
AttachedMode string `json:"attached_mode"`
|
||||
Readonly string `json:"readonly"`
|
||||
}
|
||||
|
||||
type VolumeImageMetadata struct {
|
||||
QuickStart string `json:"__quick_start"`
|
||||
ContainerFormat string `json:"container_format"`
|
||||
MinRAM string `json:"min_ram"`
|
||||
ImageName string `json:"image_name"`
|
||||
ImageId string `json:"image_id"`
|
||||
OSType string `json:"__os_type"`
|
||||
OSFeatureList string `json:"__os_feature_list"`
|
||||
MinDisk string `json:"min_disk"`
|
||||
SupportKVM string `json:"__support_kvm"`
|
||||
VirtualEnvType string `json:"virtual_env_type"`
|
||||
SizeGB string `json:"size"`
|
||||
OSVersion string `json:"__os_version"`
|
||||
OSBit string `json:"__os_bit"`
|
||||
SupportKVMHi1822Hiovs string `json:"__support_kvm_hi1822_hiovs"`
|
||||
SupportXen string `json:"__support_xen"`
|
||||
Description string `json:"__description"`
|
||||
Imagetype string `json:"__imagetype"`
|
||||
DiskFormat string `json:"disk_format"`
|
||||
ImageSourceType string `json:"__image_source_type"`
|
||||
Checksum string `json:"checksum"`
|
||||
Isregistered string `json:"__isregistered"`
|
||||
HwVifMultiqueueEnabled string `json:"hw_vif_multiqueue_enabled"`
|
||||
Platform string `json:"__platform"`
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-evs/zh-cn_topic_0124881427.html
|
||||
type SDisk struct {
|
||||
multicloud.SDisk
|
||||
multicloud.HuaweiDiskTags
|
||||
//details *SResourceDetail
|
||||
|
||||
region *SRegion
|
||||
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Attachments []Attachment `json:"attachments"`
|
||||
Description string `json:"description"`
|
||||
SizeGB int `json:"size"`
|
||||
Metadata DiskMeta `json:"metadata"`
|
||||
Encrypted bool `json:"encrypted"`
|
||||
Bootable string `json:"bootable"`
|
||||
Multiattach bool `json:"multiattach"`
|
||||
AvailabilityZone string `json:"availability_zone"`
|
||||
SourceVolid string `json:"source_volid"`
|
||||
SnapshotId string `json:"snapshot_id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
VolumeType string `json:"volume_type"`
|
||||
VolumeImageMetadata VolumeImageMetadata `json:"volume_image_metadata"`
|
||||
ReplicationStatus string `json:"replication_status"`
|
||||
UserId string `json:"user_id"`
|
||||
ConsistencygroupId string `json:"consistencygroup_id"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
EnterpriseProjectId string
|
||||
|
||||
ExpiredTime time.Time
|
||||
}
|
||||
|
||||
func (self *SDisk) GetId() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SDisk) GetName() string {
|
||||
if len(self.Name) == 0 {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
return self.Name
|
||||
}
|
||||
|
||||
func (self *SDisk) GetGlobalId() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SDisk) GetStatus() string {
|
||||
// https://support.huaweicloud.com/api-evs/zh-cn_topic_0051803385.html
|
||||
switch self.Status {
|
||||
case "creating", "downloading":
|
||||
return api.DISK_ALLOCATING
|
||||
case "available", "in-use":
|
||||
return api.DISK_READY
|
||||
case "error":
|
||||
return api.DISK_ALLOC_FAILED
|
||||
case "attaching":
|
||||
return api.DISK_ATTACHING
|
||||
case "detaching":
|
||||
return api.DISK_DETACHING
|
||||
case "restoring-backup":
|
||||
return api.DISK_REBUILD
|
||||
case "backing-up":
|
||||
return api.DISK_BACKUP_STARTALLOC // ?
|
||||
case "error_restoring":
|
||||
return api.DISK_BACKUP_ALLOC_FAILED
|
||||
case "uploading":
|
||||
return api.DISK_SAVING //?
|
||||
case "extending":
|
||||
return api.DISK_RESIZING
|
||||
case "error_extending":
|
||||
return api.DISK_ALLOC_FAILED // ?
|
||||
case "deleting":
|
||||
return api.DISK_DEALLOC //?
|
||||
case "error_deleting":
|
||||
return api.DISK_DEALLOC_FAILED // ?
|
||||
case "rollbacking":
|
||||
return api.DISK_REBUILD
|
||||
case "error_rollbacking":
|
||||
return api.DISK_UNKNOWN
|
||||
default:
|
||||
return api.DISK_UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SDisk) Refresh() error {
|
||||
ret, err := self.region.GetDisk(self.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(self, ret)
|
||||
}
|
||||
|
||||
func (self *SDisk) GetBillingType() string {
|
||||
return billing_api.BILLING_TYPE_POSTPAID
|
||||
}
|
||||
|
||||
func (self *SDisk) GetCreatedAt() time.Time {
|
||||
return self.CreatedAt
|
||||
}
|
||||
|
||||
func (self *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
// return self.storage, nil
|
||||
}
|
||||
|
||||
func (self *SDisk) GetDiskFormat() string {
|
||||
return "vhd"
|
||||
}
|
||||
|
||||
func (self *SDisk) GetDiskSizeMB() int {
|
||||
return int(self.SizeGB * 1024)
|
||||
}
|
||||
|
||||
func (self *SDisk) GetIsAutoDelete() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SDisk) GetTemplateId() string {
|
||||
return self.VolumeImageMetadata.ImageId
|
||||
}
|
||||
|
||||
// Bootable 表示硬盘是否为启动盘。
|
||||
// 启动盘 != 系统盘(必须是启动盘且挂载在root device上)
|
||||
func (self *SDisk) GetDiskType() string {
|
||||
if self.Bootable == "true" {
|
||||
return api.DISK_TYPE_SYS
|
||||
}
|
||||
return api.DISK_TYPE_DATA
|
||||
}
|
||||
|
||||
func (self *SDisk) GetFsFormat() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SDisk) GetIsNonPersistent() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SDisk) GetDriver() string {
|
||||
return "scsi"
|
||||
}
|
||||
|
||||
func (self *SDisk) GetCacheMode() string {
|
||||
return "none"
|
||||
}
|
||||
|
||||
func (self *SDisk) GetMountpoint() string {
|
||||
if len(self.Attachments) > 0 {
|
||||
return self.Attachments[0].Device
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SDisk) GetMountServerId() string {
|
||||
if len(self.Attachments) > 0 {
|
||||
return self.Attachments[0].ServerId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SDisk) GetAccessPath() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SDisk) Delete(ctx context.Context) error {
|
||||
return self.region.DeleteDisk(self.Id)
|
||||
}
|
||||
|
||||
func (self *SDisk) CreateISnapshot(ctx context.Context, name string, desc string) (cloudprovider.ICloudSnapshot, error) {
|
||||
snap, err := self.region.CreateSnapshot(self.Id, name, desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return snap, nil
|
||||
}
|
||||
|
||||
func (self *SDisk) GetISnapshot(id string) (cloudprovider.ICloudSnapshot, error) {
|
||||
snapshot, err := self.region.GetSnapshot(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return snapshot, nil
|
||||
}
|
||||
|
||||
func (self *SDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) {
|
||||
snapshots, err := self.region.GetSnapshots(self.Id, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := []cloudprovider.ICloudSnapshot{}
|
||||
for i := 0; i < len(snapshots); i++ {
|
||||
snapshots[i].region = self.region
|
||||
ret = append(ret, &snapshots[i])
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SDisk) Resize(ctx context.Context, sizeMB int64) error {
|
||||
return self.region.ResizeDisk(self.GetId(), sizeMB/1024)
|
||||
}
|
||||
|
||||
func (self *SDisk) Detach() error {
|
||||
return self.region.DetachDisk(self.GetMountServerId(), self.GetId())
|
||||
}
|
||||
|
||||
func (self *SDisk) Attach(device string) error {
|
||||
return self.region.AttachDisk(self.GetMountServerId(), self.GetId(), device)
|
||||
}
|
||||
|
||||
// 在线卸载磁盘 https://support.huaweicloud.com/usermanual-ecs/zh-cn_topic_0036046828.html
|
||||
// 对于挂载在系统盘盘位(也就是“/dev/sda”或“/dev/vda”挂载点)上的磁盘,当前仅支持离线卸载
|
||||
func (self *SDisk) Reset(ctx context.Context, snapshotId string) (string, error) {
|
||||
mountpoint := self.GetMountpoint()
|
||||
if len(mountpoint) > 0 {
|
||||
err := self.Detach()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
err := self.region.ResetDisk(self.Id, snapshotId)
|
||||
if err != nil {
|
||||
return self.Id, err
|
||||
}
|
||||
err = cloudprovider.WaitStatus(self, api.DISK_READY, 5*time.Second, 300*time.Second)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(mountpoint) > 0 {
|
||||
err := self.Attach(mountpoint)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return self.Id, nil
|
||||
}
|
||||
|
||||
// 华为云不支持重置
|
||||
func (self *SDisk) Rebuild(ctx context.Context) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SRegion) GetDisk(id string) (*SDisk, error) {
|
||||
ret := &SDisk{region: self}
|
||||
res := fmt.Sprintf("volumes/%s", id)
|
||||
return ret, self.evsGet(res, ret)
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-evs/zh-cn_topic_0058762430.html
|
||||
func (self *SRegion) GetDisks(zoneId string) ([]SDisk, error) {
|
||||
params := url.Values{}
|
||||
if len(zoneId) > 0 {
|
||||
params.Set("availability_zone", zoneId)
|
||||
}
|
||||
disks := []SDisk{}
|
||||
return disks, self.evsList("volumes/detail", params, &disks)
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateDisk(zoneId string, category string, name string, sizeGb int, snapshotId string, desc string, projectId string) (*SDisk, error) {
|
||||
volume := map[string]interface{}{
|
||||
"name": name,
|
||||
"availability_zone": zoneId,
|
||||
"description": desc,
|
||||
"volume_type": category,
|
||||
"size": sizeGb,
|
||||
}
|
||||
if len(snapshotId) > 0 {
|
||||
volume["snapshot_id"] = snapshotId
|
||||
}
|
||||
if len(projectId) > 0 {
|
||||
volume["enterprise_project_id"] = projectId
|
||||
}
|
||||
params := map[string]interface{}{
|
||||
"volume": volume,
|
||||
}
|
||||
ret := &SDisk{region: self}
|
||||
return ret, self.evsCreate("volumes", params, ret)
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteDisk(id string) error {
|
||||
res := fmt.Sprintf("volumes/%s", id)
|
||||
return self.evsDelete(res)
|
||||
}
|
||||
|
||||
func (self *SRegion) ResizeDisk(id string, sizeGb int64) error {
|
||||
params := map[string]interface{}{
|
||||
"os_extend": map[string]interface{}{
|
||||
"new_size": sizeGb,
|
||||
},
|
||||
}
|
||||
res := fmt.Sprintf("volumes/%s", id)
|
||||
return self.evsPerform(res, "action", params)
|
||||
}
|
||||
|
||||
func (self *SRegion) ResetDisk(diskId, snapshotId string) error {
|
||||
params := map[string]interface{}{
|
||||
"volume_id": diskId,
|
||||
}
|
||||
res := fmt.Sprintf("os-vendor-snapshots/%s", snapshotId)
|
||||
return self.evsPerform(res, "rollback", params)
|
||||
}
|
||||
|
||||
func (self *SDisk) GetProjectId() string {
|
||||
return self.EnterpriseProjectId
|
||||
}
|
||||
|
||||
func (self *SRegion) DetachDisk(instanceId string, diskId string) error {
|
||||
res := fmt.Sprintf("servers/%s/os-volume_attachments/%s", instanceId, diskId)
|
||||
return self.delete("ecs", "v2", res)
|
||||
}
|
||||
|
||||
func (self *SRegion) AttachDisk(instanceId string, diskId string, device string) error {
|
||||
params := map[string]interface{}{
|
||||
"volumeAttachment": map[string]interface{}{
|
||||
"volumeId": instanceId,
|
||||
"device": device,
|
||||
},
|
||||
}
|
||||
res := fmt.Sprintf("servers/%s/os-volume_attachments", instanceId)
|
||||
return self.perform("ecs", "v2", res, "action", params, nil)
|
||||
}
|
||||
|
||||
type SDiskType struct {
|
||||
ExtraSpecs ExtraSpecs `json:"extra_specs"`
|
||||
Name string `json:"name"`
|
||||
QosSpecsId string `json:"qos_specs_id"`
|
||||
Id string `json:"id"`
|
||||
IsPublic bool `json:"is_public"`
|
||||
}
|
||||
|
||||
func (self *SDiskType) IsAvaliableInZone(zoneId string) bool {
|
||||
if strings.Contains(self.ExtraSpecs.HwAvailabilityZone, zoneId) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type ExtraSpecs struct {
|
||||
VolumeBackendName string `json:"volume_backend_name"`
|
||||
HwAvailabilityZone string `json:"HW:availability-zone"`
|
||||
}
|
||||
|
||||
func (self *SRegion) GetDiskTypes() ([]SDiskType, error) {
|
||||
ret := []SDiskType{}
|
||||
return ret, self.get("evs", "v3", "types", &ret)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package hcs // import "yunion.io/x/onecloud/pkg/multicloud/hcs"
|
||||
@@ -0,0 +1,434 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
billing_api "yunion.io/x/onecloud/pkg/apis/billing"
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
type TInternetChargeType string
|
||||
|
||||
const (
|
||||
InternetChargeByTraffic = TInternetChargeType("traffic")
|
||||
InternetChargeByBandwidth = TInternetChargeType("bandwidth")
|
||||
)
|
||||
|
||||
type Bandwidth struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size"`
|
||||
ShareType string `json:"share_type"`
|
||||
PublicipInfo []PublicipInfo `json:"publicip_info"`
|
||||
TenantId string `json:"tenant_id"`
|
||||
BandwidthType string `json:"bandwidth_type"`
|
||||
ChargeMode string `json:"charge_mode"`
|
||||
BillingInfo string `json:"billing_info"`
|
||||
EnterpriseProjectId string `json:"enterprise_project_id"`
|
||||
}
|
||||
|
||||
type PublicipInfo struct {
|
||||
PublicipId string `json:"publicip_id"`
|
||||
PublicipAddress string `json:"publicip_address"`
|
||||
PublicipType string `json:"publicip_type"`
|
||||
IPVersion int64 `json:"ip_version"`
|
||||
}
|
||||
|
||||
type SProfile struct {
|
||||
UserId string `json:"user_id"`
|
||||
ProductId string `json:"product_id"`
|
||||
RegionId string `json:"region_id"`
|
||||
OrderId string `json:"order_id"`
|
||||
}
|
||||
|
||||
type SEip struct {
|
||||
multicloud.SEipBase
|
||||
multicloud.HcsTags
|
||||
|
||||
region *SRegion
|
||||
port *Port
|
||||
|
||||
Alias string
|
||||
Id string `json:"id"`
|
||||
Status string `json:"status"`
|
||||
Profile *SProfile `json:"profile,omitempty"`
|
||||
Type string `json:"type"`
|
||||
PublicIPAddress string `json:"public_ip_address"`
|
||||
PrivateIPAddress string `json:"private_ip_address"`
|
||||
TenantId string `json:"tenant_id"`
|
||||
CreateTime time.Time `json:"create_time"`
|
||||
BandwidthId string `json:"bandwidth_id"`
|
||||
BandwidthShareType string `json:"bandwidth_share_type"`
|
||||
BandwidthSize int64 `json:"bandwidth_size"`
|
||||
BandwidthName string `json:"bandwidth_name"`
|
||||
EnterpriseProjectId string `json:"enterprise_project_id"`
|
||||
IPVersion int64 `json:"ip_version"`
|
||||
PortId string `json:"port_id"`
|
||||
}
|
||||
|
||||
func (self *SEip) GetId() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SEip) GetName() string {
|
||||
if len(self.Alias) > 0 {
|
||||
return self.Alias
|
||||
}
|
||||
return self.PublicIPAddress
|
||||
}
|
||||
|
||||
func (self *SEip) GetGlobalId() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SEip) GetStatus() string {
|
||||
// https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090598.html
|
||||
switch self.Status {
|
||||
case "ACTIVE", "DOWN", "ELB":
|
||||
return api.EIP_STATUS_READY
|
||||
case "PENDING_CREATE", "NOTIFYING":
|
||||
return api.EIP_STATUS_ALLOCATE
|
||||
case "BINDING":
|
||||
return api.EIP_STATUS_ALLOCATE
|
||||
case "BIND_ERROR":
|
||||
return api.EIP_STATUS_ALLOCATE_FAIL
|
||||
case "PENDING_DELETE", "NOTIFY_DELETE":
|
||||
return api.EIP_STATUS_DEALLOCATE
|
||||
default:
|
||||
return api.EIP_STATUS_UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SEip) Refresh() error {
|
||||
if self.IsEmulated() {
|
||||
return nil
|
||||
}
|
||||
new, err := self.region.GetEip(self.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(self, new)
|
||||
}
|
||||
|
||||
func (self *SEip) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SEip) GetIpAddr() string {
|
||||
return self.PublicIPAddress
|
||||
}
|
||||
|
||||
func (self *SEip) GetMode() string {
|
||||
return api.EIP_MODE_STANDALONE_EIP
|
||||
}
|
||||
|
||||
func (self *SEip) GetPort() *Port {
|
||||
if len(self.PortId) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if self.port != nil {
|
||||
return self.port
|
||||
}
|
||||
|
||||
port, err := self.region.GetPort(self.PortId)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
self.port = port
|
||||
return self.port
|
||||
}
|
||||
|
||||
func (self *SEip) GetAssociationType() string {
|
||||
if len(self.PortId) == 0 {
|
||||
return ""
|
||||
}
|
||||
port, err := self.region.GetPort(self.PortId)
|
||||
if err != nil {
|
||||
log.Errorf("Get eip %s port %s error: %v", self.Id, self.PortId, err)
|
||||
return ""
|
||||
}
|
||||
|
||||
if strings.HasPrefix(port.DeviceOwner, "compute") {
|
||||
return api.EIP_ASSOCIATE_TYPE_SERVER
|
||||
}
|
||||
|
||||
switch port.DeviceOwner {
|
||||
case "neutron:LOADBALANCER", "neutron:LOADBALANCERV2":
|
||||
return api.EIP_ASSOCIATE_TYPE_LOADBALANCER
|
||||
case "network:nat_gateway":
|
||||
return api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY
|
||||
default:
|
||||
return port.DeviceOwner
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SEip) GetAssociationExternalId() string {
|
||||
// network/0273a359d61847fc83405926c958c746/ext-floatingips?tenantId=0273a359d61847fc83405926c958c746&limit=2000
|
||||
// 只能通过 port id 反查device id.
|
||||
if len(self.PortId) > 0 {
|
||||
port, _ := self.region.GetPort(self.PortId)
|
||||
return port.DeviceId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SEip) GetBandwidth() int {
|
||||
return int(self.BandwidthSize) // Mb
|
||||
}
|
||||
|
||||
func (self *SEip) GetINetworkId() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SEip) GetInternetChargeType() string {
|
||||
// https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090603.html
|
||||
bandwidth, err := self.region.GetEipBandwidth(self.BandwidthId)
|
||||
if err != nil {
|
||||
return api.EIP_CHARGE_TYPE_BY_TRAFFIC
|
||||
}
|
||||
if bandwidth.ChargeMode == "traffic" {
|
||||
return api.EIP_CHARGE_TYPE_BY_TRAFFIC
|
||||
}
|
||||
return api.EIP_CHARGE_TYPE_BY_BANDWIDTH
|
||||
}
|
||||
|
||||
func (self *SEip) GetBillingType() string {
|
||||
return billing_api.BILLING_TYPE_POSTPAID
|
||||
}
|
||||
|
||||
func (self *SEip) GetCreatedAt() time.Time {
|
||||
return self.CreateTime
|
||||
}
|
||||
|
||||
func (self *SEip) GetExpiredAt() time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (self *SEip) Delete() error {
|
||||
return self.region.DeallocateEIP(self.Id)
|
||||
}
|
||||
|
||||
func (self *SEip) Associate(conf *cloudprovider.AssociateConfig) error {
|
||||
portId, err := self.region.GetInstancePortId(conf.InstanceId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(self.PortId) > 0 {
|
||||
if self.PortId == portId {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("eip %s aready associate with port %s", self.GetId(), self.PortId)
|
||||
}
|
||||
|
||||
err = self.region.AssociateEipWithPortId(self.Id, portId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = cloudprovider.WaitStatusWithDelay(self, api.EIP_STATUS_READY, 10*time.Second, 10*time.Second, 180*time.Second)
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SEip) Dissociate() error {
|
||||
err := self.region.DissociateEip(self.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cloudprovider.WaitStatus(self, api.EIP_STATUS_READY, 10*time.Second, 180*time.Second)
|
||||
}
|
||||
|
||||
func (self *SEip) ChangeBandwidth(bw int) error {
|
||||
return self.region.UpdateEipBandwidth(self.BandwidthId, bw)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetInstancePortId(instanceId string) (string, error) {
|
||||
// 目前只绑定一个网卡
|
||||
// todo: 还需要按照ports状态进行过滤
|
||||
ports, err := self.GetPorts(instanceId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(ports) == 0 {
|
||||
return "", fmt.Errorf("AssociateEip instance %s port is empty", instanceId)
|
||||
}
|
||||
|
||||
return ports[0].Id, nil
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090596.html
|
||||
func (self *SRegion) AllocateEIP(name string, bwMbps int, chargeType TInternetChargeType, bgpType string, projectId string) (*SEip, error) {
|
||||
params := map[string]interface{}{
|
||||
"bandwidth": map[string]interface{}{
|
||||
"name": name,
|
||||
"size": bwMbps,
|
||||
"share_type": "PER",
|
||||
"charge_mode": chargeType,
|
||||
},
|
||||
"publicip": map[string]interface{}{
|
||||
"type": bgpType,
|
||||
"ip_version": 4,
|
||||
"alias": name,
|
||||
},
|
||||
}
|
||||
if len(projectId) > 0 {
|
||||
params["enterprise_project_id"] = projectId
|
||||
}
|
||||
eip := &SEip{region: self}
|
||||
return eip, self.vpcCreate("publicips", params, eip)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetEip(eipId string) (*SEip, error) {
|
||||
eip := &SEip{region: self}
|
||||
return eip, self.vpcGet("publicips/"+eipId, eip)
|
||||
}
|
||||
|
||||
func (self *SRegion) DeallocateEIP(eipId string) error {
|
||||
return self.vpcDelete("publicips/" + eipId)
|
||||
}
|
||||
|
||||
func (self *SRegion) AssociateEip(eipId string, instanceId string) error {
|
||||
portId, err := self.GetInstancePortId(instanceId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return self.AssociateEipWithPortId(eipId, portId)
|
||||
}
|
||||
|
||||
func (self *SRegion) AssociateEipWithPortId(eipId string, portId string) error {
|
||||
params := map[string]interface{}{
|
||||
"publicip": map[string]interface{}{
|
||||
"port_id": portId,
|
||||
},
|
||||
}
|
||||
return self.vpcUpdate("publicips/"+eipId, params)
|
||||
}
|
||||
|
||||
func (self *SRegion) DissociateEip(eipId string) error {
|
||||
return self.AssociateEipWithPortId(eipId, "")
|
||||
}
|
||||
|
||||
func (self *SRegion) UpdateEipBandwidth(bandwidthId string, bw int) error {
|
||||
params := map[string]interface{}{
|
||||
"bandwidth": map[string]interface{}{
|
||||
"size": bw,
|
||||
},
|
||||
}
|
||||
return self.vpcUpdate("bandwidths/"+bandwidthId, params)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetEipBandwidth(id string) (*Bandwidth, error) {
|
||||
ret := &Bandwidth{}
|
||||
return ret, self.vpcGet("bandwidths/"+id, ret)
|
||||
}
|
||||
|
||||
func (self *SEip) GetProjectId() string {
|
||||
return self.EnterpriseProjectId
|
||||
}
|
||||
|
||||
func (self *SRegion) GetEips(portId string, addrs []string) ([]SEip, error) {
|
||||
query := url.Values{}
|
||||
for _, addr := range addrs {
|
||||
query.Add("public_ip_address", addr)
|
||||
}
|
||||
if len(portId) > 0 {
|
||||
query.Set("port_id", portId)
|
||||
}
|
||||
eips := []SEip{}
|
||||
return eips, self.vpcList("publicips", query, &eips)
|
||||
}
|
||||
|
||||
type SEipType struct {
|
||||
Id string
|
||||
Type string
|
||||
Name string
|
||||
Group string
|
||||
}
|
||||
|
||||
func (self *SRegion) GetEipTypes() ([]SEipType, error) {
|
||||
query := url.Values{}
|
||||
ret := []SEipType{}
|
||||
return ret, self.vpcList("publicip_types", query, &ret)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIEips() ([]cloudprovider.ICloudEIP, error) {
|
||||
eips, err := self.GetEips("", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := []cloudprovider.ICloudEIP{}
|
||||
for i := 0; i < len(eips); i += 1 {
|
||||
eips[i].region = self
|
||||
ret = append(ret, &eips[i])
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIEipById(id string) (cloudprovider.ICloudEIP, error) {
|
||||
eip, err := self.GetEip(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return eip, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateEIP(eip *cloudprovider.SEip) (cloudprovider.ICloudEIP, error) {
|
||||
var ctype TInternetChargeType
|
||||
switch eip.ChargeType {
|
||||
case api.EIP_CHARGE_TYPE_BY_TRAFFIC:
|
||||
ctype = InternetChargeByTraffic
|
||||
case api.EIP_CHARGE_TYPE_BY_BANDWIDTH:
|
||||
ctype = InternetChargeByBandwidth
|
||||
}
|
||||
|
||||
// todo: 如何避免hardcode。集成到cloudmeta服务中?
|
||||
if len(eip.BGPType) == 0 {
|
||||
types, err := self.GetEipTypes()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetEipTypes")
|
||||
}
|
||||
if len(types) > 0 {
|
||||
eip.BGPType = types[0].Type
|
||||
}
|
||||
}
|
||||
|
||||
// 华为云EIP名字最大长度64
|
||||
if len(eip.Name) > 64 {
|
||||
eip.Name = eip.Name[:64]
|
||||
}
|
||||
|
||||
ieip, err := self.AllocateEIP(eip.Name, eip.BandwidthMbps, ctype, eip.BGPType, eip.ProjectId)
|
||||
ieip.region = self
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = cloudprovider.WaitStatus(ieip, api.EIP_STATUS_READY, 5*time.Second, 60*time.Second)
|
||||
return ieip, err
|
||||
}
|
||||
@@ -0,0 +1,771 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/huaweicloud/huaweicloud-sdk-go-obs/obs"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/gotypes"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud/hcso/client/auth"
|
||||
"yunion.io/x/onecloud/pkg/util/httputils"
|
||||
)
|
||||
|
||||
const (
|
||||
VERSION_SPEC = "x-version"
|
||||
|
||||
CLOUD_PROVIDER_HCS = api.CLOUD_PROVIDER_HCS
|
||||
CLOUD_PROVIDER_HCS_CN = "华为云Stack"
|
||||
CLOUD_PROVIDER_HCS_EN = "HCS"
|
||||
|
||||
HCS_API_VERSION = ""
|
||||
)
|
||||
|
||||
type HcsConfig struct {
|
||||
cpcfg cloudprovider.ProviderConfig
|
||||
authUrl string
|
||||
|
||||
projectId string // 华为云项目ID.
|
||||
accessKey string
|
||||
accessSecret string
|
||||
|
||||
debug bool
|
||||
}
|
||||
|
||||
func NewHcsConfig(accessKey, accessSecret, projectId, url string) *HcsConfig {
|
||||
cfg := &HcsConfig{
|
||||
projectId: projectId,
|
||||
accessKey: accessKey,
|
||||
accessSecret: accessSecret,
|
||||
authUrl: url,
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
func (cfg *HcsConfig) CloudproviderConfig(cpcfg cloudprovider.ProviderConfig) *HcsConfig {
|
||||
cfg.cpcfg = cpcfg
|
||||
return cfg
|
||||
}
|
||||
|
||||
func (cfg *HcsConfig) Debug(debug bool) *HcsConfig {
|
||||
cfg.debug = debug
|
||||
return cfg
|
||||
}
|
||||
|
||||
type SHcsClient struct {
|
||||
*HcsConfig
|
||||
|
||||
signer auth.Signer
|
||||
|
||||
isMainProject bool // whether the project is the main project in the region
|
||||
|
||||
domainId string
|
||||
projectName string
|
||||
|
||||
regions []SRegion
|
||||
projects []SProject
|
||||
buckets []SBucket
|
||||
|
||||
defaultRegion string
|
||||
|
||||
httpClient *http.Client
|
||||
lock sThrottlingThreshold
|
||||
}
|
||||
|
||||
func NewHcsClient(cfg *HcsConfig) (*SHcsClient, error) {
|
||||
client := SHcsClient{
|
||||
HcsConfig: cfg,
|
||||
lock: sThrottlingThreshold{locked: false, lockTime: time.Time{}},
|
||||
}
|
||||
if len(client.projectId) > 0 {
|
||||
project, err := client.GetProjectById(client.projectId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client.domainId = project.DomainId
|
||||
client.projectName = project.Name
|
||||
}
|
||||
return &client, client.fetchRegions()
|
||||
}
|
||||
|
||||
func (self *SHcsClient) GetAccountId() string {
|
||||
return self.authUrl
|
||||
}
|
||||
|
||||
func (self *SHcsClient) GetRegion(id string) (*SRegion, error) {
|
||||
for i := range self.regions {
|
||||
if self.regions[i].GetGlobalId() == id || self.regions[i].GetId() == id {
|
||||
self.regions[i].client = self
|
||||
return &self.regions[i], nil
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SHcsClient) GetCloudRegionExternalIdPrefix() string {
|
||||
if len(self.projectId) > 0 {
|
||||
if iregions := self.GetIRegions(); len(iregions) > 0 {
|
||||
return iregions[0].GetGlobalId()
|
||||
}
|
||||
}
|
||||
return CLOUD_PROVIDER_HCS
|
||||
}
|
||||
|
||||
type akClient struct {
|
||||
client *http.Client
|
||||
signer *Signer
|
||||
}
|
||||
|
||||
func (self *SHcsClient) getDefaultClient() *http.Client {
|
||||
if self.httpClient != nil {
|
||||
return self.httpClient
|
||||
}
|
||||
self.httpClient = self.cpcfg.AdaptiveTimeoutHttpClient()
|
||||
ts, _ := self.httpClient.Transport.(*http.Transport)
|
||||
self.httpClient.Transport = cloudprovider.GetCheckTransport(ts, func(req *http.Request) (func(resp *http.Response), error) {
|
||||
service, method, path := strings.Split(req.URL.Host, ".")[0], req.Method, req.URL.Path
|
||||
respCheck := func(resp *http.Response) {
|
||||
if resp.StatusCode == 403 {
|
||||
if self.cpcfg.UpdatePermission != nil {
|
||||
self.cpcfg.UpdatePermission(service, fmt.Sprintf("%s %s", method, path))
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.cpcfg.ReadOnly {
|
||||
if req.Method == "GET" {
|
||||
return respCheck, nil
|
||||
}
|
||||
return nil, errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.Path)
|
||||
}
|
||||
return respCheck, nil
|
||||
})
|
||||
return self.httpClient
|
||||
}
|
||||
|
||||
func (self *akClient) Do(req *http.Request) (*http.Response, error) {
|
||||
req.Header.Del("Accept")
|
||||
if req.Method == string(httputils.GET) || req.Method == string(httputils.DELETE) || req.Method == string(httputils.PATCH) {
|
||||
req.Header.Del("Content-Length")
|
||||
}
|
||||
err := self.signer.Sign(req)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Sign")
|
||||
}
|
||||
return self.client.Do(req)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) getAkClient() *akClient {
|
||||
return &akClient{
|
||||
client: self.getDefaultClient(),
|
||||
signer: &Signer{
|
||||
Key: self.accessKey,
|
||||
Secret: self.accessSecret,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type hcsError struct {
|
||||
Url string
|
||||
Params map[string]interface{}
|
||||
Code int `json:"code,omitzero"`
|
||||
Class string `json:"class,omitempty"`
|
||||
ErrorMsg string `json:"error_msg,omitempty"`
|
||||
Details string `json:"details,omitempty"`
|
||||
}
|
||||
|
||||
func (ce *hcsError) Error() string {
|
||||
return jsonutils.Marshal(ce).String()
|
||||
}
|
||||
|
||||
func (ce *hcsError) ParseErrorFromJsonResponse(statusCode int, body jsonutils.JSONObject) error {
|
||||
body.Unmarshal(ce)
|
||||
if ce.Code == 0 {
|
||||
ce.Code = statusCode
|
||||
}
|
||||
if len(ce.Class) == 0 {
|
||||
ce.Class = http.StatusText(statusCode)
|
||||
}
|
||||
if len(ce.Details) == 0 {
|
||||
ce.Details = body.String()
|
||||
}
|
||||
return ce
|
||||
}
|
||||
|
||||
type sThrottlingThreshold struct {
|
||||
locked bool
|
||||
lockTime time.Time
|
||||
}
|
||||
|
||||
func (t *sThrottlingThreshold) CheckingLock() {
|
||||
if !t.locked {
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
if t.lockTime.Sub(time.Now()).Seconds() < 0 {
|
||||
return
|
||||
}
|
||||
log.Debugf("throttling threshold has been reached. release at %s", t.lockTime)
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *sThrottlingThreshold) Lock() {
|
||||
// 锁定至少15秒
|
||||
t.locked = true
|
||||
t.lockTime = time.Now().Add(15 * time.Second)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) request(method httputils.THttpMethod, url string, query url.Values, params map[string]interface{}) (jsonutils.JSONObject, error) {
|
||||
self.lock.CheckingLock()
|
||||
client := self.getAkClient()
|
||||
if len(query) > 0 {
|
||||
url = fmt.Sprintf("%s?%s", url, query.Encode())
|
||||
}
|
||||
url = strings.TrimPrefix(url, "http://")
|
||||
if !strings.HasPrefix(url, "https://") {
|
||||
url = fmt.Sprintf("https://%s", url)
|
||||
}
|
||||
var body jsonutils.JSONObject = nil
|
||||
if len(params) > 0 {
|
||||
body = jsonutils.Marshal(params)
|
||||
}
|
||||
header := http.Header{}
|
||||
if len(self.projectId) > 0 && !strings.Contains(url, "v3/regions") {
|
||||
header.Set("X-Project-Id", self.projectId)
|
||||
}
|
||||
if len(self.domainId) > 0 {
|
||||
//header.Set("X-Domain-Id", self.domainId)
|
||||
}
|
||||
cli := httputils.NewJsonClient(client)
|
||||
req := httputils.NewJsonRequest(method, url, body)
|
||||
req.SetHeader(header)
|
||||
var resp jsonutils.JSONObject
|
||||
var err error
|
||||
for i := 0; i < 4; i++ {
|
||||
_, resp, err = cli.Send(context.Background(), req, &hcsError{Url: url, Params: params}, self.debug)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
e, ok := err.(*hcsError)
|
||||
if ok {
|
||||
if e.Code == 404 {
|
||||
return nil, errors.Wrapf(cloudprovider.ErrNotFound, err.Error())
|
||||
}
|
||||
if e.Code == 429 {
|
||||
log.Errorf("request %s %v try later", url, err)
|
||||
self.lock.Lock()
|
||||
time.Sleep(time.Second * 15)
|
||||
continue
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (self *SHcsClient) iamGet(resource string, query url.Values) (jsonutils.JSONObject, error) {
|
||||
url := fmt.Sprintf("iam-apigateway-proxy.%s/%s", self.authUrl, resource)
|
||||
return self.request(httputils.GET, url, query, nil)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) fetchRegions() error {
|
||||
if len(self.regions) > 0 {
|
||||
return nil
|
||||
}
|
||||
resp, err := self.iamGet("v3/regions", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
self.regions = []SRegion{}
|
||||
err = resp.Unmarshal(&self.regions, "regions")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range self.regions {
|
||||
self.defaultRegion = self.regions[i].Id
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hcscli *SHcsClient) rdsList(regionId string, resource string, query url.Values, retVal interface{}) error {
|
||||
return hcscli.list("rds", "v3", regionId, resource, query, retVal)
|
||||
}
|
||||
|
||||
func (hcscli *SHcsClient) rdsGet(regionId string, resource string, retVal interface{}) error {
|
||||
return hcscli.get("rds", "v3", regionId, resource, retVal)
|
||||
}
|
||||
|
||||
func (hcscli *SHcsClient) rdsDelete(regionId string, resource string) error {
|
||||
return hcscli._delete("rds", "v3", regionId, resource)
|
||||
}
|
||||
|
||||
func (hcscli *SHcsClient) rdsCreate(regionId string, resource string, body map[string]interface{}, retVal interface{}) error {
|
||||
return hcscli._create("rds", "v3", regionId, resource, body, retVal)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) rdsPerform(regionId string, resource, action string, params map[string]interface{}, retVal interface{}) error {
|
||||
return self._perform("rds", "v3", regionId, resource, action, params, retVal)
|
||||
}
|
||||
|
||||
func (hcscli *SHcsClient) rdsJobGet(regionId string, resource string, query url.Values, retVal interface{}) error {
|
||||
url := hcscli._url("rds", "v3", regionId, resource)
|
||||
resp, err := hcscli.request(httputils.GET, url, query, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = resp.Unmarshal(retVal)
|
||||
return err
|
||||
}
|
||||
|
||||
func (hcscli *SHcsClient) rdsDBPrivvilegesDelete(regionId string, resource string, params map[string]interface{}) error {
|
||||
return hcscli._deleteWithBody("rds", "v3", regionId, resource, params)
|
||||
}
|
||||
|
||||
func (hcscli *SHcsClient) rdsDBPrivilegesGrant(regionId string, resource string, params map[string]interface{}, retVal interface{}) error {
|
||||
url := hcscli._url("rds", "v3", regionId, resource)
|
||||
resp, err := hcscli.request(httputils.GET, url, nil, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = resp.Unmarshal(retVal)
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SHcsClient) ecsList(regionId string, resource string, query url.Values, retVal interface{}) error {
|
||||
return self.list("ecs", "v2", regionId, resource, query, retVal)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) list(product, version, regionId string, resource string, query url.Values, retVal interface{}) error {
|
||||
resp, err := self._list(product, version, regionId, resource, query)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "_list")
|
||||
}
|
||||
return resp.Unmarshal(retVal)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) ecsGet(regionId string, resource string, retVal interface{}) error {
|
||||
return self.get("ecs", "v2", regionId, resource, retVal)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) ecsDelete(regionId string, resource string) error {
|
||||
return self._delete("ecs", "v2", regionId, resource)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) ecsPerform(regionId string, resource, action string, params map[string]interface{}, retVal interface{}) error {
|
||||
return self._perform("ecs", "v2", regionId, resource, action, params, retVal)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) evsDelete(regionId string, resource string) error {
|
||||
return self._delete("evs", "v2", regionId, resource)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) evsList(regionId string, resource string, query url.Values, retVal interface{}) error {
|
||||
return self.list("evs", "v2", regionId, resource, query, retVal)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) evsGet(regionId string, resource string, retVal interface{}) error {
|
||||
return self.get("evs", "v2", regionId, resource, retVal)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) evsPerform(regionId string, resource, action string, params map[string]interface{}) error {
|
||||
return self._perform("evs", "v2", regionId, resource, action, params, nil)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) ecsCreate(regionId string, resource string, body map[string]interface{}, retVal interface{}) error {
|
||||
return self._create("ecs", "v2", regionId, resource, body, retVal)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) evsCreate(regionId string, resource string, body map[string]interface{}, retVal interface{}) error {
|
||||
return self._create("evs", "v2", regionId, resource, body, retVal)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) vpcCreate(regionId string, resource string, body map[string]interface{}, retVal interface{}) error {
|
||||
return self._create("vpc", "v1", regionId, resource, body, retVal)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) vpcDelete(regionId string, resource string) error {
|
||||
return self._delete("vpc", "v1", regionId, resource)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) vpcGet(regionId string, resource string, retVal interface{}) error {
|
||||
return self.get("vpc", "v1", regionId, resource, retVal)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) vpcList(regionId string, resource string, query url.Values, retVal interface{}) error {
|
||||
return self.list("vpc", "v1", regionId, resource, query, retVal)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) vpcUpdate(regionId string, resource string, body map[string]interface{}) error {
|
||||
return self._update("vpc", "v1", regionId, resource, body)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) imsCreate(regionId string, resource string, body map[string]interface{}, retVal interface{}) error {
|
||||
return self._create("ims", "v2", regionId, resource, body, retVal)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) imsDelete(regionId string, resource string) error {
|
||||
return self._delete("ims", "v2", regionId, resource)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) imsGet(regionId string, resource string, retVal interface{}) error {
|
||||
return self.get("ims", "v2", regionId, resource, retVal)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) imsList(regionId string, resource string, query url.Values, retVal interface{}) error {
|
||||
return self.list("ims", "v2", regionId, resource, query, retVal)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) imsPerform(regionId string, resource, action string, params map[string]interface{}, retVal interface{}) error {
|
||||
return self._perform("ims", "v2", regionId, resource, action, params, retVal)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) imsUpdate(regionId string, resource string, body map[string]interface{}) error {
|
||||
return self._update("ims", "v2", regionId, resource, body)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) dcsCreate(regionId string, resource string, body map[string]interface{}, retVal interface{}) error {
|
||||
return self._create("dcs", "v1.0", regionId, resource, body, retVal)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) dcsDelete(regionId string, resource string) error {
|
||||
return self._delete("dcs", "v1.0", regionId, resource)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) dcsGet(regionId string, resource string, retVal interface{}) error {
|
||||
return self.get("dcs", "v1.0", regionId, resource, retVal)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) dcsList(regionId string, resource string, query url.Values, retVal interface{}) error {
|
||||
return self.list("dcs", "v1.0", regionId, resource, query, retVal)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) dcsPerform(regionId string, resource, action string, params map[string]interface{}, retVal interface{}) error {
|
||||
return self._perform("dcs", "v1.0", regionId, resource, action, params, retVal)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) dcsUpdate(regionId string, resource string, body map[string]interface{}) error {
|
||||
return self._update("dcs", "v1.0", regionId, resource, body)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) _url(product, version, regionId string, resource string) string {
|
||||
url := fmt.Sprintf("%s.%s.%s/%s/%s/%s", product, regionId, self.authUrl, version, self.projectId, resource)
|
||||
for _, prefix := range []string{
|
||||
"images", "cloudimages", "nat_gateways",
|
||||
"lbaas", "products", "snat_rules",
|
||||
"dnat_rules", "vpc/peerings",
|
||||
} {
|
||||
if strings.HasPrefix(resource, prefix) {
|
||||
url = fmt.Sprintf("%s.%s.%s/%s/%s", product, regionId, self.authUrl, version, resource)
|
||||
break
|
||||
}
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
func (self *SHcsClient) _list(product, version, regionId string, resource string, query url.Values) (*jsonutils.JSONArray, error) {
|
||||
ret := jsonutils.NewArray()
|
||||
url := self._url(product, version, regionId, resource)
|
||||
offset, _ := strconv.Atoi(query.Get("offset"))
|
||||
total := int64(0)
|
||||
for {
|
||||
resp, err := self.request(httputils.GET, url, query, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if gotypes.IsNil(resp) {
|
||||
log.Warningf("%s return empty", resource)
|
||||
return ret, nil
|
||||
}
|
||||
objMap, err := resp.GetMap()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "resp.GetMap")
|
||||
}
|
||||
next := false
|
||||
for k, v := range objMap {
|
||||
if k == "links" {
|
||||
url, _ = v.GetString("next")
|
||||
next = true
|
||||
continue
|
||||
}
|
||||
if k == "count" {
|
||||
offset++
|
||||
query.Set("offset", fmt.Sprintf("%d", offset))
|
||||
total, _ = v.Int()
|
||||
next = true
|
||||
continue
|
||||
}
|
||||
if strings.Contains(resource, k) ||
|
||||
strings.Contains(strings.ReplaceAll(resource, "-", "_"), k) ||
|
||||
utils.IsInStringArray(k, []string{
|
||||
"availabilityZoneInfo",
|
||||
}) {
|
||||
objs, err := v.GetArray()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "v.GetArray")
|
||||
}
|
||||
ret.Add(objs...)
|
||||
}
|
||||
}
|
||||
if !next || len(url) == 0 || (ret.Length() == int(total)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SHcsClient) get(product, version, regionId string, resource string, retVal interface{}) error {
|
||||
resp, err := self._get(product, version, regionId, resource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
obj, err := resp.GetMap()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetMap")
|
||||
}
|
||||
for v := range obj {
|
||||
if len(obj) == 1 {
|
||||
return obj[v].Unmarshal(retVal)
|
||||
}
|
||||
}
|
||||
return resp.Unmarshal(retVal)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) _getJob(product, regionId string, jobId string) (jsonutils.JSONObject, error) {
|
||||
url := self._url(product, "v1", regionId, fmt.Sprintf("jobs/%s", jobId))
|
||||
resp, err := self.request(httputils.GET, url, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (self *SHcsClient) _get(product, version, regionId string, resource string) (jsonutils.JSONObject, error) {
|
||||
url := self._url(product, version, regionId, resource)
|
||||
resp, err := self.request(httputils.GET, url, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (self *SHcsClient) delete(product, version, regionId string, resource string) error {
|
||||
return self._delete(product, version, regionId, resource)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) _delete(product, version, regionId string, resource string) error {
|
||||
url := self._url(product, version, regionId, resource)
|
||||
resp, err := self.request(httputils.DELETE, url, nil, nil)
|
||||
if !gotypes.IsNil(resp) && resp.Contains("job_id") {
|
||||
jobId, _ := resp.GetString("job_id")
|
||||
_, err := self.waitJobSuccess(product, regionId, jobId, time.Second*10, time.Hour*2)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "wait create %s %s job", product, resource)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SHcsClient) _deleteWithBody(product, version, regionId string, resource string, params map[string]interface{}) error {
|
||||
url := self._url(product, version, regionId, resource)
|
||||
_, err := self.request(httputils.DELETE, url, nil, params)
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SHcsClient) update(product, version, regionId string, resource string, params map[string]interface{}) error {
|
||||
return self._update(product, version, regionId, resource, params)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) perform(product, version, regionId string, resource, action string, params map[string]interface{}, retVal interface{}) error {
|
||||
return self._perform(product, version, regionId, resource, action, params, retVal)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) _perform(product, version, regionId string, resource, action string, params map[string]interface{}, retVal interface{}) error {
|
||||
url := self._url(product, version, regionId, resource+"/"+action)
|
||||
resp, err := self.request(httputils.POST, url, nil, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !gotypes.IsNil(resp) && resp.Contains("job_id") {
|
||||
jobId, _ := resp.GetString("job_id")
|
||||
job, err := self.waitJobSuccess(product, regionId, jobId, time.Second*10, time.Hour*2)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "wait create %s %s job", product, resource)
|
||||
}
|
||||
if retVal != nil {
|
||||
return jsonutils.Update(retVal, jsonutils.Marshal(job))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if retVal != nil {
|
||||
return resp.Unmarshal(retVal)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SHcsClient) create(product, version, regionId string, resource string, body map[string]interface{}, retVal interface{}) error {
|
||||
return self._create(product, version, regionId, resource, body, retVal)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) _create(product, version, regionId string, resource string, body map[string]interface{}, retVal interface{}) error {
|
||||
url := self._url(product, version, regionId, resource)
|
||||
resp, err := self.request(httputils.POST, url, nil, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.Contains("job_id") {
|
||||
jobId, _ := resp.GetString("job_id")
|
||||
job, err := self.waitJobSuccess(product, regionId, jobId, time.Second*10, time.Hour*2)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "wait create %s %s job", product, resource)
|
||||
}
|
||||
if retVal != nil {
|
||||
return jsonutils.Update(retVal, jsonutils.Marshal(job))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
obj, err := resp.GetMap()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetMap")
|
||||
}
|
||||
for v := range obj {
|
||||
if len(obj) == 1 && retVal != nil {
|
||||
return obj[v].Unmarshal(retVal)
|
||||
}
|
||||
}
|
||||
if retVal != nil {
|
||||
return resp.Unmarshal(retVal)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SHcsClient) _update(product, version, regionId string, resource string, body map[string]interface{}) error {
|
||||
url := self._url(product, version, regionId, resource)
|
||||
_, err := self.request(httputils.PUT, url, nil, body)
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SHcsClient) GetRegions() []SRegion {
|
||||
return self.regions
|
||||
}
|
||||
|
||||
func (self *SHcsClient) GetIRegions() []cloudprovider.ICloudRegion {
|
||||
ret := []cloudprovider.ICloudRegion{}
|
||||
if len(self.projectId) == 0 {
|
||||
for i := range self.regions {
|
||||
self.regions[i].client = self
|
||||
self.defaultRegion = self.regions[i].Id
|
||||
ret = append(ret, &self.regions[i])
|
||||
}
|
||||
return ret
|
||||
} else {
|
||||
for i := range self.regions {
|
||||
if strings.Contains(self.projectName, self.regions[i].Id) {
|
||||
self.defaultRegion = self.regions[i].Id
|
||||
self.regions[i].client = self
|
||||
ret = append(ret, &self.regions[i])
|
||||
}
|
||||
if self.projectName == self.regions[i].Id {
|
||||
self.isMainProject = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (self *SHcsClient) GetCapabilities() []string {
|
||||
caps := []string{
|
||||
cloudprovider.CLOUD_CAPABILITY_PROJECT,
|
||||
cloudprovider.CLOUD_CAPABILITY_COMPUTE,
|
||||
cloudprovider.CLOUD_CAPABILITY_NETWORK,
|
||||
cloudprovider.CLOUD_CAPABILITY_CACHE,
|
||||
cloudprovider.CLOUD_CAPABILITY_RDS,
|
||||
cloudprovider.CLOUD_CAPABILITY_LOADBALANCER,
|
||||
cloudprovider.CLOUD_CAPABILITY_NAT,
|
||||
}
|
||||
// huawei objectstore is shared across projects(subscriptions)
|
||||
// to avoid multiple project access the same bucket
|
||||
// only main project is allow to access objectstore bucket
|
||||
if self.isMainProject {
|
||||
caps = append(caps, cloudprovider.CLOUD_CAPABILITY_OBJECTSTORE)
|
||||
}
|
||||
return caps
|
||||
}
|
||||
|
||||
func (self *SHcsClient) getOBSEndpoint(regionId string) string {
|
||||
return fmt.Sprintf("obs.%s.%s", regionId, self.authUrl)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) getOBSClient(regionId string) (*obs.ObsClient, error) {
|
||||
endpoint := self.getOBSEndpoint(regionId)
|
||||
ts, _ := self.httpClient.Transport.(*http.Transport)
|
||||
conf := obs.WithHttpTransport(ts)
|
||||
cli, err := obs.New(self.accessKey, self.accessSecret, endpoint, conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cli, nil
|
||||
}
|
||||
|
||||
func (self *SHcsClient) GetBuckets() ([]SBucket, error) {
|
||||
if len(self.buckets) > 0 {
|
||||
return self.buckets, nil
|
||||
}
|
||||
obscli, err := self.getOBSClient(self.regions[0].Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "getOBSClient")
|
||||
}
|
||||
input := &obs.ListBucketsInput{QueryLocation: true}
|
||||
output, err := obscli.ListBuckets(input)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "obscli.ListBuckets")
|
||||
}
|
||||
self.buckets = []SBucket{}
|
||||
for i := range output.Buckets {
|
||||
bInfo := output.Buckets[i]
|
||||
region, err := self.GetRegion(bInfo.Location)
|
||||
if err != nil {
|
||||
log.Errorf("fail to find region %s", bInfo.Location)
|
||||
continue
|
||||
}
|
||||
b := SBucket{
|
||||
region: region,
|
||||
|
||||
Name: bInfo.Name,
|
||||
Location: bInfo.Location,
|
||||
CreationDate: bInfo.CreationDate,
|
||||
}
|
||||
self.buckets = append(self.buckets, b)
|
||||
}
|
||||
return self.buckets, nil
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/osprofile"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
"yunion.io/x/onecloud/pkg/util/billing"
|
||||
)
|
||||
|
||||
type SHost struct {
|
||||
multicloud.SHostBase
|
||||
zone *SZone
|
||||
}
|
||||
|
||||
func (self *SHost) GetId() string {
|
||||
return fmt.Sprintf("%s-%s", self.zone.region.client.cpcfg.Id, self.zone.GetId())
|
||||
}
|
||||
|
||||
func (self *SHost) GetName() string {
|
||||
return fmt.Sprintf("%s-%s", self.zone.region.client.cpcfg.Name, self.zone.GetId())
|
||||
}
|
||||
|
||||
func (self *SHost) GetGlobalId() string {
|
||||
return fmt.Sprintf("%s-%s", self.zone.region.client.cpcfg.Id, self.zone.GetId())
|
||||
}
|
||||
|
||||
func (self *SHost) IsEmulated() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SHost) GetStatus() string {
|
||||
return api.HOST_STATUS_RUNNING
|
||||
}
|
||||
|
||||
func (self *SHost) GetIVMs() ([]cloudprovider.ICloudVM, error) {
|
||||
vms, err := self.zone.region.GetInstances("")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := []cloudprovider.ICloudVM{}
|
||||
for i := range vms {
|
||||
if vms[i].OSEXTAZAvailabilityZone == self.zone.GetId() {
|
||||
vms[i].host = self
|
||||
ret = append(ret, &vms[i])
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SHost) GetIVMById(id string) (cloudprovider.ICloudVM, error) {
|
||||
vm, err := self.zone.region.GetInstance(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vm.host = self
|
||||
return vm, nil
|
||||
}
|
||||
|
||||
func (self *SHost) GetIWires() ([]cloudprovider.ICloudWire, error) {
|
||||
return self.zone.GetIWires()
|
||||
}
|
||||
|
||||
func (self *SHost) GetIStorages() ([]cloudprovider.ICloudStorage, error) {
|
||||
return self.zone.GetIStorages()
|
||||
}
|
||||
|
||||
func (self *SHost) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
|
||||
return self.zone.GetIStorageById(id)
|
||||
}
|
||||
|
||||
func (self *SHost) GetEnabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SHost) GetHostStatus() string {
|
||||
return api.HOST_ONLINE
|
||||
}
|
||||
|
||||
func (self *SHost) GetAccessIp() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SHost) GetAccessMac() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SHost) GetSysInfo() jsonutils.JSONObject {
|
||||
info := jsonutils.NewDict()
|
||||
info.Add(jsonutils.NewString(CLOUD_PROVIDER_HCS), "manufacture")
|
||||
return info
|
||||
}
|
||||
|
||||
func (self *SHost) GetSN() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SHost) GetCpuCount() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SHost) GetNodeCount() int8 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SHost) GetCpuDesc() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SHost) GetCpuMhz() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SHost) GetMemSizeMB() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SHost) GetStorageSizeMB() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SHost) GetStorageType() string {
|
||||
return api.DISK_TYPE_HYBRID
|
||||
}
|
||||
|
||||
func (self *SHost) GetHostType() string {
|
||||
return api.HOST_TYPE_HCS
|
||||
}
|
||||
|
||||
func (self *SHost) GetIsMaintenance() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SHost) GetVersion() string {
|
||||
return HCS_API_VERSION
|
||||
}
|
||||
|
||||
func (self *SHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) {
|
||||
vm, err := self._createVM(
|
||||
desc.Name, desc.ExternalImageId, desc.SysDisk,
|
||||
desc.Cpu, desc.MemoryMB, desc.InstanceType,
|
||||
desc.ExternalNetworkId, desc.IpAddr,
|
||||
desc.Description, desc.Account,
|
||||
desc.Password, desc.DataDisks,
|
||||
desc.PublicKey, desc.ExternalSecgroupId,
|
||||
desc.UserData, desc.BillingCycle, desc.ProjectId, desc.Tags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vm, err
|
||||
}
|
||||
|
||||
func (self *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SHost) _createVM(name string, imgId string, sysDisk cloudprovider.SDiskInfo, cpu int, memMB int, instanceType string,
|
||||
networkId string, ipAddr string, desc string, account string, passwd string,
|
||||
diskSizes []cloudprovider.SDiskInfo, publicKey string, secgroupId string,
|
||||
userData string, bc *billing.SBillingCycle, projectId string, tags map[string]string) (*SInstance, error) {
|
||||
net, err := self.zone.region.GetNetwork(networkId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetNetwork(%s)", networkId)
|
||||
}
|
||||
|
||||
keypair := ""
|
||||
if len(publicKey) > 0 {
|
||||
keypair, err = self.zone.region.syncKeypair(publicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 镜像及硬盘配置
|
||||
img, err := self.zone.region.GetImage(imgId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetImage(%s)", imgId)
|
||||
}
|
||||
// passwd, windows机型直接使用密码比较方便
|
||||
if strings.ToLower(img.Platform) == strings.ToLower(osprofile.OS_TYPE_WINDOWS) && len(passwd) > 0 {
|
||||
keypair = ""
|
||||
}
|
||||
|
||||
if strings.ToLower(img.Platform) == strings.ToLower(osprofile.OS_TYPE_WINDOWS) {
|
||||
if u, err := updateWindowsUserData(userData, img.OSVersion, account, passwd); err == nil {
|
||||
userData = u
|
||||
} else {
|
||||
return nil, errors.Wrap(err, "SHost.CreateVM.updateWindowsUserData")
|
||||
}
|
||||
}
|
||||
|
||||
if sysDisk.SizeGB > 0 && sysDisk.SizeGB < img.SizeGB {
|
||||
sysDisk.SizeGB = img.SizeGB
|
||||
}
|
||||
|
||||
// 创建实例
|
||||
if len(instanceType) > 0 {
|
||||
log.Debugf("Try instancetype : %s", instanceType)
|
||||
vm, err := self.zone.region.CreateInstance(name, imgId, instanceType, networkId, secgroupId,
|
||||
net.VpcId, self.zone.GetId(), desc, sysDisk, diskSizes, ipAddr, keypair,
|
||||
passwd, userData, bc, projectId, tags)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create %s failed:%v", instanceType, err)
|
||||
}
|
||||
return vm, nil
|
||||
}
|
||||
|
||||
// 匹配实例类型
|
||||
instanceTypes, err := self.zone.region.GetMatchInstanceTypes(cpu, memMB, self.zone.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(instanceTypes) == 0 {
|
||||
return nil, fmt.Errorf("instance type %dC%dMB not avaiable", cpu, memMB)
|
||||
}
|
||||
|
||||
var vm *SInstance
|
||||
for _, instType := range instanceTypes {
|
||||
instanceTypeId := instType.Name
|
||||
log.Debugf("Try instancetype : %s", instanceTypeId)
|
||||
vm, err = self.zone.region.CreateInstance(name, imgId, instanceTypeId, networkId, secgroupId,
|
||||
net.VpcId, self.zone.GetId(), desc, sysDisk, diskSizes, ipAddr, keypair,
|
||||
passwd, userData, bc, projectId, tags)
|
||||
if err != nil {
|
||||
log.Errorf("Failed for %s: %s", instanceTypeId, err)
|
||||
} else {
|
||||
return vm, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("create failed: %v", err)
|
||||
}
|
||||
@@ -0,0 +1,466 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
"yunion.io/x/onecloud/pkg/util/imagetools"
|
||||
)
|
||||
|
||||
type TImageOwnerType string
|
||||
|
||||
const (
|
||||
ImageOwnerPublic TImageOwnerType = "gold" // 公共镜像:gold
|
||||
ImageOwnerSelf TImageOwnerType = "private" // 私有镜像:private
|
||||
ImageOwnerShared TImageOwnerType = "shared" // 共享镜像:shared
|
||||
|
||||
EnvFusionCompute = "FusionCompute"
|
||||
EnvIronic = "Ironic"
|
||||
)
|
||||
|
||||
const (
|
||||
ImageStatusQueued = "queued" // queued:表示镜像元数据已经创建成功,等待上传镜像文件。
|
||||
ImageStatusSaving = "saving" // saving:表示镜像正在上传文件到后端存储。
|
||||
ImageStatusDeleted = "deleted" // deleted:表示镜像已经删除。
|
||||
ImageStatusKilled = "killed" // killed:表示镜像上传错误。
|
||||
ImageStatusActive = "active" // active:表示镜像可以正常使用
|
||||
)
|
||||
|
||||
// https://support.huaweicloud.com/api-ims/zh-cn_topic_0020091565.html
|
||||
type SImage struct {
|
||||
multicloud.SImageBase
|
||||
multicloud.HuaweiTags
|
||||
cache *SStoragecache
|
||||
|
||||
// normalized image info
|
||||
imgInfo *imagetools.ImageInfo
|
||||
|
||||
Schema string `json:"schema"`
|
||||
MinDiskGB int64 `json:"min_disk"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
ImageSourceType string `json:"__image_source_type"`
|
||||
ContainerFormat string `json:"container_format"`
|
||||
File string `json:"file"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
Protected bool `json:"protected"`
|
||||
Checksum string `json:"checksum"`
|
||||
Id string `json:"id"`
|
||||
Isregistered string `json:"__isregistered"`
|
||||
MinRamMB int `json:"min_ram"`
|
||||
Lazyloading string `json:"__lazyloading"`
|
||||
Owner string `json:"owner"`
|
||||
OSType string `json:"__os_type"`
|
||||
Imagetype string `json:"__imagetype"`
|
||||
Visibility string `json:"visibility"`
|
||||
VirtualEnvType string `json:"virtual_env_type"`
|
||||
Platform string `json:"__platform"`
|
||||
SizeGB int `json:"size"`
|
||||
ImageSize int64 `json:"__image_size"`
|
||||
OSBit string `json:"__os_bit"`
|
||||
OSVersion string `json:"__os_version"`
|
||||
Architecture string
|
||||
Name string `json:"name"`
|
||||
Self string `json:"self"`
|
||||
DiskFormat string `json:"disk_format"`
|
||||
Status string `json:"status"`
|
||||
SupportKVMFPGAType string `json:"__support_kvm_fpga_type"`
|
||||
SupportKVMNVMEHIGHIO string `json:"__support_nvme_highio"`
|
||||
SupportLargeMemory string `json:"__support_largememory"`
|
||||
SupportDiskIntensive string `json:"__support_diskintensive"`
|
||||
SupportHighPerformance string `json:"__support_highperformance"`
|
||||
SupportXENGPUType string `json:"__support_xen_gpu_type"`
|
||||
SupportKVMGPUType string `json:"__support_kvm_gpu_type"`
|
||||
SupportGPUT4 string `json:"__support_gpu_t4"`
|
||||
SupportKVMAscend310 string `json:"__support_kvm_ascend_310"`
|
||||
SupportArm string `json:"__support_arm"`
|
||||
}
|
||||
|
||||
func (self *SImage) GetMinRamSizeMb() int {
|
||||
return self.MinRamMB
|
||||
}
|
||||
|
||||
func (self *SImage) GetId() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SImage) GetName() string {
|
||||
return self.Name
|
||||
}
|
||||
|
||||
func (self *SImage) GetGlobalId() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SImage) GetStatus() string {
|
||||
switch self.Status {
|
||||
case ImageStatusQueued:
|
||||
return api.CACHED_IMAGE_STATUS_CACHING
|
||||
case ImageStatusActive:
|
||||
return api.CACHED_IMAGE_STATUS_ACTIVE
|
||||
case ImageStatusKilled:
|
||||
return api.CACHED_IMAGE_STATUS_CACHE_FAILED
|
||||
default:
|
||||
return api.CACHED_IMAGE_STATUS_CACHE_FAILED
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SImage) GetImageStatus() string {
|
||||
switch self.Status {
|
||||
case ImageStatusQueued:
|
||||
return cloudprovider.IMAGE_STATUS_QUEUED
|
||||
case ImageStatusActive:
|
||||
return cloudprovider.IMAGE_STATUS_ACTIVE
|
||||
case ImageStatusKilled:
|
||||
return cloudprovider.IMAGE_STATUS_KILLED
|
||||
default:
|
||||
return cloudprovider.IMAGE_STATUS_KILLED
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SImage) Refresh() error {
|
||||
ret, err := self.cache.region.GetImage(self.GetId())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(self, ret)
|
||||
}
|
||||
|
||||
func (self *SImage) GetImageType() cloudprovider.TImageType {
|
||||
switch self.Imagetype {
|
||||
case "gold":
|
||||
return cloudprovider.ImageTypeSystem
|
||||
case "private":
|
||||
return cloudprovider.ImageTypeCustomized
|
||||
case "shared":
|
||||
return cloudprovider.ImageTypeShared
|
||||
default:
|
||||
return cloudprovider.ImageTypeCustomized
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SImage) GetSizeByte() int64 {
|
||||
return int64(self.MinDiskGB) * 1024 * 1024 * 1024
|
||||
}
|
||||
|
||||
func (self *SImage) getNormalizedImageInfo() *imagetools.ImageInfo {
|
||||
if self.imgInfo == nil {
|
||||
imgInfo := imagetools.NormalizeImageInfo(self.ImageSourceType, self.Architecture, self.OSType, self.Platform, "")
|
||||
self.imgInfo = &imgInfo
|
||||
}
|
||||
|
||||
return self.imgInfo
|
||||
}
|
||||
|
||||
func (self *SImage) GetFullOsName() string {
|
||||
return self.ImageSourceType
|
||||
}
|
||||
|
||||
func (self *SImage) GetOsType() cloudprovider.TOsType {
|
||||
return cloudprovider.TOsType(self.getNormalizedImageInfo().OsType)
|
||||
}
|
||||
|
||||
func (self *SImage) GetOsDist() string {
|
||||
return self.getNormalizedImageInfo().OsDistro
|
||||
}
|
||||
|
||||
func (self *SImage) GetOsVersion() string {
|
||||
return self.getNormalizedImageInfo().OsVersion
|
||||
}
|
||||
|
||||
func (self *SImage) GetOsLang() string {
|
||||
return self.getNormalizedImageInfo().OsLang
|
||||
}
|
||||
|
||||
func (self *SImage) GetOsArch() string {
|
||||
return self.getNormalizedImageInfo().OsArch
|
||||
}
|
||||
|
||||
func (self *SImage) GetBios() cloudprovider.TBiosType {
|
||||
return cloudprovider.ToBiosType(self.getNormalizedImageInfo().OsBios)
|
||||
}
|
||||
|
||||
func (self *SImage) GetMinOsDiskSizeGb() int {
|
||||
return int(self.MinDiskGB)
|
||||
}
|
||||
|
||||
func (self *SImage) GetImageFormat() string {
|
||||
return self.DiskFormat
|
||||
}
|
||||
|
||||
func (self *SImage) GetCreatedAt() time.Time {
|
||||
return self.CreatedAt
|
||||
}
|
||||
|
||||
func (self *SImage) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SImage) Delete(ctx context.Context) error {
|
||||
return self.cache.region.DeleteImage(self.GetId())
|
||||
}
|
||||
|
||||
func (self *SImage) GetIStoragecache() cloudprovider.ICloudStoragecache {
|
||||
return self.cache
|
||||
}
|
||||
|
||||
func (self *SRegion) GetImage(imageId string) (*SImage, error) {
|
||||
image := &SImage{cache: self.getStoragecache()}
|
||||
res := fmt.Sprintf("images/%s", imageId)
|
||||
return image, self.imsGet(res, image)
|
||||
}
|
||||
|
||||
func excludeImage(image SImage) bool {
|
||||
if image.VirtualEnvType == "Ironic" {
|
||||
return true
|
||||
}
|
||||
|
||||
if len(image.SupportDiskIntensive) > 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if len(image.SupportKVMFPGAType) > 0 || len(image.SupportKVMAscend310) > 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if len(image.SupportKVMGPUType) > 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if len(image.SupportKVMNVMEHIGHIO) > 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if len(image.SupportGPUT4) > 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if len(image.SupportXENGPUType) > 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if len(image.SupportHighPerformance) > 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if len(image.SupportArm) > 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-ims/zh-cn_topic_0060804959.html
|
||||
func (self *SRegion) GetImages(status string, imagetype TImageOwnerType, name string, envType string) ([]SImage, error) {
|
||||
params := url.Values{}
|
||||
if len(status) > 0 {
|
||||
params.Set("status", status)
|
||||
}
|
||||
|
||||
if len(imagetype) > 0 {
|
||||
params.Set("__imagetype", string(imagetype))
|
||||
if imagetype == ImageOwnerPublic {
|
||||
params.Set("protected", "True")
|
||||
}
|
||||
}
|
||||
if len(envType) > 0 {
|
||||
params.Set("virtual_env_type", envType)
|
||||
}
|
||||
|
||||
if len(name) > 0 {
|
||||
params.Set("name", name)
|
||||
}
|
||||
|
||||
images := []SImage{}
|
||||
err := self.imsList("images", params, &images)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 排除掉需要特定镜像才能创建的实例类型
|
||||
// https://support.huaweicloud.com/eu-west-0-api-ims/zh-cn_topic_0031617666.html#ZH-CN_TOPIC_0031617666__table48545918250
|
||||
// https://support.huaweicloud.com/productdesc-ecs/zh-cn_topic_0088142947.html
|
||||
filtedImages := make([]SImage, 0)
|
||||
for i := range images {
|
||||
if !excludeImage(images[i]) {
|
||||
filtedImages = append(filtedImages, images[i])
|
||||
}
|
||||
}
|
||||
return filtedImages, err
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteImage(imageId string) error {
|
||||
res := fmt.Sprintf("images/%s", imageId)
|
||||
return self.imsDelete(res)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetImageByName(name string) (*SImage, error) {
|
||||
if len(name) == 0 {
|
||||
return nil, fmt.Errorf("image name should not be empty")
|
||||
}
|
||||
images, err := self.GetImages("", TImageOwnerType(""), name, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(images) == 0 {
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
log.Debugf("%d image found match name %s", len(images), name)
|
||||
return &images[0], nil
|
||||
}
|
||||
|
||||
/* https://support.huaweicloud.com/api-ims/zh-cn_topic_0020092109.html
|
||||
os version 取值范围: https://support.huaweicloud.com/api-ims/zh-cn_topic_0031617666.html
|
||||
用于创建私有镜像的源云服务器系统盘大小大于等于40GB且不超过1024GB。
|
||||
目前支持vhd,zvhd、raw,qcow2
|
||||
todo: 考虑使用镜像快速导入。 https://support.huaweicloud.com/api-ims/zh-cn_topic_0133188204.html
|
||||
使用OBS文件创建镜像
|
||||
|
||||
* openstack原生接口支持的格式:https://support.huaweicloud.com/api-ims/zh-cn_topic_0031615566.html
|
||||
*/
|
||||
func (self *SRegion) ImportImageJob(name string, osDist string, osVersion string, osArch string, bucket string, key string, minDiskGB int64) error {
|
||||
os_version, err := stdVersion(osDist, osVersion, osArch)
|
||||
log.Debugf("%s %s %s: %s.min_disk %d GB", osDist, osVersion, osArch, os_version, minDiskGB)
|
||||
if err != nil {
|
||||
log.Debugln(err)
|
||||
}
|
||||
|
||||
image_url := fmt.Sprintf("%s:%s", bucket, key)
|
||||
params := map[string]interface{}{
|
||||
"name": name,
|
||||
"image_url": image_url,
|
||||
"os_version": os_version,
|
||||
"is_config_init": true,
|
||||
"is_config": true,
|
||||
"min_disk": minDiskGB,
|
||||
}
|
||||
return self.imsPerform("cloudimage", "action", params, nil)
|
||||
}
|
||||
|
||||
func formatVersion(osDist string, osVersion string) (string, error) {
|
||||
err := fmt.Errorf("unsupport version %s.reference: https://support.huaweicloud.com/api-ims/zh-cn_topic_0031617666.html", osVersion)
|
||||
dist := strings.ToLower(osDist)
|
||||
if dist == "ubuntu" || dist == "redhat" || dist == "centos" || dist == "oracle" || dist == "euleros" {
|
||||
parts := strings.Split(osVersion, ".")
|
||||
if len(parts) < 2 {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return parts[0] + "." + parts[1], nil
|
||||
}
|
||||
|
||||
if dist == "debian" {
|
||||
parts := strings.Split(osVersion, ".")
|
||||
if len(parts) < 3 {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return parts[0] + "." + parts[1] + "." + parts[2], nil
|
||||
}
|
||||
|
||||
if dist == "fedora" || dist == "windows" || dist == "suse" {
|
||||
parts := strings.Split(osVersion, ".")
|
||||
if len(parts) < 1 {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return parts[0], nil
|
||||
}
|
||||
|
||||
if dist == "opensuse" {
|
||||
parts := strings.Split(osVersion, ".")
|
||||
if len(parts) == 0 {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(parts) == 1 {
|
||||
return parts[0], nil
|
||||
}
|
||||
|
||||
if len(parts) >= 2 {
|
||||
return parts[0] + "." + parts[1], nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", err
|
||||
}
|
||||
|
||||
// todo: 如何保持同步更新
|
||||
// https://support.huaweicloud.com/api-ims/zh-cn_topic_0031617666.html
|
||||
func stdVersion(osDist string, osVersion string, osArch string) (string, error) {
|
||||
// 架构
|
||||
arch := ""
|
||||
switch osArch {
|
||||
case "64", apis.OS_ARCH_X86_64:
|
||||
arch = "64bit"
|
||||
case "32", apis.OS_ARCH_X86_32:
|
||||
arch = "32bit"
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported arch %s.reference: https://support.huaweicloud.com/api-ims/zh-cn_topic_0031617666.html", osArch)
|
||||
}
|
||||
|
||||
_dist := strings.Split(strings.TrimSpace(osDist), " ")[0]
|
||||
_dist = strings.ToLower(_dist)
|
||||
// 版本
|
||||
ver, err := formatVersion(_dist, osVersion)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 操作系统
|
||||
dist := ""
|
||||
|
||||
switch _dist {
|
||||
case "ubuntu":
|
||||
return fmt.Sprintf("Ubuntu %s server %s", ver, arch), nil
|
||||
case "redhat":
|
||||
dist = "Redhat Linux Enterprise"
|
||||
case "centos":
|
||||
dist = "CentOS"
|
||||
case "fedora":
|
||||
dist = "Fedora"
|
||||
case "debian":
|
||||
dist = "Debian GNU/Linux"
|
||||
case "windows":
|
||||
dist = "Windows Server"
|
||||
case "oracle":
|
||||
dist = "Oracle Linux Server release"
|
||||
case "suse":
|
||||
dist = "SUSE Linux Enterprise Server"
|
||||
case "opensuse":
|
||||
dist = "OpenSUSE"
|
||||
case "euleros":
|
||||
dist = "EulerOS"
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported os %s. reference: https://support.huaweicloud.com/api-ims/zh-cn_topic_0031617666.html", dist)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s %s %s", dist, ver, arch), nil
|
||||
}
|
||||
|
||||
func (self *SImage) UEFI() bool {
|
||||
return false
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,86 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
)
|
||||
|
||||
// ===========================================
|
||||
type Interface struct {
|
||||
PortState string `json:"port_state"`
|
||||
FixedIPS []FixedIP `json:"fixed_ips"`
|
||||
NetId string `json:"net_id"` // 网络Id. 与 SNetwork里的Id对应。统一使用这个Id
|
||||
PortId string `json:"port_id"`
|
||||
MACAddr string `json:"mac_addr"`
|
||||
}
|
||||
|
||||
type FixedIP struct {
|
||||
SubnetId string `json:"subnet_id"` // 子网Id, 与SNetwork中的 neutron_subnet_id对应. 注意!!! 并不是SNetwork Id。
|
||||
IPAddress string `json:"ip_address"`
|
||||
}
|
||||
|
||||
type SInstanceNic struct {
|
||||
instance *SInstance
|
||||
ipAddr string
|
||||
macAddr string
|
||||
|
||||
cloudprovider.DummyICloudNic
|
||||
}
|
||||
|
||||
func (self *SInstanceNic) GetId() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SInstanceNic) GetIP() string {
|
||||
return self.ipAddr
|
||||
}
|
||||
|
||||
func (self *SInstanceNic) GetMAC() string {
|
||||
return self.macAddr
|
||||
}
|
||||
|
||||
func (self *SInstanceNic) GetDriver() string {
|
||||
return "virtio"
|
||||
}
|
||||
|
||||
func (self *SInstanceNic) InClassicNetwork() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SInstanceNic) GetINetworkId() string {
|
||||
instanceId := self.instance.GetId()
|
||||
subnets, _ := self.instance.host.zone.region.GetInstanceInterfaces(instanceId)
|
||||
for i := range subnets {
|
||||
return subnets[i].NetId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type SInstanceInterface struct {
|
||||
NetId string
|
||||
PortId string
|
||||
MacAddr string
|
||||
PortState string
|
||||
FixedIps []struct {
|
||||
SubnetId string
|
||||
IpAddress string
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SRegion) GetInstanceInterfaces(instanceId string) ([]SInstanceInterface, error) {
|
||||
ret := []SInstanceInterface{}
|
||||
return ret, self.get("ecs", "v2.1", "servers/"+instanceId+"/os-interface", &ret)
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
// https://support.huaweicloud.com/api-ecs/zh-cn_topic_0020212656.html
|
||||
type SInstanceType struct {
|
||||
multicloud.SResourceBase
|
||||
multicloud.HcsTags
|
||||
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Vcpus int `json:"vcpus"`
|
||||
RamMB int `json:"ram"` // 内存大小
|
||||
OSExtraSpecs OSExtraSpecs `json:"os_extra_specs"` // 扩展规格
|
||||
}
|
||||
|
||||
type OSExtraSpecs struct {
|
||||
EcsPerformancetype string `json:"ecs:performancetype"`
|
||||
EcsGeneration string `json:"ecs:generation"`
|
||||
EcsInstanceArchitecture string `json:"ecs:instance_architecture"`
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetId() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetName() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetGlobalId() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetInstanceTypeFamily() string {
|
||||
if len(self.OSExtraSpecs.EcsGeneration) > 0 {
|
||||
return self.OSExtraSpecs.EcsGeneration
|
||||
}
|
||||
return strings.Split(self.Name, ".")[0]
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetStatus() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetInstanceTypeCategory() string {
|
||||
return self.OSExtraSpecs.EcsPerformancetype
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetPrepaidStatus() string {
|
||||
return api.SkuStatusSoldout
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetPostpaidStatus() string {
|
||||
return api.SkuStatusAvailable
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetCpuArch() string {
|
||||
if len(self.OSExtraSpecs.EcsInstanceArchitecture) > 0 {
|
||||
if strings.HasPrefix(self.OSExtraSpecs.EcsInstanceArchitecture, "arm") {
|
||||
return apis.OS_ARCH_AARCH64
|
||||
}
|
||||
return apis.OS_ARCH_X86
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetCpuCoreCount() int {
|
||||
return self.Vcpus
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetMemorySizeMB() int {
|
||||
return self.RamMB
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetOsName() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetSysDiskResizable() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetSysDiskType() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetSysDiskMinSizeGB() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetSysDiskMaxSizeGB() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetAttachedDiskType() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetAttachedDiskSizeGB() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetAttachedDiskCount() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetDataDiskTypes() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetDataDiskMaxCount() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetNicType() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetNicMaxCount() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetGpuAttachable() bool {
|
||||
return self.OSExtraSpecs.EcsPerformancetype == "gpu"
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetGpuSpec() string {
|
||||
if self.OSExtraSpecs.EcsPerformancetype == "gpu" {
|
||||
return self.OSExtraSpecs.EcsGeneration
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetGpuCount() int {
|
||||
if self.OSExtraSpecs.EcsPerformancetype == "gpu" {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SInstanceType) GetGpuMaxCount() int {
|
||||
if self.OSExtraSpecs.EcsPerformancetype == "gpu" {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SInstanceType) Delete() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetchInstanceTypes(zoneId string) ([]SInstanceType, error) {
|
||||
query := url.Values{}
|
||||
if len(zoneId) > 0 {
|
||||
query.Set("availability_zone", zoneId)
|
||||
}
|
||||
ret := []SInstanceType{}
|
||||
return ret, self.list("ecs", "v1", "cloudservers/flavors", query, &ret)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetchInstanceType(id string) (*SInstanceType, error) {
|
||||
ret := &SInstanceType{}
|
||||
res := fmt.Sprintf("flavors/%s", id)
|
||||
return ret, self.get("ecs", "v2", res, &ret)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetMatchInstanceTypes(cpu int, memMB int, zoneId string) ([]SInstanceType, error) {
|
||||
instanceTypes, err := self.GetchInstanceTypes(zoneId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := make([]SInstanceType, 0)
|
||||
for _, t := range instanceTypes {
|
||||
// cpu & mem & disk都匹配才行
|
||||
if t.Vcpus == cpu && t.RamMB == memMB {
|
||||
ret = append(ret, t)
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetISkus() ([]cloudprovider.ICloudSku, error) {
|
||||
flavors, err := self.GetchInstanceTypes("")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fetchInstanceTypes")
|
||||
}
|
||||
ret := []cloudprovider.ICloudSku{}
|
||||
for i := range flavors {
|
||||
ret = append(ret, &flavors[i])
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
)
|
||||
|
||||
const (
|
||||
TASK_SUCCESS = "SUCCESS"
|
||||
TASK_FAIL = "FAIL"
|
||||
)
|
||||
|
||||
type SJob struct {
|
||||
Status string `json:"status"`
|
||||
Entities struct {
|
||||
SubJobsJotal int
|
||||
ImageId string
|
||||
ServerId string
|
||||
SubJobs []SJob
|
||||
} `json:"entities"`
|
||||
JobId string `json:"job_id"`
|
||||
JobType string `json:"job_type"`
|
||||
BeginTime string `json:"begin_time"`
|
||||
EndTime string `json:"end_time"`
|
||||
ErrorCode string `json:"error_code"`
|
||||
FailReason string `json:"fail_reason"`
|
||||
}
|
||||
|
||||
func (self *SJob) GetIds() []string {
|
||||
ret := []string{}
|
||||
if len(self.Entities.ImageId) > 0 {
|
||||
ret = append(ret, self.Entities.ImageId)
|
||||
}
|
||||
if len(self.Entities.ServerId) > 0 {
|
||||
ret = append(ret, self.Entities.ServerId)
|
||||
}
|
||||
for _, sub := range self.Entities.SubJobs {
|
||||
if len(sub.Entities.ServerId) > 0 {
|
||||
ret = append(ret, sub.Entities.ServerId)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (self *SHcsClient) waitJobSuccess(serviceType, regionId string, jobId string, interval time.Duration, timeout time.Duration) (*SJob, error) {
|
||||
start := time.Now()
|
||||
var job *SJob
|
||||
var err error
|
||||
for time.Now().Sub(start) < timeout {
|
||||
job, err = self.GetJob(serviceType, regionId, jobId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Infof("wait %s job %s(%s) status: %s", serviceType, job.JobId, job.JobType, job.Status)
|
||||
if job.Status == TASK_SUCCESS {
|
||||
return job, nil
|
||||
}
|
||||
reason := []string{job.FailReason}
|
||||
for _, subJob := range job.Entities.SubJobs {
|
||||
if len(subJob.FailReason) > 0 {
|
||||
reason = append(reason, subJob.FailReason)
|
||||
}
|
||||
}
|
||||
if job.Status == TASK_FAIL {
|
||||
return nil, fmt.Errorf("job %s failed reason %s", jobId, strings.Join(reason, ";"))
|
||||
}
|
||||
time.Sleep(interval)
|
||||
}
|
||||
return nil, errors.Wrapf(cloudprovider.ErrTimeout, "wait job %s", jsonutils.Marshal(job))
|
||||
}
|
||||
|
||||
func (self *SHcsClient) GetJob(service, regionId string, jobId string) (*SJob, error) {
|
||||
resp, err := self._getJob(service, regionId, jobId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := &SJob{}
|
||||
return ret, resp.Unmarshal(ret)
|
||||
|
||||
}
|
||||
|
||||
func (self *SRegion) GetJob(service string, jobId string) (*SJob, error) {
|
||||
return self.client.GetJob(service, self.Id, jobId)
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aokoli/goutils"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// https://support.huaweicloud.com/api-ecs/zh-cn_topic_0020212676.html
|
||||
type SKeypair struct {
|
||||
Fingerprint string `json:"fingerprint"`
|
||||
Name string `json:"name"`
|
||||
PublicKey string `json:"public_key"`
|
||||
}
|
||||
|
||||
func (self *SRegion) getFingerprint(publicKey string) (string, error) {
|
||||
pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(publicKey))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("publicKey error %s", err)
|
||||
}
|
||||
|
||||
fingerprint := strings.Replace(ssh.FingerprintLegacyMD5(pk), ":", "", -1)
|
||||
return fingerprint, nil
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-ecs/zh-cn_topic_0020212676.html
|
||||
func (self *SRegion) GetKeypairs() ([]SKeypair, error) {
|
||||
ret := []SKeypair{}
|
||||
return ret, self.list("ecs", "v2.1", "os-keypairs", nil, &ret)
|
||||
}
|
||||
|
||||
func (self *SRegion) lookUpKeypair(publicKey string) (string, error) {
|
||||
keypairs, err := self.GetKeypairs()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
fingerprint, err := self.getFingerprint(publicKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, keypair := range keypairs {
|
||||
if keypair.Fingerprint == fingerprint {
|
||||
return keypair.Name, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("keypair not found %s", err)
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-ecs/zh-cn_topic_0020212678.html
|
||||
func (self *SRegion) ImportKeypair(name, publicKey string) (*SKeypair, error) {
|
||||
params := map[string]interface{}{
|
||||
"keypair": map[string]interface{}{
|
||||
"name": name,
|
||||
"public_key": publicKey,
|
||||
},
|
||||
}
|
||||
ret := &SKeypair{}
|
||||
return ret, self.create("ecs", "v2.1", "os-keypairs", params, ret)
|
||||
}
|
||||
|
||||
func (self *SRegion) importKeypair(publicKey string) (string, error) {
|
||||
prefix, e := goutils.RandomAlphabetic(6)
|
||||
if e != nil {
|
||||
return "", fmt.Errorf("publicKey error %s", e)
|
||||
}
|
||||
|
||||
name := prefix + strconv.FormatInt(time.Now().Unix(), 10)
|
||||
k, e := self.ImportKeypair(name, publicKey)
|
||||
if e != nil {
|
||||
return "", fmt.Errorf("keypair import error %s", e)
|
||||
}
|
||||
return k.Name, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) syncKeypair(publicKey string) (string, error) {
|
||||
name, e := self.lookUpKeypair(publicKey)
|
||||
if e == nil {
|
||||
return name, nil
|
||||
}
|
||||
return self.importKeypair(publicKey)
|
||||
}
|
||||
@@ -0,0 +1,601 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
var LB_ALGORITHM_MAP = map[string]string{
|
||||
api.LB_SCHEDULER_WRR: "ROUND_ROBIN",
|
||||
api.LB_SCHEDULER_WLC: "LEAST_CONNECTIONS",
|
||||
api.LB_SCHEDULER_SCH: "SOURCE_IP",
|
||||
}
|
||||
|
||||
var LB_PROTOCOL_MAP = map[string]string{
|
||||
api.LB_LISTENER_TYPE_HTTP: "HTTP",
|
||||
api.LB_LISTENER_TYPE_HTTPS: "TERMINATED_HTTPS",
|
||||
api.LB_LISTENER_TYPE_UDP: "UDP",
|
||||
api.LB_LISTENER_TYPE_TCP: "TCP",
|
||||
}
|
||||
|
||||
var LBBG_PROTOCOL_MAP = map[string]string{
|
||||
api.LB_LISTENER_TYPE_HTTP: "HTTP",
|
||||
api.LB_LISTENER_TYPE_HTTPS: "HTTP",
|
||||
api.LB_LISTENER_TYPE_UDP: "UDP",
|
||||
api.LB_LISTENER_TYPE_TCP: "TCP",
|
||||
}
|
||||
|
||||
var LB_STICKY_SESSION_MAP = map[string]string{
|
||||
api.LB_STICKY_SESSION_TYPE_INSERT: "HTTP_COOKIE",
|
||||
api.LB_STICKY_SESSION_TYPE_SERVER: "APP_COOKIE",
|
||||
}
|
||||
|
||||
var LB_HEALTHCHECK_TYPE_MAP = map[string]string{
|
||||
api.LB_HEALTH_CHECK_HTTP: "HTTP",
|
||||
api.LB_HEALTH_CHECK_TCP: "TCP",
|
||||
api.LB_HEALTH_CHECK_UDP: "UDP_CONNECT",
|
||||
}
|
||||
|
||||
type SLoadbalancer struct {
|
||||
multicloud.SResourceBase
|
||||
multicloud.HuaweiTags
|
||||
region *SRegion
|
||||
subnet *SNetwork
|
||||
eip *SEip
|
||||
|
||||
Description string `json:"description"`
|
||||
ProvisioningStatus string `json:"provisioning_status"`
|
||||
TenantId string `json:"tenant_id"`
|
||||
ProjectId string `json:"project_id"`
|
||||
AdminStateUp bool `json:"admin_state_up"`
|
||||
Provider string `json:"provider"`
|
||||
Pools []Pool `json:"pools"`
|
||||
Listeners []Listener `json:"listeners"`
|
||||
VipPortId string `json:"vip_port_id"`
|
||||
OperatingStatus string `json:"operating_status"`
|
||||
VipAddress string `json:"vip_address"`
|
||||
VipSubnetId string `json:"vip_subnet_id"`
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
type Listener struct {
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
type Pool struct {
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) GetIEIP() (cloudprovider.ICloudEIP, error) {
|
||||
if self.GetEip() == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return self.eip, nil
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) GetId() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) GetName() string {
|
||||
return self.Name
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) GetGlobalId() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) GetStatus() string {
|
||||
return api.LB_STATUS_ENABLED
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) Refresh() error {
|
||||
lb, err := self.region.GetLoadbalancer(self.GetId())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return jsonutils.Update(self, lb)
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) GetProjectId() string {
|
||||
return self.ProjectId
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) GetAddress() string {
|
||||
return self.VipAddress
|
||||
}
|
||||
|
||||
// todo: api.LB_ADDR_TYPE_INTERNET?
|
||||
func (self *SLoadbalancer) GetAddressType() string {
|
||||
return api.LB_ADDR_TYPE_INTRANET
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) GetNetworkType() string {
|
||||
return api.LB_NETWORK_TYPE_VPC
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) GetNetworkIds() []string {
|
||||
net := self.GetNetwork()
|
||||
if net != nil {
|
||||
return []string{net.GetId()}
|
||||
}
|
||||
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) GetNetwork() *SNetwork {
|
||||
if self.subnet == nil {
|
||||
port, err := self.region.GetPort(self.VipPortId)
|
||||
if err == nil {
|
||||
net, err := self.region.GetNetwork(port.NetworkId)
|
||||
if err == nil {
|
||||
self.subnet = net
|
||||
} else {
|
||||
log.Debugf("huawei.SLoadbalancer.getNetwork %s", err)
|
||||
}
|
||||
} else {
|
||||
log.Debugf("huawei.SLoadbalancer.GetPort %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return self.subnet
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) GetEip() *SEip {
|
||||
if self.eip == nil {
|
||||
eips, _ := self.region.GetEips(self.VipPortId, nil)
|
||||
for i := range eips {
|
||||
self.eip = &eips[i]
|
||||
}
|
||||
}
|
||||
return self.eip
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) GetVpcId() string {
|
||||
net := self.GetNetwork()
|
||||
if net != nil {
|
||||
return net.VpcId
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) GetZoneId() string {
|
||||
net := self.GetNetwork()
|
||||
if net != nil {
|
||||
z, err := self.region.GetZoneById(net.AvailabilityZone)
|
||||
if err != nil {
|
||||
log.Infof("getZoneById %s %s", net.AvailabilityZone, err)
|
||||
return ""
|
||||
}
|
||||
|
||||
return z.GetGlobalId()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) GetZone1Id() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) GetLoadbalancerSpec() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) GetChargeType() string {
|
||||
eip := self.GetEip()
|
||||
if eip != nil {
|
||||
return eip.GetInternetChargeType()
|
||||
}
|
||||
|
||||
return api.EIP_CHARGE_TYPE_BY_TRAFFIC
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) GetEgressMbps() int {
|
||||
eip := self.GetEip()
|
||||
if eip != nil {
|
||||
return eip.GetBandwidth()
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-elb/zh-cn_topic_0141008275.html
|
||||
func (self *SLoadbalancer) Delete(ctx context.Context) error {
|
||||
return self.region.DeleteLoadBalancer(self.GetId())
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) Stop() error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) GetILoadBalancerListeners() ([]cloudprovider.ICloudLoadbalancerListener, error) {
|
||||
ret, err := self.region.GetLoadBalancerListeners(self.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
iret := make([]cloudprovider.ICloudLoadbalancerListener, 0)
|
||||
for i := range ret {
|
||||
listener := ret[i]
|
||||
listener.lb = self
|
||||
iret = append(iret, &listener)
|
||||
}
|
||||
|
||||
return iret, nil
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) GetILoadBalancerBackendGroups() ([]cloudprovider.ICloudLoadbalancerBackendGroup, error) {
|
||||
ret, err := self.region.GetLoadBalancerBackendGroups(self.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
iret := make([]cloudprovider.ICloudLoadbalancerBackendGroup, 0)
|
||||
for i := range ret {
|
||||
bg := ret[i]
|
||||
bg.lb = self
|
||||
bg.region = self.region
|
||||
iret = append(iret, &bg)
|
||||
}
|
||||
|
||||
return iret, nil
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561549.html
|
||||
func (self *SLoadbalancer) CreateILoadBalancerBackendGroup(group *cloudprovider.SLoadbalancerBackendGroup) (cloudprovider.ICloudLoadbalancerBackendGroup, error) {
|
||||
ret, err := self.region.CreateLoadBalancerBackendGroup(group)
|
||||
ret.lb = self
|
||||
return &ret, err
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561563.html
|
||||
func (self *SLoadbalancer) CreateHealthCheck(backendGroupId string, healthcheck *cloudprovider.SLoadbalancerHealthCheck) error {
|
||||
_, err := self.region.CreateLoadBalancerHealthCheck(backendGroupId, healthcheck)
|
||||
return err
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561548.html
|
||||
func (self *SLoadbalancer) GetILoadBalancerBackendGroupById(groupId string) (cloudprovider.ICloudLoadbalancerBackendGroup, error) {
|
||||
ret := &SElbBackendGroup{lb: self, region: self.region}
|
||||
return ret, self.region.lbGet("lbaas/pools/"+groupId, ret)
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) CreateILoadBalancerListener(ctx context.Context, listener *cloudprovider.SLoadbalancerListener) (cloudprovider.ICloudLoadbalancerListener, error) {
|
||||
ret, err := self.region.CreateLoadBalancerListener(listener)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret.lb = self
|
||||
return &ret, nil
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) GetILoadBalancerListenerById(listenerId string) (cloudprovider.ICloudLoadbalancerListener, error) {
|
||||
ret := &SElbListener{lb: self}
|
||||
return ret, self.region.lbGet("lbaas/listeners/"+listenerId, ret)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetLoadbalancer(id string) (*SLoadbalancer, error) {
|
||||
ret := &SLoadbalancer{region: self}
|
||||
return ret, self.lbGet("lbaas/loadbalancers/"+id, ret)
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteLoadBalancer(elbId string) error {
|
||||
resource := fmt.Sprintf("lbaas/loadbalancers/%s", elbId)
|
||||
return self.lbDelete(resource)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetLoadBalancerListeners(lbId string) ([]SElbListener, error) {
|
||||
ret := []SElbListener{}
|
||||
params := url.Values{}
|
||||
if len(lbId) > 0 {
|
||||
params.Set("loadbalancer_id", lbId)
|
||||
}
|
||||
return ret, self.lbList("lbaas/listeners", params, &ret)
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateLoadBalancerListener(listener *cloudprovider.SLoadbalancerListener) (SElbListener, error) {
|
||||
params := map[string]interface{}{
|
||||
"name": listener.Name,
|
||||
"description": listener.Description,
|
||||
"protocol": LB_PROTOCOL_MAP[listener.ListenerType],
|
||||
"protocol_port": listener.ListenerPort,
|
||||
"loadbalancer_id": listener.LoadbalancerID,
|
||||
"http2_enable": listener.EnableHTTP2,
|
||||
}
|
||||
if len(listener.BackendGroupID) > 0 {
|
||||
params["default_pool_id"] = listener.BackendGroupID
|
||||
}
|
||||
|
||||
if listener.ListenerType == api.LB_LISTENER_TYPE_HTTPS {
|
||||
params["default_tls_container_ref"] = listener.CertificateID
|
||||
}
|
||||
|
||||
if listener.XForwardedFor {
|
||||
params["insert_headers"] = map[string]interface{}{
|
||||
"X-Forwarded-ELB-IP": listener.XForwardedFor,
|
||||
}
|
||||
}
|
||||
|
||||
ret := SElbListener{}
|
||||
err := self.lbCreate("lbaas/listeners", map[string]interface{}{"listener": params}, &ret)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561547.html
|
||||
func (self *SRegion) GetLoadBalancerBackendGroups(elbId string) ([]SElbBackendGroup, error) {
|
||||
query := url.Values{}
|
||||
if len(elbId) > 0 {
|
||||
query.Set("loadbalancer_id", elbId)
|
||||
}
|
||||
|
||||
ret := []SElbBackendGroup{}
|
||||
return ret, self.lbList("lbaas/pools", query, &ret)
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561547.html
|
||||
func (self *SRegion) CreateLoadBalancerBackendGroup(group *cloudprovider.SLoadbalancerBackendGroup) (SElbBackendGroup, error) {
|
||||
ret := SElbBackendGroup{region: self}
|
||||
var protocol, scheduler string
|
||||
if s, ok := LB_ALGORITHM_MAP[group.Scheduler]; !ok {
|
||||
return ret, fmt.Errorf("CreateILoadBalancerBackendGroup unsupported scheduler %s", group.Scheduler)
|
||||
} else {
|
||||
scheduler = s
|
||||
}
|
||||
|
||||
if t, ok := LBBG_PROTOCOL_MAP[group.ListenType]; !ok {
|
||||
return ret, fmt.Errorf("CreateILoadBalancerBackendGroup unsupported listener type %s", group.ListenType)
|
||||
} else {
|
||||
protocol = t
|
||||
}
|
||||
|
||||
params := map[string]interface{}{
|
||||
"project_id": self.client.projectId,
|
||||
"name": group.Name,
|
||||
"protocol": protocol,
|
||||
"lb_algorithm": scheduler,
|
||||
}
|
||||
|
||||
if len(group.ListenerID) > 0 {
|
||||
params["listener_id"] = group.ListenerID
|
||||
} else if len(group.LoadbalancerID) > 0 {
|
||||
params["loadbalancer_id"] = group.LoadbalancerID
|
||||
} else {
|
||||
return ret, fmt.Errorf("CreateLoadBalancerBackendGroup one of listener id / loadbalancer id must be specified")
|
||||
}
|
||||
|
||||
if group.StickySession != nil {
|
||||
s := map[string]interface{}{}
|
||||
timeout := int64(group.StickySession.StickySessionCookieTimeout / 60)
|
||||
if group.ListenType == api.LB_LISTENER_TYPE_UDP || group.ListenType == api.LB_LISTENER_TYPE_TCP {
|
||||
s["type"] = "SOURCE_IP"
|
||||
if timeout > 0 {
|
||||
s["persistence_timeout"] = timeout
|
||||
}
|
||||
} else {
|
||||
s["type"] = LB_STICKY_SESSION_MAP[group.StickySession.StickySessionType]
|
||||
if len(group.StickySession.StickySessionCookie) > 0 {
|
||||
s["cookie_name"] = group.StickySession.StickySessionCookie
|
||||
} else {
|
||||
if timeout > 0 {
|
||||
s["persistence_timeout"] = timeout
|
||||
}
|
||||
}
|
||||
}
|
||||
params["session_persistence"] = s
|
||||
}
|
||||
err := self.lbCreate("lbaas/pools", map[string]interface{}{"pool": params}, &ret)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
if group.HealthCheck != nil {
|
||||
_, err := self.CreateLoadBalancerHealthCheck(ret.GetId(), group.HealthCheck)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateLoadBalancerHealthCheck(backendGroupId string, healthCheck *cloudprovider.SLoadbalancerHealthCheck) (SElbHealthCheck, error) {
|
||||
params := map[string]interface{}{
|
||||
"delay": healthCheck.HealthCheckInterval,
|
||||
"max_retries": healthCheck.HealthCheckRise,
|
||||
"pool_id": backendGroupId,
|
||||
"timeout": healthCheck.HealthCheckTimeout,
|
||||
"type": LB_HEALTHCHECK_TYPE_MAP[healthCheck.HealthCheckType],
|
||||
}
|
||||
if healthCheck.HealthCheckType == api.LB_HEALTH_CHECK_HTTP {
|
||||
if len(healthCheck.HealthCheckDomain) > 0 {
|
||||
params["domain_name"] = healthCheck.HealthCheckDomain
|
||||
}
|
||||
|
||||
if len(healthCheck.HealthCheckURI) > 0 {
|
||||
params["url_path"] = healthCheck.HealthCheckURI
|
||||
}
|
||||
|
||||
if len(healthCheck.HealthCheckHttpCode) > 0 {
|
||||
params["expected_codes"] = ToHuaweiHealthCheckHttpCode(healthCheck.HealthCheckHttpCode)
|
||||
}
|
||||
}
|
||||
|
||||
ret := SElbHealthCheck{region: self}
|
||||
err := self.lbCreate("lbaas/healthmonitors", map[string]interface{}{"healthmonitor": params}, &ret)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561564.html
|
||||
func (self *SRegion) UpdateLoadBalancerHealthCheck(healthCheckId string, healthCheck *cloudprovider.SLoadbalancerHealthCheck) (SElbHealthCheck, error) {
|
||||
params := map[string]interface{}{
|
||||
"delay": healthCheck.HealthCheckInterval,
|
||||
"max_retries": healthCheck.HealthCheckRise,
|
||||
"timeout": healthCheck.HealthCheckTimeout,
|
||||
}
|
||||
if healthCheck.HealthCheckType == api.LB_HEALTH_CHECK_HTTP {
|
||||
if len(healthCheck.HealthCheckDomain) > 0 {
|
||||
params["domain_name"] = healthCheck.HealthCheckDomain
|
||||
}
|
||||
|
||||
if len(healthCheck.HealthCheckURI) > 0 {
|
||||
params["url_path"] = healthCheck.HealthCheckURI
|
||||
}
|
||||
|
||||
if len(healthCheck.HealthCheckHttpCode) > 0 {
|
||||
params["expected_codes"] = ToHuaweiHealthCheckHttpCode(healthCheck.HealthCheckHttpCode)
|
||||
}
|
||||
}
|
||||
|
||||
ret := SElbHealthCheck{region: self}
|
||||
res := "lbaas/healthmonitors/" + healthCheckId
|
||||
err := self.lbUpdate(res, map[string]interface{}{"healthmonitor": params})
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
return ret, self.lbGet(res, &ret)
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561565.html
|
||||
func (self *SRegion) DeleteLoadbalancerHealthCheck(healthCheckId string) error {
|
||||
return self.lbDelete("lbaas/healthmonitors/" + healthCheckId)
|
||||
}
|
||||
|
||||
func (self *SLoadbalancer) SetTags(tags map[string]string, replace bool) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SRegion) lbGet(resource string, retVal interface{}) error {
|
||||
return self.client.get("vpc", "v2.0", self.Id, resource, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) lbList(resource string, params url.Values, retVal interface{}) error {
|
||||
return self.list("vpc", "v2.0", resource, params, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) lbDelete(resource string) error {
|
||||
return self.delete("vpc", "v2.0", resource)
|
||||
}
|
||||
|
||||
func (self *SRegion) lbCreate(resource string, params map[string]interface{}, retVal interface{}) error {
|
||||
return self.create("vpc", "v2.0", resource, params, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) lbUpdate(resource string, params map[string]interface{}) error {
|
||||
return self.update("vpc", "v2.0", resource, params)
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561535.html
|
||||
func (self *SRegion) CreateLoadBalancer(loadbalancer *cloudprovider.SLoadbalancer) (*SLoadbalancer, error) {
|
||||
subnet, err := self.GetNetwork(loadbalancer.NetworkIDs[0])
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SRegion.CreateLoadBalancer.getNetwork")
|
||||
}
|
||||
|
||||
params := map[string]interface{}{
|
||||
"name": loadbalancer.Name,
|
||||
"vip_subnet_id": subnet.NeutronSubnetId,
|
||||
"tenant_id": self.client.projectId,
|
||||
}
|
||||
if len(loadbalancer.Address) > 0 {
|
||||
params["vip_address"] = loadbalancer.Address
|
||||
}
|
||||
ret := &SLoadbalancer{region: self}
|
||||
err = self.lbCreate("lbaas/loadbalancers", map[string]interface{}{"loadbalancer": params}, ret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 创建公网类型ELB
|
||||
if len(loadbalancer.EipID) > 0 {
|
||||
err := self.AssociateEipWithPortId(loadbalancer.EipID, ret.VipPortId)
|
||||
if err != nil {
|
||||
return ret, errors.Wrap(err, "SRegion.CreateLoadBalancer.AssociateEipWithPortId")
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetLoadBalancers() ([]SLoadbalancer, error) {
|
||||
lbs := []SLoadbalancer{}
|
||||
params := url.Values{}
|
||||
return lbs, self.lbList("lbaas/loadbalancers", params, &lbs)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetILoadBalancers() ([]cloudprovider.ICloudLoadbalancer, error) {
|
||||
elbs, err := self.GetLoadBalancers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := []cloudprovider.ICloudLoadbalancer{}
|
||||
for i := range elbs {
|
||||
elbs[i].region = self
|
||||
ret = append(ret, &elbs[i])
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetILoadBalancerById(id string) (cloudprovider.ICloudLoadbalancer, error) {
|
||||
elb, err := self.GetLoadbalancer(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return elb, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateILoadBalancer(loadbalancer *cloudprovider.SLoadbalancer) (cloudprovider.ICloudLoadbalancer, error) {
|
||||
ret, err := self.CreateLoadBalancer(loadbalancer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
type SElbACL struct {
|
||||
multicloud.SResourceBase
|
||||
multicloud.HuaweiTags
|
||||
region *SRegion
|
||||
|
||||
Id string `json:"id"`
|
||||
ListenerId string `json:"listener_id"`
|
||||
TenantId string `json:"tenant_id"`
|
||||
EnableWhitelist bool `json:"enable_whitelist"`
|
||||
Whitelist string `json:"whitelist"`
|
||||
}
|
||||
|
||||
func (self *SElbACL) GetAclListenerID() string {
|
||||
return self.ListenerId
|
||||
}
|
||||
|
||||
func (self *SElbACL) GetId() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SElbACL) GetName() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SElbACL) GetGlobalId() string {
|
||||
return self.GetId()
|
||||
}
|
||||
|
||||
func (self *SElbACL) GetStatus() string {
|
||||
if self.EnableWhitelist {
|
||||
return api.LB_BOOL_ON
|
||||
}
|
||||
|
||||
return api.LB_BOOL_OFF
|
||||
}
|
||||
|
||||
func (self *SElbACL) Refresh() error {
|
||||
acl, err := self.region.GetLoadBalancerAcl(self.GetId())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(self, acl)
|
||||
}
|
||||
|
||||
func (self *SElbACL) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SElbACL) GetProjectId() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SElbACL) GetAclEntries() []cloudprovider.SLoadbalancerAccessControlListEntry {
|
||||
ret := []cloudprovider.SLoadbalancerAccessControlListEntry{}
|
||||
for _, cidr := range strings.Split(self.Whitelist, ",") {
|
||||
ret = append(ret, cloudprovider.SLoadbalancerAccessControlListEntry{CIDR: cidr})
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (self *SElbACL) Sync(acl *cloudprovider.SLoadbalancerAccessControlList) error {
|
||||
whiteList := ""
|
||||
cidrs := []string{}
|
||||
for _, entry := range acl.Entrys {
|
||||
cidrs = append(cidrs, entry.CIDR)
|
||||
}
|
||||
whiteList = strings.Join(cidrs, ",")
|
||||
params := map[string]interface{}{
|
||||
"whitelist": whiteList,
|
||||
"enable_whitelist": acl.AccessControlEnable,
|
||||
}
|
||||
return self.region.lbUpdate("lbaas/whitelists/"+self.GetId(), map[string]interface{}{"whitelist": params})
|
||||
}
|
||||
|
||||
func (self *SElbACL) Delete() error {
|
||||
return self.region.lbDelete("lbaas/whitelists/" + self.GetId())
|
||||
}
|
||||
|
||||
func (self *SRegion) GetLoadBalancerAcl(aclId string) (*SElbACL, error) {
|
||||
ret := &SElbACL{region: self}
|
||||
return ret, self.lbGet("lbaas/whitelists/"+aclId, ret)
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561582.html
|
||||
func (self *SRegion) GetLoadBalancerAcls(listenerId string) ([]SElbACL, error) {
|
||||
query := url.Values{}
|
||||
if len(listenerId) > 0 {
|
||||
query.Set("listener_id", listenerId)
|
||||
}
|
||||
ret := []SElbACL{}
|
||||
return ret, self.lbList("lbaas/whitelists", query, &ret)
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateLoadBalancerAcl(acl *cloudprovider.SLoadbalancerAccessControlList) (*SElbACL, error) {
|
||||
params := map[string]interface{}{
|
||||
"listener_id": acl.ListenerId,
|
||||
}
|
||||
if len(acl.Entrys) > 0 {
|
||||
whitelist := []string{}
|
||||
for i := range acl.Entrys {
|
||||
whitelist = append(whitelist, acl.Entrys[i].CIDR)
|
||||
}
|
||||
params["enable_whitelist"] = acl.AccessControlEnable
|
||||
params["whitelist"] = strings.Join(whitelist, ",")
|
||||
} else {
|
||||
params["enable_whitelist"] = false
|
||||
}
|
||||
ret := &SElbACL{region: self}
|
||||
return ret, self.lbCreate("lbaas/whitelists", map[string]interface{}{"whitelist": params}, ret)
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateILoadBalancerAcl(acl *cloudprovider.SLoadbalancerAccessControlList) (cloudprovider.ICloudLoadbalancerAcl, error) {
|
||||
ret, err := self.CreateLoadBalancerAcl(acl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetILoadBalancerAclById(aclId string) (cloudprovider.ICloudLoadbalancerAcl, error) {
|
||||
acl, err := self.GetLoadBalancerAcl(aclId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return acl, nil
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
type SElbBackend struct {
|
||||
multicloud.SResourceBase
|
||||
multicloud.HuaweiTags
|
||||
region *SRegion
|
||||
lb *SLoadbalancer
|
||||
backendGroup *SElbBackendGroup
|
||||
|
||||
Name string `json:"name"`
|
||||
Weight int `json:"weight"`
|
||||
AdminStateUp bool `json:"admin_state_up"`
|
||||
SubnetId string `json:"subnet_id"`
|
||||
TenantId string `json:"tenant_id"`
|
||||
ProjectId string `json:"project_id"`
|
||||
Address string `json:"address"`
|
||||
ProtocolPort int `json:"protocol_port"`
|
||||
OperatingStatus string `json:"operating_status"`
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
func (self *SElbBackend) GetId() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SElbBackend) GetName() string {
|
||||
return self.Name
|
||||
}
|
||||
|
||||
func (self *SElbBackend) GetGlobalId() string {
|
||||
return self.GetId()
|
||||
}
|
||||
|
||||
func (self *SElbBackend) GetStatus() string {
|
||||
return api.LB_STATUS_ENABLED
|
||||
}
|
||||
|
||||
func (self *SElbBackend) Refresh() error {
|
||||
backend, err := self.region.GetElbBackend(self.backendGroup.GetId(), self.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(self, backend)
|
||||
}
|
||||
|
||||
func (self *SElbBackend) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SElbBackend) GetProjectId() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SElbBackend) GetWeight() int {
|
||||
return self.Weight
|
||||
}
|
||||
|
||||
func (self *SElbBackend) GetPort() int {
|
||||
return self.ProtocolPort
|
||||
}
|
||||
|
||||
func (self *SElbBackend) GetBackendType() string {
|
||||
return api.LB_BACKEND_GUEST
|
||||
}
|
||||
|
||||
func (self *SElbBackend) GetBackendRole() string {
|
||||
return api.LB_BACKEND_ROLE_DEFAULT
|
||||
}
|
||||
|
||||
func (self *SElbBackend) GetBackendId() string {
|
||||
i, err := self.lb.region.getInstanceByIP(self.Address)
|
||||
if err != nil {
|
||||
log.Errorf("ElbBackend GetBackendId %s", err)
|
||||
}
|
||||
|
||||
if i != nil {
|
||||
return i.GetId()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SElbBackend) GetIpAddress() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SElbBackend) SyncConf(ctx context.Context, port, weight int) error {
|
||||
if port > 0 {
|
||||
log.Warningf("Elb backend SyncConf unsupport modify port")
|
||||
}
|
||||
|
||||
params := map[string]interface{}{
|
||||
"weight": weight,
|
||||
}
|
||||
res := fmt.Sprintf("lbaas/pools/%s/members/%s", self.backendGroup.GetId(), self.Id)
|
||||
return self.region.lbUpdate(res, map[string]interface{}{"member": params})
|
||||
}
|
||||
|
||||
func (self *SRegion) getInstanceByIP(ip string) (*SInstance, error) {
|
||||
instances, err := self.GetInstances(ip)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetInstance(%s)", ip)
|
||||
}
|
||||
for i := range instances {
|
||||
return &instances[i], nil
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SRegion) GetElbBackend(pool, id string) (*SElbBackend, error) {
|
||||
res := fmt.Sprintf("lbaas/pools/%s/members/%s", pool, id)
|
||||
ret := &SElbBackend{region: self}
|
||||
return ret, self.lbGet(res, ret)
|
||||
}
|
||||
@@ -0,0 +1,490 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
type SElbBackendGroup struct {
|
||||
multicloud.SResourceBase
|
||||
multicloud.HuaweiTags
|
||||
lb *SLoadbalancer
|
||||
region *SRegion
|
||||
|
||||
LBAlgorithm string `json:"lb_algorithm"`
|
||||
Protocol string `json:"protocol"`
|
||||
Description string `json:"description"`
|
||||
AdminStateUp bool `json:"admin_state_up"`
|
||||
Loadbalancers []Listener `json:"loadbalancers"`
|
||||
TenantId string `json:"tenant_id"`
|
||||
ProjectId string `json:"project_id"`
|
||||
Listeners []Listener `json:"listeners"`
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
HealthMonitorId string `json:"healthmonitor_id"`
|
||||
SessionPersistence StickySession `json:"session_persistence"`
|
||||
}
|
||||
|
||||
func (self *SElbBackendGroup) GetLoadbalancerId() string {
|
||||
return self.lb.GetId()
|
||||
}
|
||||
|
||||
func (self *SElbBackendGroup) GetILoadbalancer() cloudprovider.ICloudLoadbalancer {
|
||||
return self.lb
|
||||
}
|
||||
|
||||
type StickySession struct {
|
||||
Type string `json:"type"`
|
||||
CookieName string `json:"cookie_name"`
|
||||
PersistenceTimeout int `json:"persistence_timeout"`
|
||||
}
|
||||
|
||||
func (self *SElbBackendGroup) GetProtocolType() string {
|
||||
switch self.Protocol {
|
||||
case "TCP":
|
||||
return api.LB_LISTENER_TYPE_TCP
|
||||
case "UDP":
|
||||
return api.LB_LISTENER_TYPE_UDP
|
||||
case "HTTP":
|
||||
return api.LB_LISTENER_TYPE_HTTP
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SElbBackendGroup) GetScheduler() string {
|
||||
switch self.LBAlgorithm {
|
||||
case "ROUND_ROBIN":
|
||||
return api.LB_SCHEDULER_WRR
|
||||
case "LEAST_CONNECTIONS":
|
||||
return api.LB_SCHEDULER_WLC
|
||||
case "SOURCE_IP":
|
||||
return api.LB_SCHEDULER_SCH
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func ToHuaweiHealthCheckHttpCode(c string) string {
|
||||
c = strings.TrimSpace(c)
|
||||
segs := strings.Split(c, ",")
|
||||
ret := []string{}
|
||||
for _, seg := range segs {
|
||||
seg = strings.TrimLeft(seg, "http_")
|
||||
seg = strings.TrimSpace(seg)
|
||||
seg = strings.Replace(seg, "xx", "00", -1)
|
||||
ret = append(ret, seg)
|
||||
}
|
||||
|
||||
return strings.Join(ret, ",")
|
||||
}
|
||||
|
||||
func ToOnecloudHealthCheckHttpCode(c string) string {
|
||||
c = strings.TrimSpace(c)
|
||||
segs := strings.Split(c, ",")
|
||||
ret := []string{}
|
||||
for _, seg := range segs {
|
||||
seg = strings.TrimSpace(seg)
|
||||
seg = strings.Replace(seg, "00", "xx", -1)
|
||||
seg = "http_" + seg
|
||||
ret = append(ret, seg)
|
||||
}
|
||||
|
||||
return strings.Join(ret, ",")
|
||||
}
|
||||
|
||||
func (self *SElbBackendGroup) GetHealthCheck() (*cloudprovider.SLoadbalancerHealthCheck, error) {
|
||||
if len(self.HealthMonitorId) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
health, err := self.region.GetLoadBalancerHealthCheck(self.HealthMonitorId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var healthCheckType string
|
||||
switch health.Type {
|
||||
case "TCP":
|
||||
healthCheckType = api.LB_HEALTH_CHECK_TCP
|
||||
case "UDP_CONNECT":
|
||||
healthCheckType = api.LB_HEALTH_CHECK_UDP
|
||||
case "HTTP":
|
||||
healthCheckType = api.LB_HEALTH_CHECK_HTTP
|
||||
default:
|
||||
healthCheckType = ""
|
||||
}
|
||||
|
||||
ret := cloudprovider.SLoadbalancerHealthCheck{
|
||||
HealthCheckType: healthCheckType,
|
||||
HealthCheckTimeout: health.Timeout,
|
||||
HealthCheckDomain: health.DomainName,
|
||||
HealthCheckURI: health.URLPath,
|
||||
HealthCheckInterval: health.Delay,
|
||||
HealthCheckRise: health.MaxRetries,
|
||||
HealthCheckHttpCode: ToOnecloudHealthCheckHttpCode(health.ExpectedCodes),
|
||||
}
|
||||
|
||||
return &ret, nil
|
||||
}
|
||||
|
||||
func (self *SElbBackendGroup) GetStickySession() (*cloudprovider.SLoadbalancerStickySession, error) {
|
||||
if len(self.SessionPersistence.Type) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var stickySessionType string
|
||||
switch self.SessionPersistence.Type {
|
||||
case "SOURCE_IP":
|
||||
stickySessionType = api.LB_STICKY_SESSION_TYPE_INSERT
|
||||
case "HTTP_COOKIE":
|
||||
stickySessionType = api.LB_STICKY_SESSION_TYPE_INSERT
|
||||
case "APP_COOKIE":
|
||||
stickySessionType = api.LB_STICKY_SESSION_TYPE_SERVER
|
||||
}
|
||||
|
||||
ret := cloudprovider.SLoadbalancerStickySession{
|
||||
StickySession: api.LB_BOOL_ON,
|
||||
StickySessionCookie: self.SessionPersistence.CookieName,
|
||||
StickySessionType: stickySessionType,
|
||||
StickySessionCookieTimeout: self.SessionPersistence.PersistenceTimeout * 60,
|
||||
}
|
||||
|
||||
return &ret, nil
|
||||
}
|
||||
|
||||
func (self *SElbBackendGroup) GetId() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SElbBackendGroup) GetName() string {
|
||||
return self.Name
|
||||
}
|
||||
|
||||
func (self *SElbBackendGroup) GetGlobalId() string {
|
||||
return self.GetId()
|
||||
}
|
||||
|
||||
func (self *SElbBackendGroup) GetStatus() string {
|
||||
return api.LB_STATUS_ENABLED
|
||||
}
|
||||
|
||||
func (self *SElbBackendGroup) Refresh() error {
|
||||
ret, err := self.lb.region.GetLoadBalancerBackendGroupId(self.GetId())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ret.lb = self.lb
|
||||
|
||||
err = jsonutils.Update(self, ret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SElbBackendGroup) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SElbBackendGroup) GetProjectId() string {
|
||||
return self.ProjectId
|
||||
}
|
||||
|
||||
func (self *SElbBackendGroup) IsDefault() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SElbBackendGroup) GetType() string {
|
||||
return api.LB_BACKENDGROUP_TYPE_NORMAL
|
||||
}
|
||||
|
||||
func (self *SElbBackendGroup) GetILoadbalancerBackends() ([]cloudprovider.ICloudLoadbalancerBackend, error) {
|
||||
ret, err := self.region.GetLoadBalancerBackends(self.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
iret := []cloudprovider.ICloudLoadbalancerBackend{}
|
||||
for i := range ret {
|
||||
backend := ret[i]
|
||||
backend.lb = self.lb
|
||||
backend.backendGroup = self
|
||||
|
||||
iret = append(iret, &backend)
|
||||
}
|
||||
|
||||
return iret, nil
|
||||
}
|
||||
|
||||
func (self *SElbBackendGroup) GetILoadbalancerBackendById(serverId string) (cloudprovider.ICloudLoadbalancerBackend, error) {
|
||||
backend, err := self.region.GetElbBackend(self.GetId(), serverId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
backend.lb = self.lb
|
||||
backend.backendGroup = self
|
||||
return backend, nil
|
||||
}
|
||||
|
||||
func (self *SElbBackendGroup) AddBackendServer(serverId string, weight int, port int) (cloudprovider.ICloudLoadbalancerBackend, error) {
|
||||
instance, err := self.lb.region.GetInstance(serverId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nics, err := instance.GetINics()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if len(nics) == 0 {
|
||||
return nil, fmt.Errorf("AddBackendServer %s no network interface found", serverId)
|
||||
}
|
||||
|
||||
subnets, err := self.lb.region.GetInstanceInterfaces(instance.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if len(subnets) == 0 {
|
||||
return nil, fmt.Errorf("AddBackendServer %s no subnet found", serverId)
|
||||
}
|
||||
|
||||
net, err := self.lb.region.GetNetwork(subnets[0].NetId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
backend, err := self.region.AddLoadBalancerBackend(self.GetId(), net.NeutronSubnetId, nics[0].GetIP(), port, weight)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
backend.lb = self.lb
|
||||
backend.backendGroup = self
|
||||
return backend, nil
|
||||
}
|
||||
|
||||
func (self *SElbBackendGroup) RemoveBackendServer(backendId string, weight int, port int) error {
|
||||
ibackend, err := self.GetILoadbalancerBackendById(backendId)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == cloudprovider.ErrNotFound {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.Wrap(err, "ElbBackendGroup.GetILoadbalancerBackendById")
|
||||
}
|
||||
|
||||
err = self.region.RemoveLoadBalancerBackend(self.GetId(), backendId)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "ElbBackendGroup.RemoveBackendServer")
|
||||
}
|
||||
|
||||
return cloudprovider.WaitDeleted(ibackend, 2*time.Second, 30*time.Second)
|
||||
}
|
||||
|
||||
func (self *SElbBackendGroup) Delete(ctx context.Context) error {
|
||||
if len(self.HealthMonitorId) > 0 {
|
||||
err := self.region.DeleteLoadbalancerHealthCheck(self.HealthMonitorId)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "ElbBackendGroup.Delete.DeleteLoadbalancerHealthCheck")
|
||||
}
|
||||
}
|
||||
|
||||
// 删除后端服务器组的同时,删除掉无效的后端服务器数据
|
||||
{
|
||||
backends, err := self.region.getLoadBalancerAdminStateDownBackends(self.GetId())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "SElbBackendGroup.Delete.getLoadBalancerAdminStateDownBackends")
|
||||
}
|
||||
|
||||
for i := range backends {
|
||||
backend := backends[i]
|
||||
err := self.RemoveBackendServer(backend.GetId(), backend.GetPort(), backend.GetWeight())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "SElbBackendGroup.Delete.RemoveBackendServer")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err := self.region.DeleteLoadBalancerBackendGroup(self.GetId())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "ElbBackendGroup.Delete.DeleteLoadBalancerBackendGroup")
|
||||
}
|
||||
|
||||
return cloudprovider.WaitDeleted(self, 2*time.Second, 30*time.Second)
|
||||
}
|
||||
|
||||
func (self *SElbBackendGroup) Sync(ctx context.Context, group *cloudprovider.SLoadbalancerBackendGroup) error {
|
||||
if group == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err := self.region.UpdateLoadBalancerBackendGroup(self.GetId(), group)
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SRegion) GetLoadBalancerBackendGroupId(backendGroupId string) (*SElbBackendGroup, error) {
|
||||
ret := &SElbBackendGroup{region: self}
|
||||
res := fmt.Sprintf("lbaas/pools/" + backendGroupId)
|
||||
return ret, self.lbGet(res, ret)
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561550.html
|
||||
func (self *SRegion) UpdateLoadBalancerBackendGroup(backendGroupId string, group *cloudprovider.SLoadbalancerBackendGroup) (*SElbBackendGroup, error) {
|
||||
params := map[string]interface{}{
|
||||
"name": group.Name,
|
||||
}
|
||||
var scheduler string
|
||||
if s, ok := LB_ALGORITHM_MAP[group.Scheduler]; !ok {
|
||||
return nil, fmt.Errorf("UpdateLoadBalancerBackendGroup unsupported scheduler %s", group.Scheduler)
|
||||
} else {
|
||||
scheduler = s
|
||||
}
|
||||
params["lb_algorithm"] = scheduler
|
||||
|
||||
if group.StickySession == nil || group.StickySession.StickySession == api.LB_BOOL_OFF {
|
||||
params["session_persistence"] = jsonutils.JSONNull
|
||||
} else {
|
||||
s := map[string]interface{}{}
|
||||
timeout := int64(group.StickySession.StickySessionCookieTimeout / 60)
|
||||
if group.ListenType == api.LB_LISTENER_TYPE_UDP || group.ListenType == api.LB_LISTENER_TYPE_TCP {
|
||||
s["type"] = "SOURCE_IP"
|
||||
if timeout > 0 {
|
||||
s["persistence_timeout"] = timeout
|
||||
}
|
||||
} else {
|
||||
s["type"] = LB_STICKY_SESSION_MAP[group.StickySession.StickySessionType]
|
||||
if len(group.StickySession.StickySessionCookie) > 0 {
|
||||
s["cookie_name"] = group.StickySession.StickySessionCookie
|
||||
} else {
|
||||
if timeout > 0 {
|
||||
s["persistence_timeout"] = timeout
|
||||
}
|
||||
}
|
||||
}
|
||||
params["session_persistence"] = s
|
||||
}
|
||||
err := self.lbUpdate("lbaas/pools/"+backendGroupId, map[string]interface{}{"pool": params})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, err := self.GetLoadBalancerBackendGroupId(backendGroupId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if group.HealthCheck == nil && len(ret.HealthMonitorId) > 0 {
|
||||
err := self.DeleteLoadbalancerHealthCheck(ret.HealthMonitorId)
|
||||
if err != nil {
|
||||
return ret, errors.Wrap(err, "DeleteLoadbalancerHealthCheck")
|
||||
}
|
||||
}
|
||||
|
||||
if group.HealthCheck != nil {
|
||||
if len(ret.HealthMonitorId) == 0 {
|
||||
_, err := self.CreateLoadBalancerHealthCheck(ret.GetId(), group.HealthCheck)
|
||||
if err != nil {
|
||||
return ret, errors.Wrap(err, "CreateLoadBalancerHealthCheck")
|
||||
}
|
||||
} else {
|
||||
_, err := self.UpdateLoadBalancerHealthCheck(ret.HealthMonitorId, group.HealthCheck)
|
||||
if err != nil {
|
||||
return ret, errors.Wrap(err, "UpdateLoadBalancerHealthCheck")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret.region = self
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561551.html
|
||||
func (self *SRegion) DeleteLoadBalancerBackendGroup(id string) error {
|
||||
return self.lbDelete("lbaas/pools/" + id)
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561556.html
|
||||
func (self *SRegion) AddLoadBalancerBackend(backendGroupId, subnetId, ipaddr string, port, weight int) (*SElbBackend, error) {
|
||||
params := map[string]interface{}{
|
||||
"address": ipaddr,
|
||||
"protocol_port": port,
|
||||
"subnet_id": subnetId,
|
||||
"weight": weight,
|
||||
}
|
||||
ret := &SElbBackend{}
|
||||
return ret, self.lbCreate(fmt.Sprintf("lbaas/pools/%s/members", backendGroupId), map[string]interface{}{"member": params}, ret)
|
||||
}
|
||||
|
||||
func (self *SRegion) RemoveLoadBalancerBackend(lbbgId string, backendId string) error {
|
||||
return self.lbDelete(fmt.Sprintf("lbaas/pools/%s/members/%s", lbbgId, backendId))
|
||||
}
|
||||
|
||||
func (self *SRegion) getLoadBalancerBackends(backendGroupId string) ([]SElbBackend, error) {
|
||||
res := fmt.Sprintf("lbaas/pools/%s/members", backendGroupId)
|
||||
ret := []SElbBackend{}
|
||||
return ret, self.lbList(res, url.Values{}, &ret)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetLoadBalancerBackends(backendGroupId string) ([]SElbBackend, error) {
|
||||
ret, err := self.getLoadBalancerBackends(backendGroupId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SRegion.GetLoadBalancerBackends.getLoadBalancerBackends")
|
||||
}
|
||||
|
||||
// 过滤掉服务器已经被删除的backend。原因是运管平台查询不到已删除的服务器记录,导致同步出错。产生肮数据。
|
||||
filtedRet := []SElbBackend{}
|
||||
for i := range ret {
|
||||
if ret[i].AdminStateUp {
|
||||
backend := ret[i]
|
||||
filtedRet = append(filtedRet, backend)
|
||||
}
|
||||
}
|
||||
|
||||
return filtedRet, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) getLoadBalancerAdminStateDownBackends(backendGroupId string) ([]SElbBackend, error) {
|
||||
ret, err := self.getLoadBalancerBackends(backendGroupId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SRegion.getLoadBalancerAdminStateDownBackends.getLoadBalancerBackends")
|
||||
}
|
||||
|
||||
filtedRet := []SElbBackend{}
|
||||
for i := range ret {
|
||||
if !ret[i].AdminStateUp {
|
||||
backend := ret[i]
|
||||
filtedRet = append(filtedRet, backend)
|
||||
}
|
||||
}
|
||||
|
||||
return filtedRet, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetLoadBalancerHealthCheck(healthCheckId string) (*SElbHealthCheck, error) {
|
||||
ret := &SElbHealthCheck{region: self}
|
||||
return ret, self.lbGet("lbaas/healthmonitors/"+healthCheckId, ret)
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
type SElbCert struct {
|
||||
multicloud.SResourceBase
|
||||
multicloud.HcsTags
|
||||
region *SRegion
|
||||
|
||||
Certificate string `json:"certificate"`
|
||||
CreateTime time.Time `json:"create_time"`
|
||||
ExpireTime time.Time `json:"expire_time"`
|
||||
Description string `json:"description"`
|
||||
Domain string `json:"domain"`
|
||||
ID string `json:"id"`
|
||||
AdminStateUp bool `json:"admin_state_up"`
|
||||
TenantID string `json:"tenant_id"`
|
||||
Name string `json:"name"`
|
||||
PrivateKey string `json:"private_key"`
|
||||
Type string `json:"type"`
|
||||
UpdateTime time.Time `json:"update_time"`
|
||||
}
|
||||
|
||||
func (self *SElbCert) GetPublickKey() string {
|
||||
return self.Certificate
|
||||
}
|
||||
|
||||
func (self *SElbCert) GetPrivateKey() string {
|
||||
return self.PrivateKey
|
||||
}
|
||||
|
||||
func (self *SElbCert) GetId() string {
|
||||
return self.ID
|
||||
}
|
||||
|
||||
func (self *SElbCert) GetName() string {
|
||||
return self.Name
|
||||
}
|
||||
|
||||
func (self *SElbCert) GetGlobalId() string {
|
||||
return self.GetId()
|
||||
}
|
||||
|
||||
func (self *SElbCert) GetStatus() string {
|
||||
return api.LB_STATUS_ENABLED
|
||||
}
|
||||
|
||||
func (self *SElbCert) Refresh() error {
|
||||
cert, err := self.region.GetLoadBalancerCertificate(self.GetId())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(self, cert)
|
||||
}
|
||||
|
||||
func (self *SElbCert) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SElbCert) GetProjectId() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SElbCert) Sync(name, privateKey, publickKey string) error {
|
||||
params := map[string]interface{}{
|
||||
"name": name,
|
||||
"private_key": privateKey,
|
||||
"certificate": publickKey,
|
||||
}
|
||||
return self.region.lbUpdate("lbaas/certificates/"+self.GetId(), params)
|
||||
}
|
||||
|
||||
func (self *SElbCert) Delete() error {
|
||||
return self.region.lbDelete("lbaas/certificates/" + self.GetId())
|
||||
}
|
||||
|
||||
func (self *SElbCert) GetCommonName() string {
|
||||
return self.Domain
|
||||
}
|
||||
|
||||
func (self *SElbCert) GetSubjectAlternativeNames() string {
|
||||
return self.Domain
|
||||
}
|
||||
|
||||
func (self *SElbCert) GetFingerprint() string {
|
||||
_fp := sha1.Sum([]byte(self.Certificate))
|
||||
fp := fmt.Sprintf("sha1:% x", _fp)
|
||||
return strings.Replace(fp, " ", ":", -1)
|
||||
}
|
||||
|
||||
func (self *SElbCert) GetExpireTime() time.Time {
|
||||
return self.ExpireTime
|
||||
}
|
||||
|
||||
func (self *SRegion) GetLoadBalancerCertificate(id string) (*SElbCert, error) {
|
||||
ret := &SElbCert{region: self}
|
||||
err := self.lbGet("lbaas/certificates/"+id, ret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-elb/elb_qy_zs_0001.html
|
||||
func (self *SRegion) CreateLoadBalancerCertificate(cert *cloudprovider.SLoadbalancerCertificate) (*SElbCert, error) {
|
||||
params := map[string]interface{}{
|
||||
"name": cert.Name,
|
||||
"private_key": cert.PrivateKey,
|
||||
"certificate": cert.Certificate,
|
||||
}
|
||||
ret := &SElbCert{region: self}
|
||||
err := self.lbCreate("lbaas/certificates", params, ret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetLoadBalancerCertificates() ([]SElbCert, error) {
|
||||
ret := []SElbCert{}
|
||||
err := self.lbList("lbaas/certificates", url.Values{}, &ret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetILoadBalancerCertificates() ([]cloudprovider.ICloudLoadbalancerCertificate, error) {
|
||||
ret, err := self.GetLoadBalancerCertificates()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
iret := make([]cloudprovider.ICloudLoadbalancerCertificate, len(ret))
|
||||
for i := range ret {
|
||||
ret[i].region = self
|
||||
iret[i] = &ret[i]
|
||||
}
|
||||
return iret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateILoadBalancerCertificate(cert *cloudprovider.SLoadbalancerCertificate) (cloudprovider.ICloudLoadbalancerCertificate, error) {
|
||||
ret, err := self.CreateLoadBalancerCertificate(cert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetILoadBalancerCertificateById(certId string) (cloudprovider.ICloudLoadbalancerCertificate, error) {
|
||||
cert, err := self.GetLoadBalancerCertificate(certId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cert, nil
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hcs
|
||||
|
||||
type SElbHealthCheck struct {
|
||||
region *SRegion
|
||||
|
||||
Name string `json:"name"`
|
||||
AdminStateUp bool `json:"admin_state_up"`
|
||||
TenantId string `json:"tenant_id"`
|
||||
ProjectId string `json:"project_id"`
|
||||
DomainName string `json:"domain_name"`
|
||||
Delay int `json:"delay"`
|
||||
ExpectedCodes string `json:"expected_codes"`
|
||||
MaxRetries int `json:"max_retries"`
|
||||
HTTPMethod string `json:"http_method"`
|
||||
Timeout int `json:"timeout"`
|
||||
Pools []Pool `json:"pools"`
|
||||
URLPath string `json:"url_path"`
|
||||
Type string `json:"type"`
|
||||
Id string `json:"id"`
|
||||
MonitorPort int `json:"monitor_port"`
|
||||
}
|
||||
@@ -0,0 +1,643 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
type InsertHeaders struct {
|
||||
XForwardedELBIP bool `json:"X-Forwarded-ELB-IP"`
|
||||
}
|
||||
|
||||
type Loadbalancer struct {
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
type SElbListener struct {
|
||||
multicloud.SResourceBase
|
||||
multicloud.SLoadbalancerRedirectBase
|
||||
multicloud.HuaweiTags
|
||||
lb *SLoadbalancer
|
||||
acl *SElbACL
|
||||
backendgroup *SElbBackendGroup
|
||||
|
||||
ProtocolPort int `json:"protocol_port"`
|
||||
Protocol string `json:"protocol"`
|
||||
Description string `json:"description"`
|
||||
AdminStateUp bool `json:"admin_state_up"`
|
||||
Http2Enable bool `json:"http2_enable"`
|
||||
Loadbalancers []Loadbalancer `json:"loadbalancers"`
|
||||
TenantId string `json:"tenant_id"`
|
||||
ProjectId string `json:"project_id"`
|
||||
ConnectionLimit int `json:"connection_limit"`
|
||||
DefaultPoolId string `json:"default_pool_id"`
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
InsertHeaders InsertHeaders `json:"insert_headers"`
|
||||
DefaultTlsContainerRef string `json:"default_tls_container_ref"`
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetId() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetName() string {
|
||||
return self.Name
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetGlobalId() string {
|
||||
return self.GetId()
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetStatus() string {
|
||||
return api.LB_STATUS_ENABLED
|
||||
}
|
||||
|
||||
func (self *SElbListener) Refresh() error {
|
||||
ilistener, err := self.lb.GetILoadBalancerListenerById(self.GetId())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
listener := ilistener.(*SElbListener)
|
||||
listener.lb = self.lb
|
||||
err = jsonutils.Update(self, listener)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SElbListener) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetProjectId() string {
|
||||
return self.ProjectId
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetListenerType() string {
|
||||
switch self.Protocol {
|
||||
case "TCP":
|
||||
return api.LB_LISTENER_TYPE_TCP
|
||||
case "UDP":
|
||||
return api.LB_LISTENER_TYPE_UDP
|
||||
case "HTTP":
|
||||
return api.LB_LISTENER_TYPE_HTTP
|
||||
case "TERMINATED_HTTPS":
|
||||
return api.LB_LISTENER_TYPE_HTTPS
|
||||
case "HTTPS":
|
||||
return api.LB_LISTENER_TYPE_HTTPS
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetListenerPort() int {
|
||||
return self.ProtocolPort
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetBackendGroup() (*SElbBackendGroup, error) {
|
||||
if self.backendgroup == nil {
|
||||
lbbgId := self.GetBackendGroupId()
|
||||
if len(lbbgId) > 0 {
|
||||
lbbg, err := self.lb.GetILoadBalancerBackendGroupById(lbbgId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
self.backendgroup = lbbg.(*SElbBackendGroup)
|
||||
}
|
||||
}
|
||||
|
||||
return self.backendgroup, nil
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetScheduler() string {
|
||||
lbbg, err := self.GetBackendGroup()
|
||||
if err != nil {
|
||||
log.Errorf("ElbListener GetScheduler %s", err.Error())
|
||||
}
|
||||
|
||||
if lbbg == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return lbbg.GetScheduler()
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetAcl() (*SElbACL, error) {
|
||||
if self.acl != nil {
|
||||
return self.acl, nil
|
||||
}
|
||||
|
||||
acls, err := self.lb.region.GetLoadBalancerAcls(self.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(acls) == 0 {
|
||||
return nil, nil
|
||||
} else {
|
||||
self.acl = &acls[0]
|
||||
return &acls[0], nil
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetAclStatus() string {
|
||||
acl, err := self.GetAcl()
|
||||
if err != nil {
|
||||
log.Debugf("GetAclStatus %s", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
if acl != nil && acl.EnableWhitelist {
|
||||
return api.LB_BOOL_ON
|
||||
}
|
||||
|
||||
return api.LB_BOOL_OFF
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetAclType() string {
|
||||
return api.LB_ACL_TYPE_WHITE
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetAclId() string {
|
||||
acl, err := self.GetAcl()
|
||||
if err != nil {
|
||||
log.Debugf("GetAclStatus %s", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
if acl == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return acl.GetId()
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetEgressMbps() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetHealthCheck() string {
|
||||
lbbg, err := self.GetBackendGroup()
|
||||
if err != nil {
|
||||
log.Errorf("ElbListener GetHealthCheck %s", err.Error())
|
||||
}
|
||||
|
||||
if lbbg == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
health, err := lbbg.GetHealthCheck()
|
||||
if err != nil {
|
||||
log.Errorf("ElbListener GetHealthCheck %s", err.Error())
|
||||
}
|
||||
|
||||
if health != nil {
|
||||
return api.LB_BOOL_ON
|
||||
} else {
|
||||
return api.LB_BOOL_OFF
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetHealthCheckType() string {
|
||||
lbbg, err := self.GetBackendGroup()
|
||||
if err != nil {
|
||||
log.Errorf("ElbListener GetHealthCheckType %s", err.Error())
|
||||
}
|
||||
|
||||
if lbbg == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
health, err := lbbg.GetHealthCheck()
|
||||
if err != nil {
|
||||
log.Errorf("ElbListener GetHealthCheckType %s", err.Error())
|
||||
}
|
||||
|
||||
if health != nil {
|
||||
return health.HealthCheckType
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetHealthCheckTimeout() int {
|
||||
lbbg, err := self.GetBackendGroup()
|
||||
if err != nil {
|
||||
log.Errorf("ElbListener GetHealthCheckTimeout %s", err.Error())
|
||||
}
|
||||
|
||||
if lbbg == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
health, err := lbbg.GetHealthCheck()
|
||||
if err != nil {
|
||||
log.Errorf("ElbListener GetHealthCheckTimeout %s", err.Error())
|
||||
}
|
||||
|
||||
if health != nil {
|
||||
return health.HealthCheckTimeout
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetHealthCheckInterval() int {
|
||||
lbbg, err := self.GetBackendGroup()
|
||||
if err != nil {
|
||||
log.Errorf("ElbListener GetHealthCheckInterval %s", err.Error())
|
||||
}
|
||||
|
||||
if lbbg == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
health, err := lbbg.GetHealthCheck()
|
||||
if err != nil {
|
||||
log.Errorf("ElbListener GetHealthCheckInterval %s", err.Error())
|
||||
}
|
||||
|
||||
if health != nil {
|
||||
return health.HealthCheckInterval
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetHealthCheckRise() int {
|
||||
lbbg, err := self.GetBackendGroup()
|
||||
if err != nil {
|
||||
log.Errorf("ElbListener GetHealthCheckRise %s", err.Error())
|
||||
}
|
||||
|
||||
if lbbg == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
health, err := lbbg.GetHealthCheck()
|
||||
if err != nil {
|
||||
log.Errorf("ElbListener GetHealthCheckRise %s", err.Error())
|
||||
}
|
||||
|
||||
if health != nil {
|
||||
return health.HealthCheckRise
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetHealthCheckFail() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetHealthCheckReq() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetHealthCheckExp() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetBackendGroupId() string {
|
||||
return self.DefaultPoolId
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetBackendServerPort() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetHealthCheckDomain() string {
|
||||
lbbg, err := self.GetBackendGroup()
|
||||
if err != nil {
|
||||
log.Errorf("ElbListener GetHealthCheckDomain %s", err.Error())
|
||||
}
|
||||
|
||||
if lbbg == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
health, err := lbbg.GetHealthCheck()
|
||||
if err != nil {
|
||||
log.Errorf("ElbListener GetHealthCheckDomain %s", err.Error())
|
||||
}
|
||||
|
||||
if health != nil {
|
||||
return health.HealthCheckDomain
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetHealthCheckURI() string {
|
||||
lbbg, err := self.GetBackendGroup()
|
||||
if err != nil {
|
||||
log.Errorf("ElbListener GetHealthCheckURI %s", err.Error())
|
||||
}
|
||||
|
||||
if lbbg == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
health, err := lbbg.GetHealthCheck()
|
||||
if err != nil {
|
||||
log.Errorf("ElbListener GetHealthCheckURI %s", err.Error())
|
||||
}
|
||||
|
||||
if health != nil {
|
||||
return health.HealthCheckURI
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetHealthCheckCode() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-elb/zh-cn_topic_0136295317.html
|
||||
func (self *SElbListener) CreateILoadBalancerListenerRule(rule *cloudprovider.SLoadbalancerListenerRule) (cloudprovider.ICloudLoadbalancerListenerRule, error) {
|
||||
l7policy, err := self.lb.region.CreateLoadBalancerPolicy(self.GetId(), rule)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l7policy.region = self.lb.region
|
||||
l7policy.lb = self.lb
|
||||
l7policy.listener = self
|
||||
return l7policy, nil
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetILoadBalancerListenerRuleById(ruleId string) (cloudprovider.ICloudLoadbalancerListenerRule, error) {
|
||||
ret := &SElbListenerPolicy{region: self.lb.region, lb: self.lb, listener: self}
|
||||
return ret, self.lb.region.lbGet("lbaas/l7policies/"+ruleId, ret)
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetILoadbalancerListenerRules() ([]cloudprovider.ICloudLoadbalancerListenerRule, error) {
|
||||
ret, err := self.lb.region.GetLoadBalancerPolicies(self.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
iret := []cloudprovider.ICloudLoadbalancerListenerRule{}
|
||||
for i := range ret {
|
||||
rule := ret[i]
|
||||
rule.listener = self
|
||||
rule.lb = self.lb
|
||||
rule.region = self.lb.region
|
||||
iret = append(iret, &rule)
|
||||
}
|
||||
|
||||
return iret, nil
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetStickySession() string {
|
||||
lbbg, err := self.GetBackendGroup()
|
||||
if err != nil {
|
||||
log.Errorf("ElbListener GetStickySession %s", err.Error())
|
||||
}
|
||||
|
||||
if lbbg == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
stickySession, err := lbbg.GetStickySession()
|
||||
if err != nil {
|
||||
log.Errorf("ElbListener GetStickySession %s", err.Error())
|
||||
}
|
||||
|
||||
if stickySession != nil {
|
||||
return stickySession.StickySession
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetStickySessionType() string {
|
||||
lbbg, err := self.GetBackendGroup()
|
||||
if err != nil {
|
||||
log.Errorf("ElbListener GetStickySessionType %s", err.Error())
|
||||
}
|
||||
|
||||
if lbbg == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
stickySession, err := lbbg.GetStickySession()
|
||||
if err != nil {
|
||||
log.Errorf("ElbListener GetStickySessionType %s", err.Error())
|
||||
}
|
||||
|
||||
if stickySession != nil {
|
||||
return stickySession.StickySessionType
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetStickySessionCookie() string {
|
||||
lbbg, err := self.GetBackendGroup()
|
||||
if err != nil {
|
||||
log.Errorf("ElbListener GetStickySessionCookie %s", err.Error())
|
||||
}
|
||||
|
||||
if lbbg == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
stickySession, err := lbbg.GetStickySession()
|
||||
if err != nil {
|
||||
log.Errorf("ElbListener GetStickySessionCookie %s", err.Error())
|
||||
}
|
||||
|
||||
if stickySession != nil {
|
||||
return stickySession.StickySessionCookie
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetStickySessionCookieTimeout() int {
|
||||
lbbg, err := self.GetBackendGroup()
|
||||
if err != nil {
|
||||
log.Errorf("ElbListener GetStickySessionCookieTimeout %s", err.Error())
|
||||
}
|
||||
|
||||
if lbbg == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
stickySession, err := lbbg.GetStickySession()
|
||||
if err != nil {
|
||||
log.Errorf("ElbListener GetStickySessionCookieTimeout %s", err.Error())
|
||||
}
|
||||
|
||||
if stickySession != nil {
|
||||
return stickySession.StickySessionCookieTimeout
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SElbListener) XForwardedForEnabled() bool {
|
||||
return self.InsertHeaders.XForwardedELBIP
|
||||
}
|
||||
|
||||
func (self *SElbListener) GzipEnabled() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetCertificateId() string {
|
||||
return self.DefaultTlsContainerRef
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetTLSCipherPolicy() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SElbListener) HTTP2Enabled() bool {
|
||||
return self.Http2Enable
|
||||
}
|
||||
|
||||
func (self *SElbListener) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SElbListener) Stop() error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561544.html
|
||||
/*
|
||||
default_pool_id有如下限制:
|
||||
不能更新为其他监听器的default_pool。
|
||||
不能更新为其他监听器的关联的转发策略所使用的pool。
|
||||
default_pool_id对应的后端云服务器组的protocol和监听器的protocol有如下关系:
|
||||
监听器的protocol为TCP时,后端云服务器组的protocol必须为TCP。
|
||||
监听器的protocol为UDP时,后端云服务器组的protocol必须为UDP。
|
||||
监听器的protocol为HTTP或TERMINATED_HTTPS时,后端云服务器组的protocol必须为HTTP。
|
||||
*/
|
||||
func (self *SElbListener) Sync(ctx context.Context, listener *cloudprovider.SLoadbalancerListener) error {
|
||||
return self.lb.region.UpdateLoadBalancerListener(self.GetId(), listener)
|
||||
}
|
||||
|
||||
func (self *SElbListener) Delete(ctx context.Context) error {
|
||||
return self.lb.region.lbDelete("lbaas/listeners/" + self.GetId())
|
||||
}
|
||||
|
||||
func (self *SRegion) UpdateLoadBalancerListener(listenerId string, listener *cloudprovider.SLoadbalancerListener) error {
|
||||
params := map[string]interface{}{
|
||||
"name": listener.Name,
|
||||
"description": listener.Description,
|
||||
"http2_enable": listener.EnableHTTP2,
|
||||
"default_pool_id": jsonutils.JSONNull,
|
||||
}
|
||||
if len(listener.BackendGroupID) > 0 {
|
||||
params["default_pool_id"] = listener.BackendGroupID
|
||||
}
|
||||
|
||||
if listener.ListenerType == api.LB_LISTENER_TYPE_HTTPS {
|
||||
params["default_tls_container_ref"] = listener.CertificateID
|
||||
}
|
||||
|
||||
if listener.XForwardedFor {
|
||||
params["insert_headers"] = map[string]interface{}{
|
||||
"X-Forwarded-ELB-IP": listener.XForwardedFor,
|
||||
}
|
||||
}
|
||||
return self.lbUpdate("elb/listeners/"+listenerId, map[string]interface{}{"listener": params})
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-elb/zh-cn_topic_0136295315.html
|
||||
func (self *SRegion) GetLoadBalancerPolicies(listenerId string) ([]SElbListenerPolicy, error) {
|
||||
query := url.Values{}
|
||||
if len(listenerId) > 0 {
|
||||
query.Set("listener_id", listenerId)
|
||||
}
|
||||
|
||||
ret := []SElbListenerPolicy{}
|
||||
return ret, self.lbList("lbaas/l7policies", query, &ret)
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-elb/zh-cn_topic_0116649234.html
|
||||
func (self *SRegion) GetLoadBalancerPolicyRules(policyId string) ([]SElbListenerPolicyRule, error) {
|
||||
ret := []SElbListenerPolicyRule{}
|
||||
return ret, self.lbList(fmt.Sprintf("lbaas/l7policies/%s/rules", policyId), url.Values{}, &ret)
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-elb/zh-cn_topic_0136295317.html
|
||||
func (self *SRegion) CreateLoadBalancerPolicy(listenerId string, rule *cloudprovider.SLoadbalancerListenerRule) (*SElbListenerPolicy, error) {
|
||||
ret := &SElbListenerPolicy{}
|
||||
params := map[string]interface{}{
|
||||
"name": rule.Name,
|
||||
"listener_id": listenerId,
|
||||
"action": "REDIRECT_TO_POOL",
|
||||
"redirect_pool_id": rule.BackendGroupID,
|
||||
}
|
||||
err := self.lbCreate("lbaas/l7policies", map[string]interface{}{"l7policy": params}, ret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(rule.Domain) > 0 {
|
||||
params := map[string]interface{}{
|
||||
"type": "HOST_NAME",
|
||||
"value": rule.Domain,
|
||||
"compare_type": "EQUAL_TO",
|
||||
}
|
||||
err := self.lbCreate(fmt.Sprintf("lbaas/l7policies/%s/rules", ret.GetId()), map[string]interface{}{"rule": params}, nil)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(rule.Path) > 0 {
|
||||
params := map[string]interface{}{
|
||||
"type": "PATH",
|
||||
"value": rule.Path,
|
||||
"compare_type": "EQUAL_TO",
|
||||
}
|
||||
err := self.lbCreate(fmt.Sprintf("lbaas/l7policies/%s/rules", ret.GetId()), map[string]interface{}{"rule": params}, nil)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetClientIdleTimeout() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SElbListener) GetBackendConnectTimeout() int {
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
type SElbListenerPolicy struct {
|
||||
multicloud.SResourceBase
|
||||
multicloud.SLoadbalancerRedirectBase
|
||||
multicloud.HuaweiTags
|
||||
region *SRegion
|
||||
lb *SLoadbalancer
|
||||
listener *SElbListener
|
||||
|
||||
RedirectPoolId string `json:"redirect_pool_id"`
|
||||
RedirectListenerId *string `json:"redirect_listener_id"`
|
||||
Description string `json:"description"`
|
||||
AdminStateUp bool `json:"admin_state_up"`
|
||||
Rules []Rule `json:"rules"`
|
||||
TenantId string `json:"tenant_id"`
|
||||
ProjectId string `json:"project_id"`
|
||||
ListenerId string `json:"listener_id"`
|
||||
RedirectURL *string `json:"redirect_url"`
|
||||
ProvisioningStatus string `json:"provisioning_status"`
|
||||
Action string `json:"action"`
|
||||
Position int64 `json:"position"`
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type Rule struct {
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
type SElbListenerPolicyRule struct {
|
||||
region *SRegion
|
||||
policy *SElbListenerPolicy
|
||||
|
||||
CompareType string `json:"compare_type"`
|
||||
ProvisioningStatus string `json:"provisioning_status"`
|
||||
AdminStateUp bool `json:"admin_state_up"`
|
||||
TenantId string `json:"tenant_id"`
|
||||
ProjectId string `json:"project_id"`
|
||||
Invert bool `json:"invert"`
|
||||
Value string `json:"value"`
|
||||
Key interface{} `json:"key"`
|
||||
Type string `json:"type"`
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
func (self *SElbListenerPolicy) GetId() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SElbListenerPolicy) GetName() string {
|
||||
return self.Name
|
||||
}
|
||||
|
||||
func (self *SElbListenerPolicy) GetGlobalId() string {
|
||||
return self.GetId()
|
||||
}
|
||||
|
||||
// 负载均衡没有启用禁用操作
|
||||
func (self *SElbListenerPolicy) GetStatus() string {
|
||||
return api.LB_STATUS_ENABLED
|
||||
}
|
||||
|
||||
func (self *SElbListenerPolicy) Refresh() error {
|
||||
res := "lbaas/l7policies/" + self.Id
|
||||
ret := &SElbListenerPolicy{}
|
||||
err := self.lb.region.lbGet(res, ret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(self, ret)
|
||||
}
|
||||
|
||||
func (self *SElbListenerPolicy) IsDefault() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SElbListenerPolicy) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SElbListenerPolicy) GetProjectId() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SElbListenerPolicy) GetRules() ([]SElbListenerPolicyRule, error) {
|
||||
ret, err := self.region.GetLoadBalancerPolicyRules(self.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range ret {
|
||||
ret[i].region = self.lb.region
|
||||
ret[i].policy = self
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SElbListenerPolicy) GetDomain() string {
|
||||
rules, err := self.GetRules()
|
||||
if err != nil {
|
||||
log.Errorf("loadbalancer rule GetDomain %s", err)
|
||||
}
|
||||
|
||||
for i := range rules {
|
||||
if rules[i].Type == "HOST_NAME" {
|
||||
return rules[i].Value
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SElbListenerPolicy) GetCondition() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SElbListenerPolicy) GetPath() string {
|
||||
rules, err := self.GetRules()
|
||||
if err != nil {
|
||||
log.Errorf("loadbalancer rule GetPath %s", err)
|
||||
}
|
||||
|
||||
for i := range rules {
|
||||
if rules[i].Type == "PATH" {
|
||||
return rules[i].Value
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SElbListenerPolicy) GetBackendGroupId() string {
|
||||
return self.RedirectPoolId
|
||||
}
|
||||
|
||||
func (self *SElbListenerPolicy) Delete(ctx context.Context) error {
|
||||
return self.region.DeleteLoadBalancerPolicy(self.GetId())
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteLoadBalancerPolicy(policyId string) error {
|
||||
return self.lbDelete("lbaas/l7policies/" + policyId)
|
||||
}
|
||||
@@ -0,0 +1,494 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/util/httputils"
|
||||
)
|
||||
|
||||
type MetricData struct {
|
||||
Namespace string
|
||||
MetricName string
|
||||
Dimensions []struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
Datapoints []struct {
|
||||
Average float64
|
||||
Timestamp int64
|
||||
}
|
||||
Unit string
|
||||
}
|
||||
|
||||
func (self *SHcsClient) monitorPost(resource string, params map[string]interface{}) (jsonutils.JSONObject, error) {
|
||||
url := self._url("ces", "V1.0", self.defaultRegion, resource)
|
||||
return self.request(httputils.POST, url, nil, params)
|
||||
}
|
||||
|
||||
func (self *SHcsClient) getServerMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
|
||||
params := map[string]interface{}{
|
||||
"from": fmt.Sprintf("%d", opts.StartTime.UnixMilli()),
|
||||
"to": fmt.Sprintf("%d", opts.EndTime.UnixMilli()),
|
||||
"period": "1",
|
||||
"filter": "average",
|
||||
}
|
||||
metrics := []interface{}{}
|
||||
namespace, dimesionName, metricNames := "SYS.ECS", "instance_id", []string{
|
||||
"cpu_util",
|
||||
"network_incoming_bytes_aggregate_rate",
|
||||
"network_outgoing_bytes_aggregate_rate",
|
||||
"disk_read_bytes_rate",
|
||||
"disk_write_bytes_rate",
|
||||
"disk_read_requests_rate",
|
||||
"disk_write_requests_rate",
|
||||
}
|
||||
for _, metricName := range metricNames {
|
||||
metrics = append(metrics, map[string]interface{}{
|
||||
"namespace": namespace,
|
||||
"metric_name": metricName,
|
||||
"dimensions": []map[string]string{
|
||||
{
|
||||
"name": dimesionName,
|
||||
"value": opts.ResourceId,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
params["metrics"] = metrics
|
||||
resp, err := self.monitorPost("batch-query-metric-data", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
metricData := []MetricData{}
|
||||
err = resp.Unmarshal(&metricData, "metrics")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "resp.Unmarshal")
|
||||
}
|
||||
result := []cloudprovider.MetricValues{}
|
||||
for i := range metricData {
|
||||
ret := cloudprovider.MetricValues{
|
||||
Id: opts.ResourceId,
|
||||
Unit: metricData[i].Unit,
|
||||
Values: []cloudprovider.MetricValue{},
|
||||
}
|
||||
tags := map[string]string{}
|
||||
switch metricData[i].MetricName {
|
||||
case "cpu_util":
|
||||
ret.MetricType = cloudprovider.VM_METRIC_TYPE_CPU_USAGE
|
||||
case "network_incoming_bytes_aggregate_rate":
|
||||
ret.MetricType = cloudprovider.VM_METRIC_TYPE_NET_BPS_RX
|
||||
tags = map[string]string{"net_type": "internet"}
|
||||
case "network_outgoing_bytes_aggregate_rate":
|
||||
ret.MetricType = cloudprovider.VM_METRIC_TYPE_NET_BPS_TX
|
||||
tags = map[string]string{"net_type": "internet"}
|
||||
case "disk_read_bytes_rate":
|
||||
ret.MetricType = cloudprovider.VM_METRIC_TYPE_DISK_IO_READ_BPS
|
||||
case "disk_write_bytes_rate":
|
||||
ret.MetricType = cloudprovider.VM_METRIC_TYPE_DISK_IO_WRITE_BPS
|
||||
case "disk_read_requests_rate":
|
||||
ret.MetricType = cloudprovider.VM_METRIC_TYPE_DISK_IO_READ_IOPS
|
||||
case "disk_write_requests_rate":
|
||||
ret.MetricType = cloudprovider.VM_METRIC_TYPE_DISK_IO_WRITE_IOPS
|
||||
default:
|
||||
log.Warningf("invalid metricName %s for %s %s", metricData[i].MetricName, opts.ResourceType, opts.ResourceId)
|
||||
continue
|
||||
}
|
||||
for _, value := range metricData[i].Datapoints {
|
||||
metricValue := cloudprovider.MetricValue{
|
||||
Value: value.Average,
|
||||
Timestamp: time.UnixMilli(value.Timestamp),
|
||||
Tags: tags,
|
||||
}
|
||||
ret.Values = append(ret.Values, metricValue)
|
||||
}
|
||||
result = append(result, ret)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (self *SHcsClient) getRedisMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
|
||||
params := map[string]interface{}{
|
||||
"from": fmt.Sprintf("%d", opts.StartTime.UnixMilli()),
|
||||
"to": fmt.Sprintf("%d", opts.EndTime.UnixMilli()),
|
||||
"period": "1",
|
||||
"filter": "average",
|
||||
}
|
||||
metrics := []interface{}{}
|
||||
namespace, dimesionName, metricNames := "SYS.DCS", "dcs_instance_id", []string{
|
||||
"cpu_usage",
|
||||
"memory_usage",
|
||||
"instantaneous_input_kbps",
|
||||
"instantaneous_output_kbps",
|
||||
"connected_clients",
|
||||
"instantaneous_ops",
|
||||
"keys",
|
||||
"expires",
|
||||
"used_memory_dataset",
|
||||
}
|
||||
for _, metricName := range metricNames {
|
||||
metrics = append(metrics, map[string]interface{}{
|
||||
"namespace": namespace,
|
||||
"metric_name": metricName,
|
||||
"dimensions": []map[string]string{
|
||||
{
|
||||
"name": dimesionName,
|
||||
"value": opts.ResourceId,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
params["metrics"] = metrics
|
||||
resp, err := self.monitorPost("batch-query-metric-data", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
metricData := []MetricData{}
|
||||
err = resp.Unmarshal(&metricData, "metrics")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "resp.Unmarshal")
|
||||
}
|
||||
result := []cloudprovider.MetricValues{}
|
||||
for i := range metricData {
|
||||
ret := cloudprovider.MetricValues{
|
||||
Id: opts.ResourceId,
|
||||
Unit: metricData[i].Unit,
|
||||
Values: []cloudprovider.MetricValue{},
|
||||
}
|
||||
tags := map[string]string{}
|
||||
switch metricData[i].MetricName {
|
||||
case "cpu_usage":
|
||||
ret.MetricType = cloudprovider.REDIS_METRIC_TYPE_CPU_USAGE
|
||||
case "memory_usage":
|
||||
ret.MetricType = cloudprovider.REDIS_METRIC_TYPE_MEM_USAGE
|
||||
case "instantaneous_input_kbps":
|
||||
ret.MetricType = cloudprovider.REDIS_METRIC_TYPE_NET_BPS_RX
|
||||
case "instantaneous_output_kbps":
|
||||
ret.MetricType = cloudprovider.REDIS_METRIC_TYPE_NET_BPS_TX
|
||||
case "connected_clients":
|
||||
ret.MetricType = cloudprovider.REDIS_METRIC_TYPE_USED_CONN
|
||||
case "instantaneous_ops":
|
||||
ret.MetricType = cloudprovider.REDIS_METRIC_TYPE_OPT_SES
|
||||
case "keys":
|
||||
ret.MetricType = cloudprovider.REDIS_METRIC_TYPE_CACHE_KEYS
|
||||
case "expires":
|
||||
ret.MetricType = cloudprovider.REDIS_METRIC_TYPE_CACHE_EXP_KEYS
|
||||
case "used_memory_dataset":
|
||||
ret.MetricType = cloudprovider.REDIS_METRIC_TYPE_DATA_MEM_USAGE
|
||||
default:
|
||||
log.Warningf("invalid metricName %s for %s %s", metricData[i].MetricName, opts.ResourceType, opts.ResourceId)
|
||||
continue
|
||||
}
|
||||
for _, value := range metricData[i].Datapoints {
|
||||
metricValue := cloudprovider.MetricValue{
|
||||
Value: value.Average,
|
||||
Timestamp: time.UnixMilli(value.Timestamp),
|
||||
Tags: tags,
|
||||
}
|
||||
ret.Values = append(ret.Values, metricValue)
|
||||
}
|
||||
result = append(result, ret)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (self *SHcsClient) getRdsMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
|
||||
params := map[string]interface{}{
|
||||
"from": fmt.Sprintf("%d", opts.StartTime.UnixMilli()),
|
||||
"to": fmt.Sprintf("%d", opts.EndTime.UnixMilli()),
|
||||
"period": "1",
|
||||
"filter": "average",
|
||||
}
|
||||
metrics := []interface{}{}
|
||||
namespace, dimesionName, metricNames := "SYS.RDS", "rds_cluster_id", []string{
|
||||
"rds001_cpu_util",
|
||||
"rds002_mem_util",
|
||||
"rds004_bytes_in",
|
||||
"rds004_bytes_in",
|
||||
"rds005_bytes_out",
|
||||
"rds039_disk_util",
|
||||
"rds049_disk_read_throughput",
|
||||
"rds050_disk_write_throughput",
|
||||
"rds006_conn_count",
|
||||
"rds008_qps",
|
||||
"rds009_tps",
|
||||
"rds013_innodb_reads",
|
||||
"rds014_innodb_writes",
|
||||
}
|
||||
switch opts.Engine {
|
||||
case api.DBINSTANCE_TYPE_POSTGRESQL:
|
||||
dimesionName = "postgresql_cluster_id"
|
||||
case api.DBINSTANCE_TYPE_SQLSERVER:
|
||||
dimesionName = "rds_cluster_sqlserver_id"
|
||||
}
|
||||
|
||||
for _, metricName := range metricNames {
|
||||
metrics = append(metrics, map[string]interface{}{
|
||||
"namespace": namespace,
|
||||
"metric_name": metricName,
|
||||
"dimensions": []map[string]string{
|
||||
{
|
||||
"name": dimesionName,
|
||||
"value": opts.ResourceId,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
params["metrics"] = metrics
|
||||
resp, err := self.monitorPost("batch-query-metric-data", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
metricData := []MetricData{}
|
||||
err = resp.Unmarshal(&metricData, "metrics")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "resp.Unmarshal")
|
||||
}
|
||||
result := []cloudprovider.MetricValues{}
|
||||
for i := range metricData {
|
||||
ret := cloudprovider.MetricValues{
|
||||
Id: opts.ResourceId,
|
||||
Unit: metricData[i].Unit,
|
||||
Values: []cloudprovider.MetricValue{},
|
||||
}
|
||||
tags := map[string]string{}
|
||||
switch metricData[i].MetricName {
|
||||
case "rds001_cpu_util":
|
||||
ret.MetricType = cloudprovider.RDS_METRIC_TYPE_CPU_USAGE
|
||||
case "rds002_mem_util":
|
||||
ret.MetricType = cloudprovider.RDS_METRIC_TYPE_MEM_USAGE
|
||||
case "rds004_bytes_in":
|
||||
ret.MetricType = cloudprovider.RDS_METRIC_TYPE_NET_BPS_RX
|
||||
case "rds005_bytes_out":
|
||||
ret.MetricType = cloudprovider.RDS_METRIC_TYPE_NET_BPS_TX
|
||||
case "rds039_disk_util":
|
||||
ret.MetricType = cloudprovider.RDS_METRIC_TYPE_DISK_USAGE
|
||||
case "rds049_disk_read_throughput":
|
||||
ret.MetricType = cloudprovider.RDS_METRIC_TYPE_DISK_READ_BPS
|
||||
case "rds050_disk_write_throughput":
|
||||
ret.MetricType = cloudprovider.RDS_METRIC_TYPE_DISK_WRITE_BPS
|
||||
case "rds006_conn_count":
|
||||
ret.MetricType = cloudprovider.RDS_METRIC_TYPE_CONN_COUNT
|
||||
case "rds008_qps":
|
||||
ret.MetricType = cloudprovider.RDS_METRIC_TYPE_QPS
|
||||
case "rds009_tps":
|
||||
ret.MetricType = cloudprovider.RDS_METRIC_TYPE_TPS
|
||||
case "rds013_innodb_reads":
|
||||
ret.MetricType = cloudprovider.RDS_METRIC_TYPE_INNODB_READ_BPS
|
||||
case "rds014_innodb_writes":
|
||||
ret.MetricType = cloudprovider.RDS_METRIC_TYPE_INNODB_WRITE_BPS
|
||||
default:
|
||||
log.Warningf("invalid metricName %s for %s %s", metricData[i].MetricName, opts.ResourceType, opts.ResourceId)
|
||||
continue
|
||||
}
|
||||
for _, value := range metricData[i].Datapoints {
|
||||
metricValue := cloudprovider.MetricValue{
|
||||
Value: value.Average,
|
||||
Timestamp: time.UnixMilli(value.Timestamp),
|
||||
Tags: tags,
|
||||
}
|
||||
ret.Values = append(ret.Values, metricValue)
|
||||
}
|
||||
result = append(result, ret)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (self *SHcsClient) getBucketMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
|
||||
params := map[string]interface{}{
|
||||
"from": opts.StartTime.UnixMilli(),
|
||||
"to": opts.EndTime.UnixMilli(),
|
||||
"period": "1",
|
||||
"filter": "average",
|
||||
}
|
||||
metrics := []interface{}{}
|
||||
namespace, dimesionName, metricNames := "SYS.OBS", "bucket_name", []string{
|
||||
"download_bytes",
|
||||
"upload_bytes",
|
||||
"first_byte_latency",
|
||||
"get_request_count",
|
||||
"request_count_4xx",
|
||||
"request_count_5xx",
|
||||
}
|
||||
|
||||
for _, metricName := range metricNames {
|
||||
metrics = append(metrics, map[string]interface{}{
|
||||
"namespace": namespace,
|
||||
"metric_name": metricName,
|
||||
"dimensions": []map[string]string{
|
||||
{
|
||||
"name": dimesionName,
|
||||
"value": opts.ResourceId,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
params["metrics"] = metrics
|
||||
resp, err := self.monitorPost("batch-query-metric-data", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
metricData := []MetricData{}
|
||||
err = resp.Unmarshal(&metricData, "metrics")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "resp.Unmarshal")
|
||||
}
|
||||
result := []cloudprovider.MetricValues{}
|
||||
for i := range metricData {
|
||||
ret := cloudprovider.MetricValues{
|
||||
Id: opts.ResourceId,
|
||||
Unit: metricData[i].Unit,
|
||||
Values: []cloudprovider.MetricValue{},
|
||||
}
|
||||
tags := map[string]string{}
|
||||
switch metricData[i].MetricName {
|
||||
case "download_bytes":
|
||||
ret.MetricType = cloudprovider.BUCKET_METRIC_TYPE_NET_BPS_TX
|
||||
case "upload_bytes":
|
||||
ret.MetricType = cloudprovider.BUCKET_METRIC_TYPE_NET_BPS_RX
|
||||
case "first_byte_latency":
|
||||
ret.MetricType = cloudprovider.BUCKET_METRIC_TYPE_LATECY
|
||||
tags = map[string]string{"request": "get"}
|
||||
case "get_request_count":
|
||||
ret.MetricType = cloudprovider.BUCKET_METRYC_TYPE_REQ_COUNT
|
||||
tags = map[string]string{"request": "get"}
|
||||
case "request_count_4xx":
|
||||
ret.MetricType = cloudprovider.BUCKET_METRYC_TYPE_REQ_COUNT
|
||||
tags = map[string]string{"request": "4xx"}
|
||||
case "request_count_5xx":
|
||||
ret.MetricType = cloudprovider.BUCKET_METRYC_TYPE_REQ_COUNT
|
||||
tags = map[string]string{"request": "5xx"}
|
||||
default:
|
||||
log.Warningf("invalid metricName %s for %s %s", metricData[i].MetricName, opts.ResourceType, opts.ResourceId)
|
||||
continue
|
||||
}
|
||||
for _, value := range metricData[i].Datapoints {
|
||||
metricValue := cloudprovider.MetricValue{
|
||||
Value: value.Average,
|
||||
Timestamp: time.UnixMilli(value.Timestamp),
|
||||
Tags: tags,
|
||||
}
|
||||
ret.Values = append(ret.Values, metricValue)
|
||||
}
|
||||
result = append(result, ret)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (self *SHcsClient) getLoadbalancerMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
|
||||
params := map[string]interface{}{
|
||||
"from": fmt.Sprintf("%d", opts.StartTime.UnixMilli()),
|
||||
"to": fmt.Sprintf("%d", opts.EndTime.UnixMilli()),
|
||||
"period": "1",
|
||||
"filter": "average",
|
||||
}
|
||||
metrics := []interface{}{}
|
||||
namespace, dimesionName, metricNames := "SYS.ELB", "lb_instance_id", []string{
|
||||
"m7_in_Bps",
|
||||
"m8_out_Bps",
|
||||
"mc_l7_http_2xx",
|
||||
"md_l7_http_3xx",
|
||||
"me_l7_http_4xx",
|
||||
"mf_l7_http_5xx",
|
||||
}
|
||||
|
||||
for _, metricName := range metricNames {
|
||||
metrics = append(metrics, map[string]interface{}{
|
||||
"namespace": namespace,
|
||||
"metric_name": metricName,
|
||||
"dimensions": []map[string]string{
|
||||
{
|
||||
"name": dimesionName,
|
||||
"value": opts.ResourceId,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
params["metrics"] = metrics
|
||||
resp, err := self.monitorPost("batch-query-metric-data", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
metricData := []MetricData{}
|
||||
err = resp.Unmarshal(&metricData, "metrics")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "resp.Unmarshal")
|
||||
}
|
||||
result := []cloudprovider.MetricValues{}
|
||||
for i := range metricData {
|
||||
ret := cloudprovider.MetricValues{
|
||||
Id: opts.ResourceId,
|
||||
Unit: metricData[i].Unit,
|
||||
Values: []cloudprovider.MetricValue{},
|
||||
}
|
||||
tags := map[string]string{}
|
||||
switch metricData[i].MetricName {
|
||||
case "m7_in_Bps":
|
||||
ret.MetricType = cloudprovider.LB_METRIC_TYPE_NET_BPS_RX
|
||||
case "m8_out_Bps":
|
||||
ret.MetricType = cloudprovider.LB_METRIC_TYPE_NET_BPS_TX
|
||||
case "mc_l7_http_2xx":
|
||||
ret.MetricType = cloudprovider.LB_METRIC_TYPE_HRSP_COUNT
|
||||
tags = map[string]string{"request": "2xx"}
|
||||
case "md_l7_http_3xx":
|
||||
ret.MetricType = cloudprovider.LB_METRIC_TYPE_HRSP_COUNT
|
||||
tags = map[string]string{"request": "3xx"}
|
||||
case "md_l7_http_4xx":
|
||||
ret.MetricType = cloudprovider.LB_METRIC_TYPE_HRSP_COUNT
|
||||
tags = map[string]string{"request": "4xx"}
|
||||
case "md_l7_http_5xx":
|
||||
ret.MetricType = cloudprovider.LB_METRIC_TYPE_HRSP_COUNT
|
||||
tags = map[string]string{"request": "5xx"}
|
||||
default:
|
||||
log.Warningf("invalid metricName %s for %s %s", metricData[i].MetricName, opts.ResourceType, opts.ResourceId)
|
||||
continue
|
||||
}
|
||||
for _, value := range metricData[i].Datapoints {
|
||||
metricValue := cloudprovider.MetricValue{
|
||||
Value: value.Average,
|
||||
Timestamp: time.UnixMilli(value.Timestamp),
|
||||
Tags: tags,
|
||||
}
|
||||
ret.Values = append(ret.Values, metricValue)
|
||||
}
|
||||
result = append(result, ret)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (self *SHcsClient) GetMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
|
||||
switch opts.ResourceType {
|
||||
case cloudprovider.METRIC_RESOURCE_TYPE_SERVER:
|
||||
return self.getServerMetrics(opts)
|
||||
case cloudprovider.METRIC_RESOURCE_TYPE_REDIS:
|
||||
return self.getRedisMetrics(opts)
|
||||
case cloudprovider.METRIC_RESOURCE_TYPE_RDS:
|
||||
return self.getRdsMetrics(opts)
|
||||
case cloudprovider.METRIC_RESOURCE_TYPE_BUCKET:
|
||||
return self.getBucketMetrics(opts)
|
||||
case cloudprovider.METRIC_RESOURCE_TYPE_LB:
|
||||
return self.getLoadbalancerMetrics(opts)
|
||||
default:
|
||||
return nil, errors.Wrapf(cloudprovider.ErrNotSupported, "%s", opts.ResourceType)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
)
|
||||
|
||||
const (
|
||||
HUAWEI_NAT_SKU_SMALL = "Small"
|
||||
HUAWEI_NAT_SKU_MIDDLE = "Middle"
|
||||
HUAWEI_NAT_SKU_LARGE = "Large"
|
||||
HUAWEI_NAT_SKU_XLARGE = "XLarge"
|
||||
)
|
||||
|
||||
type SNatSku struct {
|
||||
spec string
|
||||
}
|
||||
|
||||
func (self *SNatSku) GetName() string {
|
||||
return self.spec
|
||||
}
|
||||
|
||||
func (self *SNatSku) GetPps() int {
|
||||
return 1000000
|
||||
}
|
||||
|
||||
func (self *SNatSku) GetConns() int {
|
||||
switch self.spec {
|
||||
case HUAWEI_NAT_SKU_SMALL:
|
||||
return 10000
|
||||
case HUAWEI_NAT_SKU_MIDDLE:
|
||||
return 50000
|
||||
case HUAWEI_NAT_SKU_LARGE:
|
||||
return 200000
|
||||
case HUAWEI_NAT_SKU_XLARGE:
|
||||
return 1000000
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SNatSku) GetThroughput() int {
|
||||
return 10
|
||||
}
|
||||
|
||||
func (self *SNatSku) GetDesc() string {
|
||||
switch self.spec {
|
||||
case HUAWEI_NAT_SKU_SMALL:
|
||||
return "小型"
|
||||
case HUAWEI_NAT_SKU_MIDDLE:
|
||||
return "中型"
|
||||
case HUAWEI_NAT_SKU_LARGE:
|
||||
return "大型"
|
||||
case HUAWEI_NAT_SKU_XLARGE:
|
||||
return "超大型"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SNatSku) GetPostpaidStatus() string {
|
||||
return api.NAT_SKU_AVAILABLE
|
||||
}
|
||||
|
||||
func (self *SNatSku) GetPrepaidStatus() string {
|
||||
return api.NAT_SKU_SOLDOUT
|
||||
}
|
||||
|
||||
func (self *SNatSku) GetGlobalId() string {
|
||||
return self.spec
|
||||
}
|
||||
|
||||
func (self *SRegion) GetICloudNatSkus() ([]cloudprovider.ICloudNatSku, error) {
|
||||
ret := []cloudprovider.ICloudNatSku{}
|
||||
for _, spec := range []string{
|
||||
HUAWEI_NAT_SKU_SMALL,
|
||||
HUAWEI_NAT_SKU_MIDDLE,
|
||||
HUAWEI_NAT_SKU_LARGE,
|
||||
HUAWEI_NAT_SKU_XLARGE,
|
||||
} {
|
||||
sku := &SNatSku{spec: spec}
|
||||
ret = append(ret, sku)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
type SNatDEntry struct {
|
||||
multicloud.SResourceBase
|
||||
multicloud.HcsTags
|
||||
gateway *SNatGateway
|
||||
|
||||
Id string `json:"id"`
|
||||
NatGatewayId string `json:"nat_gateway_id"`
|
||||
Protocol string `json:"protocol"`
|
||||
Status string `json:"status"`
|
||||
ExternalIP string `json:"floating_ip_address"`
|
||||
ExternalPort int `json:"external_service_port"`
|
||||
InternalIP string `json:"private_ip"`
|
||||
InternalPort int `json:"internal_service_port"`
|
||||
PortId string `json:"port_id"`
|
||||
AdminStateUp bool `json:"admin_state_up"`
|
||||
}
|
||||
|
||||
func (nat *SNatDEntry) GetId() string {
|
||||
return nat.Id
|
||||
}
|
||||
|
||||
func (nat *SNatDEntry) GetName() string {
|
||||
// No name so return id
|
||||
return nat.GetId()
|
||||
}
|
||||
|
||||
func (nat *SNatDEntry) GetGlobalId() string {
|
||||
return nat.GetId()
|
||||
}
|
||||
|
||||
func (nat *SNatDEntry) GetStatus() string {
|
||||
return NatResouceStatusTransfer(nat.Status)
|
||||
}
|
||||
|
||||
func (nat *SNatDEntry) GetIpProtocol() string {
|
||||
return nat.Protocol
|
||||
}
|
||||
|
||||
func (nat *SNatDEntry) GetExternalIp() string {
|
||||
return nat.ExternalIP
|
||||
}
|
||||
|
||||
func (nat *SNatDEntry) GetExternalPort() int {
|
||||
return nat.ExternalPort
|
||||
}
|
||||
|
||||
func (nat *SNatDEntry) GetInternalIp() string {
|
||||
return nat.InternalIP
|
||||
}
|
||||
|
||||
func (nat *SNatDEntry) GetInternalPort() int {
|
||||
return nat.InternalPort
|
||||
}
|
||||
|
||||
func (nat *SNatDEntry) Delete() error {
|
||||
return nat.gateway.region.DeleteNatDEntry(nat.GetId())
|
||||
}
|
||||
|
||||
// getNatSTable return all snat rules of gateway
|
||||
func (gateway *SNatGateway) getNatDTable() ([]SNatDEntry, error) {
|
||||
ret, err := gateway.region.GetNatDTable(gateway.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range ret {
|
||||
ret[i].gateway = gateway
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetNatDTable(natGatewayId string) ([]SNatDEntry, error) {
|
||||
query := url.Values{}
|
||||
query.Set("nat_gateway_id", natGatewayId)
|
||||
ret := []SNatDEntry{}
|
||||
err := self.list("nat", "v2.0", "dnat_rules", query, &ret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range ret {
|
||||
nat := &ret[i]
|
||||
if len(nat.InternalIP) == 0 {
|
||||
port, err := self.GetPort(nat.PortId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, `get port info for transfer to ip of port_id %q error`, nat.PortId)
|
||||
}
|
||||
nat.InternalIP = port.FixedIps[0].IpAddress
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteNatDEntry(entryId string) error {
|
||||
return self.delete("nat", "v2.0", "dnat_rules/"+entryId)
|
||||
}
|
||||
|
||||
func (nat *SNatDEntry) Refresh() error {
|
||||
ret, err := nat.gateway.region.GetNatDEntry(nat.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(nat, ret)
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateNatDEntry(rule cloudprovider.SNatDRule, gatewayId string) (*SNatDEntry, error) {
|
||||
params := make(map[string]interface{})
|
||||
params["nat_gateway_id"] = gatewayId
|
||||
params["private_ip"] = rule.InternalIP
|
||||
params["internal_service_port"] = rule.InternalPort
|
||||
params["floating_ip_id"] = rule.ExternalIPID
|
||||
params["external_service_port"] = rule.ExternalPort
|
||||
params["protocol"] = rule.Protocol
|
||||
|
||||
packParams := map[string]interface{}{
|
||||
"dnat_rule": params,
|
||||
}
|
||||
ret := &SNatDEntry{}
|
||||
return ret, self.create("nat", "v2.0", "dnat_rules", packParams, ret)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetNatDEntry(id string) (*SNatDEntry, error) {
|
||||
ret := &SNatDEntry{}
|
||||
return ret, self.get("nat", "v2.0", "dnat_rules/"+id, ret)
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
billing_api "yunion.io/x/onecloud/pkg/apis/billing"
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
type SNatGateway struct {
|
||||
multicloud.SNatGatewayBase
|
||||
multicloud.HcsTags
|
||||
region *SRegion
|
||||
|
||||
Id string
|
||||
Name string
|
||||
Description string
|
||||
Spec string
|
||||
Status string
|
||||
InternalNetworkId string
|
||||
CreatedTime string `json:"created_at"`
|
||||
}
|
||||
|
||||
func (gateway *SNatGateway) GetId() string {
|
||||
return gateway.Id
|
||||
}
|
||||
|
||||
func (gateway *SNatGateway) GetName() string {
|
||||
return gateway.Name
|
||||
}
|
||||
|
||||
func (gateway *SNatGateway) GetGlobalId() string {
|
||||
return gateway.GetId()
|
||||
}
|
||||
|
||||
func (gateway *SNatGateway) GetStatus() string {
|
||||
return NatResouceStatusTransfer(gateway.Status)
|
||||
}
|
||||
|
||||
func (self *SNatGateway) Delete() error {
|
||||
return self.region.DeleteNatGateway(self.Id)
|
||||
}
|
||||
|
||||
func (self *SNatGateway) Refresh() error {
|
||||
nat, err := self.region.GetNatGateway(self.Id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetNatGateway(%s)", self.Id)
|
||||
}
|
||||
return jsonutils.Update(self, nat)
|
||||
}
|
||||
|
||||
func (self *SNatGateway) GetINetworkId() string {
|
||||
return self.InternalNetworkId
|
||||
}
|
||||
|
||||
func (gateway *SNatGateway) GetNatSpec() string {
|
||||
switch gateway.Spec {
|
||||
case "1":
|
||||
return api.NAT_SPEC_SMALL
|
||||
case "2":
|
||||
return api.NAT_SPEC_MIDDLE
|
||||
case "3":
|
||||
return api.NAT_SPEC_LARGE
|
||||
case "4":
|
||||
return api.NAT_SPEC_XLARGE
|
||||
}
|
||||
return gateway.Spec
|
||||
}
|
||||
|
||||
func (gateway *SNatGateway) GetDescription() string {
|
||||
return gateway.Description
|
||||
}
|
||||
|
||||
func (gateway *SNatGateway) GetBillingType() string {
|
||||
return billing_api.BILLING_TYPE_POSTPAID
|
||||
}
|
||||
|
||||
func (gateway *SNatGateway) GetCreatedAt() time.Time {
|
||||
t, _ := time.Parse("2006-01-02 15:04:05.000000", gateway.CreatedTime)
|
||||
return t
|
||||
}
|
||||
|
||||
func (gateway *SNatGateway) GetExpiredAt() time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (gateway *SNatGateway) GetIEips() ([]cloudprovider.ICloudEIP, error) {
|
||||
IEips, err := gateway.region.GetIEips()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, `get all Eips of region %q error`, gateway.region.GetId())
|
||||
}
|
||||
dNatTables, err := gateway.GetINatDTable()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, `get all DNatTable of gateway %q error`, gateway.GetId())
|
||||
}
|
||||
sNatTables, err := gateway.GetINatSTable()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, `get all SNatTable of gateway %q error`, gateway.GetId())
|
||||
}
|
||||
|
||||
// Get natIPSet of nat rules
|
||||
natIPSet := make(map[string]struct{})
|
||||
for _, snat := range sNatTables {
|
||||
natIPSet[snat.GetIP()] = struct{}{}
|
||||
}
|
||||
for _, dnat := range dNatTables {
|
||||
natIPSet[dnat.GetExternalIp()] = struct{}{}
|
||||
}
|
||||
|
||||
// Add Eip whose GetIpAddr() in natIPSet to ret
|
||||
ret := make([]cloudprovider.ICloudEIP, 0, 2)
|
||||
for i := range IEips {
|
||||
if _, ok := natIPSet[IEips[i].GetIpAddr()]; ok {
|
||||
ret = append(ret, IEips[i])
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (gateway *SNatGateway) GetINatDTable() ([]cloudprovider.ICloudNatDEntry, error) {
|
||||
dNatTable, err := gateway.getNatDTable()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, `get dnat table of nat gateway %q`, gateway.GetId())
|
||||
}
|
||||
ret := make([]cloudprovider.ICloudNatDEntry, len(dNatTable))
|
||||
for i := range dNatTable {
|
||||
ret[i] = &dNatTable[i]
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (gateway *SNatGateway) GetINatSTable() ([]cloudprovider.ICloudNatSEntry, error) {
|
||||
sNatTable, err := gateway.getNatSTable()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, `get dnat table of nat gateway %q`, gateway.GetId())
|
||||
}
|
||||
ret := make([]cloudprovider.ICloudNatSEntry, len(sNatTable))
|
||||
for i := range sNatTable {
|
||||
ret[i] = &sNatTable[i]
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (gateway *SNatGateway) CreateINatDEntry(rule cloudprovider.SNatDRule) (cloudprovider.ICloudNatDEntry, error) {
|
||||
dnat, err := gateway.region.CreateNatDEntry(rule, gateway.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dnat.gateway = gateway
|
||||
return dnat, nil
|
||||
}
|
||||
|
||||
func (gateway *SNatGateway) CreateINatSEntry(rule cloudprovider.SNatSRule) (cloudprovider.ICloudNatSEntry, error) {
|
||||
snat, err := gateway.region.CreateNatSEntry(rule, gateway.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
snat.gateway = gateway
|
||||
return snat, nil
|
||||
}
|
||||
|
||||
func (gateway *SNatGateway) GetINatDEntryByID(id string) (cloudprovider.ICloudNatDEntry, error) {
|
||||
dnat, err := gateway.region.GetNatDEntry(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dnat.gateway = gateway
|
||||
return dnat, nil
|
||||
}
|
||||
|
||||
func (gateway *SNatGateway) GetINatSEntryByID(id string) (cloudprovider.ICloudNatSEntry, error) {
|
||||
snat, err := gateway.region.GetNatSEntry(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
snat.gateway = gateway
|
||||
return snat, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetNatGateways(vpcId string) ([]SNatGateway, error) {
|
||||
query := url.Values{}
|
||||
if len(vpcId) > 0 {
|
||||
query.Set("router_id", vpcId)
|
||||
}
|
||||
ret := []SNatGateway{}
|
||||
return ret, self.list("nat", "v2.0", "nat_gateways", query, &ret)
|
||||
}
|
||||
|
||||
func NatResouceStatusTransfer(status string) string {
|
||||
// In Huawei Cloud, there are isx resource status of Nat, "ACTIVE", "PENDING_CREATE",
|
||||
// "PENDING_UPDATE", "PENDING_DELETE", "EIP_FREEZED", "INACTIVE".
|
||||
switch status {
|
||||
case "ACTIVE":
|
||||
return api.NAT_STAUTS_AVAILABLE
|
||||
case "PENDING_CREATE":
|
||||
return api.NAT_STATUS_ALLOCATE
|
||||
case "PENDING_UPDATE", "PENDING_DELETE":
|
||||
return api.NAT_STATUS_DEPLOYING
|
||||
default:
|
||||
return api.NAT_STATUS_UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SRegion) GetNatGateway(id string) (*SNatGateway, error) {
|
||||
ret := &SNatGateway{region: self}
|
||||
return ret, self.get("nat", "v2.0", "nat_gateways/"+id, ret)
|
||||
}
|
||||
|
||||
func (self *SVpc) CreateINatGateway(opts *cloudprovider.NatGatewayCreateOptions) (cloudprovider.ICloudNatGateway, error) {
|
||||
nat, err := self.region.CreateNatGateway(opts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "CreateNatGateway")
|
||||
}
|
||||
return nat, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateNatGateway(opts *cloudprovider.NatGatewayCreateOptions) (*SNatGateway, error) {
|
||||
spec := ""
|
||||
switch strings.ToLower(opts.NatSpec) {
|
||||
case api.NAT_SPEC_SMALL:
|
||||
spec = "1"
|
||||
case api.NAT_SPEC_MIDDLE:
|
||||
spec = "2"
|
||||
case api.NAT_SPEC_LARGE:
|
||||
spec = "3"
|
||||
case api.NAT_SPEC_XLARGE:
|
||||
spec = "4"
|
||||
}
|
||||
params := map[string]interface{}{
|
||||
"nat_gateway": map[string]interface{}{
|
||||
"name": opts.Name,
|
||||
"description": opts.Desc,
|
||||
"router_id": opts.VpcId,
|
||||
"internal_network_id": opts.NetworkId,
|
||||
"spec": spec,
|
||||
},
|
||||
}
|
||||
ret := SNatGateway{region: self}
|
||||
return &ret, self.create("nat", "v2.0", "nat_gateways", params, &ret)
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteNatGateway(id string) error {
|
||||
return self.delete("nat", "v2.0", "nat_gateways/"+id)
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
type SNatSEntry struct {
|
||||
multicloud.SResourceBase
|
||||
multicloud.HuaweiTags
|
||||
gateway *SNatGateway
|
||||
|
||||
Id string `json:"id"`
|
||||
NatGatewayId string `json:"nat_gateway_id"`
|
||||
NetworkId string `json:"network_id"`
|
||||
SourceCIDR string `json:"cidr"`
|
||||
Status string `json:"status"`
|
||||
SNatIP string `json:"floating_ip_address"`
|
||||
AdminStateUp bool `json:"admin_state_up"`
|
||||
}
|
||||
|
||||
func (nat *SNatSEntry) GetId() string {
|
||||
return nat.Id
|
||||
}
|
||||
|
||||
func (nat *SNatSEntry) GetName() string {
|
||||
// Snat rule has no name in Huawei Cloud, so return Id
|
||||
return nat.GetId()
|
||||
}
|
||||
|
||||
func (nat *SNatSEntry) GetGlobalId() string {
|
||||
return nat.GetId()
|
||||
}
|
||||
|
||||
func (nat *SNatSEntry) GetStatus() string {
|
||||
return NatResouceStatusTransfer(nat.Status)
|
||||
}
|
||||
|
||||
func (nat *SNatSEntry) GetIP() string {
|
||||
return nat.SNatIP
|
||||
}
|
||||
|
||||
func (nat *SNatSEntry) GetSourceCIDR() string {
|
||||
return nat.SourceCIDR
|
||||
}
|
||||
|
||||
func (nat *SNatSEntry) GetNetworkId() string {
|
||||
return nat.NetworkId
|
||||
}
|
||||
|
||||
func (nat *SNatSEntry) Delete() error {
|
||||
return nat.gateway.region.DeleteNatSEntry(nat.GetId())
|
||||
}
|
||||
|
||||
// getNatSTable return all snat rules of gateway
|
||||
func (gateway *SNatGateway) getNatSTable() ([]SNatSEntry, error) {
|
||||
ret, err := gateway.region.GetNatSTable(gateway.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range ret {
|
||||
ret[i].gateway = gateway
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetNatSTable(natGatewayId string) ([]SNatSEntry, error) {
|
||||
query := url.Values{}
|
||||
query.Set("nat_gateway_id", natGatewayId)
|
||||
ret := []SNatSEntry{}
|
||||
err := self.list("nat", "v2.0", "snat_rules", query, &ret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range ret {
|
||||
nat := &ret[i]
|
||||
if len(nat.SourceCIDR) != 0 {
|
||||
continue
|
||||
}
|
||||
net, err := self.GetNetwork(nat.NetworkId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetNetwork(%s)", nat.NetworkId)
|
||||
}
|
||||
nat.SourceCIDR = net.CIDR
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteNatSEntry(entryId string) error {
|
||||
return self.delete("nat", "v2.0", "snat_rules/"+entryId)
|
||||
}
|
||||
|
||||
func (nat *SNatSEntry) Refresh() error {
|
||||
ret, err := nat.gateway.region.GetNatSEntry(nat.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(nat, ret)
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateNatSEntry(rule cloudprovider.SNatSRule, gatewayId string) (*SNatSEntry, error) {
|
||||
params := make(map[string]interface{})
|
||||
params["nat_gateway_id"] = gatewayId
|
||||
if len(rule.NetworkID) != 0 {
|
||||
params["network_id"] = rule.NetworkID
|
||||
}
|
||||
if len(rule.SourceCIDR) != 0 {
|
||||
params["cidr"] = rule.SourceCIDR
|
||||
}
|
||||
params["floating_ip_id"] = rule.ExternalIPID
|
||||
|
||||
packParams := map[string]interface{}{
|
||||
"snat_rule": params,
|
||||
}
|
||||
|
||||
ret := &SNatSEntry{}
|
||||
return ret, self.create("nat", "v2.0", "snat_rules", packParams, ret)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetNatSEntry(id string) (*SNatSEntry, error) {
|
||||
ret := &SNatSEntry{}
|
||||
return ret, self.get("nat", "v2.0", "snat_rules/"+id, ret)
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
)
|
||||
|
||||
/*
|
||||
Subnets
|
||||
*/
|
||||
|
||||
// https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090590.html
|
||||
type SNetwork struct {
|
||||
multicloud.SResourceBase
|
||||
multicloud.HcsTags
|
||||
wire *SWire
|
||||
|
||||
AvailabilityZone string `json:"availability_zone"`
|
||||
CIDR string `json:"cidr"`
|
||||
DHCPEnable bool `json:"dhcp_enable"`
|
||||
DNSList []string `json:"dnsList"`
|
||||
GatewayIP string `json:"gateway_ip"`
|
||||
Id string `json:"id"`
|
||||
Ipv6Enable bool `json:"ipv6_enable"`
|
||||
Name string `json:"name"`
|
||||
NeutronNetworkId string `json:"neutron_network_id"`
|
||||
NeutronSubnetId string `json:"neutron_subnet_id"`
|
||||
PrimaryDNS string `json:"primary_dns"`
|
||||
SecondaryDNS string `json:"secondary_dns"`
|
||||
Status string `json:"status"`
|
||||
VpcId string `json:"vpc_id"`
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetId() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetName() string {
|
||||
if len(self.Name) == 0 {
|
||||
return self.Id
|
||||
}
|
||||
return self.Name
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetGlobalId() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090591.html
|
||||
func (self *SNetwork) GetStatus() string {
|
||||
switch self.Status {
|
||||
case "ACTIVE", "UNKNOWN":
|
||||
return api.NETWORK_STATUS_AVAILABLE // ? todo: // UNKNOWN
|
||||
case "ERROR":
|
||||
return api.NETWORK_STATUS_UNKNOWN
|
||||
default:
|
||||
return api.NETWORK_STATUS_UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SNetwork) Refresh() error {
|
||||
ret, err := self.wire.region.GetNetwork(self.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(self, ret)
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetIWire() cloudprovider.ICloudWire {
|
||||
return self.wire
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetIpStart() string {
|
||||
pref, _ := netutils.NewIPV4Prefix(self.CIDR)
|
||||
startIp := pref.Address.NetAddr(pref.MaskLen) // 0
|
||||
startIp = startIp.StepUp() // 1
|
||||
startIp = startIp.StepUp() // 2
|
||||
return startIp.String()
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetIpEnd() string {
|
||||
pref, _ := netutils.NewIPV4Prefix(self.CIDR)
|
||||
endIp := pref.Address.BroadcastAddr(pref.MaskLen) // 255
|
||||
endIp = endIp.StepDown() // 254
|
||||
endIp = endIp.StepDown() // 253
|
||||
endIp = endIp.StepDown() // 252
|
||||
return endIp.String()
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetIpMask() int8 {
|
||||
pref, _ := netutils.NewIPV4Prefix(self.CIDR)
|
||||
return pref.MaskLen
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetGateway() string {
|
||||
pref, _ := netutils.NewIPV4Prefix(self.CIDR)
|
||||
startIp := pref.Address.NetAddr(pref.MaskLen) // 0
|
||||
startIp = startIp.StepUp() // 1
|
||||
return startIp.String()
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetServerType() string {
|
||||
return api.NETWORK_TYPE_GUEST
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetIsPublic() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetPublicScope() rbacutils.TRbacScope {
|
||||
return rbacutils.ScopeDomain
|
||||
}
|
||||
|
||||
func (self *SNetwork) Delete() error {
|
||||
return self.wire.region.DeleteNetwork(self.Id)
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetAllocTimeoutSeconds() int {
|
||||
return 120 // 2 minutes
|
||||
}
|
||||
|
||||
func (self *SRegion) GetNetwork(id string) (*SNetwork, error) {
|
||||
ret := &SNetwork{}
|
||||
res := fmt.Sprintf("subnets/%s", id)
|
||||
return ret, self.vpcGet(res, ret)
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090592.html
|
||||
func (self *SRegion) GetNetwroks(vpcId string) ([]SNetwork, error) {
|
||||
params := url.Values{}
|
||||
if len(vpcId) > 0 {
|
||||
params.Set("vpc_id", vpcId)
|
||||
}
|
||||
ret := []SNetwork{}
|
||||
return ret, self.vpcList("subnets", params, &ret)
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteNetwork(id string) error {
|
||||
res := fmt.Sprintf("subnets/%s", id)
|
||||
return self.vpcDelete(res)
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetProjectId() string {
|
||||
return self.wire.vpc.EnterpriseProjectId
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/huaweicloud/huaweicloud-sdk-go-obs/obs"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
)
|
||||
|
||||
type SObject struct {
|
||||
bucket *SBucket
|
||||
|
||||
cloudprovider.SBaseCloudObject
|
||||
}
|
||||
|
||||
func (o *SObject) GetIBucket() cloudprovider.ICloudBucket {
|
||||
return o.bucket
|
||||
}
|
||||
|
||||
func (o *SObject) GetAcl() cloudprovider.TBucketACLType {
|
||||
acl := cloudprovider.ACLPrivate
|
||||
obscli, err := o.bucket.region.getOBSClient()
|
||||
if err != nil {
|
||||
log.Errorf("o.bucket.region.GetOssClient error %s", err)
|
||||
return acl
|
||||
}
|
||||
input := &obs.GetObjectAclInput{}
|
||||
input.Bucket = o.bucket.Name
|
||||
input.Key = o.Key
|
||||
output, err := obscli.GetObjectAcl(input)
|
||||
if err != nil {
|
||||
log.Errorf("GetObjectAcl error: %v", err)
|
||||
return acl
|
||||
}
|
||||
acl = obsAcl2CannedAcl(output.Grants)
|
||||
return acl
|
||||
}
|
||||
|
||||
func (o *SObject) SetAcl(aclStr cloudprovider.TBucketACLType) error {
|
||||
obscli, err := o.bucket.region.getOBSClient()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "o.bucket.region.getOBSClient")
|
||||
}
|
||||
input := &obs.SetObjectAclInput{}
|
||||
input.Bucket = o.bucket.Name
|
||||
input.Key = o.Key
|
||||
input.ACL = obs.AclType(string(aclStr))
|
||||
_, err = obscli.SetObjectAcl(input)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "obscli.SetObjectAcl")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *SObject) GetMeta() http.Header {
|
||||
if o.Meta != nil {
|
||||
return o.Meta
|
||||
}
|
||||
obscli, err := o.bucket.region.getOBSClient()
|
||||
if err != nil {
|
||||
log.Errorf("getOBSClient fail %s", err)
|
||||
return nil
|
||||
}
|
||||
input := &obs.GetObjectMetadataInput{}
|
||||
input.Bucket = o.bucket.Name
|
||||
input.Key = o.Key
|
||||
output, err := obscli.GetObjectMetadata(input)
|
||||
if err != nil {
|
||||
log.Errorf("obscli.GetObjectMetadata fail %s", err)
|
||||
return nil
|
||||
}
|
||||
meta := http.Header{}
|
||||
for k, v := range output.Metadata {
|
||||
meta.Add(k, v)
|
||||
}
|
||||
if len(output.ContentType) > 0 {
|
||||
meta.Add(cloudprovider.META_HEADER_CONTENT_TYPE, output.ContentType)
|
||||
}
|
||||
o.Meta = meta
|
||||
return meta
|
||||
}
|
||||
|
||||
func (o *SObject) SetMeta(ctx context.Context, meta http.Header) error {
|
||||
return cloudprovider.ObjectSetMeta(ctx, o.bucket, o, meta)
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
type SFixedIP struct {
|
||||
IpAddress string
|
||||
SubnetId string
|
||||
NetworkId string
|
||||
}
|
||||
|
||||
func (fixip *SFixedIP) GetGlobalId() string {
|
||||
return fixip.IpAddress
|
||||
}
|
||||
|
||||
func (fixip *SFixedIP) GetIP() string {
|
||||
return fixip.IpAddress
|
||||
}
|
||||
|
||||
func (fixip *SFixedIP) GetINetworkId() string {
|
||||
return fixip.NetworkId
|
||||
}
|
||||
|
||||
func (fixip *SFixedIP) IsPrimary() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type Port struct {
|
||||
multicloud.SNetworkInterfaceBase
|
||||
multicloud.HuaweiTags
|
||||
region *SRegion
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
AdminStateUp string `json:"admin_state_up"`
|
||||
DNSName string `json:"dns_name"`
|
||||
MACAddress string `json:"mac_address"`
|
||||
NetworkId string `json:"network_id"`
|
||||
TenantId string `json:"tenant_id"`
|
||||
DeviceId string `json:"device_id"`
|
||||
DeviceOwner string `json:"device_owner"`
|
||||
BindingVnicType string `json:"binding:vnic_type"`
|
||||
FixedIps []SFixedIP
|
||||
}
|
||||
|
||||
func (port *Port) GetName() string {
|
||||
if len(port.Name) > 0 {
|
||||
return port.Name
|
||||
}
|
||||
return port.Id
|
||||
}
|
||||
|
||||
func (port *Port) GetId() string {
|
||||
return port.Id
|
||||
}
|
||||
|
||||
func (port *Port) GetGlobalId() string {
|
||||
return port.Id
|
||||
}
|
||||
|
||||
func (port *Port) GetMacAddress() string {
|
||||
return port.MACAddress
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-vpc/zh-cn_topic_0133195888.html
|
||||
func (port *Port) GetAssociateType() string {
|
||||
switch port.DeviceOwner {
|
||||
case "compute:nova":
|
||||
return api.NETWORK_INTERFACE_ASSOCIATE_TYPE_SERVER
|
||||
case "network:router_gateway", "network:router_interface", "network:router_interface_distributed":
|
||||
return api.NETWORK_INTERFACE_ASSOCIATE_TYPE_RESERVED
|
||||
case "network:dhcp":
|
||||
return api.NETWORK_INTERFACE_ASSOCIATE_TYPE_DHCP
|
||||
case "neutron:LOADBALANCERV2":
|
||||
return api.NETWORK_INTERFACE_ASSOCIATE_TYPE_LOADBALANCER
|
||||
case "neutron:VIP_PORT":
|
||||
return api.NETWORK_INTERFACE_ASSOCIATE_TYPE_VIP
|
||||
default:
|
||||
if strings.HasPrefix(port.DeviceOwner, "compute:") {
|
||||
return api.NETWORK_INTERFACE_ASSOCIATE_TYPE_SERVER
|
||||
}
|
||||
}
|
||||
return port.DeviceOwner
|
||||
}
|
||||
|
||||
func (port *Port) GetAssociateId() string {
|
||||
return port.DeviceId
|
||||
}
|
||||
|
||||
func (port *Port) GetStatus() string {
|
||||
switch port.Status {
|
||||
case "ACTIVE", "DOWN":
|
||||
return api.NETWORK_INTERFACE_STATUS_AVAILABLE
|
||||
case "BUILD":
|
||||
return api.NETWORK_INTERFACE_STATUS_CREATING
|
||||
}
|
||||
return port.Status
|
||||
}
|
||||
|
||||
func (port *Port) GetICloudInterfaceAddresses() ([]cloudprovider.ICloudInterfaceAddress, error) {
|
||||
address := []cloudprovider.ICloudInterfaceAddress{}
|
||||
for i := 0; i < len(port.FixedIps); i++ {
|
||||
port.FixedIps[i].NetworkId = port.NetworkId
|
||||
address = append(address, &port.FixedIps[i])
|
||||
}
|
||||
return address, nil
|
||||
}
|
||||
|
||||
func (region *SRegion) GetINetworkInterfaces() ([]cloudprovider.ICloudNetworkInterface, error) {
|
||||
ports, err := region.GetPorts("")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := []cloudprovider.ICloudNetworkInterface{}
|
||||
for i := 0; i < len(ports); i++ {
|
||||
if len(ports[i].DeviceId) == 0 || !utils.IsInStringArray(ports[i].DeviceOwner, []string{"compute:CCI", "compute:nova", "neutron:LOADBALANCERV2"}) {
|
||||
ports[i].region = region
|
||||
ret = append(ret, &ports[i])
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetPort(id string) (*Port, error) {
|
||||
port := &Port{}
|
||||
res := fmt.Sprintf("ports/%s", id)
|
||||
return port, self.vpcGet(res, port)
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-vpc/zh-cn_topic_0133195888.html
|
||||
func (self *SRegion) GetPorts(instanceId string) ([]Port, error) {
|
||||
ports := []Port{}
|
||||
params := url.Values{}
|
||||
if len(instanceId) > 0 {
|
||||
params.Set("device_id", instanceId)
|
||||
}
|
||||
return ports, self.vpcList("ports", params, &ports)
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
)
|
||||
|
||||
type SProject struct {
|
||||
Id string
|
||||
Name string
|
||||
DomainId string
|
||||
Description bool
|
||||
Enabled bool
|
||||
ParentId string
|
||||
IsDomain bool
|
||||
}
|
||||
|
||||
func (self *SProject) GetHealthStatus() string {
|
||||
if self.Enabled {
|
||||
return api.CLOUD_PROVIDER_HEALTH_NORMAL
|
||||
}
|
||||
return api.CLOUD_PROVIDER_HEALTH_SUSPENDED
|
||||
}
|
||||
|
||||
func (self *SHcsClient) GetProjects() ([]SProject, error) {
|
||||
if len(self.projects) > 0 {
|
||||
return self.projects, nil
|
||||
}
|
||||
resp, err := self.iamGet("v3/projects", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
self.projects = []SProject{}
|
||||
return self.projects, resp.Unmarshal(&self.projects, "projects")
|
||||
}
|
||||
|
||||
func (self *SHcsClient) GetSubAccounts() ([]cloudprovider.SSubAccount, error) {
|
||||
projects, err := self.GetProjects()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := make([]cloudprovider.SSubAccount, 0)
|
||||
for i := range projects {
|
||||
project := projects[i]
|
||||
// name 为MOS的project是华为云内部的一个特殊project。不需要同步到本地
|
||||
if strings.ToLower(project.Name) == "mos" {
|
||||
continue
|
||||
}
|
||||
s := cloudprovider.SSubAccount{
|
||||
Name: fmt.Sprintf("%s-%s", self.cpcfg.Name, project.Name),
|
||||
Account: fmt.Sprintf("%s/%s", self.accessKey, project.Id),
|
||||
HealthStatus: project.GetHealthStatus(),
|
||||
DefaultProjectId: "0",
|
||||
}
|
||||
for j := range self.regions {
|
||||
region := self.regions[j]
|
||||
if strings.Contains(project.Name, region.Id) {
|
||||
s.Desc = region.Locales.ZhCN
|
||||
break
|
||||
}
|
||||
}
|
||||
ret = append(ret, s)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SHcsClient) GetProjectById(id string) (*SProject, error) {
|
||||
projects, err := self.GetProjects()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range projects {
|
||||
if projects[i].Id == id {
|
||||
return &projects[i], nil
|
||||
}
|
||||
}
|
||||
return nil, errors.Wrapf(cloudprovider.ErrNotFound, id)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package provider // import "yunion.io/x/onecloud/pkg/multicloud/hcs/provider"
|
||||
@@ -0,0 +1,217 @@
|
||||
// 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 provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/multicloud/hcs"
|
||||
)
|
||||
|
||||
type SHcsProviderFactory struct {
|
||||
cloudprovider.SPrivateCloudBaseProviderFactory
|
||||
}
|
||||
|
||||
func (self *SHcsProviderFactory) GetId() string {
|
||||
return hcs.CLOUD_PROVIDER_HCS
|
||||
}
|
||||
|
||||
func (self *SHcsProviderFactory) GetName() string {
|
||||
return hcs.CLOUD_PROVIDER_HCS_CN
|
||||
}
|
||||
|
||||
func (self *SHcsProviderFactory) IsCloudeventRegional() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SHcsProviderFactory) GetMaxCloudEventSyncDays() int {
|
||||
return 7
|
||||
}
|
||||
|
||||
func (self *SHcsProviderFactory) GetMaxCloudEventKeepDays() int {
|
||||
return 7
|
||||
}
|
||||
|
||||
func (factory *SHcsProviderFactory) IsSupportModifyRouteTable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (factory *SHcsProviderFactory) IsSupportSAMLAuth() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SHcsProviderFactory) ValidateCreateCloudaccountData(ctx context.Context, userCred mcclient.TokenCredential, input cloudprovider.SCloudaccountCredential) (cloudprovider.SCloudaccount, error) {
|
||||
output := cloudprovider.SCloudaccount{}
|
||||
if len(input.AccessKeyId) == 0 {
|
||||
return output, errors.Wrap(httperrors.ErrMissingParameter, "access_key_id")
|
||||
}
|
||||
if len(input.AccessKeySecret) == 0 {
|
||||
return output, errors.Wrap(httperrors.ErrMissingParameter, "access_key_secret")
|
||||
}
|
||||
if len(input.AuthUrl) == 0 {
|
||||
return output, errors.Wrap(httperrors.ErrMissingParameter, "auth_url")
|
||||
}
|
||||
output.Account = input.AccessKeyId
|
||||
output.Secret = input.AccessKeySecret
|
||||
output.AccessUrl = input.AuthUrl
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func (self *SHcsProviderFactory) ValidateUpdateCloudaccountCredential(ctx context.Context, userCred mcclient.TokenCredential, input cloudprovider.SCloudaccountCredential, cloudaccount string) (cloudprovider.SCloudaccount, error) {
|
||||
output := cloudprovider.SCloudaccount{}
|
||||
if len(input.AccessKeyId) == 0 {
|
||||
return output, errors.Wrap(httperrors.ErrMissingParameter, "access_key_id")
|
||||
}
|
||||
if len(input.AccessKeySecret) == 0 {
|
||||
return output, errors.Wrap(httperrors.ErrMissingParameter, "access_key_secret")
|
||||
}
|
||||
output = cloudprovider.SCloudaccount{
|
||||
Account: input.AccessKeyId,
|
||||
Secret: input.AccessKeySecret,
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func parseAccount(account string) (accessKey string, projectId string) {
|
||||
segs := strings.Split(account, "/")
|
||||
if len(segs) == 2 {
|
||||
accessKey = segs[0]
|
||||
projectId = segs[1]
|
||||
} else {
|
||||
accessKey = account
|
||||
projectId = ""
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *SHcsProviderFactory) GetProvider(cfg cloudprovider.ProviderConfig) (cloudprovider.ICloudProvider, error) {
|
||||
accessKey, projectId := parseAccount(cfg.Account)
|
||||
client, err := hcs.NewHcsClient(
|
||||
hcs.NewHcsConfig(
|
||||
accessKey, cfg.Secret, projectId, cfg.URL,
|
||||
).CloudproviderConfig(cfg),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SHcsProvider{
|
||||
SBaseProvider: cloudprovider.NewBaseProvider(self),
|
||||
client: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (self *SHcsProviderFactory) GetClientRC(info cloudprovider.SProviderInfo) (map[string]string, error) {
|
||||
accessKey, projectId := parseAccount(info.Account)
|
||||
return map[string]string{
|
||||
"HCS_AUTH_URL": info.Url,
|
||||
"HCS_ACCESS_KEY": accessKey,
|
||||
"HCS_SECRET": info.Secret,
|
||||
"HCS_PROJECT_ID": projectId,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
factory := SHcsProviderFactory{}
|
||||
cloudprovider.RegisterFactory(&factory)
|
||||
}
|
||||
|
||||
type SHcsProvider struct {
|
||||
cloudprovider.SBaseProvider
|
||||
client *hcs.SHcsClient
|
||||
}
|
||||
|
||||
func (self *SHcsProvider) GetVersion() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SHcsProvider) GetSysInfo() (jsonutils.JSONObject, error) {
|
||||
regions := self.client.GetIRegions()
|
||||
info := jsonutils.NewDict()
|
||||
info.Add(jsonutils.NewInt(int64(len(regions))), "region_count")
|
||||
info.Add(jsonutils.NewString(hcs.HCS_API_VERSION), "api_version")
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (self *SHcsProvider) GetIRegions() []cloudprovider.ICloudRegion {
|
||||
return self.client.GetIRegions()
|
||||
}
|
||||
|
||||
func (self *SHcsProvider) GetIRegionById(id string) (cloudprovider.ICloudRegion, error) {
|
||||
region, err := self.client.GetRegion(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return region, nil
|
||||
}
|
||||
|
||||
func (self *SHcsProvider) GetBalance() (float64, string, error) {
|
||||
return 0, api.CLOUD_PROVIDER_HEALTH_NORMAL, nil
|
||||
}
|
||||
|
||||
func (self *SHcsProvider) GetSubAccounts() ([]cloudprovider.SSubAccount, error) {
|
||||
return self.client.GetSubAccounts()
|
||||
}
|
||||
|
||||
func (self *SHcsProvider) GetAccountId() string {
|
||||
return self.client.GetAccountId()
|
||||
}
|
||||
|
||||
func (self *SHcsProvider) GetCloudRegionExternalIdPrefix() string {
|
||||
return self.client.GetCloudRegionExternalIdPrefix()
|
||||
}
|
||||
|
||||
func (self *SHcsProvider) GetIProjects() ([]cloudprovider.ICloudProject, error) {
|
||||
return []cloudprovider.ICloudProject{}, nil
|
||||
}
|
||||
|
||||
func (self *SHcsProvider) CreateIProject(name string) (cloudprovider.ICloudProject, error) {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SHcsProvider) GetStorageClasses(regionId string) []string {
|
||||
return []string{
|
||||
"STANDARD", "WARM", "COLD",
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SHcsProvider) GetBucketCannedAcls(regionId string) []string {
|
||||
return []string{
|
||||
string(cloudprovider.ACLPrivate),
|
||||
string(cloudprovider.ACLAuthRead),
|
||||
string(cloudprovider.ACLPublicRead),
|
||||
string(cloudprovider.ACLPublicReadWrite),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SHcsProvider) GetObjectCannedAcls(regionId string) []string {
|
||||
return []string{
|
||||
string(cloudprovider.ACLPrivate),
|
||||
string(cloudprovider.ACLAuthRead),
|
||||
string(cloudprovider.ACLPublicRead),
|
||||
string(cloudprovider.ACLPublicReadWrite),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SHcsProvider) GetCapabilities() []string {
|
||||
return self.client.GetCapabilities()
|
||||
}
|
||||
@@ -0,0 +1,608 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
billing_api "yunion.io/x/onecloud/pkg/apis/billing"
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
"yunion.io/x/onecloud/pkg/util/billing"
|
||||
)
|
||||
|
||||
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423020.html
|
||||
type SElasticcache struct {
|
||||
multicloud.SElasticcacheBase
|
||||
multicloud.HcsTags
|
||||
|
||||
region *SRegion
|
||||
|
||||
Name string `json:"name"`
|
||||
Engine string `json:"engine"`
|
||||
CapacityGB int `json:"capacity"`
|
||||
IP string `json:"ip"`
|
||||
DomainName string `json:"domainName"`
|
||||
Port int `json:"port"`
|
||||
Status string `json:"status"`
|
||||
Libos bool `json:"libos"`
|
||||
Description string `json:"description"`
|
||||
Task string `json:"task"`
|
||||
MaxMemoryMB int `json:"max_memory"`
|
||||
UsedMemoryMB int `json:"used_memory"`
|
||||
InstanceId string `json:"instance_id"`
|
||||
ResourceSpecCode string `json:"resource_spec_code"`
|
||||
EngineVersion string `json:"engine_version"`
|
||||
InternalVersion string `json:"internal_version"`
|
||||
ChargingMode int `json:"charging_mode"`
|
||||
CapacityMinor string `json:"capacity_minor"`
|
||||
VpcId string `json:"vpc_id"`
|
||||
VpcName string `json:"vpc_name"`
|
||||
TaskStatus string `json:"task_status"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
ErrorCode string `json:"error_code"`
|
||||
UserId string `json:"user_id"`
|
||||
UserName string `json:"user_name"`
|
||||
MaintainBegin string `json:"maintain_begin"`
|
||||
MaintainEnd string `json:"maintain_end"`
|
||||
NoPasswordAccess string `json:"no_password_access"`
|
||||
AccessUser string `json:"access_user"`
|
||||
EnablePublicip bool `json:"enable_publicip"`
|
||||
PublicipId string `json:"publicip_id"`
|
||||
PublicipAddress string `json:"publicip_address"`
|
||||
EnableSSL bool `json:"enable_ssl"`
|
||||
ServiceUpgrade bool `json:"service_upgrade"`
|
||||
ServiceTaskId string `json:"service_task_id"`
|
||||
IsFree string `json:"is_free"`
|
||||
EnterpriseProjectId string `json:"enterprise_project_id"`
|
||||
AvailableZones []string `json:"available_zones"`
|
||||
SubnetId string `json:"subnet_id"`
|
||||
SecurityGroupId string `json:"security_group_id"`
|
||||
BackendAddrs []string `json:"backend_addrs"`
|
||||
ProductId string `json:"product_id"`
|
||||
SecurityGroupName string `json:"security_group_name"`
|
||||
SubnetName string `json:"subnet_name"`
|
||||
OrderId string `json:"order_id"`
|
||||
SubnetCIdR string `json:"subnet_cidr"`
|
||||
InstanceBackupPolicy string `json:"instance_backup_policy"`
|
||||
EnterpriseProjectName string `json:"enterprise_project_name"`
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetId() string {
|
||||
return self.InstanceId
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetName() string {
|
||||
return self.Name
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetGlobalId() string {
|
||||
return self.GetId()
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetProjectId() string {
|
||||
return self.EnterpriseProjectId
|
||||
}
|
||||
|
||||
func (self *SElasticcache) Refresh() error {
|
||||
cache, err := self.region.GetElasticCache(self.GetId())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Elasticcache.Refresh.GetElasticCache")
|
||||
}
|
||||
|
||||
err = jsonutils.Update(self, cache)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Elasticcache.Refresh.Update")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetStatus() string {
|
||||
switch self.Status {
|
||||
case "RUNNING":
|
||||
return api.ELASTIC_CACHE_STATUS_RUNNING
|
||||
case "CREATING":
|
||||
return api.ELASTIC_CACHE_STATUS_DEPLOYING
|
||||
case "CREATEFAILED":
|
||||
return api.ELASTIC_CACHE_STATUS_CREATE_FAILED
|
||||
case "ERROR":
|
||||
return api.ELASTIC_CACHE_STATUS_ERROR
|
||||
case "RESTARTING":
|
||||
return api.ELASTIC_CACHE_STATUS_RESTARTING
|
||||
case "FROZEN":
|
||||
return api.ELASTIC_CACHE_STATUS_UNAVAILABLE
|
||||
case "EXTENDING":
|
||||
return api.ELASTIC_CACHE_STATUS_CHANGING
|
||||
case "RESTORING":
|
||||
return api.ELASTIC_CACHE_STATUS_TRANSFORMING // ?
|
||||
case "FLUSHING":
|
||||
return api.ELASTIC_CACHE_STATUS_FLUSHING
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetBillingType() string {
|
||||
return billing_api.BILLING_TYPE_POSTPAID
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetCreatedAt() time.Time {
|
||||
var createtime time.Time
|
||||
if len(self.CreatedAt) > 0 {
|
||||
createtime, _ = time.Parse("2006-01-02T15:04:05.000Z", self.CreatedAt)
|
||||
}
|
||||
|
||||
return createtime
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetInstanceType() string {
|
||||
return self.ResourceSpecCode
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetCapacityMB() int {
|
||||
return self.CapacityGB * 1024
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetArchType() string {
|
||||
if strings.Contains(self.ResourceSpecCode, "single") {
|
||||
return api.ELASTIC_CACHE_ARCH_TYPE_SINGLE
|
||||
}
|
||||
if strings.Contains(self.ResourceSpecCode, "ha") {
|
||||
return api.ELASTIC_CACHE_ARCH_TYPE_MASTER
|
||||
}
|
||||
if strings.Contains(self.ResourceSpecCode, "cluster") {
|
||||
return api.ELASTIC_CACHE_ARCH_TYPE_CLUSTER
|
||||
}
|
||||
if strings.Contains(self.ResourceSpecCode, "proxy") {
|
||||
return api.ELASTIC_CACHE_ARCH_TYPE_CLUSTER
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetNodeType() string {
|
||||
// single(单副本) | double(双副本)
|
||||
if strings.Contains(self.ResourceSpecCode, "single") {
|
||||
return "single"
|
||||
}
|
||||
return "double"
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetEngine() string {
|
||||
return self.Engine
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetEngineVersion() string {
|
||||
return self.EngineVersion
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetVpcId() string {
|
||||
return self.VpcId
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetZoneId() string {
|
||||
if len(self.AvailableZones) > 0 {
|
||||
zone, err := self.region.GetZoneById(self.AvailableZones[0])
|
||||
if err != nil {
|
||||
log.Errorf("elasticcache.GetZoneId %s", err)
|
||||
return ""
|
||||
}
|
||||
return zone.GetGlobalId()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetNetworkType() string {
|
||||
return api.LB_NETWORK_TYPE_VPC
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetNetworkId() string {
|
||||
return self.SubnetId
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetPrivateDNS() string {
|
||||
return self.DomainName
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetPrivateIpAddr() string {
|
||||
return self.IP
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetPrivateConnectPort() int {
|
||||
return self.Port
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetPublicDNS() string {
|
||||
return self.PublicipAddress
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetPublicIpAddr() string {
|
||||
return self.PublicipAddress
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetPublicConnectPort() int {
|
||||
return self.Port
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetMaintainStartTime() string {
|
||||
return self.MaintainBegin
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetMaintainEndTime() string {
|
||||
return self.MaintainEnd
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetICloudElasticcacheAccounts() ([]cloudprovider.ICloudElasticcacheAccount, error) {
|
||||
iaccounts := []cloudprovider.ICloudElasticcacheAccount{}
|
||||
iaccount := &SElasticcacheAccount{cacheDB: self}
|
||||
iaccounts = append(iaccounts, iaccount)
|
||||
return iaccounts, nil
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetICloudElasticcacheAcls() ([]cloudprovider.ICloudElasticcacheAcl, error) {
|
||||
// 华为云使用安全组做访问控制。目前未支持
|
||||
return []cloudprovider.ICloudElasticcacheAcl{}, nil
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetICloudElasticcacheBackups() ([]cloudprovider.ICloudElasticcacheBackup, error) {
|
||||
start := self.GetCreatedAt().Format("20060102150405")
|
||||
end := time.Now().Format("20060102150405")
|
||||
backups, err := self.region.GetElasticCacheBackups(self.GetId(), start, end)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ibackups := make([]cloudprovider.ICloudElasticcacheBackup, len(backups))
|
||||
for i := range backups {
|
||||
backups[i].cacheDB = self
|
||||
ibackups[i] = &backups[i]
|
||||
}
|
||||
|
||||
return ibackups, nil
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetICloudElasticcacheParameters() ([]cloudprovider.ICloudElasticcacheParameter, error) {
|
||||
parameters, err := self.region.GetElasticCacheParameters(self.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
iparameters := make([]cloudprovider.ICloudElasticcacheParameter, len(parameters))
|
||||
for i := range parameters {
|
||||
parameters[i].cacheDB = self
|
||||
iparameters[i] = ¶meters[i]
|
||||
}
|
||||
|
||||
return iparameters, nil
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423035.html
|
||||
func (self *SRegion) GetElasticCacheBackups(instanceId, startTime, endTime string) ([]SElasticcacheBackup, error) {
|
||||
params := url.Values{}
|
||||
params.Set("beginTime", startTime)
|
||||
params.Set("endTime", endTime)
|
||||
res := fmt.Sprintf("instances/%s/backups", instanceId)
|
||||
ret := []SElasticcacheBackup{}
|
||||
return ret, self.redisList(res, params, &ret)
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423027.html
|
||||
func (self *SRegion) GetElasticCacheParameters(instanceId string) ([]SElasticcacheParameter, error) {
|
||||
params := url.Values{}
|
||||
res := fmt.Sprintf("instances/%s/configs", instanceId)
|
||||
ret := []SElasticcacheParameter{}
|
||||
return ret, self.redisList(res, params, &ret)
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423044.html
|
||||
func (self *SRegion) GetElasticCaches() ([]SElasticcache, error) {
|
||||
params := url.Values{}
|
||||
ret := []SElasticcache{}
|
||||
return ret, self.redisList("instances", params, &ret)
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423020.html
|
||||
func (self *SRegion) GetElasticCache(instanceId string) (*SElasticcache, error) {
|
||||
cache := &SElasticcache{region: self}
|
||||
res := fmt.Sprintf("instances/%s", instanceId)
|
||||
return cache, self.redisGet(res, cache)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIElasticcacheById(id string) (cloudprovider.ICloudElasticcache, error) {
|
||||
ec, err := self.GetElasticCache(id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetElasticCache(%s)", id)
|
||||
}
|
||||
return ec, nil
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423047.html
|
||||
func (self *SRegion) zoneNameToDcsZoneIds(zoneIds []string) ([]string, error) {
|
||||
type Z struct {
|
||||
Id string `json:"id"`
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
Port string `json:"port"`
|
||||
ResourceAvailability string `json:"resource_availability"`
|
||||
}
|
||||
|
||||
rs := []Z{}
|
||||
err := self.redisList("availableZones", url.Values{}, &rs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "availableZones")
|
||||
}
|
||||
zoneMap := map[string]string{}
|
||||
for i := range rs {
|
||||
if rs[i].ResourceAvailability == "true" {
|
||||
zoneMap[rs[i].Code] = rs[i].Id
|
||||
}
|
||||
}
|
||||
|
||||
ret := []string{}
|
||||
for _, zone := range zoneIds {
|
||||
if id, ok := zoneMap[zone]; ok {
|
||||
ret = append(ret, id)
|
||||
} else {
|
||||
return nil, errors.Wrap(fmt.Errorf("zone %s not found or not available", zone), "region.zoneNameToDcsZoneIds")
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateElasticcache(opts *cloudprovider.SCloudElasticCacheInput) (*SElasticcache, error) {
|
||||
params := map[string]interface{}{
|
||||
"name": opts.InstanceName,
|
||||
"engine": opts.Engine,
|
||||
"engine_version": opts.EngineVersion,
|
||||
"capacity": opts.CapacityGB,
|
||||
"vpc_id": opts.VpcId,
|
||||
"subnet_id": opts.NetworkId,
|
||||
"product_id": opts.InstanceType,
|
||||
"spec_code": opts.InstanceType,
|
||||
"available_zones": opts.ZoneIds,
|
||||
"no_password_access": true,
|
||||
"enable_publicip": false,
|
||||
}
|
||||
if len(opts.SecurityGroupIds) > 0 {
|
||||
params["security_group_id"] = opts.SecurityGroupIds[0]
|
||||
}
|
||||
|
||||
if len(opts.ProjectId) > 0 {
|
||||
params["enterprise_project_id"] = opts.ProjectId
|
||||
}
|
||||
|
||||
if len(opts.Password) > 0 {
|
||||
params["no_password_access"] = false
|
||||
params["password"] = opts.Password
|
||||
if opts.Engine == "Memcache" {
|
||||
params["access_user"] = opts.UserName
|
||||
}
|
||||
}
|
||||
|
||||
if len(opts.EipId) > 0 {
|
||||
params["enable_publicip"] = true
|
||||
params["publicip_id"] = opts.EipId
|
||||
}
|
||||
|
||||
if len(opts.PrivateIpAddress) > 0 {
|
||||
params["private_ip"] = opts.PrivateIpAddress
|
||||
}
|
||||
|
||||
if len(opts.MaintainBegin) > 0 {
|
||||
params["maintain_begin"] = opts.MaintainBegin
|
||||
params["maintain_end"] = opts.MaintainEnd
|
||||
}
|
||||
|
||||
ret := struct {
|
||||
InstanceId string
|
||||
}{}
|
||||
err := self.redisCreate("instances", params, &ret)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "redisCreate")
|
||||
}
|
||||
return self.GetElasticCache(ret.InstanceId)
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423019.html
|
||||
func (self *SRegion) CreateIElasticcaches(opts *cloudprovider.SCloudElasticCacheInput) (cloudprovider.ICloudElasticcache, error) {
|
||||
cache, err := self.CreateElasticcache(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cache, nil
|
||||
}
|
||||
|
||||
func (self *SElasticcache) Restart() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SElasticcache) Delete() error {
|
||||
return self.region.DeleteElasticcache(self.InstanceId)
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteElasticcache(id string) error {
|
||||
res := fmt.Sprintf("instances/%s", id)
|
||||
return self.redisDelete(res)
|
||||
}
|
||||
|
||||
func (self *SElasticcache) ChangeInstanceSpec(spec string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423021.html
|
||||
func (self *SElasticcache) SetMaintainTime(maintainStartTime, maintainEndTime string) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/usermanual-dcs/dcs-zh-ug-180314001.html
|
||||
// 目前只有Redis3.0版本密码模式的实例支持通过公网访问Redis实例,其他版本暂不支持公网访问。
|
||||
// todo: 目前没找到api
|
||||
func (self *SElasticcache) AllocatePublicConnection(port int) (string, error) {
|
||||
return "", cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
// todo: 目前没找到api
|
||||
func (self *SElasticcache) ReleasePublicConnection() error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SElasticcache) CreateAccount(account cloudprovider.SCloudElasticCacheAccountInput) (cloudprovider.ICloudElasticcacheAccount, error) {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423031.html
|
||||
|
||||
func (self *SElasticcache) CreateAcl(aclName, securityIps string) (cloudprovider.ICloudElasticcacheAcl, error) {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423029.html
|
||||
func (self *SElasticcache) UpdateInstanceParameters(config jsonutils.JSONObject) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423033.html
|
||||
func (self *SElasticcache) CreateBackup(desc string) (cloudprovider.ICloudElasticcacheBackup, error) {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func backupPeriodTrans(config cloudprovider.SCloudElasticCacheBackupPolicyUpdateInput) *jsonutils.JSONArray {
|
||||
segs := strings.Split(config.PreferredBackupPeriod, ",")
|
||||
ret := jsonutils.NewArray()
|
||||
for _, seg := range segs {
|
||||
switch seg {
|
||||
case "Monday":
|
||||
ret.Add(jsonutils.NewString("1"))
|
||||
case "Tuesday":
|
||||
ret.Add(jsonutils.NewString("2"))
|
||||
case "Wednesday":
|
||||
ret.Add(jsonutils.NewString("3"))
|
||||
case "Thursday":
|
||||
ret.Add(jsonutils.NewString("4"))
|
||||
case "Friday":
|
||||
ret.Add(jsonutils.NewString("5"))
|
||||
case "Saturday":
|
||||
ret.Add(jsonutils.NewString("6"))
|
||||
case "Sunday":
|
||||
ret.Add(jsonutils.NewString("7"))
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423021.html
|
||||
func (self *SElasticcache) UpdateBackupPolicy(config cloudprovider.SCloudElasticCacheBackupPolicyUpdateInput) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423030.html
|
||||
// 当前版本,只有DCS2.0实例支持清空数据功能,即flush操作。
|
||||
func (self *SElasticcache) FlushInstance(input cloudprovider.SCloudElasticCacheFlushInstanceInput) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SElasticcacheAccount => ResetPassword
|
||||
func (self *SElasticcache) UpdateAuthMode(noPwdAccess bool, password string) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetAuthMode() string {
|
||||
switch self.NoPasswordAccess {
|
||||
case "true":
|
||||
return "off"
|
||||
default:
|
||||
return "on"
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetSecurityGroupIds() ([]string, error) {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetICloudElasticcacheAccount(accountId string) (cloudprovider.ICloudElasticcacheAccount, error) {
|
||||
accounts, err := self.GetICloudElasticcacheAccounts()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Elasticcache.GetICloudElasticcacheAccount.Accounts")
|
||||
}
|
||||
|
||||
for i := range accounts {
|
||||
account := accounts[i]
|
||||
if account.GetGlobalId() == accountId {
|
||||
return account, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetICloudElasticcacheAcl(aclId string) (cloudprovider.ICloudElasticcacheAcl, error) {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SElasticcache) GetICloudElasticcacheBackup(backupId string) (cloudprovider.ICloudElasticcacheBackup, error) {
|
||||
backups, err := self.GetICloudElasticcacheBackups()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, backup := range backups {
|
||||
if backup.GetId() == backupId {
|
||||
return backup, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (instance *SElasticcache) SetTags(tags map[string]string, replace bool) error {
|
||||
return cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (self *SElasticcache) UpdateSecurityGroups(secgroupIds []string) error {
|
||||
return errors.Wrap(cloudprovider.ErrNotSupported, "UpdateSecurityGroups")
|
||||
}
|
||||
|
||||
func (self *SElasticcache) Renew(bc billing.SBillingCycle) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SElasticcache) SetAutoRenew(bc billing.SBillingCycle) error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIElasticcaches() ([]cloudprovider.ICloudElasticcache, error) {
|
||||
caches, err := self.GetElasticCaches()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := []cloudprovider.ICloudElasticcache{}
|
||||
for i := range caches {
|
||||
caches[i].region = self
|
||||
ret = append(ret, &caches[i])
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
type SElasticcacheAccount struct {
|
||||
multicloud.SElasticcacheAccountBase
|
||||
multicloud.HuaweiTags
|
||||
|
||||
cacheDB *SElasticcache
|
||||
}
|
||||
|
||||
func (self *SElasticcacheAccount) GetId() string {
|
||||
return fmt.Sprintf("%s/root", self.cacheDB.InstanceId)
|
||||
}
|
||||
|
||||
func (self *SElasticcacheAccount) GetName() string {
|
||||
if len(self.cacheDB.AccessUser) > 0 {
|
||||
return self.cacheDB.AccessUser
|
||||
}
|
||||
|
||||
return "root"
|
||||
}
|
||||
|
||||
func (self *SElasticcacheAccount) GetGlobalId() string {
|
||||
return self.GetId()
|
||||
}
|
||||
|
||||
func (self *SElasticcacheAccount) GetStatus() string {
|
||||
return api.ELASTIC_CACHE_ACCOUNT_STATUS_AVAILABLE
|
||||
}
|
||||
|
||||
func (self *SElasticcacheAccount) GetAccountType() string {
|
||||
return api.ELASTIC_CACHE_ACCOUNT_TYPE_ADMIN
|
||||
}
|
||||
|
||||
func (self *SElasticcacheAccount) GetAccountPrivilege() string {
|
||||
return api.ELASTIC_CACHE_ACCOUNT_PRIVILEGE_WRITE
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423031.html
|
||||
// 未找到关闭密码的开放api, 不支持开启/关闭密码访问
|
||||
// https://console.huaweicloud.com/dcs/rest/v2/41f6bfe48d7f4455b7754f7c1b11ae34/instances/26db46e2-c7d8-4b5e-bd36-b5278d2fe17c/password/reset
|
||||
// new_password: "26db46e2!"
|
||||
// no_password_access: false
|
||||
func (self *SElasticcacheAccount) ResetPassword(input cloudprovider.SCloudElasticCacheAccountResetPasswordInput) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SElasticcacheAccount) UpdateAccount(input cloudprovider.SCloudElasticCacheAccountUpdateInput) error {
|
||||
if input.Password != nil {
|
||||
inputPassword := cloudprovider.SCloudElasticCacheAccountResetPasswordInput{}
|
||||
inputPassword.NewPassword = *input.Password
|
||||
inputPassword.OldPassword = input.OldPassword
|
||||
inputPassword.NoPasswordAccess = input.NoPasswordAccess
|
||||
return self.ResetPassword(inputPassword)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SElasticcacheAccount) Delete() error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423035.html
|
||||
type SElasticcacheBackup struct {
|
||||
multicloud.SElasticcacheBackupBase
|
||||
multicloud.HuaweiTags
|
||||
|
||||
cacheDB *SElasticcache
|
||||
|
||||
Status string `json:"status"`
|
||||
Remark string `json:"remark"`
|
||||
Period string `json:"period"`
|
||||
Progress string `json:"progress"`
|
||||
SizeByte int64 `json:"size"`
|
||||
InstanceId string `json:"instance_id"`
|
||||
BackupId string `json:"backup_id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
ExecutionAt time.Time `json:"execution_at"`
|
||||
BackupType string `json:"backup_type"`
|
||||
BackupName string `json:"backup_name"`
|
||||
ErrorCode string `json:"error_code"`
|
||||
IsSupportRestore string `json:"is_support_restore"`
|
||||
}
|
||||
|
||||
func (self *SElasticcacheBackup) GetId() string {
|
||||
return self.BackupId
|
||||
}
|
||||
|
||||
func (self *SElasticcacheBackup) GetName() string {
|
||||
return self.BackupName
|
||||
}
|
||||
|
||||
func (self *SElasticcacheBackup) GetGlobalId() string {
|
||||
return self.GetId()
|
||||
}
|
||||
|
||||
func (self *SElasticcacheBackup) Refresh() error {
|
||||
cache, err := self.cacheDB.GetICloudElasticcacheBackup(self.GetId())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "ElasticcacheBackup.Refresh.GetICloudElasticcacheBackup")
|
||||
}
|
||||
|
||||
err = jsonutils.Update(self, cache)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "ElasticcacheBackup.Refresh.Update")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SElasticcacheBackup) GetStatus() string {
|
||||
switch self.Status {
|
||||
case "waiting", "backuping":
|
||||
return api.ELASTIC_CACHE_BACKUP_STATUS_CREATING
|
||||
case "succeed":
|
||||
return api.ELASTIC_CACHE_BACKUP_STATUS_SUCCESS
|
||||
case "failed":
|
||||
return api.ELASTIC_CACHE_BACKUP_STATUS_FAILED
|
||||
case "expired":
|
||||
return api.ELASTIC_CACHE_BACKUP_STATUS_CREATE_EXPIRED
|
||||
case "deleted":
|
||||
return api.ELASTIC_CACHE_BACKUP_STATUS_CREATE_DELETED
|
||||
default:
|
||||
return self.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SElasticcacheBackup) GetBackupSizeMb() int {
|
||||
return int(self.SizeByte / 1024 / 1024)
|
||||
}
|
||||
|
||||
func (self *SElasticcacheBackup) GetBackupType() string {
|
||||
switch self.BackupType {
|
||||
case "manual":
|
||||
return api.ELASTIC_CACHE_BACKUP_MODE_MANUAL
|
||||
case "auto":
|
||||
return api.ELASTIC_CACHE_BACKUP_MODE_AUTOMATED
|
||||
default:
|
||||
return self.BackupType
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (self *SElasticcacheBackup) GetBackupMode() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SElasticcacheBackup) GetDownloadURL() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SElasticcacheBackup) GetStartTime() time.Time {
|
||||
return self.CreatedAt
|
||||
}
|
||||
|
||||
func (self *SElasticcacheBackup) GetEndTime() time.Time {
|
||||
return self.UpdatedAt
|
||||
}
|
||||
|
||||
func (self *SElasticcacheBackup) Delete() error {
|
||||
return cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423034.html
|
||||
func (self *SElasticcacheBackup) RestoreInstance(instanceId string) error {
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
type SRedisSkuDetails struct {
|
||||
Capacity int
|
||||
MaxMemory float32
|
||||
MaxConnections int
|
||||
MaxClients int
|
||||
MaxBandwidth int
|
||||
MaxInBandwidth int
|
||||
TenantIpCount int
|
||||
ShardingNum int
|
||||
ProxyNum int
|
||||
DBNumber int
|
||||
}
|
||||
|
||||
type SRedisFlavor struct {
|
||||
Capacity int
|
||||
Unit string
|
||||
AvailableZones []string
|
||||
AzCodes []string
|
||||
}
|
||||
|
||||
type SRedisInstanceType struct {
|
||||
multicloud.SResourceBase
|
||||
ProductId string
|
||||
SpecCode string
|
||||
CacheMode string
|
||||
ProductType string
|
||||
CpuType string
|
||||
StorageType string
|
||||
Details SRedisSkuDetails
|
||||
Engine string
|
||||
EngineVersions string
|
||||
SpecDetails string
|
||||
SpecDetails2 string
|
||||
CharingType string
|
||||
Price float32
|
||||
ProdType string
|
||||
CloudServiceTypeCode string
|
||||
CloudResourceTypeCode string
|
||||
Flavors []SRedisFlavor
|
||||
}
|
||||
|
||||
type SRedisSku struct {
|
||||
SRedisSkuDetails
|
||||
SRedisFlavor
|
||||
ProductId string
|
||||
SpecCode string
|
||||
CacheMode string
|
||||
ProductType string
|
||||
CpuType string
|
||||
StorageType string
|
||||
Engine string
|
||||
EngineVersion string
|
||||
ProdType string
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetName() string {
|
||||
return self.SpecCode
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetGlobalId() string {
|
||||
return fmt.Sprintf("%s-%s-%s-%s-%d-%.2f", self.SpecCode, self.Engine, self.EngineVersion, self.CacheMode, self.SRedisFlavor.Capacity, self.MaxMemory)
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetZoneId() string {
|
||||
if len(self.AzCodes) > 0 {
|
||||
return self.AzCodes[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetSlaveZoneId() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetEngineArch() string {
|
||||
return self.CpuType
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetLocalCategory() string {
|
||||
return self.CacheMode
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetPrepaidStatus() string {
|
||||
return apis.SKU_STATUS_SOLDOUT
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetPostpaidStatus() string {
|
||||
return apis.SKU_STATUS_AVAILABLE
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetEngine() string {
|
||||
return self.Engine
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetEngineVersion() string {
|
||||
return self.EngineVersion
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetCpuArch() string {
|
||||
return self.CpuType
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetStorageType() string {
|
||||
return self.StorageType
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetPerformanceType() string {
|
||||
return "standard"
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetNodeType() string {
|
||||
return self.CacheMode
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetDiskSizeGb() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetShardNum() int {
|
||||
return self.ShardingNum
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetMaxShardNum() int {
|
||||
return self.ShardingNum
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetReplicasNum() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetMaxReplicasNum() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetMaxClients() int {
|
||||
return self.MaxClients
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetMaxConnections() int {
|
||||
return self.MaxConnections
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetMaxInBandwidthMb() int {
|
||||
return self.MaxInBandwidth
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetMemorySizeMb() int {
|
||||
return self.SRedisSkuDetails.Capacity * 1024
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetMaxMemoryMb() int {
|
||||
return int(self.MaxMemory) * 1024
|
||||
}
|
||||
|
||||
func (self *SRedisSku) GetQps() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *SRegion) getRedisInstnaceTypes() ([]SRedisInstanceType, error) {
|
||||
ret := []SRedisInstanceType{}
|
||||
return ret, self.redisList("products", nil, &ret)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetRedisInstnaceTypes() ([]SRedisSku, error) {
|
||||
skus, err := self.getRedisInstnaceTypes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := []SRedisSku{}
|
||||
for i := range skus {
|
||||
details := []SRedisSkuDetails{}
|
||||
detailsStr := strings.TrimPrefix(skus[i].SpecDetails2, "\n")
|
||||
detailsStr = strings.TrimSuffix(detailsStr, "\n")
|
||||
obj, err := jsonutils.ParseString(detailsStr)
|
||||
if err != nil {
|
||||
log.Errorf("parase details [%s], error: %v", detailsStr, err)
|
||||
continue
|
||||
}
|
||||
err = obj.Unmarshal(&details)
|
||||
if err != nil {
|
||||
log.Errorf("unmarshal error: %v", err)
|
||||
continue
|
||||
}
|
||||
for j := range details {
|
||||
sku := SRedisSku{
|
||||
SRedisSkuDetails: details[j],
|
||||
ProductId: skus[i].ProductId,
|
||||
SpecCode: skus[i].SpecCode,
|
||||
CacheMode: skus[i].CacheMode,
|
||||
ProductType: skus[i].ProdType,
|
||||
CpuType: skus[i].CpuType,
|
||||
StorageType: skus[i].StorageType,
|
||||
Engine: skus[i].Engine,
|
||||
EngineVersion: skus[i].EngineVersions,
|
||||
ProdType: skus[i].ProdType,
|
||||
}
|
||||
for k := range skus[i].Flavors {
|
||||
if skus[i].Flavors[k].Capacity == details[j].Capacity {
|
||||
sku.SRedisFlavor = skus[i].Flavors[k]
|
||||
if len(sku.GetZoneId()) > 0 {
|
||||
ret = append(ret, sku)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIElasticcacheSkus() ([]cloudprovider.ICloudElasticcacheSku, error) {
|
||||
skus, err := self.GetRedisInstnaceTypes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := []cloudprovider.ICloudElasticcacheSku{}
|
||||
for i := range skus {
|
||||
ret = append(ret, &skus[i])
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423027.html
|
||||
type SElasticcacheParameter struct {
|
||||
multicloud.SElasticcacheParameterBase
|
||||
multicloud.HuaweiTags
|
||||
|
||||
cacheDB *SElasticcache
|
||||
|
||||
Description string `json:"description"`
|
||||
ParamId int64 `json:"param_id"`
|
||||
ParamName string `json:"param_name"`
|
||||
ParamValue string `json:"param_value"`
|
||||
DefaultValue string `json:"default_value"`
|
||||
ValueType string `json:"value_type"`
|
||||
ValueRange string `json:"value_range"`
|
||||
}
|
||||
|
||||
func (self *SElasticcacheParameter) GetId() string {
|
||||
return fmt.Sprintf("%d", self.ParamId)
|
||||
}
|
||||
|
||||
func (self *SElasticcacheParameter) GetName() string {
|
||||
return self.ParamName
|
||||
}
|
||||
|
||||
func (self *SElasticcacheParameter) GetGlobalId() string {
|
||||
return fmt.Sprintf("%s/%s", self.cacheDB.InstanceId, self.GetId())
|
||||
}
|
||||
|
||||
func (self *SElasticcacheParameter) GetStatus() string {
|
||||
return api.ELASTIC_CACHE_PARAMETER_STATUS_AVAILABLE
|
||||
}
|
||||
|
||||
func (self *SElasticcacheParameter) GetParameterKey() string {
|
||||
return self.ParamName
|
||||
}
|
||||
|
||||
func (self *SElasticcacheParameter) GetParameterValue() string {
|
||||
return self.ParamValue
|
||||
}
|
||||
|
||||
func (self *SElasticcacheParameter) GetParameterValueRange() string {
|
||||
return self.Description
|
||||
}
|
||||
|
||||
func (self *SElasticcacheParameter) GetDescription() string {
|
||||
return self.ValueRange
|
||||
}
|
||||
|
||||
func (self *SElasticcacheParameter) GetModifiable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SElasticcacheParameter) GetForceRestart() bool {
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,332 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/huaweicloud/huaweicloud-sdk-go-obs/obs"
|
||||
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
type SRegion struct {
|
||||
multicloud.HcsTags
|
||||
multicloud.SRegion
|
||||
multicloud.SRegionEipBase
|
||||
multicloud.SRegionOssBase
|
||||
multicloud.SRegionLbBase
|
||||
multicloud.SRegionVpcBase
|
||||
multicloud.SRegionZoneBase
|
||||
multicloud.SRegionSecurityGroupBase
|
||||
|
||||
obsClient *obs.ObsClient // 对象存储client.请勿直接引用。
|
||||
|
||||
client *SHcsClient
|
||||
|
||||
Id string
|
||||
CloudInfras []string
|
||||
Description string
|
||||
Locales struct {
|
||||
EnUS string `json:"en-us"`
|
||||
ZhCN string `json:"zh-cn"`
|
||||
}
|
||||
ParentRegionId string
|
||||
}
|
||||
|
||||
func (self *SRegion) GetClient() *SHcsClient {
|
||||
return self.client
|
||||
}
|
||||
|
||||
func (self *SRegion) GetId() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SRegion) GetGlobalId() string {
|
||||
return fmt.Sprintf("%s/%s", CLOUD_PROVIDER_HCS, self.Id)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetName() string {
|
||||
return fmt.Sprintf("%s %s", CLOUD_PROVIDER_HCS_CN, self.Locales.ZhCN)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetStatus() string {
|
||||
return api.CLOUD_REGION_STATUS_INSERVER
|
||||
}
|
||||
|
||||
func (self *SRegion) GetILoadBalancerBackendGroups() ([]cloudprovider.ICloudLoadbalancerBackendGroup, error) {
|
||||
return nil, cloudprovider.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (self *SRegion) GetI18n() cloudprovider.SModelI18nTable {
|
||||
en := fmt.Sprintf("%s %s", CLOUD_PROVIDER_HCS_EN, self.Locales.EnUS)
|
||||
table := cloudprovider.SModelI18nTable{}
|
||||
table["name"] = cloudprovider.NewSModelI18nEntry(self.GetName()).CN(self.GetName()).EN(en)
|
||||
return table
|
||||
}
|
||||
|
||||
func (self *SRegion) GetCapabilities() []string {
|
||||
return self.client.GetCapabilities()
|
||||
}
|
||||
|
||||
func (self *SRegion) GetCloudEnv() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SRegion) GetProvider() string {
|
||||
return CLOUD_PROVIDER_HCS
|
||||
}
|
||||
|
||||
func (self *SRegion) GetGeographicInfo() cloudprovider.SGeographicInfo {
|
||||
return cloudprovider.SGeographicInfo{}
|
||||
}
|
||||
|
||||
func (self *SRegion) ecsList(resource string, query url.Values, retVal interface{}) error {
|
||||
return self.client.ecsList(self.Id, resource, query, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) ecsGet(resource string, retVal interface{}) error {
|
||||
return self.client.ecsGet(self.Id, resource, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) ecsDelete(resource string) error {
|
||||
return self.client.ecsDelete(self.Id, resource)
|
||||
}
|
||||
|
||||
func (self *SRegion) ecsCreate(resource string, body map[string]interface{}, retVal interface{}) error {
|
||||
return self.client.ecsCreate(self.Id, resource, body, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) ecsPerform(resource, action string, params map[string]interface{}, retVal interface{}) error {
|
||||
return self.client.ecsPerform(self.Id, resource, action, params, retVal)
|
||||
}
|
||||
|
||||
func (region *SRegion) rdsList(resource string, query url.Values, retVal interface{}) error {
|
||||
return region.client.rdsList(region.Id, resource, query, retVal)
|
||||
}
|
||||
|
||||
func (region *SRegion) rdsGet(resource string, retVal interface{}) error {
|
||||
return region.client.rdsGet(region.Id, resource, retVal)
|
||||
}
|
||||
|
||||
func (region *SRegion) rdsDelete(resource string) error {
|
||||
return region.client.rdsDelete(region.Id, resource)
|
||||
}
|
||||
|
||||
func (region *SRegion) rdsCreate(resource string, body map[string]interface{}, retVal interface{}) error {
|
||||
return region.client.rdsCreate(region.Id, resource, body, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) rdsPerform(resource, action string, params map[string]interface{}, retVal interface{}) error {
|
||||
return self.client.rdsPerform(self.Id, resource, action, params, retVal)
|
||||
}
|
||||
|
||||
func (region *SRegion) rdsJobGet(resource string, query url.Values, retVal interface{}) error {
|
||||
return region.client.rdsJobGet(region.Id, resource, query, retVal)
|
||||
}
|
||||
|
||||
func (region *SRegion) rdsDBPrivilegesDelete(resource string, params map[string]interface{}) error {
|
||||
return region.client.rdsDBPrivvilegesDelete(region.Id, resource, params)
|
||||
}
|
||||
|
||||
func (region *SRegion) rdsDBPrivilegesGrant(resource string, params map[string]interface{}, retVal interface{}) error {
|
||||
return region.client.rdsDBPrivilegesGrant(region.Id, resource, params, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) perform(product, version, resource, action string, params map[string]interface{}, retVal interface{}) error {
|
||||
return self.client.perform(product, version, self.Id, resource, action, params, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) create(product, version, resource string, body map[string]interface{}, retVal interface{}) error {
|
||||
return self.client.create(product, version, self.Id, resource, body, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) delete(product, version, resource string) error {
|
||||
return self.client.delete(product, version, self.Id, resource)
|
||||
}
|
||||
|
||||
func (self *SRegion) list(product, version, resource string, query url.Values, retVal interface{}) error {
|
||||
return self.client.list(product, version, self.Id, resource, query, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) get(product, version, resource string, retVal interface{}) error {
|
||||
return self.client.get(product, version, self.Id, resource, retVal)
|
||||
}
|
||||
func (self *SRegion) update(product, version, resource string, params map[string]interface{}) error {
|
||||
return self.client.update(product, version, self.Id, resource, params)
|
||||
}
|
||||
|
||||
func (self *SRegion) evsList(resource string, query url.Values, retVal interface{}) error {
|
||||
return self.client.evsList(self.Id, resource, query, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) evsGet(resource string, retVal interface{}) error {
|
||||
return self.client.evsGet(self.Id, resource, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) evsDelete(resource string) error {
|
||||
return self.client.evsDelete(self.Id, resource)
|
||||
}
|
||||
|
||||
func (self *SRegion) evsPerform(resource, action string, params map[string]interface{}) error {
|
||||
return self.client.evsPerform(self.Id, resource, action, params)
|
||||
}
|
||||
|
||||
func (self *SRegion) evsCreate(resource string, body map[string]interface{}, retVal interface{}) error {
|
||||
return self.client.evsCreate(self.Id, resource, body, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) vpcList(resource string, query url.Values, retVal interface{}) error {
|
||||
return self.client.vpcList(self.Id, resource, query, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) vpcCreate(resource string, body map[string]interface{}, retVal interface{}) error {
|
||||
return self.client.vpcCreate(self.Id, resource, body, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) vpcGet(resource string, retVal interface{}) error {
|
||||
return self.client.vpcGet(self.Id, resource, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) vpcUpdate(resource string, params map[string]interface{}) error {
|
||||
return self.client.vpcUpdate(self.Id, resource, params)
|
||||
}
|
||||
|
||||
func (self *SRegion) vpcDelete(resource string) error {
|
||||
return self.client.vpcDelete(self.Id, resource)
|
||||
}
|
||||
|
||||
func (self *SRegion) imsList(resource string, query url.Values, retVal interface{}) error {
|
||||
return self.client.imsList(self.Id, resource, query, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) imsCreate(resource string, body map[string]interface{}, retVal interface{}) error {
|
||||
return self.client.imsCreate(self.Id, resource, body, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) imsGet(resource string, retVal interface{}) error {
|
||||
return self.client.imsGet(self.Id, resource, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) imsUpdate(resource string, params map[string]interface{}) error {
|
||||
return self.client.imsUpdate(self.Id, resource, params)
|
||||
}
|
||||
|
||||
func (self *SRegion) imsDelete(resource string) error {
|
||||
return self.client.imsDelete(self.Id, resource)
|
||||
}
|
||||
|
||||
func (self *SRegion) imsPerform(resource, action string, params map[string]interface{}, retVal interface{}) error {
|
||||
return self.client.imsPerform(self.Id, resource, action, params, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) redisList(resource string, query url.Values, retVal interface{}) error {
|
||||
return self.client.dcsList(self.Id, resource, query, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) redisCreate(resource string, body map[string]interface{}, retVal interface{}) error {
|
||||
return self.client.dcsCreate(self.Id, resource, body, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) redisGet(resource string, retVal interface{}) error {
|
||||
return self.client.dcsGet(self.Id, resource, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) redisUpdate(resource string, params map[string]interface{}) error {
|
||||
return self.client.dcsUpdate(self.Id, resource, params)
|
||||
}
|
||||
|
||||
func (self *SRegion) redisDelete(resource string) error {
|
||||
return self.client.dcsDelete(self.Id, resource)
|
||||
}
|
||||
|
||||
func (self *SRegion) redisPerform(resource, action string, params map[string]interface{}, retVal interface{}) error {
|
||||
return self.client.dcsPerform(self.Id, resource, action, params, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetICloudSku(skuId string) (cloudprovider.ICloudSku, error) {
|
||||
sku, err := self.GetchInstanceType(skuId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sku, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) getOBSClient() (*obs.ObsClient, error) {
|
||||
if self.obsClient == nil {
|
||||
obsClient, err := self.client.getOBSClient(self.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
self.obsClient = obsClient
|
||||
}
|
||||
return self.obsClient, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) HeadBucket(name string) (*obs.BaseModel, error) {
|
||||
obsClient, err := self.getOBSClient()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "region.getOBSClient")
|
||||
}
|
||||
return obsClient.HeadBucket(name)
|
||||
}
|
||||
|
||||
func (self *SRegion) getOBSEndpoint() string {
|
||||
return self.client.getOBSEndpoint(self.GetId())
|
||||
}
|
||||
|
||||
func str2StorageClass(storageClassStr string) (obs.StorageClassType, error) {
|
||||
if strings.EqualFold(storageClassStr, string(obs.StorageClassStandard)) {
|
||||
return obs.StorageClassStandard, nil
|
||||
} else if strings.EqualFold(storageClassStr, string(obs.StorageClassWarm)) {
|
||||
return obs.StorageClassWarm, nil
|
||||
} else if strings.EqualFold(storageClassStr, string(obs.StorageClassCold)) {
|
||||
return obs.StorageClassCold, nil
|
||||
} else {
|
||||
return obs.StorageClassStandard, errors.Error("unsupported storageClass")
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SRegion) GetBuckets() ([]SBucket, error) {
|
||||
buckets, err := self.client.GetBuckets()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := []SBucket{}
|
||||
for i := range buckets {
|
||||
if buckets[i].region.GetGlobalId() == self.GetGlobalId() {
|
||||
ret = append(ret, buckets[i])
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIBuckets() ([]cloudprovider.ICloudBucket, error) {
|
||||
buckets, err := self.GetBuckets()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetBuckets")
|
||||
}
|
||||
ret := make([]cloudprovider.ICloudBucket, 0)
|
||||
for i := range buckets {
|
||||
ret = append(ret, &buckets[i])
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
var hoptypes = map[string]string{
|
||||
api.NEXT_HOP_TYPE_VPCPEERING: "peering",
|
||||
api.NEXT_HOP_TYPE_INSTANCE: "ecs",
|
||||
api.NEXT_HOP_TYPE_NAT: "nat",
|
||||
api.NEXT_HOP_TYPE_HAVIP: "vip",
|
||||
api.NEXT_HOP_TYPE_NETWORK: "eni",
|
||||
api.NEXT_HOP_TYPE_INTERVPCNETWORK: "cc",
|
||||
}
|
||||
|
||||
type SRoute struct {
|
||||
multicloud.SResourceBase
|
||||
multicloud.HuaweiTags
|
||||
|
||||
Type string `json:"type"`
|
||||
Destination string `json:"destination"`
|
||||
Nexthop string `json:"nexthop"`
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
func (self *SRoute) GetCidr() string {
|
||||
return self.Destination
|
||||
}
|
||||
|
||||
func (self *SRoute) GetGlobalId() string {
|
||||
return self.GetId()
|
||||
}
|
||||
|
||||
func (self *SRoute) GetId() string {
|
||||
return fmt.Sprintf("%s:%s:%s", self.Type, self.Nexthop, self.Destination)
|
||||
}
|
||||
|
||||
func (self *SRoute) GetName() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (route *SRoute) GetStatus() string {
|
||||
return api.ROUTE_ENTRY_STATUS_AVAILIABLE
|
||||
}
|
||||
|
||||
func (route *SRoute) GetType() string {
|
||||
if route.Type != "local" {
|
||||
return api.ROUTE_ENTRY_TYPE_CUSTOM
|
||||
}
|
||||
return api.ROUTE_ENTRY_TYPE_SYSTEM
|
||||
}
|
||||
|
||||
func (self *SRoute) GetNextHopType() string {
|
||||
for k, v := range hoptypes {
|
||||
if v == self.Type {
|
||||
return k
|
||||
}
|
||||
}
|
||||
return self.Type
|
||||
}
|
||||
|
||||
type Subnet struct {
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
type SRouteTable struct {
|
||||
multicloud.SResourceBase
|
||||
multicloud.HuaweiTags
|
||||
vpc *SVpc
|
||||
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Routes []SRoute `json:"routes"`
|
||||
Subnets []Subnet `json:"subnets"`
|
||||
VpcId string `json:"vpc_id"`
|
||||
Default bool `json:"default"`
|
||||
TenantId string `json:"tenant_id"`
|
||||
}
|
||||
|
||||
func (self *SRouteTable) GetDescription() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SRouteTable) GetGlobalId() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SRouteTable) GetId() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SRouteTable) GetName() string {
|
||||
return self.Name
|
||||
}
|
||||
|
||||
func (self *SRouteTable) GetRegionId() string {
|
||||
return self.vpc.region.GetId()
|
||||
}
|
||||
|
||||
func (self *SRouteTable) GetVpcId() string {
|
||||
return self.VpcId
|
||||
}
|
||||
|
||||
func (self *SRouteTable) GetType() cloudprovider.RouteTableType {
|
||||
return cloudprovider.RouteTableTypeSystem
|
||||
}
|
||||
|
||||
func (self *SRouteTable) GetStatus() string {
|
||||
return api.ROUTE_TABLE_AVAILABLE
|
||||
}
|
||||
|
||||
func (self *SRouteTable) Refresh() error {
|
||||
rtb, err := self.vpc.region.GetRouteTable(self.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(self, rtb)
|
||||
}
|
||||
|
||||
func (self *SRouteTable) GetIRoutes() ([]cloudprovider.ICloudRoute, error) {
|
||||
if len(self.Routes) == 0 {
|
||||
err := self.Refresh()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
ret := []cloudprovider.ICloudRoute{}
|
||||
for i := range self.Routes {
|
||||
ret = append(ret, &self.Routes[i])
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRoute) GetNextHop() string {
|
||||
return self.Nexthop
|
||||
}
|
||||
|
||||
func (self *SRegion) GetRouteTables(vpcId string) ([]SRouteTable, error) {
|
||||
params := url.Values{}
|
||||
if len(vpcId) > 0 {
|
||||
params.Set("vpc_id", vpcId)
|
||||
}
|
||||
rtbs := []SRouteTable{}
|
||||
return rtbs, self.list("vpc", "v2.0", "vpc/routes", params, &rtbs)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetRouteTable(id string) (*SRouteTable, error) {
|
||||
tb := &SRouteTable{}
|
||||
return tb, self.get("vpc", "v2.0", "vpc/routes/"+id, tb)
|
||||
}
|
||||
|
||||
func (self *SRouteTable) CreateRoute(route cloudprovider.RouteSet) error {
|
||||
routeType, ok := hoptypes[route.NextHopType]
|
||||
if !ok {
|
||||
return errors.Wrapf(cloudprovider.ErrNotSupported, route.NextHopType)
|
||||
}
|
||||
params := map[string]interface{}{
|
||||
"routes": map[string]interface{}{
|
||||
"add": []map[string]interface{}{
|
||||
{
|
||||
"type": routeType,
|
||||
"destination": route.Destination,
|
||||
"nexthop": route.NextHop,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return self.vpc.region.create("vpc", "v2.0", "vpc/routes", params, nil)
|
||||
}
|
||||
|
||||
func (self *SRouteTable) RemoveRoute(route cloudprovider.RouteSet) error {
|
||||
if len(route.RouteId) > 0 {
|
||||
return self.vpc.region.delete("vpc", "v2.0", "vpc/routes/"+route.RouteId)
|
||||
}
|
||||
return fmt.Errorf("missing route id")
|
||||
}
|
||||
|
||||
func (self *SRouteTable) UpdateRoute(route cloudprovider.RouteSet) error {
|
||||
err := self.RemoveRoute(route)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "self.RemoveRoute(route)")
|
||||
}
|
||||
err = self.CreateRoute(route)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "self.CreateRoute(route)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SRouteTable) GetAssociations() []cloudprovider.RouteTableAssociation {
|
||||
return []cloudprovider.RouteTableAssociation{}
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
// 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 hcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/secrules"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud"
|
||||
)
|
||||
|
||||
type SecurityGroupRule struct {
|
||||
Direction string `json:"direction"`
|
||||
Ethertype string `json:"ethertype"`
|
||||
Id string `json:"id"`
|
||||
Description string `json:"description"`
|
||||
SecurityGroupId string `json:"security_group_id"`
|
||||
RemoteGroupId string `json:"remote_group_id"`
|
||||
}
|
||||
|
||||
type SecurityGroupRuleDetail struct {
|
||||
Direction string `json:"direction"`
|
||||
Ethertype string `json:"ethertype"`
|
||||
Id string `json:"id"`
|
||||
Description string `json:"description"`
|
||||
PortRangeMax int64 `json:"port_range_max"`
|
||||
PortRangeMin int64 `json:"port_range_min"`
|
||||
Protocol string `json:"protocol"`
|
||||
RemoteGroupId string `json:"remote_group_id"`
|
||||
RemoteIPPrefix string `json:"remote_ip_prefix"`
|
||||
SecurityGroupId string `json:"security_group_id"`
|
||||
TenantId string `json:"tenant_id"`
|
||||
}
|
||||
|
||||
type SSecurityGroup struct {
|
||||
multicloud.SSecurityGroup
|
||||
multicloud.HcsTags
|
||||
region *SRegion
|
||||
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
VpcId string `json:"vpc_id"`
|
||||
EnterpriseProjectId string `json:"enterprise_project_id "`
|
||||
SecurityGroupRules []SecurityGroupRule `json:"security_group_rules"`
|
||||
}
|
||||
|
||||
// 判断是否兼容云端安全组规则
|
||||
func compatibleSecurityGroupRule(r SecurityGroupRule) bool {
|
||||
// 忽略了源地址是安全组的规则
|
||||
if len(r.RemoteGroupId) > 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// 忽略IPV6
|
||||
if r.Ethertype == "IPv6" {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) GetId() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) GetVpcId() string {
|
||||
return self.VpcId
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) GetName() string {
|
||||
if len(self.Name) > 0 {
|
||||
return self.Name
|
||||
}
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) GetGlobalId() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) GetStatus() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) Refresh() error {
|
||||
if new, err := self.region.GetSecurityGroupDetails(self.GetId()); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return jsonutils.Update(self, new)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) IsEmulated() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) GetDescription() string {
|
||||
if self.Description == self.VpcId {
|
||||
return ""
|
||||
}
|
||||
return self.Description
|
||||
}
|
||||
|
||||
// todo: 这里需要优化查询太多了
|
||||
func (self *SSecurityGroup) GetRules() ([]cloudprovider.SecurityRule, error) {
|
||||
rules := make([]cloudprovider.SecurityRule, 0)
|
||||
for _, r := range self.SecurityGroupRules {
|
||||
if !compatibleSecurityGroupRule(r) {
|
||||
continue
|
||||
}
|
||||
|
||||
rule, err := self.GetSecurityRule(r.Id)
|
||||
if err != nil {
|
||||
return rules, err
|
||||
}
|
||||
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) GetSecurityRule(ruleId string) (cloudprovider.SecurityRule, error) {
|
||||
remoteRule := SecurityGroupRuleDetail{}
|
||||
err := self.region.vpcGet("security-group-rules/"+ruleId, &remoteRule)
|
||||
if err != nil {
|
||||
return cloudprovider.SecurityRule{}, err
|
||||
}
|
||||
|
||||
var direction secrules.TSecurityRuleDirection
|
||||
if remoteRule.Direction == "ingress" {
|
||||
direction = secrules.SecurityRuleIngress
|
||||
} else {
|
||||
direction = secrules.SecurityRuleEgress
|
||||
}
|
||||
|
||||
protocol := secrules.PROTO_ANY
|
||||
if remoteRule.Protocol != "" {
|
||||
protocol = remoteRule.Protocol
|
||||
}
|
||||
|
||||
var portStart int
|
||||
var portEnd int
|
||||
if protocol == secrules.PROTO_ICMP {
|
||||
portStart = -1
|
||||
portEnd = -1
|
||||
} else {
|
||||
portStart = int(remoteRule.PortRangeMin)
|
||||
portEnd = int(remoteRule.PortRangeMax)
|
||||
}
|
||||
|
||||
ipNet := &net.IPNet{}
|
||||
if len(remoteRule.RemoteIPPrefix) > 0 {
|
||||
_, ipNet, err = net.ParseCIDR(remoteRule.RemoteIPPrefix)
|
||||
} else {
|
||||
_, ipNet, err = net.ParseCIDR("0.0.0.0/0")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return cloudprovider.SecurityRule{}, err
|
||||
}
|
||||
|
||||
rule := cloudprovider.SecurityRule{
|
||||
ExternalId: ruleId,
|
||||
SecurityRule: secrules.SecurityRule{
|
||||
Priority: 1,
|
||||
Action: secrules.SecurityRuleAllow,
|
||||
IPNet: ipNet,
|
||||
Protocol: protocol,
|
||||
Direction: direction,
|
||||
PortStart: portStart,
|
||||
PortEnd: portEnd,
|
||||
Ports: nil,
|
||||
Description: remoteRule.Description,
|
||||
},
|
||||
}
|
||||
|
||||
err = rule.ValidateRule()
|
||||
return rule, err
|
||||
}
|
||||
|
||||
func (self *SRegion) GetSecurityGroupDetails(id string) (*SSecurityGroup, error) {
|
||||
ret := &SSecurityGroup{region: self}
|
||||
return ret, self.vpcGet("security-groups/"+id, ret)
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090617.html
|
||||
func (self *SRegion) GetSecurityGroups(vpcId string) ([]SSecurityGroup, error) {
|
||||
query := url.Values{}
|
||||
if len(vpcId) > 0 && !utils.IsInStringArray(vpcId, []string{"default", api.NORMAL_VPC_ID}) { // vpc_id = default or normal 时报错 '{"code":"VPC.0601","message":"Query security groups error vpcId is invalid."}'
|
||||
query.Set("vpc_id", vpcId)
|
||||
}
|
||||
ret := []SSecurityGroup{}
|
||||
return ret, self.vpcList("security-groups", query, &ret)
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) GetProjectId() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) Delete() error {
|
||||
return self.region.DeleteSecurityGroup(self.Id)
|
||||
}
|
||||
|
||||
func (self *SSecurityGroup) SyncRules(common, inAdds, outAdds, inDels, outDels []cloudprovider.SecurityRule) error {
|
||||
for _, r := range append(inDels, outDels...) {
|
||||
err := self.region.DeleteSecurityGroupRule(r.ExternalId)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "delSecurityGroupRule(%s %s)", r.ExternalId, r.String())
|
||||
}
|
||||
}
|
||||
for _, r := range append(inAdds, outAdds...) {
|
||||
err := self.region.CreateSecurityGroupRule(self.Id, r)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "addSecurityGroupRule(%d %s)", r.Priority, r.String())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteSecurityGroup(id string) error {
|
||||
return self.vpcDelete("security-groups/" + id)
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteSecurityGroupRule(ruleId string) error {
|
||||
return self.vpcDelete("security-group-rules/" + ruleId)
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateSecurityGroupRule(secGrpId string, rule cloudprovider.SecurityRule) error {
|
||||
direction := ""
|
||||
if rule.Direction == secrules.SecurityRuleIngress {
|
||||
direction = "ingress"
|
||||
} else {
|
||||
direction = "egress"
|
||||
}
|
||||
|
||||
protocal := rule.Protocol
|
||||
if rule.Protocol == secrules.PROTO_ANY {
|
||||
protocal = ""
|
||||
}
|
||||
|
||||
// imcp协议默认为any
|
||||
if rule.Protocol == secrules.PROTO_ICMP {
|
||||
return self.AddSecurityGroupRule(secGrpId, direction, "-1", "-1", protocal, rule.IPNet.String())
|
||||
}
|
||||
|
||||
if len(rule.Ports) > 0 {
|
||||
for _, port := range rule.Ports {
|
||||
portStr := fmt.Sprintf("%d", port)
|
||||
err := self.AddSecurityGroupRule(secGrpId, direction, portStr, portStr, protocal, rule.IPNet.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
portStart := fmt.Sprintf("%d", rule.PortStart)
|
||||
portEnd := fmt.Sprintf("%d", rule.PortEnd)
|
||||
err := self.AddSecurityGroupRule(secGrpId, direction, portStart, portEnd, protocal, rule.IPNet.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SRegion) AddSecurityGroupRule(secGrpId, direction, portStart, portEnd, protocol, ipNet string) error {
|
||||
rule := map[string]interface{}{
|
||||
"security_group_id": secGrpId,
|
||||
"direction": direction,
|
||||
"remote_ip_prefix": ipNet,
|
||||
"ethertype": "IPV4",
|
||||
}
|
||||
if len(portStart) > 0 && portStart != "0" && portStart != "-1" {
|
||||
rule["port_range_min"] = portStart
|
||||
}
|
||||
if len(portEnd) > 0 && portEnd != "0" && portEnd != "-1" {
|
||||
rule["port_range_max"] = portEnd
|
||||
}
|
||||
if len(protocol) > 0 {
|
||||
rule["protocol"] = protocol
|
||||
}
|
||||
params := map[string]interface{}{
|
||||
"security_group_rule": rule,
|
||||
}
|
||||
return self.vpcCreate("security-group-rules", params, nil)
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateSecurityGroup(vpcId string, name string, desc string) (*SSecurityGroup, error) {
|
||||
params := map[string]interface{}{
|
||||
"security_group": map[string]interface{}{
|
||||
"name": name,
|
||||
"vpc_id": vpcId,
|
||||
},
|
||||
}
|
||||
ret := &SSecurityGroup{region: self}
|
||||
return ret, self.vpcCreate("security-groups", params, ret)
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateISecurityGroup(conf *cloudprovider.SecurityGroupCreateInput) (cloudprovider.ICloudSecurityGroup, error) {
|
||||
return self.CreateSecurityGroup(conf.VpcId, conf.Name, conf.Desc)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetISecurityGroupById(secgroupId string) (cloudprovider.ICloudSecurityGroup, error) {
|
||||
return self.GetSecurityGroupDetails(secgroupId)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetISecurityGroupByName(opts *cloudprovider.SecurityGroupFilterOptions) (cloudprovider.ICloudSecurityGroup, error) {
|
||||
secgroups, err := self.GetSecurityGroups(opts.VpcId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range secgroups {
|
||||
if secgroups[i].GetName() == opts.Name {
|
||||
secgroups[i].region = self
|
||||
return &secgroups[i], nil
|
||||
}
|
||||
}
|
||||
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "vpc:%s, name:%s", opts.VpcId, opts.Name)
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// 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 shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/multicloud/hcs"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type BucketListOptions struct {
|
||||
}
|
||||
shellutils.R(&BucketListOptions{}, "bucket-list", "List buckets", func(cli *hcs.SRegion, args *BucketListOptions) error {
|
||||
buckets, err := cli.GetBuckets()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(buckets, 0, 0, 0, nil)
|
||||
return nil
|
||||
})
|
||||
|
||||
type BucketCreateOptions struct {
|
||||
NAME string
|
||||
Storage string `default:"STANDARD" choices:"STANDARD|WARM|COLD"`
|
||||
Acl string `default:"private" choices:"private|public-read|public-read-write"`
|
||||
}
|
||||
shellutils.R(&BucketCreateOptions{}, "bucket-create", "Create bucket", func(cli *hcs.SRegion, args *BucketCreateOptions) error {
|
||||
return cli.CreateBucket(args.NAME, args.Storage, args.Acl)
|
||||
})
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
// 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 shell
|
||||
|
||||
import (
|
||||
huawei "yunion.io/x/onecloud/pkg/multicloud/hcs"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type DBInstanceListOptions struct {
|
||||
}
|
||||
shellutils.R(&DBInstanceListOptions{}, "dbinstance-list", "List dbinstances", func(cli *huawei.SRegion, args *DBInstanceListOptions) error {
|
||||
dbinstances, err := cli.GetDBInstances()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(dbinstances, 0, 0, 0, nil)
|
||||
return nil
|
||||
})
|
||||
|
||||
type DBInstanceIdOptions struct {
|
||||
ID string `help:"DBInstance ID"`
|
||||
}
|
||||
|
||||
shellutils.R(&DBInstanceIdOptions{}, "dbinstance-open-public-connection", "Open dbinstance public connection", func(cli *huawei.SRegion, args *DBInstanceIdOptions) error {
|
||||
return cli.PublicConnectionAction(args.ID, "openRC")
|
||||
})
|
||||
|
||||
shellutils.R(&DBInstanceIdOptions{}, "dbinstance-close-public-connection", "Close dbinstance public connection", func(cli *huawei.SRegion, args *DBInstanceIdOptions) error {
|
||||
return cli.PublicConnectionAction(args.ID, "closeRC")
|
||||
})
|
||||
|
||||
shellutils.R(&DBInstanceIdOptions{}, "dbinstance-show", "Show dbinstance", func(cli *huawei.SRegion, args *DBInstanceIdOptions) error {
|
||||
dbinstance, err := cli.GetDBInstance(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(dbinstance)
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&DBInstanceIdOptions{}, "dbinstance-parameter-list", "Show dbinstance parameters", func(cli *huawei.SRegion, args *DBInstanceIdOptions) error {
|
||||
parameters, err := cli.GetDBInstanceParameters(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(parameters, 0, 0, 0, nil)
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&DBInstanceIdOptions{}, "dbinstance-backup-list", "Show dbinstance backups", func(cli *huawei.SRegion, args *DBInstanceIdOptions) error {
|
||||
backups, err := cli.GetDBInstanceBackups(args.ID, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(backups, 0, 0, 0, nil)
|
||||
return nil
|
||||
})
|
||||
|
||||
type DBInstanceFlavorListOption struct {
|
||||
ENGINE string `help:"DBInstance engine" choices:"MySQL|SQLServer|PostgreSQL"`
|
||||
VERSION string `help:"DBInstance engine version" choices:"5.6|5.7|9.5|9.6|10.0|2014 SE|2014 EE|2016 SE|2016 EE|2008 R2 EE|2008 R2 WEB|2014 WEB|2016 WEB"`
|
||||
}
|
||||
|
||||
shellutils.R(&DBInstanceFlavorListOption{}, "dbinstance-flavor-list", "Show dbinstance backups", func(cli *huawei.SRegion, args *DBInstanceFlavorListOption) error {
|
||||
flavors, err := cli.GetDBInstanceFlavors(args.ENGINE, args.VERSION)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(flavors, 0, 0, 0, nil)
|
||||
return nil
|
||||
})
|
||||
|
||||
type DBInstanceChangeConfigOptions struct {
|
||||
INSTANCE string
|
||||
InstanceType string
|
||||
DiskSizeGB int
|
||||
}
|
||||
|
||||
shellutils.R(&DBInstanceChangeConfigOptions{}, "dbinstance-change-config", "Change dbinstance config", func(cli *huawei.SRegion, args *DBInstanceChangeConfigOptions) error {
|
||||
return cli.ChangeDBInstanceConfig(args.INSTANCE, args.InstanceType, args.DiskSizeGB)
|
||||
})
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package shell
|
||||
|
||||
import (
|
||||
huawei "yunion.io/x/onecloud/pkg/multicloud/hcso"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type DBInstanceIdOptions struct {
|
||||
ID string `help:"DBInstance ID"`
|
||||
}
|
||||
|
||||
shellutils.R(&DBInstanceIdOptions{}, "dbinstance-account-list", "Show dbinstance accounts", func(cli *huawei.SRegion, args *DBInstanceIdOptions) error {
|
||||
accounts, err := cli.GetDBInstanceAccounts(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(accounts, 0, 0, 0, nil)
|
||||
return nil
|
||||
})
|
||||
|
||||
type DBInstanceAccountDeleteOptions struct {
|
||||
INSTANCE string
|
||||
ACCOUNT string
|
||||
}
|
||||
|
||||
shellutils.R(&DBInstanceAccountDeleteOptions{}, "dbinstance-account-delete", "Delete dbinstance account", func(cli *huawei.SRegion, args *DBInstanceAccountDeleteOptions) error {
|
||||
return cli.DeleteDBInstanceAccount(args.INSTANCE, args.ACCOUNT)
|
||||
})
|
||||
|
||||
type DBInstanceAccountCreateOptions struct {
|
||||
INSTANCE string
|
||||
ACCOUNT string
|
||||
PASSWORD string
|
||||
}
|
||||
|
||||
shellutils.R(&DBInstanceAccountCreateOptions{}, "dbinstance-account-create", "Create dbinstance account", func(cli *huawei.SRegion, args *DBInstanceAccountCreateOptions) error {
|
||||
return cli.CreateDBInstanceAccount(args.INSTANCE, args.ACCOUNT, args.PASSWORD)
|
||||
})
|
||||
|
||||
type DBInstanceAccountRevokePrivilegeOptions struct {
|
||||
INSTANCE string
|
||||
ACCOUNT string
|
||||
DATABASE string
|
||||
}
|
||||
|
||||
shellutils.R(&DBInstanceAccountRevokePrivilegeOptions{}, "dbinstance-account-revoke-provilege", "Revoke dbinstance account privilege", func(cli *huawei.SRegion, args *DBInstanceAccountRevokePrivilegeOptions) error {
|
||||
return cli.RevokeDBInstancePrivilege(args.INSTANCE, args.ACCOUNT, args.DATABASE)
|
||||
})
|
||||
|
||||
type DBInstanceAccountGrantPrivilegeOptions struct {
|
||||
INSTANCE string
|
||||
ACCOUNT string
|
||||
DATABASE string
|
||||
PRIVILEGE string `help:"database privilege" choices:"r|rw"`
|
||||
}
|
||||
|
||||
shellutils.R(&DBInstanceAccountGrantPrivilegeOptions{}, "dbinstance-account-grant-provilege", "Grant dbinstance account privilege", func(cli *huawei.SRegion, args *DBInstanceAccountGrantPrivilegeOptions) error {
|
||||
return cli.GrantDBInstancePrivilege(args.INSTANCE, args.ACCOUNT, args.DATABASE, args.PRIVILEGE)
|
||||
})
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// 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 shell
|
||||
|
||||
import (
|
||||
huawei "yunion.io/x/onecloud/pkg/multicloud/hcso"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type DBInstanceIdOptions struct {
|
||||
INSTANCE string `help:"DBInstance ID"`
|
||||
}
|
||||
|
||||
shellutils.R(&DBInstanceIdOptions{}, "dbinstance-database-list", "Show dbinstance databases", func(cli *huawei.SRegion, args *DBInstanceIdOptions) error {
|
||||
databases, err := cli.GetDBInstanceDatabases(args.INSTANCE)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(databases, 0, 0, 0, nil)
|
||||
return nil
|
||||
})
|
||||
|
||||
type DBInstanceDatabaseDeleteOptions struct {
|
||||
INSTANCE string
|
||||
DATABASE string
|
||||
}
|
||||
|
||||
shellutils.R(&DBInstanceDatabaseDeleteOptions{}, "dbinstance-database-delete", "Delete dbinstance database", func(cli *huawei.SRegion, args *DBInstanceDatabaseDeleteOptions) error {
|
||||
return cli.DeleteDBInstanceDatabase(args.INSTANCE, args.DATABASE)
|
||||
})
|
||||
|
||||
type DBInstanceDatabaseCreateOptions struct {
|
||||
INSTANCE string
|
||||
DATABASE string
|
||||
CHARACTER_SET string
|
||||
}
|
||||
|
||||
shellutils.R(&DBInstanceDatabaseCreateOptions{}, "dbinstance-database-create", "Create dbinstance database", func(cli *huawei.SRegion, args *DBInstanceDatabaseCreateOptions) error {
|
||||
return cli.CreateDBInstanceDatabase(args.INSTANCE, args.DATABASE, args.CHARACTER_SET)
|
||||
})
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// 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 shell
|
||||
|
||||
import (
|
||||
huawei "yunion.io/x/onecloud/pkg/multicloud/hcso"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type DBInstanceDatastoreOptions struct {
|
||||
ENGINE string `help:"DBInstance Engine" choices:"MySQL|PostgreSQL|SQLServer"`
|
||||
}
|
||||
|
||||
shellutils.R(&DBInstanceDatastoreOptions{}, "dbinstance-datastore-list", "List dbinstance datastores", func(cli *huawei.SRegion, args *DBInstanceDatastoreOptions) error {
|
||||
stores, err := cli.GetDBInstanceDatastores(args.ENGINE)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(stores, 0, 0, 0, nil)
|
||||
return nil
|
||||
})
|
||||
|
||||
type DBInstanceSkuListOptions struct {
|
||||
}
|
||||
|
||||
shellutils.R(&DBInstanceSkuListOptions{}, "dbinstance-sku-list", "List dbinstance datastores", func(cli *huawei.SRegion, args *DBInstanceSkuListOptions) error {
|
||||
skus, err := cli.GetDBInstanceSkus()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(skus, 0, 0, 0, nil)
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// 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 shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/multicloud/hcs"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type DiskListOptions struct {
|
||||
Zone string `help:"Zone ID"`
|
||||
}
|
||||
shellutils.R(&DiskListOptions{}, "disk-list", "List disks", func(cli *hcs.SRegion, args *DiskListOptions) error {
|
||||
disks, e := cli.GetDisks(args.Zone)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
printList(disks, 0, 0, 0, nil)
|
||||
return nil
|
||||
})
|
||||
|
||||
type DiskCreateOptions struct {
|
||||
ZONE string
|
||||
STORAGE_TYPE string
|
||||
NAME string
|
||||
SIZE_GB int
|
||||
SnapshotId string
|
||||
Desc string
|
||||
ProjectId string
|
||||
}
|
||||
|
||||
shellutils.R(&DiskCreateOptions{}, "disk-create", "Create disk", func(cli *hcs.SRegion, args *DiskCreateOptions) error {
|
||||
disk, e := cli.CreateDisk(args.ZONE, args.STORAGE_TYPE, args.NAME, args.SIZE_GB, args.SnapshotId, args.Desc, args.ProjectId)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
printObject(disk)
|
||||
return nil
|
||||
})
|
||||
|
||||
type DiskIdOptions struct {
|
||||
ID string `help:"Disk ID"`
|
||||
}
|
||||
|
||||
shellutils.R(&DiskIdOptions{}, "disk-delete", "Delete disks", func(cli *hcs.SRegion, args *DiskIdOptions) error {
|
||||
e := cli.DeleteDisk(args.ID)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&DiskIdOptions{}, "disk-show", "Show disks", func(cli *hcs.SRegion, args *DiskIdOptions) error {
|
||||
disk, e := cli.GetDisk(args.ID)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
printObject(disk)
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&DiskListOptions{}, "disk-types", "List disk types", func(cli *hcs.SRegion, args *DiskListOptions) error {
|
||||
ret, e := cli.GetDiskTypes()
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
printList(ret, 0, 0, 0, nil)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package shell // import "yunion.io/x/onecloud/pkg/multicloud/hcs/shell"
|
||||
@@ -0,0 +1,88 @@
|
||||
// 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 shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/multicloud/hcs"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type EipListOptions struct {
|
||||
PortId string
|
||||
Address []string
|
||||
}
|
||||
shellutils.R(&EipListOptions{}, "eip-list", "List eips", func(cli *hcs.SRegion, args *EipListOptions) error {
|
||||
eips, err := cli.GetEips(args.PortId, args.Address)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
printList(eips, 0, 0, 0, nil)
|
||||
return nil
|
||||
})
|
||||
|
||||
type EipTypeListOptions struct {
|
||||
}
|
||||
|
||||
shellutils.R(&EipTypeListOptions{}, "eip-type-list", "List eip types", func(cli *hcs.SRegion, args *EipTypeListOptions) error {
|
||||
types, err := cli.GetEipTypes()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
printList(types, 0, 0, 0, nil)
|
||||
return nil
|
||||
})
|
||||
|
||||
type EipAllocateOptions struct {
|
||||
NAME string `help:"eip name"`
|
||||
BW int `help:"Bandwidth limit in Mbps"`
|
||||
BGP string `help:"bgp type"`
|
||||
ChargeType string `help:"eip charge type" default:"traffic" choices:"traffic|bandwidth"`
|
||||
ProjectId string
|
||||
}
|
||||
shellutils.R(&EipAllocateOptions{}, "eip-create", "Allocate an EIP", func(cli *hcs.SRegion, args *EipAllocateOptions) error {
|
||||
eip, err := cli.AllocateEIP(args.NAME, args.BW, hcs.TInternetChargeType(args.ChargeType), args.BGP, args.ProjectId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(eip)
|
||||
return nil
|
||||
})
|
||||
|
||||
type EipReleaseOptions struct {
|
||||
ID string `help:"EIP allocation ID"`
|
||||
}
|
||||
shellutils.R(&EipReleaseOptions{}, "eip-delete", "Release an EIP", func(cli *hcs.SRegion, args *EipReleaseOptions) error {
|
||||
err := cli.DeallocateEIP(args.ID)
|
||||
return err
|
||||
})
|
||||
|
||||
type EipAssociateOptions struct {
|
||||
ID string `help:"EIP allocation ID"`
|
||||
INSTANCE string `help:"Instance ID"`
|
||||
}
|
||||
shellutils.R(&EipAssociateOptions{}, "eip-associate", "Associate an EIP", func(cli *hcs.SRegion, args *EipAssociateOptions) error {
|
||||
return cli.AssociateEip(args.ID, args.INSTANCE)
|
||||
})
|
||||
|
||||
type EipDissociateOptions struct {
|
||||
ID string `help:"EIP allocation ID"`
|
||||
}
|
||||
|
||||
shellutils.R(&EipDissociateOptions{}, "eip-dissociate", "Dissociate an EIP", func(cli *hcs.SRegion, args *EipDissociateOptions) error {
|
||||
return cli.DissociateEip(args.ID)
|
||||
})
|
||||
|
||||
}
|
||||
@@ -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 shell
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/multicloud/hcs"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type ImageListOptions struct {
|
||||
Status string `help:"image status type" choices:"queued|saving|deleted|killed|active"`
|
||||
Owner string `help:"Owner type" choices:"gold|private|shared"`
|
||||
// Id []string `help:"Image ID"`
|
||||
Name string `help:"image name"`
|
||||
// Marker string `help:"marker"`
|
||||
// Limit int `help:"page Limit"`
|
||||
Env string `help:"virtualization env, e.g. FusionCompute, Ironic" choices:"FusionCompute|Ironic"`
|
||||
}
|
||||
shellutils.R(&ImageListOptions{}, "image-list", "List images", func(cli *hcs.SRegion, args *ImageListOptions) error {
|
||||
images, e := cli.GetImages(args.Status, hcs.TImageOwnerType(args.Owner), args.Name, args.Env)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
printList(images, 0, 0, 0, []string{})
|
||||
return nil
|
||||
})
|
||||
|
||||
type ImageDeleteOptions struct {
|
||||
ID string `help:"ID or Name to delete"`
|
||||
}
|
||||
shellutils.R(&ImageDeleteOptions{}, "image-delete", "Delete image", func(cli *hcs.SRegion, args *ImageDeleteOptions) error {
|
||||
return cli.DeleteImage(args.ID)
|
||||
})
|
||||
|
||||
shellutils.R(&ImageDeleteOptions{}, "image-show", "Show image", func(cli *hcs.SRegion, args *ImageDeleteOptions) error {
|
||||
img, err := cli.GetImage(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(img)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
// 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 shell
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud/hcs"
|
||||
"yunion.io/x/onecloud/pkg/util/shellutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type InstanceListOptions struct {
|
||||
Ip string
|
||||
}
|
||||
shellutils.R(&InstanceListOptions{}, "instance-list", "List intances", func(cli *hcs.SRegion, args *InstanceListOptions) error {
|
||||
instances, e := cli.GetInstances(args.Ip)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
printList(instances, 0, 0, 0, nil)
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceDiskOperationOptions struct {
|
||||
ID string `help:"instance ID"`
|
||||
DISK string `help:"disk ID"`
|
||||
}
|
||||
|
||||
type InstanceDiskAttachOptions struct {
|
||||
ID string `help:"instance ID"`
|
||||
DISK string `help:"disk ID"`
|
||||
DEVICE string `help:"disk device name. eg. /dev/sdb"`
|
||||
}
|
||||
|
||||
shellutils.R(&InstanceDiskAttachOptions{}, "instance-attach-disk", "Attach a disk to instance", func(cli *hcs.SRegion, args *InstanceDiskAttachOptions) error {
|
||||
err := cli.AttachDisk(args.ID, args.DISK, args.DEVICE)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&InstanceDiskOperationOptions{}, "instance-detach-disk", "Detach a disk to instance", func(cli *hcs.SRegion, args *InstanceDiskOperationOptions) error {
|
||||
err := cli.DetachDisk(args.ID, args.DISK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceOperationOptions struct {
|
||||
ID string `help:"instance ID"`
|
||||
}
|
||||
shellutils.R(&InstanceOperationOptions{}, "instance-start", "Start a instance", func(cli *hcs.SRegion, args *InstanceOperationOptions) error {
|
||||
err := cli.StartVM(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
shellutils.R(&InstanceOperationOptions{}, "instance-interface-list", "List instance interface", func(cli *hcs.SRegion, args *InstanceOperationOptions) error {
|
||||
ret, err := cli.GetInstanceInterfaces(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(ret, 0, 0, 0, nil)
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceStopOptions struct {
|
||||
ID string `help:"instance ID"`
|
||||
Force bool `help:"Force stop instance"`
|
||||
}
|
||||
shellutils.R(&InstanceStopOptions{}, "instance-stop", "Stop a instance", func(cli *hcs.SRegion, args *InstanceStopOptions) error {
|
||||
err := cli.StopVM(args.ID, args.Force)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
shellutils.R(&InstanceOperationOptions{}, "instance-delete", "Delete a instance", func(cli *hcs.SRegion, args *InstanceOperationOptions) error {
|
||||
err := cli.DeleteVM(args.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
/*
|
||||
server-change-config 更改系统配置
|
||||
server-reset
|
||||
*/
|
||||
type InstanceDeployOptions struct {
|
||||
ID string `help:"instance ID"`
|
||||
Name string `help:"new instance name"`
|
||||
Hostname string `help:"new hostname"`
|
||||
Keypair string `help:"Keypair Name"`
|
||||
DeleteKeypair bool `help:"Remove SSH keypair"`
|
||||
Password string `help:"new password"`
|
||||
// ResetPassword bool `help:"Force reset password"`
|
||||
Description string `help:"new instances description"`
|
||||
}
|
||||
|
||||
shellutils.R(&InstanceDeployOptions{}, "instance-deploy", "Deploy keypair/password to a stopped virtual server", func(cli *hcs.SRegion, args *InstanceDeployOptions) error {
|
||||
err := cli.DeployVM(args.ID, args.Name, args.Password, args.Keypair, args.DeleteKeypair, args.Description)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceRebuildRootOptions struct {
|
||||
ID string `help:"instance ID"`
|
||||
UserId string `help:"instance user ID"`
|
||||
Image string `help:"Image ID"`
|
||||
Password string `help:"admin password"`
|
||||
PublicKeyName string `help:"public key name"`
|
||||
UserData string `help:"cloud-init user data"`
|
||||
}
|
||||
|
||||
shellutils.R(&InstanceRebuildRootOptions{}, "instance-rebuild-root", "Reinstall virtual server system image", func(cli *hcs.SRegion, args *InstanceRebuildRootOptions) error {
|
||||
ctx := context.Background()
|
||||
return cli.ChangeRoot(ctx, args.UserId, args.ID, args.Image, args.Password, args.PublicKeyName, args.UserData)
|
||||
})
|
||||
|
||||
type InstanceChangeConfigOptions struct {
|
||||
ID string `help:"instance ID"`
|
||||
InstanceType string `help:"instance type"`
|
||||
}
|
||||
|
||||
shellutils.R(&InstanceChangeConfigOptions{}, "instance-change-config", "Deploy keypair/password to a stopped virtual server", func(cli *hcs.SRegion, args *InstanceChangeConfigOptions) error {
|
||||
err := cli.ChangeVMConfig(args.ID, args.InstanceType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceSaveImageOptions struct {
|
||||
ID string `help:"Instance ID"`
|
||||
IMAGE_NAME string `help:"Image name"`
|
||||
Notes string `hlep:"Image desc"`
|
||||
}
|
||||
shellutils.R(&InstanceSaveImageOptions{}, "instance-save-image", "Save instance to image", func(cli *hcs.SRegion, args *InstanceSaveImageOptions) error {
|
||||
opts := cloudprovider.SaveImageOptions{
|
||||
Name: args.IMAGE_NAME,
|
||||
Notes: args.Notes,
|
||||
}
|
||||
image, err := cli.SaveImage(args.ID, &opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(image)
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceSetTagsOptions struct {
|
||||
ID string `help:"Instance ID"`
|
||||
Tags []string
|
||||
}
|
||||
shellutils.R(&InstanceSetTagsOptions{}, "instance-set-tags", "get intance metadata", func(cli *hcs.SRegion, args *InstanceSetTagsOptions) error {
|
||||
tags := map[string]string{}
|
||||
for i := range args.Tags {
|
||||
splited := strings.Split(args.Tags[i], "=")
|
||||
if len(splited) == 2 {
|
||||
tags[splited[0]] = splited[1]
|
||||
}
|
||||
}
|
||||
err := cli.CreateServerTags(args.ID, tags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceDelTagsOptions struct {
|
||||
ID string `help:"Instance ID"`
|
||||
Tags []string
|
||||
}
|
||||
shellutils.R(&InstanceDelTagsOptions{}, "instance-del-tags", "del intance metadata", func(cli *hcs.SRegion, args *InstanceDelTagsOptions) error {
|
||||
|
||||
err := cli.DeleteServerTags(args.ID, args.Tags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceUpdateNameOptions struct {
|
||||
ID string `help:"Instance ID"`
|
||||
Name string
|
||||
}
|
||||
shellutils.R(&InstanceUpdateNameOptions{}, "instance-set-name", "set intance name", func(cli *hcs.SRegion, args *InstanceUpdateNameOptions) error {
|
||||
|
||||
err := cli.UpdateVM(args.ID, args.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
type InstanceCreateOptions struct {
|
||||
NAME string
|
||||
IMAGE_ID string
|
||||
INSTANCE_TYPE string
|
||||
NETWORK_ID string
|
||||
IpAddr string
|
||||
Secgroup string `default:"default"`
|
||||
VPC_ID string
|
||||
ZONE_ID string
|
||||
Desc string
|
||||
Keypair string
|
||||
Password string
|
||||
UserData string
|
||||
ProjectId string
|
||||
DISKS []string
|
||||
}
|
||||
shellutils.R(&InstanceCreateOptions{}, "instance-create", "create instance", func(cli *hcs.SRegion, args *InstanceCreateOptions) error {
|
||||
sysDisk := cloudprovider.SDiskInfo{}
|
||||
dataDisks := []cloudprovider.SDiskInfo{}
|
||||
for i, disk := range args.DISKS {
|
||||
info := strings.Split(args.DISKS[i], ":")
|
||||
if len(info) != 2 {
|
||||
return fmt.Errorf("invalid disk %s", disk)
|
||||
}
|
||||
if i == 0 {
|
||||
sysDisk.StorageType = info[0]
|
||||
sysDisk.SizeGB, _ = strconv.Atoi(info[1])
|
||||
} else {
|
||||
dataDisk := cloudprovider.SDiskInfo{}
|
||||
dataDisk.StorageType = info[0]
|
||||
dataDisk.SizeGB, _ = strconv.Atoi(info[1])
|
||||
dataDisks = append(dataDisks, dataDisk)
|
||||
}
|
||||
}
|
||||
vm, err := cli.CreateInstance(args.NAME, args.IMAGE_ID, args.INSTANCE_TYPE, args.NETWORK_ID, args.Secgroup, args.VPC_ID, args.ZONE_ID, args.Desc, sysDisk, dataDisks, args.IpAddr, args.Keypair, args.Password, args.UserData, nil, args.ProjectId, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(vm)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user