support hcs

This commit is contained in:
ioito
2022-10-25 14:48:20 +08:00
parent 630f31912b
commit 11cf497268
165 changed files with 28934 additions and 1121 deletions
+3
View File
@@ -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{})
+1
View File
@@ -47,6 +47,7 @@ func init() {
"apsara",
"cloudpods",
"hcso",
"hcs",
"nutanix",
"s3",
"ceph",
+157
View File
@@ -0,0 +1,157 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package 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)
}
}
+3 -1
View File
@@ -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
+21 -2
View File
@@ -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=
+7 -1
View File
@@ -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,
},
+24 -5
View File
@@ -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"`
}
+5
View File
@@ -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,
+2
View File
@@ -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,
+1
View File
@@ -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,
+3
View File
@@ -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 (
+35
View File
@@ -0,0 +1,35 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package 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
}
+1 -2
View File
@@ -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
}
+29
View File
@@ -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
+207
View File
@@ -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
}
+67
View File
@@ -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
}
+1 -1
View File
@@ -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
}
+30
View File
@@ -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
+8 -4
View File
@@ -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)
+104 -366
View File
@@ -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
+93
View File
@@ -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)
}
+3 -3
View File
@@ -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 {
+4 -4
View File
@@ -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)
})
+4 -2
View File
@@ -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)
+1 -1
View File
@@ -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)
+10
View File
@@ -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
}
+1 -1
View File
@@ -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)
}
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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)
+4 -172
View File
@@ -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) {
+4
View File
@@ -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")
}
+49
View File
@@ -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
}
+10 -219
View File
@@ -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) {
+2 -6
View File
@@ -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 {
+189 -11
View File
@@ -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(&params.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) {
+5 -235
View File
@@ -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")
+22 -23
View File
@@ -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
+29
View File
@@ -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
}
+14 -11
View File
@@ -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)")
}
+803
View File
@@ -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
}
+706
View File
@@ -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, &parameters[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
}
+125
View File
@@ -0,0 +1,125 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package 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")
}
+238
View File
@@ -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)
}
+72
View File
@@ -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
}
+273
View File
@@ -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
}
+438
View File
@@ -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)
}
+1
View File
@@ -0,0 +1 @@
package hcs // import "yunion.io/x/onecloud/pkg/multicloud/hcs"
+434
View File
@@ -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
}
+771
View File
@@ -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
}
+254
View File
@@ -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)
}
+466
View File
@@ -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
+86
View File
@@ -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)
}
+223
View File
@@ -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
}
+105
View File
@@ -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)
}
+102
View File
@@ -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)
}
+601
View File
@@ -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
}
+155
View File
@@ -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
}
+141
View File
@@ -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)
}
+182
View File
@@ -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"`
}
+643
View File
@@ -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)
}
+494
View File
@@ -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)
}
}
+97
View File
@@ -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
}
+149
View File
@@ -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)
}
+265
View File
@@ -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)
}
+142
View File
@@ -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)
}
+167
View File
@@ -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
}
+104
View File
@@ -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)
}
+161
View File
@@ -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)
}
+98
View File
@@ -0,0 +1,98 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package 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)
}
+1
View File
@@ -0,0 +1 @@
package provider // import "yunion.io/x/onecloud/pkg/multicloud/hcs/provider"
+217
View File
@@ -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()
}
+608
View File
@@ -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] = &parameters[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
}
+83
View File
@@ -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
}
+133
View File
@@ -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
}
+250
View File
@@ -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
}
+78
View File
@@ -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
}
+332
View File
@@ -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
}
+214
View File
@@ -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{}
}
+340
View File
@@ -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)
}
+43
View File
@@ -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)
})
}
+97
View File
@@ -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
})
}
+83
View File
@@ -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
})
}
+1
View File
@@ -0,0 +1 @@
package shell // import "yunion.io/x/onecloud/pkg/multicloud/hcs/shell"
+88
View File
@@ -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)
})
}
+56
View File
@@ -0,0 +1,56 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package 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
})
}
+261
View File
@@ -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