Merge branch 'master' into feature/tb-support-ctyun-crm-account

This commit is contained in:
tb365
2021-11-12 15:11:48 +08:00
committed by GitHub
77 changed files with 1425 additions and 3042 deletions
+21 -1
View File
@@ -16,10 +16,15 @@ 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/aliyun"
_ "yunion.io/x/onecloud/pkg/multicloud/aliyun/shell"
"yunion.io/x/onecloud/pkg/util/shellutils"
@@ -85,12 +90,27 @@ func newClient(options *BaseOptions) (*aliyun.SRegion, error) {
return nil, fmt.Errorf("Missing secret")
}
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 := aliyun.NewAliyunClient(
aliyun.NewAliyunClientConfig(
options.CloudEnv,
options.AccessKey,
options.Secret,
).Debug(options.Debug),
).Debug(options.Debug).
CloudproviderConfig(
cloudprovider.ProviderConfig{
ProxyFunc: proxyFunc,
},
),
)
if err != nil {
return nil, err
+20 -1
View File
@@ -16,8 +16,12 @@ 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"
@@ -87,13 +91,28 @@ func newClient(options *BaseOptions) (*apsara.SRegion, error) {
return nil, fmt.Errorf("Missing secret")
}
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 := apsara.NewApsaraClient(
apsara.NewApsaraClientConfig(
options.AccessKey,
options.Secret,
options.Endpoint,
options.SApsaraEndpoints,
).Debug(options.Debug),
).Debug(options.Debug).
CloudproviderConfig(
cloudprovider.ProviderConfig{
ProxyFunc: proxyFunc,
},
),
)
if err != nil {
return nil, err
+21 -1
View File
@@ -16,11 +16,16 @@ package main
import (
"fmt"
"net/http"
"net/url"
"os"
"golang.org/x/net/http/httpproxy"
"yunion.io/x/pkg/errors"
"yunion.io/x/structarg"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/multicloud/aws"
_ "yunion.io/x/onecloud/pkg/multicloud/aws/shell"
"yunion.io/x/onecloud/pkg/util/shellutils"
@@ -87,13 +92,28 @@ func newClient(options *BaseOptions) (*aws.SRegion, error) {
return nil, fmt.Errorf("Missing secret")
}
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 := aws.NewAwsClient(
aws.NewAwsClientConfig(
options.AccessUrl,
options.AccessKey,
options.Secret,
options.AccountId,
).Debug(options.Debug),
).Debug(options.Debug).
CloudproviderConfig(
cloudprovider.ProviderConfig{
ProxyFunc: proxyFunc,
},
),
)
if err != nil {
return nil, err
+21 -1
View File
@@ -16,10 +16,15 @@ 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/azure"
_ "yunion.io/x/onecloud/pkg/multicloud/azure/shell"
"yunion.io/x/onecloud/pkg/util/printutils"
@@ -100,6 +105,16 @@ func newClient(options *BaseOptions) (*azure.SRegion, error) {
return nil, fmt.Errorf("Missing Cloud Environment")
}
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 := azure.NewAzureClient(
azure.NewAzureClientConfig(
options.CloudEnv,
@@ -108,7 +123,12 @@ func newClient(options *BaseOptions) (*azure.SRegion, error) {
options.ApplicationKey,
).
SubscriptionId(options.SubscriptionID).
Debug(options.Debug),
Debug(options.Debug).
CloudproviderConfig(
cloudprovider.ProviderConfig{
ProxyFunc: proxyFunc,
},
),
)
if err != nil {
+21 -1
View File
@@ -16,10 +16,15 @@ 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/cloudpods"
_ "yunion.io/x/onecloud/pkg/multicloud/cloudpods/shell"
"yunion.io/x/onecloud/pkg/util/shellutils"
@@ -88,12 +93,27 @@ func newClient(options *BaseOptions) (*cloudpods.SRegion, error) {
return nil, fmt.Errorf("Missing access secret")
}
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 := cloudpods.NewCloudpodsClient(
cloudpods.NewCloudpodsClientConfig(
options.AuthURL,
options.AccessKey,
options.AccessSecret,
).Debug(options.Debug),
).Debug(options.Debug).
CloudproviderConfig(
cloudprovider.ProviderConfig{
ProxyFunc: proxyFunc,
},
),
)
if err != nil {
return nil, err
+20 -1
View File
@@ -16,8 +16,12 @@ 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"
@@ -87,10 +91,25 @@ func newClient(options *BaseOptions) (*ctyun.SRegion, error) {
return nil, fmt.Errorf("Missing secret")
}
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 := ctyun.NewSCtyunClient(
ctyun.NewSCtyunClientConfig(
options.AccessKey, options.Secret, &options.SCtyunExtraOptions,
).Debug(options.Debug),
).Debug(options.Debug).
CloudproviderConfig(
cloudprovider.ProviderConfig{
ProxyFunc: proxyFunc,
},
),
)
if err != nil {
return nil, err
+21 -1
View File
@@ -16,10 +16,15 @@ 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/ecloud"
_ "yunion.io/x/onecloud/pkg/multicloud/ecloud/shell"
"yunion.io/x/onecloud/pkg/util/shellutils"
@@ -84,10 +89,25 @@ func newClient(options *Options) (*ecloud.SRegion, error) {
return nil, fmt.Errorf("Missing access secret")
}
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 := ecloud.NewEcloudClient(
ecloud.NewEcloudClientConfig(
ecloud.NewRamRoleSigner(options.AccessKey, options.AccessSecret),
).SetDebug(options.Debug),
).SetDebug(options.Debug).
SetCloudproviderConfig(
cloudprovider.ProviderConfig{
ProxyFunc: proxyFunc,
},
),
)
if err != nil {
return nil, err
+21 -1
View File
@@ -16,10 +16,15 @@ 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/esxi"
_ "yunion.io/x/onecloud/pkg/multicloud/esxi/shell"
"yunion.io/x/onecloud/pkg/util/shellutils"
@@ -88,13 +93,28 @@ func newClient(options *BaseOptions) (*esxi.SESXiClient, error) {
return nil, fmt.Errorf("Missing password")
}
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)
}
return esxi.NewESXiClient2(
esxi.NewESXiClientConfig(
options.Host,
options.Port,
options.Account,
options.Password,
),
).
CloudproviderConfig(
cloudprovider.ProviderConfig{
ProxyFunc: proxyFunc,
},
),
)
}
+21 -1
View File
@@ -16,13 +16,18 @@ package main
import (
"fmt"
"net/http"
"net/url"
"os"
"golang.org/x/net/http/httpproxy"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/structarg"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/multicloud/google"
_ "yunion.io/x/onecloud/pkg/multicloud/google/shell"
"yunion.io/x/onecloud/pkg/util/fileutils2"
@@ -112,13 +117,28 @@ func newClient(options *BaseOptions) (*google.SRegion, error) {
return nil, fmt.Errorf("Missing ProjectID")
}
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 := google.NewGoogleClient(
google.NewGoogleClientConfig(
options.ProjectID,
options.ClientEmail,
options.PrivateKeyID,
options.PrivateKey,
).Debug(options.Debug),
).Debug(options.Debug).
CloudproviderConfig(
cloudprovider.ProviderConfig{
ProxyFunc: proxyFunc,
},
),
)
if err != nil {
return nil, err
+20 -1
View File
@@ -16,8 +16,12 @@ 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"
@@ -87,13 +91,28 @@ func newClient(options *BaseOptions) (*huawei.SRegion, error) {
return nil, fmt.Errorf("Missing secret")
}
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 := huawei.NewHuaweiClient(
huawei.NewHuaweiClientConfig(
options.AccessKey,
options.Secret,
options.ProjectId,
&options.SHCSOEndpoints,
).Debug(options.Debug),
).Debug(options.Debug).
CloudproviderConfig(
cloudprovider.ProviderConfig{
ProxyFunc: proxyFunc,
},
),
)
if err != nil {
return nil, err
+21 -1
View File
@@ -16,10 +16,15 @@ 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/huawei"
_ "yunion.io/x/onecloud/pkg/multicloud/huawei/shell"
"yunion.io/x/onecloud/pkg/util/shellutils"
@@ -86,13 +91,28 @@ func newClient(options *BaseOptions) (*huawei.SRegion, error) {
return nil, fmt.Errorf("Missing secret")
}
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 := huawei.NewHuaweiClient(
huawei.NewHuaweiClientConfig(
options.CloudEnv,
options.AccessKey,
options.Secret,
options.ProjectId,
).Debug(options.Debug),
).Debug(options.Debug).
CloudproviderConfig(
cloudprovider.ProviderConfig{
ProxyFunc: proxyFunc,
},
),
)
if err != nil {
return nil, err
+20 -1
View File
@@ -16,13 +16,17 @@ package main
import (
"fmt"
"net/http"
"net/url"
"os"
"github.com/sirupsen/logrus"
"golang.org/x/net/http/httpproxy"
"yunion.io/x/log"
"yunion.io/x/structarg"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/multicloud/jdcloud"
_ "yunion.io/x/onecloud/pkg/multicloud/jdcloud/shell"
"yunion.io/x/onecloud/pkg/util/shellutils"
@@ -89,7 +93,22 @@ func newClient(options *Options) (*jdcloud.SRegion, error) {
if regionId == "" {
regionId = jdcloud.JDCLOUD_DEFAULT_REGION
}
region := jdcloud.NewRegion(regionId, options.AccessKey, options.AccessSecret, nil, options.Debug)
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)
}
cfcg := &cloudprovider.ProviderConfig{
ProxyFunc: proxyFunc,
}
region := jdcloud.NewRegion(regionId, options.AccessKey, options.AccessSecret, cfcg, options.Debug)
if region == nil {
return nil, fmt.Errorf("no such region %s", regionId)
}
+21 -1
View File
@@ -16,10 +16,15 @@ 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/openstack"
_ "yunion.io/x/onecloud/pkg/multicloud/openstack/shell"
"yunion.io/x/onecloud/pkg/util/shellutils"
@@ -93,6 +98,16 @@ func newClient(options *BaseOptions) (*openstack.SRegion, error) {
return nil, fmt.Errorf("Missing Password")
}
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 := openstack.NewOpenStackClient(
openstack.NewOpenstackClientConfig(
options.AuthURL,
@@ -103,7 +118,12 @@ func newClient(options *BaseOptions) (*openstack.SRegion, error) {
).
EndpointType(options.EndpointType).
DomainName(options.DomainName).
Debug(options.Debug),
Debug(options.Debug).
CloudproviderConfig(
cloudprovider.ProviderConfig{
ProxyFunc: proxyFunc,
},
),
)
if err != nil {
return nil, err
+22 -1
View File
@@ -16,10 +16,15 @@ 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/qcloud"
_ "yunion.io/x/onecloud/pkg/multicloud/qcloud/shell"
"yunion.io/x/onecloud/pkg/util/shellutils"
@@ -85,11 +90,27 @@ func newClient(options *BaseOptions) (*qcloud.SRegion, error) {
return nil, fmt.Errorf("Missing SecretID")
}
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)
}
if cli, err := qcloud.NewQcloudClient(
qcloud.NewQcloudClientConfig(
options.SecretID,
options.SecretKey,
).AppId(options.AppID).Debug(options.Debug),
).AppId(options.AppID).
Debug(options.Debug).
CloudproviderConfig(
cloudprovider.ProviderConfig{
ProxyFunc: proxyFunc,
},
),
); err != nil {
return nil, err
} else if region := cli.GetRegion(options.RegionId); region == nil {
-130
View File
@@ -1,130 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"fmt"
"os"
"yunion.io/x/pkg/errors"
"yunion.io/x/structarg"
_ "yunion.io/x/onecloud/cmd/rbdcli/shell"
"yunion.io/x/onecloud/pkg/util/rbdutils"
"yunion.io/x/onecloud/pkg/util/shellutils"
)
type BaseOptions struct {
Help bool `help:"Show help"`
Debug bool `help:"debug mode"`
MonHost string `help:"Ceph Mon Host" default:"$MON_HOST"`
Pool string `help:"Ceph pool" default:"$CEPH_POOL" metavar:"CEPH_POOL"`
Key string `help:"Secret" default:"$CEPH_KEY" metavar:"CEPH_KEY"`
SUBCOMMAND string `help:"rbdcli subcommand" subcommand:"true"`
}
func getSubcommandParser() (*structarg.ArgumentParser, error) {
parse, e := structarg.NewArgumentParser(&BaseOptions{},
"rbdcli",
"Command-line interface to rbd API.",
`See "rbdcli help COMMAND" 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.")
}
type HelpOptions struct {
SUBCOMMAND string `help:"sub-command name"`
}
shellutils.R(&HelpOptions{}, "help", "Show help of a subcommand", func(args *HelpOptions) error {
helpstr, e := subcmd.SubHelpString(args.SUBCOMMAND)
if e != nil {
return e
} else {
fmt.Print(helpstr)
return nil
}
})
for _, v := range shellutils.CommandTable {
_, e := subcmd.AddSubParser(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 newPool(opts *BaseOptions) (*rbdutils.SPool, error) {
cli, err := rbdutils.NewCluster(opts.MonHost, opts.Key)
if err != nil {
return nil, err
}
pool, err := cli.GetPool(opts.Pool)
if err != nil {
return nil, errors.Wrapf(err, "GetPool(%s)", opts.Pool)
}
return pool, nil
}
func main() {
parser, e := getSubcommandParser()
if e != nil {
showErrorAndExit(e)
}
e = parser.ParseArgs(os.Args[1:], false)
options := parser.Options().(*BaseOptions)
if options.Help {
fmt.Print(parser.HelpString())
return
}
subcmd := parser.GetSubcommand()
subparser := subcmd.GetSubParser()
if e != nil {
if subparser != nil {
fmt.Print(subparser.Usage())
} else {
fmt.Print(parser.Usage())
}
showErrorAndExit(e)
return
}
suboptions := subparser.Options()
if options.SUBCOMMAND == "help" {
e = subcmd.Invoke(suboptions)
} else {
var pool *rbdutils.SPool
pool, e = newPool(options)
if e != nil {
showErrorAndExit(e)
}
e = subcmd.Invoke(pool, suboptions)
}
if e != nil {
showErrorAndExit(e)
}
}
-51
View File
@@ -1,51 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package shell
import (
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/util/rbdutils"
"yunion.io/x/onecloud/pkg/util/shellutils"
)
func init() {
type ClusterStatOption struct {
}
shellutils.R(&ClusterStatOption{}, "cluster-stat", "Show Cluster State", func(cli *rbdutils.SPool, args *ClusterStatOption) error {
cluster := cli.GetCluster()
stat, err := cluster.GetClusterStats()
if err != nil {
return errors.Wrapf(err, "GetClusterStats")
}
printObject(stat)
return nil
})
type CmdOptions struct {
CMD string
}
shellutils.R(&CmdOptions{}, "run", "Run Cluster command", func(cli *rbdutils.SPool, args *CmdOptions) error {
ret, err := cli.GetCluster().MonCommand([]byte(args.CMD))
if err != nil {
return errors.Wrapf(err, "MonCommand")
}
printObject(ret)
return nil
})
}
-58
View File
@@ -1,58 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package shell
import (
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/util/rbdutils"
"yunion.io/x/onecloud/pkg/util/shellutils"
)
func init() {
type PoolListOptions struct {
}
shellutils.R(&PoolListOptions{}, "pool-list", "List pools", func(cli *rbdutils.SPool, args *PoolListOptions) error {
cluster := cli.GetCluster()
pools, err := cluster.ListPools()
if err != nil {
return errors.Wrapf(err, "ListPools")
}
printObject(jsonutils.Marshal(map[string][]string{"pools": pools}))
return nil
})
type PoolDeleteOptions struct {
POOL string
}
shellutils.R(&PoolDeleteOptions{}, "pool-delete", "Delete Cluster pool", func(cli *rbdutils.SPool, args *PoolDeleteOptions) error {
return cli.GetCluster().DeletePool(args.POOL)
})
type ImageListOptions struct {
}
shellutils.R(&ImageListOptions{}, "image-list", "List Pool images", func(cli *rbdutils.SPool, args *ImageListOptions) error {
images, err := cli.ListImages()
if err != nil {
return err
}
printObject(jsonutils.Marshal(map[string][]string{"images": images}))
return nil
})
}
-25
View File
@@ -1,25 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package shell
import "yunion.io/x/onecloud/pkg/util/printutils"
func printList(data interface{}, total, offset, limit int, columns []string) {
printutils.PrintInterfaceList(data, total, offset, limit, columns)
}
func printObject(obj interface{}) {
printutils.PrintInterfaceObject(obj)
}
+21 -1
View File
@@ -16,10 +16,15 @@ 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/ucloud"
_ "yunion.io/x/onecloud/pkg/multicloud/ucloud/shell"
"yunion.io/x/onecloud/pkg/util/shellutils"
@@ -86,11 +91,26 @@ func newClient(options *BaseOptions) (*ucloud.SRegion, error) {
return nil, fmt.Errorf("Missing secret")
}
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 := ucloud.NewUcloudClient(
ucloud.NewUcloudClientConfig(
options.AccessKey,
options.Secret,
).ProjectId(options.ProjectId).Debug(options.Debug),
).ProjectId(options.ProjectId).Debug(options.Debug).
CloudproviderConfig(
cloudprovider.ProviderConfig{
ProxyFunc: proxyFunc,
},
),
)
if err != nil {
return nil, err
+21 -1
View File
@@ -16,10 +16,15 @@ 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/zstack"
_ "yunion.io/x/onecloud/pkg/multicloud/zstack/shell"
"yunion.io/x/onecloud/pkg/util/shellutils"
@@ -89,12 +94,27 @@ func newClient(options *BaseOptions) (*zstack.SRegion, error) {
return nil, fmt.Errorf("Missing Password")
}
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 := zstack.NewZStackClient(
zstack.NewZstackClientConfig(
options.AuthURL,
options.Username,
options.Password,
).Debug(options.Debug),
).Debug(options.Debug).
CloudproviderConfig(
cloudprovider.ProviderConfig{
ProxyFunc: proxyFunc,
},
),
)
if err != nil {
return nil, err
+2 -5
View File
@@ -36,7 +36,6 @@ require (
github.com/bitly/go-simplejson v0.5.0
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
github.com/c-bata/go-prompt v0.2.1
github.com/ceph/go-ceph v0.0.0-20181217221554-e32f9f0f2e94
github.com/cheggaaa/pb/v3 v3.0.8
github.com/coredns/coredns v1.3.0
github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a // indirect
@@ -60,7 +59,7 @@ require (
github.com/go-ole/go-ole v1.2.2 // indirect
github.com/go-sql-driver/mysql v1.5.0
github.com/go-yaml/yaml v2.1.0+incompatible
github.com/gofrs/uuid v3.2.0+incompatible // indirect
github.com/gofrs/uuid v4.1.0+incompatible // indirect
github.com/golang-plus/errors v1.0.0
github.com/golang-plus/testing v1.0.0 // indirect
github.com/golang-plus/uuid v1.0.0
@@ -152,7 +151,7 @@ require (
k8s.io/client-go v0.19.3
k8s.io/cluster-bootstrap v0.19.3
yunion.io/x/executor v0.0.0-20211018100936-39a2cd966656
yunion.io/x/jsonutils v0.0.0-20210709075951-798a67800349
yunion.io/x/jsonutils v0.0.0-20211105163012-d846c05a3c9a
yunion.io/x/log v0.0.0-20201210064738-43181789dc74
yunion.io/x/ovsdb v0.0.0-20200526071744-27bf0940cbc7
yunion.io/x/pkg v0.0.0-20210918114143-ce839f862c5f
@@ -160,5 +159,3 @@ require (
yunion.io/x/sqlchemy v0.0.0-20210918113031-c1c107f37ada
yunion.io/x/structarg v0.0.0-20200720093445-9f850fa222ce
)
replace github.com/ceph/go-ceph v0.0.0-20181217221554-e32f9f0f2e94 => github.com/yunionio/go-ceph v0.0.0-20190912101231-6f05a06b3859
+4 -6
View File
@@ -266,8 +266,8 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o=
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.1.0+incompatible h1:sIa2eCvUTwgjbqXrPLfNwUf9S3i3mpH1O1atV+iL/Wk=
github.com/gofrs/uuid v4.1.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
@@ -632,8 +632,6 @@ github.com/willf/bloom v2.0.3+incompatible/go.mod h1:MmAltL9pDMNTrvUkxdg0k0q5I0s
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yunionio/go-ceph v0.0.0-20190912101231-6f05a06b3859 h1:wu596gn6sV3j5wy+GDfiG8nwtzIDpalJihfmC/TjoYc=
github.com/yunionio/go-ceph v0.0.0-20190912101231-6f05a06b3859/go.mod h1:8XuBae5AzsgotLArJSewMruYVaQs8AlfsK5jBCG8T9Y=
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.5.0-alpha.5.0.20200819165624-17cef6e3e9d5 h1:Gqga3zA9tdAcfqobUGjSoCob5L3f8Dt5EuOp3ihNZko=
@@ -932,8 +930,8 @@ sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
yunion.io/x/executor v0.0.0-20211018100936-39a2cd966656 h1:0zlZD5uhZoIHgLVAWCz2aHaYk2ZrNsACCYD7R6EIBII=
yunion.io/x/executor v0.0.0-20211018100936-39a2cd966656/go.mod h1:Uxuou9WQIeJXNpy7t2fPLL0BYLvLiMvGQwY7Qc6aSws=
yunion.io/x/jsonutils v0.0.0-20190625054549-a964e1e8a051/go.mod h1:4N0/RVzsYL3kH3WE/H1BjUQdFiWu50JGCFQuuy+Z634=
yunion.io/x/jsonutils v0.0.0-20210709075951-798a67800349 h1:ESeezAb9LM2Dcy28DDrD2nI+h22qEf4MyAPXwuDO4uM=
yunion.io/x/jsonutils v0.0.0-20210709075951-798a67800349/go.mod h1:p0nyMqGA/apTxxyLIU/o1k4V7Vujl2O6ey30L594sYE=
yunion.io/x/jsonutils v0.0.0-20211105163012-d846c05a3c9a h1:X/ucfLNcYsCfc4GFEX7R7GNWiDPr3vopLdnE4PuU46U=
yunion.io/x/jsonutils v0.0.0-20211105163012-d846c05a3c9a/go.mod h1:p0nyMqGA/apTxxyLIU/o1k4V7Vujl2O6ey30L594sYE=
yunion.io/x/log v0.0.0-20190514041436-04ce53b17c6b/go.mod h1:+gauLs73omeJAPlsXcevLsJLKixV+sR/E7WSYTSx1fE=
yunion.io/x/log v0.0.0-20190629062853-9f6483a7103d/go.mod h1:LC6f/4FozL0iaAbnFt2eDX9jlsyo3WiOUPm03d7+U4U=
yunion.io/x/log v0.0.0-20201210064738-43181789dc74 h1:7D+sQ/XaUTUEm+NCrKXOhXBKlzEd0RyS2qZ4vBGjx2o=
+2 -1
View File
@@ -80,8 +80,9 @@ type GuestnetworkJsonDesc struct {
Virtual bool `json:"virtual"`
Ip string `json:"ip"`
Gateway string `json:"gateway"`
DNS string `json:"dns"`
Dns string `json:"dns"`
Domain string `json:"domain"`
Ntp string `json:"ntp"`
Routes jsonutils.JSONObject `json:"routes"`
Ifname string `json:"ifname"`
Masklen int8 `json:"masklen"`
+5 -1
View File
@@ -56,7 +56,11 @@ type InterVpcNetworkSyncstatusInput struct {
}
type InterVpcNetworkAddVpcInput struct {
VpcId string
// 待加入的vpc id
// vpc和当前vpc互联所必须是同一平台,且运营平台一致,例如aws中国区不能和aws国际区运营平台不一致
// 可以通过 /vpcs?usable_for_inter_vpc_network_id=<当前vpc互联id> 过滤可以加入的vpc列表
// required: true
VpcId string `json:"vpc_id"`
}
type InterVpcNetworkRemoveVpcInput struct {
+9 -1
View File
@@ -99,6 +99,8 @@ type NetworkListInput struct {
GuestDns []string `json:"guest_dns"`
// allow multiple dhcp, seperated by ","
GuestDhcp []string `json:"guest_dhcp"`
// NTP
GuestNtp []string `json:"guest_ntp"`
GuestDomain []string `json:"guest_domain"`
@@ -172,13 +174,17 @@ type NetworkCreateInput struct {
GuestGateway string `json:"guest_gateway"`
// description: guest dns
// example: 114.114.114.114
// example: 114.114.114.114,8.8.8.8
GuestDns string `json:"guest_dns"`
// description: guest dhcp
// example: 192.168.222.1,192.168.222.4
GuestDHCP string `json:"guest_dhcp"`
// description: guest ntp
// example: cn.pool.ntp.org,0.cn.pool.ntp.org
GuestNtp string `json:"guest_ntp"`
// swagger:ignore
WireId string `json:"wire_id"`
@@ -349,6 +355,8 @@ type NetworkUpdateInput struct {
GuestDns string `json:"guest_dns"`
// allow multiple dhcp, seperated by ","
GuestDhcp string `json:"guest_dhcp"`
// NTP
GuestNtp string `json:"guest_ntp"`
GuestDomain string `json:"guest_domain"`
+4
View File
@@ -18,6 +18,10 @@ import (
"time"
)
const (
DefaultDNSServers = "223.5.5.5,223.6.6.6"
)
const (
// # DEFAULT_BANDWIDTH = options.default_bandwidth
MAX_BANDWIDTH = 100000
+2 -1
View File
@@ -28,7 +28,8 @@ const (
type VpcPeeringConnectionDetails struct {
apis.EnabledStatusInfrasResourceBaseDetails
VpcName string
VpcResourceInfo
PeerVpcName string
}
+2
View File
@@ -58,6 +58,8 @@ type VpcListInput struct {
DnsZoneFilterListBase
InterVpcNetworkFilterListBase
// 过滤可以加入指定vpc互联的vpc
UsableForInterVpcNetworkId string `json:"usable_for_inter_vpc_network_id"`
UsableResourceListInput
UsableVpcResourceListInput
+5 -1
View File
@@ -18,7 +18,11 @@ import (
"yunion.io/x/pkg/util/netutils"
)
const VPC_OVN_ENCAP_COST = 58
// IP: 20
// UDP: 8
// GENEVE HDR: 8 + 4x
// total: 36 + 4x
const VPC_OVN_ENCAP_COST = 60
const (
VPC_EXTERNAL_ACCESS_MODE_DISTGW = "distgw" // distgw only
+24 -1
View File
@@ -19,6 +19,7 @@ import (
"net"
"os"
"path/filepath"
"strings"
"time"
"yunion.io/x/pkg/util/netutils"
@@ -77,13 +78,35 @@ func GetNicDHCPConfig(
routes = append(routes, []string{route[0], route[1]})
}
parseIPs := func(ips string) []net.IP {
ret := make([]net.IP, 0)
iplist := strings.Split(ips, ",")
for _, ip := range iplist {
ret = append(ret, net.ParseIP(ip))
}
return ret
}
parseDomains := func(domains string) []net.IP {
ret := make([]net.IP, 0)
domainlist := strings.Split(domains, ",")
for _, domain := range domainlist {
addrs, _ := net.LookupHost(domain)
for _, addr := range addrs {
ret = append(ret, net.ParseIP(addr))
}
}
return ret
}
conf := &dhcp.ResponseConfig{
ServerIP: net.ParseIP(serverIP),
ClientIP: net.ParseIP(ipAddr.String()),
Gateway: net.ParseIP(n.Gateway),
SubnetMask: subnetMask,
BroadcastAddr: net.ParseIP(ipAddr.BroadcastAddr(n.MaskLen).String()),
DNSServer: net.ParseIP(n.Dns),
DNSServers: parseIPs(n.Dns),
NTPServers: parseDomains(n.Ntp),
Domain: n.Domain,
OsName: "Linux",
Hostname: hostName,
+31 -3
View File
@@ -185,7 +185,7 @@ func ValueToJSONObject(out reflect.Value) jsonutils.JSONObject {
if obj, ok := isJSONObject(out); ok {
return obj
}
return jsonutils.Marshal(out.Interface())
return jsonutils.MarshalAll(out.Interface())
}
func ValueToJSONDict(out reflect.Value) *jsonutils.JSONDict {
@@ -207,8 +207,36 @@ func ValueToError(out reflect.Value) error {
func mergeInputOutputData(data *jsonutils.JSONDict, resVal reflect.Value) *jsonutils.JSONDict {
retJson := ValueToJSONDict(resVal)
// preserve the input info not returned by caller
data.Update(retJson)
return data
output := data.Copy()
jsonMap, _ := retJson.GetMap()
for k, v := range jsonMap {
if output.Contains(k) {
if v == jsonutils.JSONNull {
output.Remove(k)
} else {
switch v.(type) {
case *jsonutils.JSONString:
if v.IsZero() {
output.Remove(k)
} else {
output.Set(k, v)
}
default:
output.Set(k, v)
}
}
} else if v != jsonutils.JSONNull {
switch v.(type) {
case *jsonutils.JSONString:
if !v.IsZero() {
output.Add(v, k)
}
default:
output.Add(v, k)
}
}
}
return output
}
func ValidateCreateData(funcName string, manager IModelManager, ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
+1 -1
View File
@@ -40,7 +40,7 @@ func Test_valueToJSONObject(t *testing.T) {
{
name: "struct2json",
args: &api.ServerRebuildRootInput{Image: "image"},
want: jsonutils.Marshal(api.ServerRebuildRootInput{Image: "image"}),
want: jsonutils.MarshalAll(api.ServerRebuildRootInput{Image: "image"}),
},
}
for _, tt := range tests {
+1 -1
View File
@@ -114,7 +114,7 @@ const (
type CommonOptions struct {
AuthURL string `help:"Keystone auth URL" alias:"auth-uri"`
AdminUser string `help:"Admin username"`
AdminDomain string `help:"Admin user domain" default:"default"`
AdminDomain string `help:"Admin user domain" default:"Default"`
AdminPassword string `help:"Admin password" alias:"admin-passwd"`
AdminProject string `help:"Admin project" default:"system" alias:"admin-tenant-name"`
AdminProjectDomain string `help:"Domain of Admin project" default:"default"`
+3
View File
@@ -31,6 +31,7 @@ type SNic struct {
Mtu int64 `json:"mtu"`
Mac string `json:"mac"`
Dns string `json:"dns"`
Ntp string `json:"ntp"`
MaskLen int8 `json:"masklen"`
Net string `json:"net"`
Gateway string `json:"gateway"`
@@ -65,6 +66,7 @@ type SServerNic struct {
BandWidth int `json:"bw"`
Mtu int `json:"mtu,omitempty"`
Dns string `json:"dns"`
Ntp string `json:"ntp"`
Net string `json:"net"`
Interface string `json:"interface"`
Gateway string `json:"gateway"`
@@ -95,6 +97,7 @@ func (n SServerNic) ToNic() SNic {
NetId: n.NetId,
Mac: n.Mac,
Dns: n.Dns,
Ntp: n.Ntp,
MaskLen: int8(n.Masklen),
Net: n.Net,
Gateway: n.Gateway,
+1
View File
@@ -1477,6 +1477,7 @@ func (self *SCloudprovider) RealDelete(ctx context.Context, userCred mcclient.To
CDNDomainManager,
NetworkInterfaceManager,
KubeClusterManager,
InterVpcNetworkManager,
CloudproviderRegionManager,
CloudregionManager,
CloudproviderQuotaManager,
+10 -7
View File
@@ -260,6 +260,7 @@ func syncRegionVPCs(ctx context.Context, userCred mcclient.TokenCredential, sync
}
db.OpsLog.LogEvent(provider, db.ACT_SYNC_HOST_COMPLETE, msg, userCred)
// logclient.AddActionLog(provider, getAction(task.Params), notes, task.UserCred, true)
globalVpcIds := []string{}
for j := 0; j < len(localVpcs); j += 1 {
func() {
// lock vpc
@@ -271,7 +272,12 @@ func syncRegionVPCs(ctx context.Context, userCred mcclient.TokenCredential, sync
}
syncVpcWires(ctx, userCred, syncResults, provider, &localVpcs[j], remoteVpcs[j], syncRange)
if localRegion.GetDriver().IsSecurityGroupBelongVpc() || localRegion.GetDriver().IsSupportClassicSecurityGroup() || j == 0 { //有vpc属性的每次都同步,支持classic的vpc也同步,否则仅同步一次
if localRegion.GetDriver().IsSecurityGroupBelongVpc() ||
(localRegion.GetDriver().IsSecurityGroupBelongGlobalVpc() && !utils.IsInStringArray(localVpcs[j].GlobalvpcId, globalVpcIds)) ||
localRegion.GetDriver().IsSupportClassicSecurityGroup() || j == 0 { //有vpc属性的每次都同步,支持classic的vpc也同步,否则仅同步一次
if len(localVpcs[j].GlobalvpcId) > 0 {
globalVpcIds = append(globalVpcIds, localVpcs[j].GlobalvpcId)
}
syncVpcSecGroup(ctx, userCred, syncResults, provider, &localVpcs[j], remoteVpcs[j], syncRange)
}
syncVpcNatgateways(ctx, userCred, syncResults, provider, &localVpcs[j], remoteVpcs[j], syncRange)
@@ -1464,18 +1470,15 @@ func syncKubeClusters(ctx context.Context, userCred mcclient.TokenCredential, sy
return
}
err = syncKubeClusterNodePools(ctx, userCred, syncResults, &localClusters[i], remoteClusters[i])
if err != nil {
if err := syncKubeClusterNodePools(ctx, userCred, syncResults, &localClusters[i], remoteClusters[i]); err != nil {
log.Errorf("syncKubeClusterNodePools for %s error: %v", localClusters[i].Name, err)
}
err = syncKubeClusterNodes(ctx, userCred, syncResults, &localClusters[i], remoteClusters[i])
if err != nil {
if err := syncKubeClusterNodes(ctx, userCred, syncResults, &localClusters[i], remoteClusters[i]); err != nil {
log.Errorf("syncKubeClusterNodes for %s error: %v", localClusters[i].Name, err)
}
err = localClusters[i].Import(ctx, userCred, remoteClusters[i])
if err != nil {
if err := localClusters[i].ImportOrUpdate(ctx, userCred, remoteClusters[i]); err != nil {
log.Errorf("Import cluster %s error: %v", localClusters[i].Name, err)
}
}()
+2 -1
View File
@@ -563,8 +563,9 @@ func (self *SGuestnetwork) getJsonDesc() *api.GuestnetworkJsonDesc {
desc.Ip = self.IpAddr
}
desc.Gateway = net.GuestGateway
desc.DNS = net.GetDNS()
desc.Dns = net.GetDNS()
desc.Domain = net.GetDomain()
desc.Ntp = net.GetNTP()
routes := net.GetRoutes()
if routes != nil && len(routes) > 0 {
+24 -6
View File
@@ -31,6 +31,7 @@ import (
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/cloudcommon/validators"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
@@ -111,6 +112,25 @@ func (manager *SInterVpcNetworkManager) ListItemFilter(
return q, nil
}
func (manager *SInterVpcNetworkManager) ListItemExportKeys(ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
keys stringutils2.SSortedStrings,
) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SEnabledStatusInfrasResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
if err != nil {
return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.ListItemExportKeys")
}
if keys.ContainsAny(manager.SManagedResourceBaseManager.GetExportKeys()...) {
q, err = manager.SManagedResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
if err != nil {
return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemExportKeys")
}
}
return q, nil
}
func (manager *SInterVpcNetworkManager) ValidateCreateData(
ctx context.Context,
userCred mcclient.TokenCredential,
@@ -141,10 +161,12 @@ func (manager *SInterVpcNetworkManager) FetchCustomizeColumns(
) []api.InterVpcNetworkDetails {
rows := make([]api.InterVpcNetworkDetails, len(objs))
stdRows := manager.SEnabledStatusInfrasResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
manRows := manager.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
vpcNetworkIds := make([]string, len(objs))
for i := range rows {
rows[i] = api.InterVpcNetworkDetails{
EnabledStatusInfrasResourceBaseDetails: stdRows[i],
ManagedResourceInfo: manRows[i],
}
vpcNetwork := objs[i].(*SInterVpcNetwork)
vpcNetworkIds[i] = vpcNetwork.Id
@@ -264,13 +286,9 @@ func (self *SInterVpcNetwork) PerformAddvpc(ctx context.Context, userCred mcclie
if len(input.VpcId) == 0 {
return nil, httperrors.NewMissingParameterError("vpc_id")
}
// get vpc
_vpc, err := VpcManager.FetchByIdOrName(userCred, input.VpcId)
_vpc, err := validators.ValidateModel(userCred, VpcManager, &input.VpcId)
if err != nil {
if errors.Cause(err) == sql.ErrNoRows {
return nil, httperrors.NewResourceNotFoundError2("vpc", input.VpcId)
}
return nil, httperrors.NewGeneralError(err)
return nil, err
}
vpc := _vpc.(*SVpc)
+39 -27
View File
@@ -200,39 +200,51 @@ func (self *SKubeCluster) syncRemoveCloudKubeCluster(ctx context.Context, userCr
return self.RealDelete(ctx, userCred)
}
func (self *SKubeCluster) Import(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudKubeCluster) error {
func (self *SKubeCluster) ImportOrUpdate(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudKubeCluster) error {
if len(self.ExternalClusterId) == 0 {
config, err := ext.GetKubeConfig(false, 0)
if err != nil {
return errors.Wrapf(err, "GetKubeConfig")
}
params := map[string]interface{}{
"name": self.Name,
"mode": "import",
"provider": "external",
"resource_type": "unknown",
"import_data": map[string]interface{}{
"kubeconfig": config.Config,
},
}
s := auth.GetAdminSession(ctx, options.Options.Region, "")
resp, err := k8s.KubeClusters.Create(s, jsonutils.Marshal(params))
if err != nil {
return errors.Wrapf(err, "Create")
}
id, err := resp.GetString("id")
if err != nil {
return errors.Wrapf(err, "resp.GetId")
}
_, err = db.Update(self, func() error {
self.ExternalClusterId = id
return nil
})
return self.doRemoteImport(ctx, userCred, ext)
}
return self.doRemoteUpdate(ctx, userCred, ext)
}
func (self *SKubeCluster) doRemoteImport(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudKubeCluster) error {
config, err := ext.GetKubeConfig(false, 0)
if err != nil {
return errors.Wrapf(err, "GetKubeConfig")
}
params := map[string]interface{}{
"name": self.Name,
"mode": "import",
"external_cluster_id": self.GetId(),
"resource_type": "guest",
"import_data": map[string]interface{}{
"kubeconfig": config.Config,
},
}
s := auth.GetAdminSession(ctx, options.Options.Region, "")
resp, err := k8s.KubeClusters.Create(s, jsonutils.Marshal(params))
if err != nil {
return errors.Wrapf(err, "Create")
}
id, err := resp.GetString("id")
if err != nil {
return errors.Wrapf(err, "resp.GetId")
}
if _, err := db.Update(self, func() error {
self.ExternalClusterId = id
return nil
}); err != nil {
return errors.Wrapf(err, "db.Update")
}
return nil
}
func (self *SKubeCluster) doRemoteUpdate(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudKubeCluster) error {
// TODO
return nil
}
func (self *SKubeCluster) SyncAllWithCloudKubeCluster(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudKubeCluster, provider *SCloudprovider) error {
err := self.SyncWithCloudKubeCluster(ctx, userCred, ext, provider)
if err != nil {
@@ -977,6 +977,10 @@ func (lbbg *SLoadbalancerBackendGroup) syncRemoveCloudLoadbalancerBackendgroup(c
lockman.LockObject(ctx, lbbg)
defer lockman.ReleaseObject(ctx, lbbg)
if lbbg.GetProviderName() == api.CLOUD_PROVIDER_GOOGLE {
return nil
}
err := lbbg.ValidateDeleteCondition(ctx, nil)
if err != nil { // cannot delete
err = lbbg.SetStatus(userCred, api.LB_STATUS_UNKNOWN, "sync to delete")
+2 -1
View File
@@ -1134,7 +1134,8 @@ func (lblis *SLoadbalancerListener) constructFieldsFromCloudListener(userCred mc
lblis.BackendGroupId = lb.BackendGroupId
} else if group, err := db.FetchByExternalIdAndManagerId(LoadbalancerBackendGroupManager, groupId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
sq := LoadbalancerManager.Query().SubQuery()
return q.Join(sq, sqlchemy.Equals(sq.Field("id"), q.Field("loadbalancer_id"))).Filter(sqlchemy.Equals(sq.Field("manager_id"), lb.ManagerId))
q = q.Join(sq, sqlchemy.Equals(sq.Field("id"), q.Field("loadbalancer_id"))).Filter(sqlchemy.Equals(sq.Field("manager_id"), lb.ManagerId))
return q.IsFalse("pending_deleted")
}); err == nil {
lblis.BackendGroupId = group.GetId()
}
+1
View File
@@ -155,6 +155,7 @@ func (self *SNetInterface) networkToJson(ipAddr string, network *SNetwork, desc
}
desc.Add(jsonutils.NewString(network.GetDNS()), "dns")
desc.Add(jsonutils.NewString(network.GetDomain()), "domain")
desc.Add(jsonutils.NewString(network.GetNTP()), "ntp")
routes := network.GetRoutes()
if routes != nil && len(routes) > 0 {
+90 -15
View File
@@ -111,10 +111,12 @@ type SNetwork struct {
GuestIpMask int8 `nullable:"false" list:"user" update:"user" create:"required"`
// 网关地址
GuestGateway string `width:"16" charset:"ascii" nullable:"true" list:"user" update:"user" create:"optional"`
// DNS
GuestDns string `width:"16" charset:"ascii" nullable:"true" list:"user" update:"user" create:"optional"`
// DNS, allow multiple dns, seperated by ","
GuestDns string `width:"64" charset:"ascii" nullable:"true" list:"user" update:"user" create:"optional"`
// allow multiple dhcp, seperated by ","
GuestDhcp string `width:"64" charset:"ascii" nullable:"true" list:"user" update:"user" create:"optional"`
// allow mutiple ntp, seperated by ","
GuestNtp string `width:"64" charset:"ascii" nullable:"true" list:"user" update:"user" create:"optional"`
GuestDomain string `width:"128" charset:"ascii" nullable:"true" get:"user" update:"user"`
@@ -382,10 +384,15 @@ func (self *SNetwork) getFreeIP(addrTable map[string]bool, recentUsedAddrTable m
return candidate, nil
}
}
// If network's alloc_policy is not none, then use network's alloc_policy
if len(self.AllocPolicy) > 0 && api.IPAllocationDirection(self.AllocPolicy) != api.IPAllocationNone {
allocDir = api.IPAllocationDirection(self.AllocPolicy)
}
if len(allocDir) == 0 || allocDir == api.IPAllocationStepdown {
// if alloc_dir is not speicified, and network's alloc_policy is not either, use default
if len(allocDir) == 0 {
allocDir = api.IPAllocationDirection(options.Options.DefaultIPAllocationDirection)
}
if allocDir == api.IPAllocationStepdown {
ip, _ := netutils.NewIPV4Addr(self.GuestIpEnd)
for iprange.Contains(ip) {
if !isIpUsed(ip.String(), addrTable, recentUsedAddrTable) {
@@ -457,7 +464,42 @@ func (self *SNetwork) GetDNS() string {
if len(self.GuestDns) > 0 {
return self.GuestDns
} else {
return options.Options.DNSServer
zoneName := ""
wire, _ := self.GetWire()
if wire != nil {
zone, _ := wire.GetZone()
if zone != nil {
zoneName = zone.Name
}
}
srvs, _ := auth.GetDNSServers(options.Options.Region, zoneName)
if len(srvs) > 0 {
return strings.Join(srvs, ",")
}
if len(options.Options.DNSServer) > 0 {
return options.Options.DNSServer
}
return api.DefaultDNSServers
}
}
func (self *SNetwork) GetNTP() string {
if len(self.GuestNtp) > 0 {
return self.GuestNtp
} else {
zoneName := ""
wire, _ := self.GetWire()
if wire != nil {
zone, _ := wire.GetZone()
if zone != nil {
zoneName = zone.Name
}
}
srvs, _ := auth.GetNTPServers(options.Options.Region, zoneName)
if len(srvs) > 0 {
return strings.Join(srvs, ",")
}
return ""
}
}
@@ -1532,25 +1574,34 @@ func (manager *SNetworkManager) ValidateCreateData(ctx context.Context, userCred
}
}
if len(input.GuestDns) == 0 {
input.GuestDns = options.Options.DNSServer
}
// do not set default dns
// if len(input.GuestDns) == 0 {
// input.GuestDns = options.Options.DNSServer
// }
for key, ipStr := range map[string]string{
"guest_gateway": input.GuestGateway,
"guest_dns": input.GuestDns,
"guest_dhcp": input.GuestDHCP,
"guest_ntp": input.GuestNtp,
} {
if ipStr == "" {
continue
}
if key == "guest_dhcp" {
if key == "guest_dhcp" || key == "guest_dns" {
ipList := strings.Split(ipStr, ",")
for _, ipstr := range ipList {
if !regutils.MatchIPAddr(ipstr) {
return input, httperrors.NewInputParameterError("%s: Invalid IP address %s", key, ipstr)
}
}
} else if key == "guest_ntp" {
ipList := strings.Split(ipStr, ",")
for _, ipstr := range ipList {
if !regutils.MatchDomainName(ipstr) && !regutils.MatchIPAddr(ipstr) {
return input, httperrors.NewInputParameterError("%s: Invalid domain name or IP address %s", key, ipstr)
}
}
} else if !regutils.MatchIPAddr(ipStr) {
return input, httperrors.NewInputParameterError("%s: Invalid IP address %s", key, ipStr)
}
@@ -1737,17 +1788,25 @@ func (self *SNetwork) validateUpdateData(ctx context.Context, userCred mcclient.
"guest_gateway": input.GuestGateway,
"guest_dns": input.GuestDns,
"guest_dhcp": input.GuestDhcp,
"guest_ntp": input.GuestNtp,
} {
if ipStr == "" {
continue
}
if key == "guest_dhcp" {
if key == "guest_dhcp" || key == "guest_dns" {
ipList := strings.Split(ipStr, ",")
for _, ipstr := range ipList {
if !regutils.MatchIPAddr(ipstr) {
return input, httperrors.NewInputParameterError("%s: Invalid IP address %s", key, ipstr)
}
}
} else if key == "guest_ntp" {
ipList := strings.Split(ipStr, ",")
for _, ipstr := range ipList {
if !regutils.MatchDomainName(ipstr) && !regutils.MatchIPAddr(ipstr) {
return input, httperrors.NewInputParameterError("%s: Invalid domain name or IP address %s", key, ipstr)
}
}
} else if !regutils.MatchIPAddr(ipStr) {
return input, httperrors.NewInputParameterError("%s: Invalid IP address %s", key, ipStr)
}
@@ -1772,13 +1831,19 @@ func (self *SNetwork) validateUpdateData(ctx context.Context, userCred mcclient.
}
func (self *SNetwork) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.NetworkUpdateInput) (api.NetworkUpdateInput, error) {
if !self.isManaged() && !self.isOneCloudVpcNetwork() {
var err error
input, err = self.validateUpdateData(ctx, userCred, query, input)
if err != nil {
return input, errors.Wrap(err, "validateUpdateData")
if !self.isManaged() {
if !self.isOneCloudVpcNetwork() {
// classic network
} else {
// vpc network
input.GuestIpStart = ""
input.GuestIpEnd = ""
input.GuestIpMask = nil
input.GuestGateway = ""
input.GuestDhcp = ""
}
} else {
// managed network
input.GuestIpStart = ""
input.GuestIpEnd = ""
input.GuestIpMask = nil
@@ -1786,9 +1851,14 @@ func (self *SNetwork) ValidateUpdateData(ctx context.Context, userCred mcclient.
input.GuestDns = ""
input.GuestDomain = ""
input.GuestDhcp = ""
input.GuestNtp = ""
}
var err error
input, err = self.validateUpdateData(ctx, userCred, query, input)
if err != nil {
return input, errors.Wrap(err, "validateUpdateData")
}
var err error
input.SharableVirtualResourceBaseUpdateInput, err = self.SSharableVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input.SharableVirtualResourceBaseUpdateInput)
if err != nil {
return input, errors.Wrap(err, "SSharableVirtualResourceBase.ValidateUpdateData")
@@ -2162,6 +2232,9 @@ func (manager *SNetworkManager) ListItemFilter(
if len(input.GuestDhcp) > 0 {
q = q.In("guest_dhcp", input.GuestDhcp)
}
if len(input.GuestNtp) > 0 {
q = q.In("guest_ntp", input.GuestNtp)
}
if len(input.GuestDomain) > 0 {
q = q.In("guest_domain", input.GuestDomain)
}
@@ -2534,6 +2607,7 @@ func (self *SNetwork) PerformSplit(ctx context.Context, userCred mcclient.TokenC
network.GuestGateway = self.GuestGateway
network.GuestDns = self.GuestDns
network.GuestDhcp = self.GuestDhcp
network.GuestNtp = self.GuestNtp
network.GuestDomain = self.GuestDomain
network.VlanId = self.VlanId
network.WireId = self.WireId
@@ -2688,6 +2762,7 @@ func (manager *SNetworkManager) PerformTryCreateNetwork(ctx context.Context, use
newNetwork.GuestIpMask = int8(input.Mask)
newNetwork.GuestDns = nm.GuestDns
newNetwork.GuestDhcp = nm.GuestDhcp
newNetwork.GuestNtp = nm.GuestNtp
newNetwork.WireId = nm.WireId
newNetwork.ServerType = input.ServerType
newNetwork.IsPublic = nm.IsPublic
+15
View File
@@ -1936,6 +1936,21 @@ func (vpcPC *SVpcPeeringConnection) purge(ctx context.Context, userCred mcclient
return vpcPC.RealDelete(ctx, userCred)
}
func (manager *SInterVpcNetworkManager) purgeAll(ctx context.Context, userCred mcclient.TokenCredential, providerId string) error {
networks := []SInterVpcNetwork{}
err := fetchByManagerId(manager, providerId, &networks)
if err != nil {
return errors.Wrapf(err, "fetchByManagerId")
}
for i := range networks {
err := networks[i].RealDelete(ctx, userCred)
if err != nil {
return errors.Wrapf(err, "inter vpc network delete")
}
}
return nil
}
func (manager *SWafRuleGroupCacheManager) purgeAll(ctx context.Context, userCred mcclient.TokenCredential, providerId string) error {
caches := []SWafRuleGroupCache{}
err := fetchByManagerId(manager, providerId, &caches)
@@ -253,20 +253,21 @@ func (manager *SVpcPeeringConnectionManager) FetchCustomizeColumns(
) []api.VpcPeeringConnectionDetails {
rows := make([]api.VpcPeeringConnectionDetails, len(objs))
stdRows := manager.SEnabledStatusInfrasResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
vpcIds := make([]string, len(objs))
vpcObjs := make([]interface{}, len(objs))
peerVpcIds := make([]string, len(objs))
for i := range rows {
rows[i] = api.VpcPeeringConnectionDetails{
EnabledStatusInfrasResourceBaseDetails: stdRows[i],
}
vpcPC := objs[i].(*SVpcPeeringConnection)
vpcIds[i] = vpcPC.VpcId
vpcObj := &SVpcResourceBase{VpcId: vpcPC.VpcId}
vpcObjs[i] = vpcObj
peerVpcIds[i] = vpcPC.PeerVpcId
}
vpcMap, err := db.FetchIdNameMap2(VpcManager, vpcIds)
if err != nil {
return rows
vpcRows := manager.SVpcResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, vpcObjs, fields, isList)
for i := range rows {
rows[i].VpcResourceInfo = vpcRows[i]
}
peerVpcMap, err := db.FetchIdNameMap2(VpcManager, peerVpcIds)
if err != nil {
@@ -274,7 +275,6 @@ func (manager *SVpcPeeringConnectionManager) FetchCustomizeColumns(
}
for i := range rows {
rows[i].VpcName, _ = vpcMap[vpcIds[i]]
rows[i].PeerVpcName, _ = peerVpcMap[peerVpcIds[i]]
}
return rows
@@ -341,6 +341,7 @@ func (manager *SVpcPeeringConnectionManager) ListItemExportKeys(ctx context.Cont
if err != nil {
return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.ListItemExportKeys")
}
return q, nil
}
+24
View File
@@ -1103,6 +1103,30 @@ func (manager *SVpcManager) ListItemFilter(
q = q.In("id", sq.SubQuery())
}
if len(query.UsableForInterVpcNetworkId) > 0 {
_interVpc, err := validators.ValidateModel(userCred, InterVpcNetworkManager, &query.UsableForInterVpcNetworkId)
if err != nil {
return nil, err
}
interVpc := _interVpc.(*SInterVpcNetwork)
sq := InterVpcNetworkVpcManager.Query("vpc_id").Equals("inter_vpc_network_id", interVpc.GetId())
q = q.NotIn("id", sq.SubQuery())
account := interVpc.GetCloudaccount()
if account == nil {
return nil, httperrors.NewNotSupportedError("not supported for inter vpc network %s", interVpc.Name)
}
vpcs := VpcManager.Query().SubQuery()
managers := CloudproviderManager.Query().SubQuery()
accounts := CloudaccountManager.Query().SubQuery()
vpcSQ := vpcs.Query(vpcs.Field("id")).Join(managers, sqlchemy.Equals(vpcs.Field("manager_id"), managers.Field("id"))).Join(accounts, sqlchemy.Equals(managers.Field("cloudaccount_id"), accounts.Field("id"))).Filter(
sqlchemy.AND(
sqlchemy.Equals(accounts.Field("provider"), account.Provider),
sqlchemy.Equals(accounts.Field("access_url"), account.AccessUrl),
),
)
q = q.In("id", vpcSQ.SubQuery())
}
if len(query.InterVpcNetworkId) > 0 {
vpcNetwork, err := InterVpcNetworkManager.FetchByIdOrName(userCred, query.InterVpcNetworkId)
if err != nil {
+2
View File
@@ -177,6 +177,8 @@ type ComputeOptions struct {
GlobalMacPrefix string `help:"Global prefix of MAC address, default to 00:22" default:"00:22"`
DefaultIPAllocationDirection string `help:"default IP allocation direction" default:"stepdown"`
esxi.EsxiOptions
}
+54 -4
View File
@@ -19,6 +19,7 @@ import (
"database/sql"
"fmt"
"regexp"
"sort"
"strconv"
"yunion.io/x/jsonutils"
@@ -916,6 +917,52 @@ func (self *SKVMRegionDriver) ValidateEipChargeType(chargeType string) error {
return nil
}
type eipNetwork struct {
owner mcclient.TokenCredential
freeCnt int
net *models.SNetwork
}
func (en eipNetwork) isOwnerProject() bool {
return en.owner.GetProjectId() == en.net.ProjectId
}
func (en eipNetwork) isOwnerProjectDomain() bool {
return en.owner.GetProjectDomainId() == en.net.DomainId
}
type eipNetworks []eipNetwork
func (a eipNetworks) Len() int {
return len(a)
}
func (a eipNetworks) Swap(i, j int) {
a[i], a[j] = a[j], a[i]
}
func (a eipNetworks) Less(i, j int) bool {
if a[i].isOwnerProject() && !a[j].isOwnerProject() {
return true
} else if !a[i].isOwnerProject() && a[j].isOwnerProject() {
return false
}
if a[i].isOwnerProjectDomain() && !a[j].isOwnerProjectDomain() {
return true
} else if !a[i].isOwnerProjectDomain() && a[j].isOwnerProjectDomain() {
return false
}
if a[i].freeCnt > a[j].freeCnt {
return true
} else if a[i].freeCnt < a[j].freeCnt {
return false
}
if a[i].net.Id < a[j].net.Id {
return true
}
return false
}
func (self *SKVMRegionDriver) ValidateCreateEipData(ctx context.Context, userCred mcclient.TokenCredential, input *api.SElasticipCreateInput) error {
if err := self.ValidateEipChargeType(input.ChargeType); err != nil {
return err
@@ -939,18 +986,21 @@ func (self *SKVMRegionDriver) ValidateCreateEipData(ctx context.Context, userCre
if err := db.FetchModelObjects(models.NetworkManager, q, &nets); err != nil {
return err
}
eipNets := make([]eipNetwork, 0)
for i := range nets {
net := &nets[i]
cnt, _ := net.GetFreeAddressCount()
if cnt > 0 {
network = net
input.NetworkId = net.Id
break
eipNets = append(eipNets, eipNetwork{net: net, freeCnt: cnt, owner: userCred})
}
}
if network == nil {
if len(eipNets) == 0 {
return httperrors.NewNotFoundError("no available eip network")
}
// prefer networks with identical project, domain, more free address, Id
sort.Sort(eipNetworks(eipNets))
network = eipNets[0].net
input.NetworkId = network.Id
}
if network.ServerType != api.NETWORK_TYPE_EIP {
return httperrors.NewInputParameterError("bad network type %q, want %q", network.ServerType, api.NETWORK_TYPE_EIP)
+12 -1
View File
@@ -135,8 +135,19 @@ func (s *SGuestDHCPServer) getGuestConfig(guestDesc, guestNic jsonutils.JSONObje
conf.Routes = route
if len(nicdesc.Dns) > 0 {
conf.DNSServer = net.ParseIP(nicdesc.Dns)
conf.DNSServers = make([]net.IP, 0)
for _, dns := range strings.Split(nicdesc.Dns, ",") {
conf.DNSServers = append(conf.DNSServers, net.ParseIP(dns))
}
}
if len(nicdesc.Ntp) > 0 {
conf.NTPServers = make([]net.IP, 0)
for _, ntp := range strings.Split(nicdesc.Ntp, ",") {
conf.NTPServers = append(conf.NTPServers, net.ParseIP(ntp))
}
}
conf.OsName, _ = guestDesc.GetString("os_name")
conf.LeaseTime = time.Duration(options.HostOptions.DhcpLeaseTime) * time.Second
conf.RenewalTime = time.Duration(options.HostOptions.DhcpRenewalTime) * time.Second
+1 -1
View File
@@ -1696,7 +1696,7 @@ func (h *SHostInfo) OnCatalogChanged(catalog mcclient.KeystoneServiceCatalogV3)
}
if options.HostOptions.ManageNtpConfiguration {
ntpd := system_service.GetService("ntpd")
urls, _ := catalog.GetServiceURLs("ntp", options.HostOptions.Region, "", defaultEndpointType)
urls, _ := catalog.GetServiceURLs("ntp", options.HostOptions.Region, h.Zone, defaultEndpointType)
if len(urls) > 0 {
log.Infof("Get Ntp urls: %v", urls)
} else {
+36
View File
@@ -17,7 +17,9 @@ package auth
import (
"context"
"fmt"
"net"
"net/http"
"strings"
"time"
"yunion.io/x/jsonutils"
@@ -28,6 +30,7 @@ import (
"yunion.io/x/onecloud/pkg/apis/identity"
"yunion.io/x/onecloud/pkg/cloudcommon/syncman"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/util/stringutils2"
)
var (
@@ -231,6 +234,31 @@ func (a *authManager) GetServiceURLs(service, region, zone, endpointType string)
return a.adminCredential.GetServiceURLs(service, region, zone, endpointType)
}
func (a *authManager) getServiceIPs(service, region, zone, endpointType string, needResolve bool) ([]string, error) {
urls, err := a.GetServiceURLs(service, region, zone, endpointType)
if err != nil {
return nil, errors.Wrap(err, "GetServiceURLs")
}
ret := stringutils2.NewSortedStrings(nil)
for _, url := range urls {
slashIdx := strings.Index(url, "://")
if slashIdx >= 0 {
url = url[slashIdx+3:]
}
if needResolve {
addrs, err := net.LookupHost(url)
if err != nil {
log.Errorf("Lookup host %s fail: %s", url, err)
} else {
ret = ret.Append(addrs...)
}
} else {
ret = ret.Append(url)
}
}
return ret, nil
}
func (a *authManager) getTokenString() string {
return a.adminCredential.GetTokenString()
}
@@ -273,6 +301,14 @@ func GetServiceURLs(service, region, zone, endpointType string) ([]string, error
return manager.GetServiceURLs(service, region, zone, endpointType)
}
func GetDNSServers(region, zone string) ([]string, error) {
return manager.getServiceIPs("dns", region, zone, identity.EndpointInterfacePublic, false)
}
func GetNTPServers(region, zone string) ([]string, error) {
return manager.getServiceIPs("ntp", region, zone, identity.EndpointInterfacePublic, true)
}
func GetTokenString() string {
return manager.getTokenString()
}
@@ -52,7 +52,11 @@ func (this *ReservedIPManager) DoBatchReleaseReservedIps(s *mcclient.ClientSessi
}
// filter ip and network pairs.
ipFilterOps := jsonutils.NewDict()
originFilter, _ := params.Get("query")
if originFilter == nil {
originFilter = jsonutils.NewDict()
}
ipFilterOps := originFilter.(*jsonutils.JSONDict)
arr := jsonutils.NewArray()
filterCondition := "ip_addr.in(" + strings.Join(ips, ",") + ")"
arr.Add(jsonutils.NewString(filterCondition))
+14 -5
View File
@@ -204,9 +204,12 @@ func (o K8SClusterCreateOptions) Params() (jsonutils.JSONObject, error) {
}
type KubeClusterImportOptions struct {
NAME string `help:"Name of cluster"`
KUBECONFIG string `help:"Cluster kubeconfig file path"`
Distro string `help:"Kubernetes distribution, e.g. openshift"`
NAME string `help:"Name of cluster"`
KUBECONFIG string `help:"Cluster kubeconfig file path"`
Distro string `help:"Kubernetes distribution, e.g. openshift"`
Provider string `help:"Provider type" choices:"external|aliyun|qcloud|azure"`
ResourceType string `help:"Node resource type" choices:"unknown|guest"`
CloudKubeCluster string `help:"Cloud kube cluster id or name"`
}
func (o KubeClusterImportOptions) Params() (jsonutils.JSONObject, error) {
@@ -217,8 +220,14 @@ func (o KubeClusterImportOptions) Params() (jsonutils.JSONObject, error) {
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(o.NAME), "name")
params.Add(jsonutils.NewString("import"), "mode")
params.Add(jsonutils.NewString("external"), "provider")
params.Add(jsonutils.NewString("unknown"), "resource_type")
params.Add(jsonutils.NewString(o.Provider), "provider")
if o.ResourceType == "" {
o.ResourceType = "unknown"
}
params.Add(jsonutils.NewString(o.ResourceType), "resource_type")
if o.CloudKubeCluster != "" {
params.Add(jsonutils.NewString(o.CloudKubeCluster), "external_cluster_id")
}
importData := jsonutils.NewDict()
importData.Add(jsonutils.NewString(string(kubeconfig)), "kubeconfig")
+8
View File
@@ -66,6 +66,7 @@ type NetworkUpdateOptions struct {
Dns string `help:"IP of DNS server"`
Domain string `help:"Domain"`
Dhcp string `help:"DHCP server IP"`
Ntp string `help:"Ntp server domain names"`
VlanId int64 `help:"Vlan ID" default:"1"`
ExternalId string `help:"External ID"`
AllocPolicy string `help:"Address allocation policy" choices:"none|stepdown|stepup|random"`
@@ -113,6 +114,13 @@ func (opts *NetworkUpdateOptions) Params() (jsonutils.JSONObject, error) {
params.Add(jsonutils.NewString(opts.Dhcp), "guest_dhcp")
}
}
if len(opts.Ntp) > 0 {
if opts.Ntp == "none" {
params.Add(jsonutils.NewString(""), "guest_ntp")
} else {
params.Add(jsonutils.NewString(opts.Ntp), "guest_ntp")
}
}
if opts.VlanId > 0 {
params.Add(jsonutils.NewInt(opts.VlanId), "vlan_id")
}
+8 -7
View File
@@ -23,13 +23,14 @@ import (
type VpcListOptions struct {
BaseListOptions
Usable *bool `help:"Filter usable vpcs"`
Region string `help:"ID or Name of region" json:"-"`
Globalvpc string `help:"Filter by globalvpc"`
DnsZoneId string `help:"Filter by DnsZone"`
InterVpcNetworkId string `help:"Filter by InterVpcNetwork"`
ExternalAccessMode string `help:"Filter by external access mode" choices:"distgw|eip|eip-distgw"`
ZoneId string `help:"Filter by zone which has networks"`
Usable *bool `help:"Filter usable vpcs"`
Region string `help:"ID or Name of region" json:"-"`
Globalvpc string `help:"Filter by globalvpc"`
DnsZoneId string `help:"Filter by DnsZone"`
InterVpcNetworkId string `help:"Filter by InterVpcNetwork"`
ExternalAccessMode string `help:"Filter by external access mode" choices:"distgw|eip|eip-distgw"`
ZoneId string `help:"Filter by zone which has networks"`
UsableForInterVpcNetworkId string `help:"Filter usable vpcs for inter vpc network"`
}
func (opts *VpcListOptions) GetContextId() string {
+37 -1
View File
@@ -67,7 +67,20 @@ func (self *SLoadbalancer) IsEmulated() bool {
}
func (self *SLoadbalancer) GetSysTags() map[string]string {
return nil
frs, err := self.GetForwardingRules()
if err != nil {
return nil
}
ips := []string{}
for i := range frs {
if len(frs[i].IPAddress) > 0 {
ips = append(ips, frs[i].IPAddress)
}
}
data := map[string]string{}
data["FrontendIPs"] = strings.Join(ips, ",")
return data
}
func (self *SLoadbalancer) GetTags() (map[string]string, error) {
@@ -139,6 +152,29 @@ func (self *SLoadbalancer) GetNetworkIds() []string {
}
func (self *SLoadbalancer) GetVpcId() string {
networkIds := self.GetNetworkIds()
if len(networkIds) == 0 {
return ""
}
if len(networkIds) >= 1 {
network, err := self.region.GetNetwork(networkIds[0])
if err == nil && network != nil {
wire := network.GetIWire()
if wire == nil {
return ""
}
vpc := wire.GetIVpc()
if vpc == nil {
return ""
}
return vpc.GetGlobalId()
}
log.Debugf("GetVpcId %s", err)
}
return ""
}
@@ -659,11 +659,10 @@ func (self *SLoadbalancer) GetInstanceGroups() ([]SInstanceGroup, error) {
if fs, ok := zonesFilter[zoneId]; ok {
f := fmt.Sprintf(`(selfLink="%s")`, ig)
if !utils.IsInStringArray(f, fs) {
fs = append(fs, f)
zonesFilter[zoneId] = append(fs, f)
}
} else {
fs = []string{fmt.Sprintf(`(selfLink="%s")`, ig)}
zonesFilter[zoneId] = fs
zonesFilter[zoneId] = []string{fmt.Sprintf(`(selfLink="%s")`, ig)}
}
}
}
@@ -232,6 +232,10 @@ func (self *SLoadbalancerListener) GetStickySessionCookie() string {
}
func (self *SLoadbalancerListener) GetStickySessionCookieTimeout() int {
if len(self.backendService.ConsistentHash.HTTPCookie.TTL.Seconds) == 0 {
return 0
}
sec, err := strconv.Atoi(self.backendService.ConsistentHash.HTTPCookie.TTL.Seconds)
if err != nil {
log.Debugf("GetStickySessionCookieTimeout %s", err)
+16 -3
View File
@@ -32,6 +32,7 @@ const (
OptClasslessRouteWin OptionCode = 249
)
// http://www.networksorcery.com/enp/rfc/rfc2132.txt
type ResponseConfig struct {
OsName string
ServerIP net.IP // OptServerIdentifier 54
@@ -43,8 +44,9 @@ type ResponseConfig struct {
BroadcastAddr net.IP // OptBroadcastAddr 28
Hostname string // OptHostname 12
SubnetMask net.IP // OptSubnetMask 1
DNSServer net.IP // OptDNSServers
DNSServers []net.IP // OptDNSServers
Routes [][]string // TODO: 249 for windows, 121 for linux
NTPServers []net.IP // OptNTPServers 42
// TFTP config
BootServer string
@@ -64,6 +66,14 @@ func GetOptIP(ip net.IP) []byte {
return []byte(ip.To4())
}
func GetOptIPs(ips []net.IP) []byte {
buf := make([]byte, 0)
for _, ip := range ips {
buf = append(buf, []byte(ip.To4())...)
}
return buf
}
func GetOptTime(d time.Duration) []byte {
timeBytes := make([]byte, 4)
binary.BigEndian.PutUint32(timeBytes, uint32(d/time.Second))
@@ -138,8 +148,11 @@ func makeDHCPReplyPacket(req Packet, conf *ResponseConfig, msgType MessageType)
if conf.Hostname != "" {
opts = append(opts, Option{OptionHostName, []byte(conf.GetHostname())})
}
if conf.DNSServer != nil {
opts = append(opts, Option{OptionDomainNameServer, GetOptIP(conf.DNSServer)})
if len(conf.DNSServers) > 0 {
opts = append(opts, Option{OptionDomainNameServer, GetOptIPs(conf.DNSServers)})
}
if len(conf.NTPServers) > 0 {
opts = append(opts, Option{OptionNetworkTimeProtocolServers, GetOptIPs(conf.NTPServers)})
}
resp := ReplyPacket(req, msgType, conf.ServerIP, conf.ClientIP, conf.LeaseTime, opts)
if conf.BootServer != "" {
-15
View File
@@ -1,15 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rbdutils // import "yunion.io/x/onecloud/pkg/util/rbdutils"
-154
View File
@@ -1,154 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rbdutils
import (
"fmt"
"github.com/ceph/go-ceph/rados"
"github.com/ceph/go-ceph/rbd"
"yunion.io/x/pkg/errors"
)
type SCluster struct {
conn *rados.Conn
}
func (self *SCluster) withCluster(doFunc func(*rados.Conn) (interface{}, error)) (interface{}, error) {
defer self.conn.Shutdown()
return doFunc(self.conn)
}
type SPool struct {
name string
cluster *SCluster
}
func (self *SPool) withIOContext(doFunc func(*rados.IOContext) (interface{}, error)) (interface{}, error) {
return self.cluster.withCluster(func(conn *rados.Conn) (interface{}, error) {
ioctx, err := conn.OpenIOContext(self.name)
if err != nil {
return nil, errors.Wrapf(err, "OpenIOContext(%s)", self.name)
}
return doFunc(ioctx)
})
}
func (self *SPool) GetCluster() *SCluster {
return self.cluster
}
func NewCluster(monHost, key string) (*SCluster, error) {
conn, err := rados.NewConn()
if err != nil {
return nil, err
}
for k, v := range map[string]string{"mon_host": monHost, "key": key} {
if len(v) > 0 {
err = conn.SetConfigOption(k, v)
if err != nil {
return nil, errors.Wrapf(err, "SetConfigOption %s %s", k, v)
}
}
}
for k, v := range map[string]int64{
"rados_osd_op_timeout": 20 * 60,
"rados_mon_op_timeout": 5,
"client_mount_timeout": 2 * 60,
} {
err = conn.SetConfigOption(k, fmt.Sprintf("%d", v))
if err != nil {
return nil, errors.Wrapf(err, "SetConfigOption %s %d", k, v)
}
}
err = conn.Connect()
if err != nil {
return nil, errors.Wrapf(err, "conn.Connect")
}
return &SCluster{conn: conn}, nil
}
func (self *SCluster) GetPool(name string) (*SPool, error) {
return &SPool{name: name, cluster: self}, nil
}
func (self *SCluster) ListPools() ([]string, error) {
pools, err := self.withCluster(func(conn *rados.Conn) (interface{}, error) {
return conn.ListPools()
})
if err != nil {
return nil, errors.Wrapf(err, "ListPools")
}
return pools.([]string), nil
}
func (self *SCluster) GetClusterStats() (rados.ClusterStat, error) {
stat, err := self.withCluster(func(conn *rados.Conn) (interface{}, error) {
return conn.GetClusterStats()
})
if err != nil {
return rados.ClusterStat{}, errors.Wrapf(err, "GetClusterStats")
}
return stat.(rados.ClusterStat), nil
}
func (self *SCluster) GetFSID() (string, error) {
fsid, err := self.withCluster(func(conn *rados.Conn) (interface{}, error) {
return conn.GetFSID()
})
if err != nil {
return "", errors.Wrapf(err, "GetFSID")
}
return fsid.(string), nil
}
func (self *SCluster) DeletePool(pool string) error {
_, err := self.withCluster(func(conn *rados.Conn) (interface{}, error) {
return nil, conn.DeletePool(pool)
})
return errors.Wrapf(err, "DeletePool")
}
type cmdOutput struct {
Buffer string
Info string
}
func (self *SCluster) MonCommand(args []byte) (cmdOutput, error) {
result := cmdOutput{}
_, err := self.withCluster(func(conn *rados.Conn) (interface{}, error) {
buffer, info, err := conn.MonCommand(args)
if err != nil {
return nil, errors.Wrapf(err, "MonCommand")
}
result.Buffer = string(buffer)
result.Info = info
return nil, nil
})
return result, errors.Wrapf(err, "DeletePool")
}
func (self *SPool) ListImages() ([]string, error) {
images, err := self.withIOContext(func(ioctx *rados.IOContext) (interface{}, error) {
return rbd.GetImageNames(ioctx)
})
if err != nil {
return nil, errors.Wrapf(err, "GetImageNames")
}
return images.([]string), nil
}
+36 -5
View File
@@ -27,7 +27,9 @@ import (
"yunion.io/x/pkg/errors"
apis "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/mcclient/auth"
agentmodels "yunion.io/x/onecloud/pkg/vpcagent/models"
"yunion.io/x/onecloud/pkg/vpcagent/options"
"yunion.io/x/onecloud/pkg/vpcagent/ovn/mac"
"yunion.io/x/onecloud/pkg/vpcagent/ovnutil"
)
@@ -264,7 +266,7 @@ func (keeper *OVNNorthboundKeeper) ClaimVpc(ctx context.Context, vpc *agentmodel
return keeper.cli.Must(ctx, "ClaimVpc", args)
}
func (keeper *OVNNorthboundKeeper) ClaimNetwork(ctx context.Context, network *agentmodels.Network, mtu int) error {
func (keeper *OVNNorthboundKeeper) ClaimNetwork(ctx context.Context, network *agentmodels.Network, opts *options.Options) error {
var (
rpMac = mac.HashSubnetRouterPortMac(network.Id)
dhcpMac = mac.HashSubnetDhcpMac(network.Id)
@@ -296,6 +298,7 @@ func (keeper *OVNNorthboundKeeper) ClaimNetwork(ctx context.Context, network *ag
mdIp, "0.0.0.0",
"0.0.0.0/0", network.GuestGateway,
}
mtu := opts.OvnUnderlayMtu
mtu -= apis.VPC_OVN_ENCAP_COST
const (
leaseTime = 86400 * 365 * 3
@@ -318,10 +321,38 @@ func (keeper *OVNNorthboundKeeper) ClaimNetwork(ctx context.Context, network *ag
externalKeyOcRef: network.Id,
},
}
if network.GuestDns != "" {
dhcpopts.Options["dns_server"] = "{" + network.GuestDns + "}"
} else {
dhcpopts.Options["dns_server"] = "{223.5.5.5,223.6.6.6}"
{
dnsSrvs := ""
if network.GuestDns != "" {
dnsSrvs = network.GuestDns
} else {
dns, err := auth.GetDNSServers(opts.Region, "")
if err != nil {
log.Errorf("auth.GetDNSServers fail %s", err)
} else {
dnsSrvs = strings.Join(dns, ",")
}
}
if len(dnsSrvs) == 0 {
dnsSrvs = apis.DefaultDNSServers
}
dhcpopts.Options["dns_server"] = "{" + dnsSrvs + "}"
}
{
ntpSrvs := ""
if network.GuestNtp != "" {
ntpSrvs = network.GuestNtp
} else {
ntp, err := auth.GetNTPServers(opts.Region, "")
if err != nil {
log.Errorf("auth.GetNTPServers fail %s", err)
} else {
ntpSrvs = strings.Join(ntp, ",")
}
}
if len(ntpSrvs) > 0 {
dhcpopts.Options["ntp_server"] = "{" + ntpSrvs + "}"
}
}
var (
+1 -1
View File
@@ -124,7 +124,7 @@ func (w *Worker) run(ctx context.Context, mss *agentmodels.ModelSets) (err error
ovndb.ClaimVpcEipgw(ctx, vpc)
}
for _, network := range vpc.Networks {
ovndb.ClaimNetwork(ctx, network, w.opts.OvnUnderlayMtu)
ovndb.ClaimNetwork(ctx, network, w.opts)
for _, guestnetwork := range network.Guestnetworks {
if guestnetwork.Guest == nil {
continue
-21
View File
@@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2014 Noah Watkins
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-328
View File
@@ -1,328 +0,0 @@
package rados
// #cgo LDFLAGS: -lrados
// #include <stdlib.h>
// #include <rados/librados.h>
import "C"
import "unsafe"
import "bytes"
import "fmt"
// ClusterStat represents Ceph cluster statistics.
type ClusterStat struct {
Kb uint64
Kb_used uint64
Kb_avail uint64
Num_objects uint64
}
// Conn is a connection handle to a Ceph cluster.
type Conn struct {
cluster C.rados_t
connected bool
}
// PingMonitor sends a ping to a monitor and returns the reply.
func (c *Conn) PingMonitor(id string) (string, error) {
c_id := C.CString(id)
defer C.free(unsafe.Pointer(c_id))
var strlen C.size_t
var strout *C.char
ret := C.rados_ping_monitor(c.cluster, c_id, &strout, &strlen)
defer C.rados_buffer_free(strout)
if ret == 0 {
reply := C.GoStringN(strout, (C.int)(strlen))
return reply, nil
} else {
return "", RadosError(int(ret))
}
}
// Connect establishes a connection to a RADOS cluster. It returns an error,
// if any.
func (c *Conn) Connect() error {
ret := C.rados_connect(c.cluster)
if ret != 0 {
return RadosError(int(ret))
}
c.connected = true
return nil
}
// Shutdown disconnects from the cluster.
func (c *Conn) Shutdown() {
if err := c.ensure_connected(); err != nil {
return
}
C.rados_shutdown(c.cluster)
}
// ReadConfigFile configures the connection using a Ceph configuration file.
func (c *Conn) ReadConfigFile(path string) error {
c_path := C.CString(path)
defer C.free(unsafe.Pointer(c_path))
ret := C.rados_conf_read_file(c.cluster, c_path)
if ret == 0 {
return nil
} else {
return RadosError(int(ret))
}
}
// ReadDefaultConfigFile configures the connection using a Ceph configuration
// file located at default locations.
func (c *Conn) ReadDefaultConfigFile() error {
ret := C.rados_conf_read_file(c.cluster, nil)
if ret == 0 {
return nil
} else {
return RadosError(int(ret))
}
}
func (c *Conn) OpenIOContext(pool string) (*IOContext, error) {
c_pool := C.CString(pool)
defer C.free(unsafe.Pointer(c_pool))
ioctx := &IOContext{}
ret := C.rados_ioctx_create(c.cluster, c_pool, &ioctx.ioctx)
if ret == 0 {
return ioctx, nil
} else {
return nil, RadosError(int(ret))
}
}
// ListPools returns the names of all existing pools.
func (c *Conn) ListPools() (names []string, err error) {
buf := make([]byte, 4096)
for {
ret := int(C.rados_pool_list(c.cluster,
(*C.char)(unsafe.Pointer(&buf[0])), C.size_t(len(buf))))
if ret < 0 {
return nil, RadosError(int(ret))
}
if ret > len(buf) {
buf = make([]byte, ret)
continue
}
tmp := bytes.SplitAfter(buf[:ret-1], []byte{0})
for _, s := range tmp {
if len(s) > 0 {
name := C.GoString((*C.char)(unsafe.Pointer(&s[0])))
names = append(names, name)
}
}
return names, nil
}
}
// SetConfigOption sets the value of the configuration option identified by
// the given name.
func (c *Conn) SetConfigOption(option, value string) error {
c_opt, c_val := C.CString(option), C.CString(value)
defer C.free(unsafe.Pointer(c_opt))
defer C.free(unsafe.Pointer(c_val))
ret := C.rados_conf_set(c.cluster, c_opt, c_val)
if ret < 0 {
return RadosError(int(ret))
} else {
return nil
}
}
// GetConfigOption returns the value of the Ceph configuration option
// identified by the given name.
func (c *Conn) GetConfigOption(name string) (value string, err error) {
buf := make([]byte, 4096)
c_name := C.CString(name)
defer C.free(unsafe.Pointer(c_name))
ret := int(C.rados_conf_get(c.cluster, c_name,
(*C.char)(unsafe.Pointer(&buf[0])), C.size_t(len(buf))))
// FIXME: ret may be -ENAMETOOLONG if the buffer is not large enough. We
// can handle this case, but we need a reliable way to test for
// -ENAMETOOLONG constant. Will the syscall/Errno stuff in Go help?
if ret == 0 {
value = C.GoString((*C.char)(unsafe.Pointer(&buf[0])))
return value, nil
} else {
return "", RadosError(ret)
}
}
// WaitForLatestOSDMap blocks the caller until the latest OSD map has been
// retrieved.
func (c *Conn) WaitForLatestOSDMap() error {
ret := C.rados_wait_for_latest_osdmap(c.cluster)
if ret < 0 {
return RadosError(int(ret))
} else {
return nil
}
}
func (c *Conn) ensure_connected() error {
if c.connected {
return nil
} else {
return RadosError(1)
}
}
// GetClusterStat returns statistics about the cluster associated with the
// connection.
func (c *Conn) GetClusterStats() (stat ClusterStat, err error) {
if err := c.ensure_connected(); err != nil {
return ClusterStat{}, err
}
c_stat := C.struct_rados_cluster_stat_t{}
ret := C.rados_cluster_stat(c.cluster, &c_stat)
if ret < 0 {
return ClusterStat{}, RadosError(int(ret))
} else {
return ClusterStat{
Kb: uint64(c_stat.kb),
Kb_used: uint64(c_stat.kb_used),
Kb_avail: uint64(c_stat.kb_avail),
Num_objects: uint64(c_stat.num_objects),
}, nil
}
}
// ParseCmdLineArgs configures the connection from command line arguments.
func (c *Conn) ParseCmdLineArgs(args []string) error {
// add an empty element 0 -- Ceph treats the array as the actual contents
// of argv and skips the first element (the executable name)
argc := C.int(len(args) + 1)
argv := make([]*C.char, argc)
// make the first element a string just in case it is ever examined
argv[0] = C.CString("placeholder")
defer C.free(unsafe.Pointer(argv[0]))
for i, arg := range args {
argv[i+1] = C.CString(arg)
defer C.free(unsafe.Pointer(argv[i+1]))
}
ret := C.rados_conf_parse_argv(c.cluster, argc, &argv[0])
if ret < 0 {
return RadosError(int(ret))
} else {
return nil
}
}
// ParseDefaultConfigEnv configures the connection from the default Ceph
// environment variable(s).
func (c *Conn) ParseDefaultConfigEnv() error {
ret := C.rados_conf_parse_env(c.cluster, nil)
if ret == 0 {
return nil
} else {
return RadosError(int(ret))
}
}
// GetFSID returns the fsid of the cluster as a hexadecimal string. The fsid
// is a unique identifier of an entire Ceph cluster.
func (c *Conn) GetFSID() (fsid string, err error) {
buf := make([]byte, 37)
ret := int(C.rados_cluster_fsid(c.cluster,
(*C.char)(unsafe.Pointer(&buf[0])), C.size_t(len(buf))))
// FIXME: the success case isn't documented correctly in librados.h
if ret == 36 {
fsid = C.GoString((*C.char)(unsafe.Pointer(&buf[0])))
return fsid, nil
} else {
return "", RadosError(int(ret))
}
}
// GetInstanceID returns a globally unique identifier for the cluster
// connection instance.
func (c *Conn) GetInstanceID() uint64 {
// FIXME: are there any error cases for this?
return uint64(C.rados_get_instance_id(c.cluster))
}
// MakePool creates a new pool with default settings.
func (c *Conn) MakePool(name string) error {
c_name := C.CString(name)
defer C.free(unsafe.Pointer(c_name))
ret := int(C.rados_pool_create(c.cluster, c_name))
if ret == 0 {
return nil
} else {
return RadosError(ret)
}
}
// DeletePool deletes a pool and all the data inside the pool.
func (c *Conn) DeletePool(name string) error {
if err := c.ensure_connected(); err != nil {
fmt.Println("NOT CONNECTED WHOOPS")
return err
}
c_name := C.CString(name)
defer C.free(unsafe.Pointer(c_name))
ret := int(C.rados_pool_delete(c.cluster, c_name))
if ret == 0 {
return nil
} else {
return RadosError(ret)
}
}
// MonCommand sends a command to one of the monitors
func (c *Conn) MonCommand(args []byte) (buffer []byte, info string, err error) {
return c.monCommand(args, nil)
}
// MonCommand sends a command to one of the monitors, with an input buffer
func (c *Conn) MonCommandWithInputBuffer(args, inputBuffer []byte) (buffer []byte, info string, err error) {
return c.monCommand(args, inputBuffer)
}
func (c *Conn) monCommand(args, inputBuffer []byte) (buffer []byte, info string, err error) {
argv := C.CString(string(args))
defer C.free(unsafe.Pointer(argv))
var (
outs, outbuf *C.char
outslen, outbuflen C.size_t
)
inbuf := C.CString(string(inputBuffer))
inbufLen := len(inputBuffer)
defer C.free(unsafe.Pointer(inbuf))
ret := C.rados_mon_command(c.cluster,
&argv, 1,
inbuf, // bulk input (e.g. crush map)
C.size_t(inbufLen), // length inbuf
&outbuf, // buffer
&outbuflen, // buffer length
&outs, // status string
&outslen)
if outslen > 0 {
info = C.GoStringN(outs, C.int(outslen))
C.free(unsafe.Pointer(outs))
}
if outbuflen > 0 {
buffer = C.GoBytes(unsafe.Pointer(outbuf), C.int(outbuflen))
C.free(unsafe.Pointer(outbuf))
}
if ret != 0 {
err = RadosError(int(ret))
return nil, info, err
}
return
}
-4
View File
@@ -1,4 +0,0 @@
/*
Set of wrappers around librados API.
*/
package rados
-890
View File
@@ -1,890 +0,0 @@
package rados
// #cgo LDFLAGS: -lrados
// #include <errno.h>
// #include <stdlib.h>
// #include <rados/librados.h>
//
// char* nextChunk(char **idx) {
// char *copy;
// copy = strdup(*idx);
// *idx += strlen(*idx) + 1;
// return copy;
// }
//
// #if __APPLE__
// #define ceph_time_t __darwin_time_t
// #define ceph_suseconds_t __darwin_suseconds_t
// #elif __GLIBC__
// #define ceph_time_t __time_t
// #define ceph_suseconds_t __suseconds_t
// #else
// #define ceph_time_t time_t
// #define ceph_suseconds_t suseconds_t
// #endif
import "C"
import (
"syscall"
"time"
"unsafe"
)
// PoolStat represents Ceph pool statistics.
type PoolStat struct {
// space used in bytes
Num_bytes uint64
// space used in KB
Num_kb uint64
// number of objects in the pool
Num_objects uint64
// number of clones of objects
Num_object_clones uint64
// num_objects * num_replicas
Num_object_copies uint64
Num_objects_missing_on_primary uint64
// number of objects found on no OSDs
Num_objects_unfound uint64
// number of objects replicated fewer times than they should be
// (but found on at least one OSD)
Num_objects_degraded uint64
Num_rd uint64
Num_rd_kb uint64
Num_wr uint64
Num_wr_kb uint64
}
// ObjectStat represents an object stat information
type ObjectStat struct {
// current length in bytes
Size uint64
// last modification time
ModTime time.Time
}
// LockInfo represents information on a current Ceph lock
type LockInfo struct {
NumLockers int
Exclusive bool
Tag string
Clients []string
Cookies []string
Addrs []string
}
// IOContext represents a context for performing I/O within a pool.
type IOContext struct {
ioctx C.rados_ioctx_t
}
// Pointer returns a uintptr representation of the IOContext.
func (ioctx *IOContext) Pointer() uintptr {
return uintptr(ioctx.ioctx)
}
// SetNamespace sets the namespace for objects within this IO context (pool).
// Setting namespace to a empty or zero length string sets the pool to the default namespace.
func (ioctx *IOContext) SetNamespace(namespace string) {
var c_ns *C.char
if len(namespace) > 0 {
c_ns = C.CString(namespace)
defer C.free(unsafe.Pointer(c_ns))
}
C.rados_ioctx_set_namespace(ioctx.ioctx, c_ns)
}
// Write writes len(data) bytes to the object with key oid starting at byte
// offset offset. It returns an error, if any.
func (ioctx *IOContext) Write(oid string, data []byte, offset uint64) error {
c_oid := C.CString(oid)
defer C.free(unsafe.Pointer(c_oid))
dataPointer := unsafe.Pointer(nil)
if len(data) > 0 {
dataPointer = unsafe.Pointer(&data[0])
}
ret := C.rados_write(ioctx.ioctx, c_oid,
(*C.char)(dataPointer),
(C.size_t)(len(data)),
(C.uint64_t)(offset))
return GetRadosError(int(ret))
}
// WriteFull writes len(data) bytes to the object with key oid.
// The object is filled with the provided data. If the object exists,
// it is atomically truncated and then written. It returns an error, if any.
func (ioctx *IOContext) WriteFull(oid string, data []byte) error {
c_oid := C.CString(oid)
defer C.free(unsafe.Pointer(c_oid))
ret := C.rados_write_full(ioctx.ioctx, c_oid,
(*C.char)(unsafe.Pointer(&data[0])),
(C.size_t)(len(data)))
return GetRadosError(int(ret))
}
// Append appends len(data) bytes to the object with key oid.
// The object is appended with the provided data. If the object exists,
// it is atomically appended to. It returns an error, if any.
func (ioctx *IOContext) Append(oid string, data []byte) error {
c_oid := C.CString(oid)
defer C.free(unsafe.Pointer(c_oid))
ret := C.rados_append(ioctx.ioctx, c_oid,
(*C.char)(unsafe.Pointer(&data[0])),
(C.size_t)(len(data)))
return GetRadosError(int(ret))
}
// Read reads up to len(data) bytes from the object with key oid starting at byte
// offset offset. It returns the number of bytes read and an error, if any.
func (ioctx *IOContext) Read(oid string, data []byte, offset uint64) (int, error) {
c_oid := C.CString(oid)
defer C.free(unsafe.Pointer(c_oid))
var buf *C.char
if len(data) > 0 {
buf = (*C.char)(unsafe.Pointer(&data[0]))
}
ret := C.rados_read(
ioctx.ioctx,
c_oid,
buf,
(C.size_t)(len(data)),
(C.uint64_t)(offset))
if ret >= 0 {
return int(ret), nil
} else {
return 0, GetRadosError(int(ret))
}
}
// Delete deletes the object with key oid. It returns an error, if any.
func (ioctx *IOContext) Delete(oid string) error {
c_oid := C.CString(oid)
defer C.free(unsafe.Pointer(c_oid))
return GetRadosError(int(C.rados_remove(ioctx.ioctx, c_oid)))
}
// Truncate resizes the object with key oid to size size. If the operation
// enlarges the object, the new area is logically filled with zeroes. If the
// operation shrinks the object, the excess data is removed. It returns an
// error, if any.
func (ioctx *IOContext) Truncate(oid string, size uint64) error {
c_oid := C.CString(oid)
defer C.free(unsafe.Pointer(c_oid))
return GetRadosError(int(C.rados_trunc(ioctx.ioctx, c_oid, (C.uint64_t)(size))))
}
// Destroy informs librados that the I/O context is no longer in use.
// Resources associated with the context may not be freed immediately, and the
// context should not be used again after calling this method.
func (ioctx *IOContext) Destroy() {
C.rados_ioctx_destroy(ioctx.ioctx)
}
// Stat returns a set of statistics about the pool associated with this I/O
// context.
func (ioctx *IOContext) GetPoolStats() (stat PoolStat, err error) {
c_stat := C.struct_rados_pool_stat_t{}
ret := C.rados_ioctx_pool_stat(ioctx.ioctx, &c_stat)
if ret < 0 {
return PoolStat{}, GetRadosError(int(ret))
} else {
return PoolStat{
Num_bytes: uint64(c_stat.num_bytes),
Num_kb: uint64(c_stat.num_kb),
Num_objects: uint64(c_stat.num_objects),
Num_object_clones: uint64(c_stat.num_object_clones),
Num_object_copies: uint64(c_stat.num_object_copies),
Num_objects_missing_on_primary: uint64(c_stat.num_objects_missing_on_primary),
Num_objects_unfound: uint64(c_stat.num_objects_unfound),
Num_objects_degraded: uint64(c_stat.num_objects_degraded),
Num_rd: uint64(c_stat.num_rd),
Num_rd_kb: uint64(c_stat.num_rd_kb),
Num_wr: uint64(c_stat.num_wr),
Num_wr_kb: uint64(c_stat.num_wr_kb),
}, nil
}
}
// GetPoolName returns the name of the pool associated with the I/O context.
func (ioctx *IOContext) GetPoolName() (name string, err error) {
buf := make([]byte, 128)
for {
ret := C.rados_ioctx_get_pool_name(ioctx.ioctx,
(*C.char)(unsafe.Pointer(&buf[0])), C.unsigned(len(buf)))
if ret == -C.ERANGE {
buf = make([]byte, len(buf)*2)
continue
} else if ret < 0 {
return "", GetRadosError(int(ret))
}
name = C.GoStringN((*C.char)(unsafe.Pointer(&buf[0])), ret)
return name, nil
}
}
// ObjectListFunc is the type of the function called for each object visited
// by ListObjects.
type ObjectListFunc func(oid string)
// ListObjects lists all of the objects in the pool associated with the I/O
// context, and called the provided listFn function for each object, passing
// to the function the name of the object. Call SetNamespace with
// RadosAllNamespaces before calling this function to return objects from all
// namespaces
func (ioctx *IOContext) ListObjects(listFn ObjectListFunc) error {
var ctx C.rados_list_ctx_t
ret := C.rados_nobjects_list_open(ioctx.ioctx, &ctx)
if ret < 0 {
return GetRadosError(int(ret))
}
defer func() { C.rados_nobjects_list_close(ctx) }()
for {
var c_entry *C.char
ret := C.rados_nobjects_list_next(ctx, &c_entry, nil, nil)
if ret == -C.ENOENT {
return nil
} else if ret < 0 {
return GetRadosError(int(ret))
}
listFn(C.GoString(c_entry))
}
}
// Stat returns the size of the object and its last modification time
func (ioctx *IOContext) Stat(object string) (stat ObjectStat, err error) {
var c_psize C.uint64_t
var c_pmtime C.time_t
c_object := C.CString(object)
defer C.free(unsafe.Pointer(c_object))
ret := C.rados_stat(
ioctx.ioctx,
c_object,
&c_psize,
&c_pmtime)
if ret < 0 {
return ObjectStat{}, GetRadosError(int(ret))
} else {
return ObjectStat{
Size: uint64(c_psize),
ModTime: time.Unix(int64(c_pmtime), 0),
}, nil
}
}
// GetXattr gets an xattr with key `name`, it returns the length of
// the key read or an error if not successful
func (ioctx *IOContext) GetXattr(object string, name string, data []byte) (int, error) {
c_object := C.CString(object)
c_name := C.CString(name)
defer C.free(unsafe.Pointer(c_object))
defer C.free(unsafe.Pointer(c_name))
ret := C.rados_getxattr(
ioctx.ioctx,
c_object,
c_name,
(*C.char)(unsafe.Pointer(&data[0])),
(C.size_t)(len(data)))
if ret >= 0 {
return int(ret), nil
} else {
return 0, GetRadosError(int(ret))
}
}
// Sets an xattr for an object with key `name` with value as `data`
func (ioctx *IOContext) SetXattr(object string, name string, data []byte) error {
c_object := C.CString(object)
c_name := C.CString(name)
defer C.free(unsafe.Pointer(c_object))
defer C.free(unsafe.Pointer(c_name))
ret := C.rados_setxattr(
ioctx.ioctx,
c_object,
c_name,
(*C.char)(unsafe.Pointer(&data[0])),
(C.size_t)(len(data)))
return GetRadosError(int(ret))
}
// function that lists all the xattrs for an object, since xattrs are
// a k-v pair, this function returns a map of k-v pairs on
// success, error code on failure
func (ioctx *IOContext) ListXattrs(oid string) (map[string][]byte, error) {
c_oid := C.CString(oid)
defer C.free(unsafe.Pointer(c_oid))
var it C.rados_xattrs_iter_t
ret := C.rados_getxattrs(ioctx.ioctx, c_oid, &it)
if ret < 0 {
return nil, GetRadosError(int(ret))
}
defer func() { C.rados_getxattrs_end(it) }()
m := make(map[string][]byte)
for {
var c_name, c_val *C.char
var c_len C.size_t
defer C.free(unsafe.Pointer(c_name))
defer C.free(unsafe.Pointer(c_val))
ret := C.rados_getxattrs_next(it, &c_name, &c_val, &c_len)
if ret < 0 {
return nil, GetRadosError(int(ret))
}
// rados api returns a null name,val & 0-length upon
// end of iteration
if c_name == nil {
return m, nil // stop iteration
}
m[C.GoString(c_name)] = C.GoBytes(unsafe.Pointer(c_val), (C.int)(c_len))
}
}
// Remove an xattr with key `name` from object `oid`
func (ioctx *IOContext) RmXattr(oid string, name string) error {
c_oid := C.CString(oid)
c_name := C.CString(name)
defer C.free(unsafe.Pointer(c_oid))
defer C.free(unsafe.Pointer(c_name))
ret := C.rados_rmxattr(
ioctx.ioctx,
c_oid,
c_name)
return GetRadosError(int(ret))
}
// Append the map `pairs` to the omap `oid`
func (ioctx *IOContext) SetOmap(oid string, pairs map[string][]byte) error {
c_oid := C.CString(oid)
defer C.free(unsafe.Pointer(c_oid))
var s C.size_t
var c *C.char
ptrSize := unsafe.Sizeof(c)
c_keys := C.malloc(C.size_t(len(pairs)) * C.size_t(ptrSize))
c_values := C.malloc(C.size_t(len(pairs)) * C.size_t(ptrSize))
c_lengths := C.malloc(C.size_t(len(pairs)) * C.size_t(unsafe.Sizeof(s)))
defer C.free(unsafe.Pointer(c_keys))
defer C.free(unsafe.Pointer(c_values))
defer C.free(unsafe.Pointer(c_lengths))
i := 0
for key, value := range pairs {
// key
c_key_ptr := (**C.char)(unsafe.Pointer(uintptr(c_keys) + uintptr(i)*ptrSize))
*c_key_ptr = C.CString(key)
defer C.free(unsafe.Pointer(*c_key_ptr))
// value and its length
c_value_ptr := (**C.char)(unsafe.Pointer(uintptr(c_values) + uintptr(i)*ptrSize))
var c_length C.size_t
if len(value) > 0 {
*c_value_ptr = (*C.char)(unsafe.Pointer(&value[0]))
c_length = C.size_t(len(value))
} else {
*c_value_ptr = nil
c_length = C.size_t(0)
}
c_length_ptr := (*C.size_t)(unsafe.Pointer(uintptr(c_lengths) + uintptr(i)*ptrSize))
*c_length_ptr = c_length
i++
}
op := C.rados_create_write_op()
C.rados_write_op_omap_set(
op,
(**C.char)(c_keys),
(**C.char)(c_values),
(*C.size_t)(c_lengths),
C.size_t(len(pairs)))
ret := C.rados_write_op_operate(op, ioctx.ioctx, c_oid, nil, 0)
C.rados_release_write_op(op)
return GetRadosError(int(ret))
}
// OmapListFunc is the type of the function called for each omap key
// visited by ListOmapValues
type OmapListFunc func(key string, value []byte)
// Iterate on a set of keys and their values from an omap
// `startAfter`: iterate only on the keys after this specified one
// `filterPrefix`: iterate only on the keys beginning with this prefix
// `maxReturn`: iterate no more than `maxReturn` key/value pairs
// `listFn`: the function called at each iteration
func (ioctx *IOContext) ListOmapValues(oid string, startAfter string, filterPrefix string, maxReturn int64, listFn OmapListFunc) error {
c_oid := C.CString(oid)
c_start_after := C.CString(startAfter)
c_filter_prefix := C.CString(filterPrefix)
c_max_return := C.uint64_t(maxReturn)
defer C.free(unsafe.Pointer(c_oid))
defer C.free(unsafe.Pointer(c_start_after))
defer C.free(unsafe.Pointer(c_filter_prefix))
op := C.rados_create_read_op()
var c_iter C.rados_omap_iter_t
var c_prval C.int
C.rados_read_op_omap_get_vals2(
op,
c_start_after,
c_filter_prefix,
c_max_return,
&c_iter,
nil,
&c_prval,
)
ret := C.rados_read_op_operate(op, ioctx.ioctx, c_oid, 0)
if int(ret) != 0 {
return GetRadosError(int(ret))
} else if int(c_prval) != 0 {
return RadosError(int(c_prval))
}
for {
var c_key *C.char
var c_val *C.char
var c_len C.size_t
ret = C.rados_omap_get_next(c_iter, &c_key, &c_val, &c_len)
if int(ret) != 0 {
return GetRadosError(int(ret))
}
if c_key == nil {
break
}
listFn(C.GoString(c_key), C.GoBytes(unsafe.Pointer(c_val), C.int(c_len)))
}
C.rados_omap_get_end(c_iter)
C.rados_release_read_op(op)
return nil
}
// Fetch a set of keys and their values from an omap and returns then as a map
// `startAfter`: retrieve only the keys after this specified one
// `filterPrefix`: retrieve only the keys beginning with this prefix
// `maxReturn`: retrieve no more than `maxReturn` key/value pairs
func (ioctx *IOContext) GetOmapValues(oid string, startAfter string, filterPrefix string, maxReturn int64) (map[string][]byte, error) {
omap := map[string][]byte{}
err := ioctx.ListOmapValues(
oid, startAfter, filterPrefix, maxReturn,
func(key string, value []byte) {
omap[key] = value
},
)
return omap, err
}
// Fetch all the keys and their values from an omap and returns then as a map
// `startAfter`: retrieve only the keys after this specified one
// `filterPrefix`: retrieve only the keys beginning with this prefix
// `iteratorSize`: internal number of keys to fetch during a read operation
func (ioctx *IOContext) GetAllOmapValues(oid string, startAfter string, filterPrefix string, iteratorSize int64) (map[string][]byte, error) {
omap := map[string][]byte{}
omapSize := 0
for {
err := ioctx.ListOmapValues(
oid, startAfter, filterPrefix, iteratorSize,
func(key string, value []byte) {
omap[key] = value
startAfter = key
},
)
if err != nil {
return omap, err
}
// End of omap
if len(omap) == omapSize {
break
}
omapSize = len(omap)
}
return omap, nil
}
// Remove the specified `keys` from the omap `oid`
func (ioctx *IOContext) RmOmapKeys(oid string, keys []string) error {
c_oid := C.CString(oid)
defer C.free(unsafe.Pointer(c_oid))
var c *C.char
ptrSize := unsafe.Sizeof(c)
c_keys := C.malloc(C.size_t(len(keys)) * C.size_t(ptrSize))
defer C.free(unsafe.Pointer(c_keys))
i := 0
for _, key := range keys {
c_key_ptr := (**C.char)(unsafe.Pointer(uintptr(c_keys) + uintptr(i)*ptrSize))
*c_key_ptr = C.CString(key)
defer C.free(unsafe.Pointer(*c_key_ptr))
i++
}
op := C.rados_create_write_op()
C.rados_write_op_omap_rm_keys(
op,
(**C.char)(c_keys),
C.size_t(len(keys)))
ret := C.rados_write_op_operate(op, ioctx.ioctx, c_oid, nil, 0)
C.rados_release_write_op(op)
return GetRadosError(int(ret))
}
// Clear the omap `oid`
func (ioctx *IOContext) CleanOmap(oid string) error {
c_oid := C.CString(oid)
defer C.free(unsafe.Pointer(c_oid))
op := C.rados_create_write_op()
C.rados_write_op_omap_clear(op)
ret := C.rados_write_op_operate(op, ioctx.ioctx, c_oid, nil, 0)
C.rados_release_write_op(op)
return GetRadosError(int(ret))
}
type Iter struct {
ctx C.rados_list_ctx_t
err error
entry string
namespace string
}
type IterToken uint32
// Return a Iterator object that can be used to list the object names in the current pool
func (ioctx *IOContext) Iter() (*Iter, error) {
iter := Iter{}
if cerr := C.rados_nobjects_list_open(ioctx.ioctx, &iter.ctx); cerr < 0 {
return nil, GetRadosError(int(cerr))
}
return &iter, nil
}
// Returns a token marking the current position of the iterator. To be used in combination with Iter.Seek()
func (iter *Iter) Token() IterToken {
return IterToken(C.rados_nobjects_list_get_pg_hash_position(iter.ctx))
}
func (iter *Iter) Seek(token IterToken) {
C.rados_nobjects_list_seek(iter.ctx, C.uint32_t(token))
}
// Next retrieves the next object name in the pool/namespace iterator.
// Upon a successful invocation (return value of true), the Value method should
// be used to obtain the name of the retrieved object name. When the iterator is
// exhausted, Next returns false. The Err method should used to verify whether the
// end of the iterator was reached, or the iterator received an error.
//
// Example:
// iter := pool.Iter()
// defer iter.Close()
// for iter.Next() {
// fmt.Printf("%v\n", iter.Value())
// }
// return iter.Err()
//
func (iter *Iter) Next() bool {
var c_entry *C.char
var c_namespace *C.char
if cerr := C.rados_nobjects_list_next(iter.ctx, &c_entry, nil, &c_namespace); cerr < 0 {
iter.err = GetRadosError(int(cerr))
return false
}
iter.entry = C.GoString(c_entry)
iter.namespace = C.GoString(c_namespace)
return true
}
// Returns the current value of the iterator (object name), after a successful call to Next.
func (iter *Iter) Value() string {
if iter.err != nil {
return ""
}
return iter.entry
}
// Returns the namespace associated with the current value of the iterator (object name), after a successful call to Next.
func (iter *Iter) Namespace() string {
if iter.err != nil {
return ""
}
return iter.namespace
}
// Checks whether the iterator has encountered an error.
func (iter *Iter) Err() error {
if iter.err == RadosErrorNotFound {
return nil
}
return iter.err
}
// Closes the iterator cursor on the server. Be aware that iterators are not closed automatically
// at the end of iteration.
func (iter *Iter) Close() {
C.rados_nobjects_list_close(iter.ctx)
}
// Take an exclusive lock on an object.
func (ioctx *IOContext) LockExclusive(oid, name, cookie, desc string, duration time.Duration, flags *byte) (int, error) {
c_oid := C.CString(oid)
c_name := C.CString(name)
c_cookie := C.CString(cookie)
c_desc := C.CString(desc)
var c_duration C.struct_timeval
if duration != 0 {
tv := syscall.NsecToTimeval(duration.Nanoseconds())
c_duration = C.struct_timeval{tv_sec: C.ceph_time_t(tv.Sec), tv_usec: C.ceph_suseconds_t(tv.Usec)}
}
var c_flags C.uint8_t
if flags != nil {
c_flags = C.uint8_t(*flags)
}
defer C.free(unsafe.Pointer(c_oid))
defer C.free(unsafe.Pointer(c_name))
defer C.free(unsafe.Pointer(c_cookie))
defer C.free(unsafe.Pointer(c_desc))
ret := C.rados_lock_exclusive(
ioctx.ioctx,
c_oid,
c_name,
c_cookie,
c_desc,
&c_duration,
c_flags)
// 0 on success, negative error code on failure
// -EBUSY if the lock is already held by another (client, cookie) pair
// -EEXIST if the lock is already held by the same (client, cookie) pair
switch ret {
case 0:
return int(ret), nil
case -C.EBUSY:
return int(ret), nil
case -C.EEXIST:
return int(ret), nil
default:
return int(ret), RadosError(int(ret))
}
}
// Take a shared lock on an object.
func (ioctx *IOContext) LockShared(oid, name, cookie, tag, desc string, duration time.Duration, flags *byte) (int, error) {
c_oid := C.CString(oid)
c_name := C.CString(name)
c_cookie := C.CString(cookie)
c_tag := C.CString(tag)
c_desc := C.CString(desc)
var c_duration C.struct_timeval
if duration != 0 {
tv := syscall.NsecToTimeval(duration.Nanoseconds())
c_duration = C.struct_timeval{tv_sec: C.ceph_time_t(tv.Sec), tv_usec: C.ceph_suseconds_t(tv.Usec)}
}
var c_flags C.uint8_t
if flags != nil {
c_flags = C.uint8_t(*flags)
}
defer C.free(unsafe.Pointer(c_oid))
defer C.free(unsafe.Pointer(c_name))
defer C.free(unsafe.Pointer(c_cookie))
defer C.free(unsafe.Pointer(c_tag))
defer C.free(unsafe.Pointer(c_desc))
ret := C.rados_lock_shared(
ioctx.ioctx,
c_oid,
c_name,
c_cookie,
c_tag,
c_desc,
&c_duration,
c_flags)
// 0 on success, negative error code on failure
// -EBUSY if the lock is already held by another (client, cookie) pair
// -EEXIST if the lock is already held by the same (client, cookie) pair
switch ret {
case 0:
return int(ret), nil
case -C.EBUSY:
return int(ret), nil
case -C.EEXIST:
return int(ret), nil
default:
return int(ret), RadosError(int(ret))
}
}
// Release a shared or exclusive lock on an object.
func (ioctx *IOContext) Unlock(oid, name, cookie string) (int, error) {
c_oid := C.CString(oid)
c_name := C.CString(name)
c_cookie := C.CString(cookie)
defer C.free(unsafe.Pointer(c_oid))
defer C.free(unsafe.Pointer(c_name))
defer C.free(unsafe.Pointer(c_cookie))
// 0 on success, negative error code on failure
// -ENOENT if the lock is not held by the specified (client, cookie) pair
ret := C.rados_unlock(
ioctx.ioctx,
c_oid,
c_name,
c_cookie)
switch ret {
case 0:
return int(ret), nil
case -C.ENOENT:
return int(ret), nil
default:
return int(ret), RadosError(int(ret))
}
}
// List clients that have locked the named object lock and information about the lock.
// The number of bytes required in each buffer is put in the corresponding size out parameter.
// If any of the provided buffers are too short, -ERANGE is returned after these sizes are filled in.
func (ioctx *IOContext) ListLockers(oid, name string) (*LockInfo, error) {
c_oid := C.CString(oid)
c_name := C.CString(name)
c_tag := (*C.char)(C.malloc(C.size_t(1024)))
c_clients := (*C.char)(C.malloc(C.size_t(1024)))
c_cookies := (*C.char)(C.malloc(C.size_t(1024)))
c_addrs := (*C.char)(C.malloc(C.size_t(1024)))
var c_exclusive C.int
c_tag_len := C.size_t(1024)
c_clients_len := C.size_t(1024)
c_cookies_len := C.size_t(1024)
c_addrs_len := C.size_t(1024)
defer C.free(unsafe.Pointer(c_oid))
defer C.free(unsafe.Pointer(c_name))
defer C.free(unsafe.Pointer(c_tag))
defer C.free(unsafe.Pointer(c_clients))
defer C.free(unsafe.Pointer(c_cookies))
defer C.free(unsafe.Pointer(c_addrs))
ret := C.rados_list_lockers(
ioctx.ioctx,
c_oid,
c_name,
&c_exclusive,
c_tag,
&c_tag_len,
c_clients,
&c_clients_len,
c_cookies,
&c_cookies_len,
c_addrs,
&c_addrs_len)
splitCString := func(items *C.char, itemsLen C.size_t) []string {
currLen := 0
clients := []string{}
for currLen < int(itemsLen) {
client := C.GoString(C.nextChunk(&items))
clients = append(clients, client)
currLen += len(client) + 1
}
return clients
}
if ret < 0 {
return nil, RadosError(int(ret))
} else {
return &LockInfo{int(ret), c_exclusive == 1, C.GoString(c_tag), splitCString(c_clients, c_clients_len), splitCString(c_cookies, c_cookies_len), splitCString(c_addrs, c_addrs_len)}, nil
}
}
// Releases a shared or exclusive lock on an object, which was taken by the specified client.
func (ioctx *IOContext) BreakLock(oid, name, client, cookie string) (int, error) {
c_oid := C.CString(oid)
c_name := C.CString(name)
c_client := C.CString(client)
c_cookie := C.CString(cookie)
defer C.free(unsafe.Pointer(c_oid))
defer C.free(unsafe.Pointer(c_name))
defer C.free(unsafe.Pointer(c_client))
defer C.free(unsafe.Pointer(c_cookie))
// 0 on success, negative error code on failure
// -ENOENT if the lock is not held by the specified (client, cookie) pair
// -EINVAL if the client cannot be parsed
ret := C.rados_break_lock(
ioctx.ioctx,
c_oid,
c_name,
c_client,
c_cookie)
switch ret {
case 0:
return int(ret), nil
case -C.ENOENT:
return int(ret), nil
case -C.EINVAL: // -EINVAL
return int(ret), nil
default:
return int(ret), RadosError(int(ret))
}
}
-85
View File
@@ -1,85 +0,0 @@
package rados
// #cgo LDFLAGS: -lrados
// #include <errno.h>
// #include <stdlib.h>
// #include <rados/librados.h>
import "C"
import (
"fmt"
"unsafe"
)
type RadosError int
func (e RadosError) Error() string {
return fmt.Sprintf("rados: %s", C.GoString(C.strerror(C.int(-e))))
}
var RadosAllNamespaces = C.LIBRADOS_ALL_NSPACES
var RadosErrorNotFound = RadosError(-C.ENOENT)
var RadosErrorPermissionDenied = RadosError(-C.EPERM)
func GetRadosError(err int) error {
if err == 0 {
return nil
}
return RadosError(err)
}
// Version returns the major, minor, and patch components of the version of
// the RADOS library linked against.
func Version() (int, int, int) {
var c_major, c_minor, c_patch C.int
C.rados_version(&c_major, &c_minor, &c_patch)
return int(c_major), int(c_minor), int(c_patch)
}
func makeConn() *Conn {
return &Conn{connected: false}
}
func newConn(user *C.char) (*Conn, error) {
conn := makeConn()
ret := C.rados_create(&conn.cluster, user)
if ret == 0 {
return conn, nil
} else {
return nil, RadosError(int(ret))
}
}
// NewConn creates a new connection object. It returns the connection and an
// error, if any.
func NewConn() (*Conn, error) {
return newConn(nil)
}
// NewConnWithUser creates a new connection object with a custom username.
// It returns the connection and an error, if any.
func NewConnWithUser(user string) (*Conn, error) {
c_user := C.CString(user)
defer C.free(unsafe.Pointer(c_user))
return newConn(c_user)
}
// NewConnWithClusterAndUser creates a new connection object for a specific cluster and username.
// It returns the connection and an error, if any.
func NewConnWithClusterAndUser(clusterName string, userName string) (*Conn, error) {
c_cluster_name := C.CString(clusterName)
defer C.free(unsafe.Pointer(c_cluster_name))
c_name := C.CString(userName)
defer C.free(unsafe.Pointer(c_name))
conn := makeConn()
ret := C.rados_create2(&conn.cluster, c_cluster_name, c_name, 0)
if ret == 0 {
return conn, nil
} else {
return nil, RadosError(int(ret))
}
}
-4
View File
@@ -1,4 +0,0 @@
/*
Wrappers around librbd.
*/
package rbd
-997
View File
@@ -1,997 +0,0 @@
package rbd
// #cgo LDFLAGS: -lrbd
// #include <errno.h>
// #include <stdlib.h>
// #include <rados/librados.h>
// #include <rbd/librbd.h>
// #include <rbd/features.h>
import "C"
import (
"bytes"
"errors"
"fmt"
"io"
"time"
"unsafe"
"github.com/ceph/go-ceph/rados"
)
const (
// RBD features.
RbdFeatureLayering = C.RBD_FEATURE_LAYERING
RbdFeatureStripingV2 = C.RBD_FEATURE_STRIPINGV2
RbdFeatureExclusiveLock = C.RBD_FEATURE_EXCLUSIVE_LOCK
RbdFeatureObjectMap = C.RBD_FEATURE_OBJECT_MAP
RbdFeatureFastDiff = C.RBD_FEATURE_FAST_DIFF
RbdFeatureDeepFlatten = C.RBD_FEATURE_DEEP_FLATTEN
RbdFeatureJournaling = C.RBD_FEATURE_JOURNALING
RbdFeatureDataPool = C.RBD_FEATURE_DATA_POOL
RbdFeaturesDefault = C.RBD_FEATURES_DEFAULT
// Features that make an image inaccessible for read or write by clients that don't understand
// them.
RbdFeaturesIncompatible = C.RBD_FEATURES_INCOMPATIBLE
// Features that make an image unwritable by clients that don't understand them.
RbdFeaturesRwIncompatible = C.RBD_FEATURES_RW_INCOMPATIBLE
// Features that may be dynamically enabled or disabled.
RbdFeaturesMutable = C.RBD_FEATURES_MUTABLE
// Features that only work when used with a single client using the image for writes.
RbdFeaturesSingleClient = C.RBD_FEATURES_SINGLE_CLIENT
)
//
type RBDError int
var (
RbdErrorImageNotOpen = errors.New("RBD image not open")
RbdErrorNotFound = errors.New("RBD image not found")
)
//
type ImageInfo struct {
Size uint64
Obj_size uint64
Num_objs uint64
Order int
Block_name_prefix string
Parent_pool int64
Parent_name string
}
//
type SnapInfo struct {
Id uint64
Size uint64
Name string
}
//
type Locker struct {
Client string
Cookie string
Addr string
}
//
type Image struct {
io.Reader
io.Writer
io.Seeker
io.ReaderAt
io.WriterAt
name string
offset int64
ioctx *rados.IOContext
image C.rbd_image_t
}
//
type Snapshot struct {
image *Image
name string
}
// TrashInfo contains information about trashed RBDs.
type TrashInfo struct {
Id string // Id string, required to remove / restore trashed RBDs.
Name string // Original name of trashed RBD.
DeletionTime time.Time // Date / time at which the RBD was moved to the trash.
DefermentEndTime time.Time // Date / time after which the trashed RBD may be permanently deleted.
}
//
func split(buf []byte) (values []string) {
tmp := bytes.Split(buf[:len(buf)-1], []byte{0})
for _, s := range tmp {
if len(s) > 0 {
go_s := C.GoString((*C.char)(unsafe.Pointer(&s[0])))
values = append(values, go_s)
}
}
return values
}
//
func (e RBDError) Error() string {
return fmt.Sprintf("rbd: ret=%d", e)
}
//
func GetError(err C.int) error {
if err != 0 {
if err == -C.ENOENT {
return RbdErrorNotFound
}
return RBDError(err)
} else {
return nil
}
}
//
func Version() (int, int, int) {
var c_major, c_minor, c_patch C.int
C.rbd_version(&c_major, &c_minor, &c_patch)
return int(c_major), int(c_minor), int(c_patch)
}
// GetImageNames returns the list of current RBD images.
func GetImageNames(ioctx *rados.IOContext) (names []string, err error) {
buf := make([]byte, 4096)
for {
size := C.size_t(len(buf))
ret := C.rbd_list(C.rados_ioctx_t(ioctx.Pointer()),
(*C.char)(unsafe.Pointer(&buf[0])), &size)
if ret == -C.ERANGE {
buf = make([]byte, size)
continue
} else if ret < 0 {
return nil, RBDError(ret)
}
tmp := bytes.Split(buf[:size-1], []byte{0})
for _, s := range tmp {
if len(s) > 0 {
name := C.GoString((*C.char)(unsafe.Pointer(&s[0])))
names = append(names, name)
}
}
return names, nil
}
}
//
func GetImage(ioctx *rados.IOContext, name string) *Image {
return &Image{
ioctx: ioctx,
name: name,
}
}
// int rbd_create(rados_ioctx_t io, const char *name, uint64_t size, int *order);
// int rbd_create2(rados_ioctx_t io, const char *name, uint64_t size,
// uint64_t features, int *order);
// int rbd_create3(rados_ioctx_t io, const char *name, uint64_t size,
// uint64_t features, int *order,
// uint64_t stripe_unit, uint64_t stripe_count);
func Create(ioctx *rados.IOContext, name string, size uint64, order int,
args ...uint64) (image *Image, err error) {
var ret C.int
c_order := C.int(order)
c_name := C.CString(name)
defer C.free(unsafe.Pointer(c_name))
switch len(args) {
case 3:
ret = C.rbd_create3(C.rados_ioctx_t(ioctx.Pointer()),
c_name, C.uint64_t(size),
C.uint64_t(args[0]), &c_order,
C.uint64_t(args[1]), C.uint64_t(args[2]))
case 1:
ret = C.rbd_create2(C.rados_ioctx_t(ioctx.Pointer()),
c_name, C.uint64_t(size),
C.uint64_t(args[0]), &c_order)
case 0:
ret = C.rbd_create(C.rados_ioctx_t(ioctx.Pointer()),
c_name, C.uint64_t(size), &c_order)
default:
return nil, errors.New("Wrong number of argument")
}
if ret < 0 {
return nil, RBDError(ret)
}
return &Image{
ioctx: ioctx,
name: name,
}, nil
}
// int rbd_clone(rados_ioctx_t p_ioctx, const char *p_name,
// const char *p_snapname, rados_ioctx_t c_ioctx,
// const char *c_name, uint64_t features, int *c_order);
// int rbd_clone2(rados_ioctx_t p_ioctx, const char *p_name,
// const char *p_snapname, rados_ioctx_t c_ioctx,
// const char *c_name, uint64_t features, int *c_order,
// uint64_t stripe_unit, int stripe_count);
func (image *Image) Clone(snapname string, c_ioctx *rados.IOContext, c_name string, features uint64, order int) (*Image, error) {
c_order := C.int(order)
c_p_name := C.CString(image.name)
c_p_snapname := C.CString(snapname)
c_c_name := C.CString(c_name)
defer C.free(unsafe.Pointer(c_p_name))
defer C.free(unsafe.Pointer(c_p_snapname))
defer C.free(unsafe.Pointer(c_c_name))
ret := C.rbd_clone(C.rados_ioctx_t(image.ioctx.Pointer()),
c_p_name, c_p_snapname,
C.rados_ioctx_t(c_ioctx.Pointer()),
c_c_name, C.uint64_t(features), &c_order)
if ret < 0 {
return nil, RBDError(ret)
}
return &Image{
ioctx: c_ioctx,
name: c_name,
}, nil
}
// int rbd_remove(rados_ioctx_t io, const char *name);
// int rbd_remove_with_progress(rados_ioctx_t io, const char *name,
// librbd_progress_fn_t cb, void *cbdata);
func (image *Image) Remove() error {
c_name := C.CString(image.name)
defer C.free(unsafe.Pointer(c_name))
return GetError(C.rbd_remove(C.rados_ioctx_t(image.ioctx.Pointer()), c_name))
}
// Trash will move an image into the RBD trash, where it will be protected (i.e., salvageable) for
// at least the specified delay.
func (image *Image) Trash(delay time.Duration) error {
c_name := C.CString(image.name)
defer C.free(unsafe.Pointer(c_name))
return GetError(C.rbd_trash_move(C.rados_ioctx_t(image.ioctx.Pointer()), c_name,
C.uint64_t(delay.Seconds())))
}
// int rbd_rename(rados_ioctx_t src_io_ctx, const char *srcname, const char *destname);
func (image *Image) Rename(destname string) error {
c_srcname := C.CString(image.name)
c_destname := C.CString(destname)
defer C.free(unsafe.Pointer(c_srcname))
defer C.free(unsafe.Pointer(c_destname))
err := RBDError(C.rbd_rename(C.rados_ioctx_t(image.ioctx.Pointer()),
c_srcname, c_destname))
if err == 0 {
image.name = destname
return nil
}
return err
}
// int rbd_open(rados_ioctx_t io, const char *name, rbd_image_t *image, const char *snap_name);
// int rbd_open_read_only(rados_ioctx_t io, const char *name, rbd_image_t *image,
// const char *snap_name);
func (image *Image) Open(args ...interface{}) error {
var c_image C.rbd_image_t
var c_snap_name *C.char
var ret C.int
var read_only bool
c_name := C.CString(image.name)
defer C.free(unsafe.Pointer(c_name))
for _, arg := range args {
switch t := arg.(type) {
case string:
if t != "" {
c_snap_name = C.CString(t)
defer C.free(unsafe.Pointer(c_snap_name))
}
case bool:
read_only = t
default:
return errors.New("Unexpected argument")
}
}
if read_only {
ret = C.rbd_open_read_only(C.rados_ioctx_t(image.ioctx.Pointer()), c_name,
&c_image, c_snap_name)
} else {
ret = C.rbd_open(C.rados_ioctx_t(image.ioctx.Pointer()), c_name,
&c_image, c_snap_name)
}
image.image = c_image
return GetError(ret)
}
// int rbd_close(rbd_image_t image);
func (image *Image) Close() error {
if image.image == nil {
return RbdErrorImageNotOpen
}
if ret := C.rbd_close(image.image); ret != 0 {
return RBDError(ret)
}
image.image = nil
return nil
}
// int rbd_resize(rbd_image_t image, uint64_t size);
func (image *Image) Resize(size uint64) error {
if image.image == nil {
return RbdErrorImageNotOpen
}
return GetError(C.rbd_resize(image.image, C.uint64_t(size)))
}
// int rbd_stat(rbd_image_t image, rbd_image_info_t *info, size_t infosize);
func (image *Image) Stat() (info *ImageInfo, err error) {
if image.image == nil {
return nil, RbdErrorImageNotOpen
}
var c_stat C.rbd_image_info_t
if ret := C.rbd_stat(image.image, &c_stat, C.size_t(unsafe.Sizeof(info))); ret < 0 {
return info, RBDError(ret)
}
return &ImageInfo{
Size: uint64(c_stat.size),
Obj_size: uint64(c_stat.obj_size),
Num_objs: uint64(c_stat.num_objs),
Order: int(c_stat.order),
Block_name_prefix: C.GoString((*C.char)(&c_stat.block_name_prefix[0])),
Parent_pool: int64(c_stat.parent_pool),
Parent_name: C.GoString((*C.char)(&c_stat.parent_name[0]))}, nil
}
// int rbd_get_old_format(rbd_image_t image, uint8_t *old);
func (image *Image) IsOldFormat() (old_format bool, err error) {
if image.image == nil {
return false, RbdErrorImageNotOpen
}
var c_old_format C.uint8_t
ret := C.rbd_get_old_format(image.image,
&c_old_format)
if ret < 0 {
return false, RBDError(ret)
}
return c_old_format != 0, nil
}
// int rbd_size(rbd_image_t image, uint64_t *size);
func (image *Image) GetSize() (size uint64, err error) {
if image.image == nil {
return 0, RbdErrorImageNotOpen
}
if ret := C.rbd_get_size(image.image, (*C.uint64_t)(&size)); ret < 0 {
return 0, RBDError(ret)
}
return size, nil
}
// int rbd_get_features(rbd_image_t image, uint64_t *features);
func (image *Image) GetFeatures() (features uint64, err error) {
if image.image == nil {
return 0, RbdErrorImageNotOpen
}
if ret := C.rbd_get_features(image.image, (*C.uint64_t)(&features)); ret < 0 {
return 0, RBDError(ret)
}
return features, nil
}
// int rbd_get_stripe_unit(rbd_image_t image, uint64_t *stripe_unit);
func (image *Image) GetStripeUnit() (stripe_unit uint64, err error) {
if image.image == nil {
return 0, RbdErrorImageNotOpen
}
if ret := C.rbd_get_stripe_unit(image.image, (*C.uint64_t)(&stripe_unit)); ret < 0 {
return 0, RBDError(ret)
}
return stripe_unit, nil
}
// int rbd_get_stripe_count(rbd_image_t image, uint64_t *stripe_count);
func (image *Image) GetStripeCount() (stripe_count uint64, err error) {
if image.image == nil {
return 0, RbdErrorImageNotOpen
}
if ret := C.rbd_get_stripe_count(image.image, (*C.uint64_t)(&stripe_count)); ret < 0 {
return 0, RBDError(ret)
}
return stripe_count, nil
}
// int rbd_get_overlap(rbd_image_t image, uint64_t *overlap);
func (image *Image) GetOverlap() (overlap uint64, err error) {
if image.image == nil {
return 0, RbdErrorImageNotOpen
}
if ret := C.rbd_get_overlap(image.image, (*C.uint64_t)(&overlap)); ret < 0 {
return overlap, RBDError(ret)
}
return overlap, nil
}
// int rbd_copy(rbd_image_t image, rados_ioctx_t dest_io_ctx, const char *destname);
// int rbd_copy2(rbd_image_t src, rbd_image_t dest);
// int rbd_copy_with_progress(rbd_image_t image, rados_ioctx_t dest_p, const char *destname,
// librbd_progress_fn_t cb, void *cbdata);
// int rbd_copy_with_progress2(rbd_image_t src, rbd_image_t dest,
// librbd_progress_fn_t cb, void *cbdata);
func (image *Image) Copy(args ...interface{}) error {
if image.image == nil {
return RbdErrorImageNotOpen
}
switch t := args[0].(type) {
case rados.IOContext:
switch t2 := args[1].(type) {
case string:
c_destname := C.CString(t2)
defer C.free(unsafe.Pointer(c_destname))
return RBDError(C.rbd_copy(image.image,
C.rados_ioctx_t(t.Pointer()),
c_destname))
default:
return errors.New("Must specify destname")
}
case Image:
var dest Image = t
if dest.image == nil {
return errors.New(fmt.Sprintf("RBD image %s is not open", dest.name))
}
return GetError(C.rbd_copy2(image.image, dest.image))
default:
return errors.New("Must specify either destination pool or destination image")
}
}
// int rbd_flatten(rbd_image_t image);
func (image *Image) Flatten() error {
if image.image == nil {
return errors.New(fmt.Sprintf("RBD image %s is not open", image.name))
}
return GetError(C.rbd_flatten(image.image))
}
// ssize_t rbd_list_children(rbd_image_t image, char *pools, size_t *pools_len,
// char *images, size_t *images_len);
func (image *Image) ListChildren() (pools []string, images []string, err error) {
if image.image == nil {
return nil, nil, RbdErrorImageNotOpen
}
var c_pools_len, c_images_len C.size_t
ret := C.rbd_list_children(image.image,
nil, &c_pools_len,
nil, &c_images_len)
if ret == 0 {
return nil, nil, nil
}
if ret < 0 && ret != -C.ERANGE {
return nil, nil, RBDError(ret)
}
pools_buf := make([]byte, c_pools_len)
images_buf := make([]byte, c_images_len)
ret = C.rbd_list_children(image.image,
(*C.char)(unsafe.Pointer(&pools_buf[0])),
&c_pools_len,
(*C.char)(unsafe.Pointer(&images_buf[0])),
&c_images_len)
if ret < 0 {
return nil, nil, RBDError(ret)
}
tmp := bytes.Split(pools_buf[:c_pools_len-1], []byte{0})
for _, s := range tmp {
if len(s) > 0 {
name := C.GoString((*C.char)(unsafe.Pointer(&s[0])))
pools = append(pools, name)
}
}
tmp = bytes.Split(images_buf[:c_images_len-1], []byte{0})
for _, s := range tmp {
if len(s) > 0 {
name := C.GoString((*C.char)(unsafe.Pointer(&s[0])))
images = append(images, name)
}
}
return pools, images, nil
}
// ssize_t rbd_list_lockers(rbd_image_t image, int *exclusive,
// char *tag, size_t *tag_len,
// char *clients, size_t *clients_len,
// char *cookies, size_t *cookies_len,
// char *addrs, size_t *addrs_len);
func (image *Image) ListLockers() (tag string, lockers []Locker, err error) {
if image.image == nil {
return "", nil, RbdErrorImageNotOpen
}
var c_exclusive C.int
var c_tag_len, c_clients_len, c_cookies_len, c_addrs_len C.size_t
var c_locker_cnt C.ssize_t
C.rbd_list_lockers(image.image, &c_exclusive,
nil, (*C.size_t)(&c_tag_len),
nil, (*C.size_t)(&c_clients_len),
nil, (*C.size_t)(&c_cookies_len),
nil, (*C.size_t)(&c_addrs_len))
// no locker held on rbd image when either c_clients_len,
// c_cookies_len or c_addrs_len is *0*, so just quickly returned
if int(c_clients_len) == 0 || int(c_cookies_len) == 0 ||
int(c_addrs_len) == 0 {
lockers = make([]Locker, 0)
return "", lockers, nil
}
tag_buf := make([]byte, c_tag_len)
clients_buf := make([]byte, c_clients_len)
cookies_buf := make([]byte, c_cookies_len)
addrs_buf := make([]byte, c_addrs_len)
c_locker_cnt = C.rbd_list_lockers(image.image, &c_exclusive,
(*C.char)(unsafe.Pointer(&tag_buf[0])), (*C.size_t)(&c_tag_len),
(*C.char)(unsafe.Pointer(&clients_buf[0])), (*C.size_t)(&c_clients_len),
(*C.char)(unsafe.Pointer(&cookies_buf[0])), (*C.size_t)(&c_cookies_len),
(*C.char)(unsafe.Pointer(&addrs_buf[0])), (*C.size_t)(&c_addrs_len))
// rbd_list_lockers returns negative value for errors
// and *0* means no locker held on rbd image.
// but *0* is unexpected here because first rbd_list_lockers already
// dealt with no locker case
if int(c_locker_cnt) <= 0 {
return "", nil, RBDError(c_locker_cnt)
}
clients := split(clients_buf)
cookies := split(cookies_buf)
addrs := split(addrs_buf)
lockers = make([]Locker, c_locker_cnt)
for i := 0; i < int(c_locker_cnt); i++ {
lockers[i] = Locker{Client: clients[i],
Cookie: cookies[i],
Addr: addrs[i]}
}
return string(tag_buf), lockers, nil
}
// int rbd_lock_exclusive(rbd_image_t image, const char *cookie);
func (image *Image) LockExclusive(cookie string) error {
if image.image == nil {
return RbdErrorImageNotOpen
}
c_cookie := C.CString(cookie)
defer C.free(unsafe.Pointer(c_cookie))
return GetError(C.rbd_lock_exclusive(image.image, c_cookie))
}
// int rbd_lock_shared(rbd_image_t image, const char *cookie, const char *tag);
func (image *Image) LockShared(cookie string, tag string) error {
if image.image == nil {
return RbdErrorImageNotOpen
}
c_cookie := C.CString(cookie)
c_tag := C.CString(tag)
defer C.free(unsafe.Pointer(c_cookie))
defer C.free(unsafe.Pointer(c_tag))
return GetError(C.rbd_lock_shared(image.image, c_cookie, c_tag))
}
// int rbd_lock_shared(rbd_image_t image, const char *cookie, const char *tag);
func (image *Image) Unlock(cookie string) error {
if image.image == nil {
return RbdErrorImageNotOpen
}
c_cookie := C.CString(cookie)
defer C.free(unsafe.Pointer(c_cookie))
return GetError(C.rbd_unlock(image.image, c_cookie))
}
// int rbd_break_lock(rbd_image_t image, const char *client, const char *cookie);
func (image *Image) BreakLock(client string, cookie string) error {
if image.image == nil {
return RbdErrorImageNotOpen
}
c_client := C.CString(client)
c_cookie := C.CString(cookie)
defer C.free(unsafe.Pointer(c_client))
defer C.free(unsafe.Pointer(c_cookie))
return GetError(C.rbd_break_lock(image.image, c_client, c_cookie))
}
// ssize_t rbd_read(rbd_image_t image, uint64_t ofs, size_t len, char *buf);
// TODO: int64_t rbd_read_iterate(rbd_image_t image, uint64_t ofs, size_t len,
// int (*cb)(uint64_t, size_t, const char *, void *), void *arg);
// TODO: int rbd_read_iterate2(rbd_image_t image, uint64_t ofs, uint64_t len,
// int (*cb)(uint64_t, size_t, const char *, void *), void *arg);
// TODO: int rbd_diff_iterate(rbd_image_t image,
// const char *fromsnapname,
// uint64_t ofs, uint64_t len,
// int (*cb)(uint64_t, size_t, int, void *), void *arg);
func (image *Image) Read(data []byte) (n int, err error) {
if image.image == nil {
return 0, RbdErrorImageNotOpen
}
if len(data) == 0 {
return 0, nil
}
ret := int(C.rbd_read(
image.image,
(C.uint64_t)(image.offset),
(C.size_t)(len(data)),
(*C.char)(unsafe.Pointer(&data[0]))))
if ret < 0 {
return 0, RBDError(ret)
}
image.offset += int64(ret)
if ret < n {
return ret, io.EOF
}
return ret, nil
}
// ssize_t rbd_write(rbd_image_t image, uint64_t ofs, size_t len, const char *buf);
func (image *Image) Write(data []byte) (n int, err error) {
ret := int(C.rbd_write(image.image, C.uint64_t(image.offset),
C.size_t(len(data)), (*C.char)(unsafe.Pointer(&data[0]))))
if ret >= 0 {
image.offset += int64(ret)
}
if ret != len(data) {
err = RBDError(-C.EPERM)
}
return ret, err
}
func (image *Image) Seek(offset int64, whence int) (int64, error) {
switch whence {
case 0:
image.offset = offset
case 1:
image.offset += offset
case 2:
stats, err := image.Stat()
if err != nil {
return 0, err
}
image.offset = int64(stats.Size) - offset
default:
return 0, errors.New("Wrong value for whence")
}
return image.offset, nil
}
// int rbd_discard(rbd_image_t image, uint64_t ofs, uint64_t len);
func (image *Image) Discard(ofs uint64, length uint64) error {
return RBDError(C.rbd_discard(image.image, C.uint64_t(ofs),
C.uint64_t(length)))
}
func (image *Image) ReadAt(data []byte, off int64) (n int, err error) {
if image.image == nil {
return 0, RbdErrorImageNotOpen
}
if len(data) == 0 {
return 0, nil
}
ret := int(C.rbd_read(
image.image,
(C.uint64_t)(off),
(C.size_t)(len(data)),
(*C.char)(unsafe.Pointer(&data[0]))))
if ret < 0 {
return 0, RBDError(ret)
}
if ret < n {
return ret, io.EOF
}
return ret, nil
}
func (image *Image) WriteAt(data []byte, off int64) (n int, err error) {
if image.image == nil {
return 0, RbdErrorImageNotOpen
}
if len(data) == 0 {
return 0, nil
}
ret := int(C.rbd_write(image.image, C.uint64_t(off),
C.size_t(len(data)), (*C.char)(unsafe.Pointer(&data[0]))))
if ret != len(data) {
err = RBDError(-C.EPERM)
}
return ret, err
}
// int rbd_flush(rbd_image_t image);
func (image *Image) Flush() error {
return GetError(C.rbd_flush(image.image))
}
// int rbd_snap_list(rbd_image_t image, rbd_snap_info_t *snaps, int *max_snaps);
// void rbd_snap_list_end(rbd_snap_info_t *snaps);
func (image *Image) GetSnapshotNames() (snaps []SnapInfo, err error) {
if image.image == nil {
return nil, RbdErrorImageNotOpen
}
var c_max_snaps C.int = 10
var c_snaps []C.rbd_snap_info_t
var snapCount C.int
for i := 0; i < 3; i++ {
c_snaps = make([]C.rbd_snap_info_t, c_max_snaps)
snapCount = C.rbd_snap_list(image.image, &c_snaps[0], &c_max_snaps)
if snapCount != -C.ERANGE {
if snapCount >= 0 {
break
} else if snapCount < 0 {
return nil, RBDError(snapCount)
}
}
}
snaps = make([]SnapInfo, snapCount)
for i := 0; i < int(snapCount); i++ {
snaps[i] = SnapInfo{Id: uint64(c_snaps[i].id),
Size: uint64(c_snaps[i].size),
Name: C.GoString(c_snaps[i].name)}
}
C.rbd_snap_list_end(&c_snaps[0])
return snaps, nil
}
// int rbd_snap_create(rbd_image_t image, const char *snapname);
func (image *Image) CreateSnapshot(snapname string) (*Snapshot, error) {
if image.image == nil {
return nil, RbdErrorImageNotOpen
}
c_snapname := C.CString(snapname)
defer C.free(unsafe.Pointer(c_snapname))
ret := C.rbd_snap_create(image.image, c_snapname)
if ret < 0 {
return nil, RBDError(ret)
}
return &Snapshot{
image: image,
name: snapname,
}, nil
}
//
func (image *Image) GetSnapshot(snapname string) *Snapshot {
return &Snapshot{
image: image,
name: snapname,
}
}
// int rbd_get_parent_info(rbd_image_t image,
// char *parent_pool_name, size_t ppool_namelen, char *parent_name,
// size_t pnamelen, char *parent_snap_name, size_t psnap_namelen)
func (image *Image) GetParentInfo(p_pool, p_name, p_snapname []byte) error {
ret := C.rbd_get_parent_info(
image.image,
(*C.char)(unsafe.Pointer(&p_pool[0])),
(C.size_t)(len(p_pool)),
(*C.char)(unsafe.Pointer(&p_name[0])),
(C.size_t)(len(p_name)),
(*C.char)(unsafe.Pointer(&p_snapname[0])),
(C.size_t)(len(p_snapname)))
if ret == 0 {
return nil
} else {
return RBDError(ret)
}
}
// int rbd_snap_remove(rbd_image_t image, const char *snapname);
func (snapshot *Snapshot) Remove() error {
if snapshot.image.image == nil {
return RbdErrorImageNotOpen
}
c_snapname := C.CString(snapshot.name)
defer C.free(unsafe.Pointer(c_snapname))
return GetError(C.rbd_snap_remove(snapshot.image.image, c_snapname))
}
// int rbd_snap_rollback(rbd_image_t image, const char *snapname);
// int rbd_snap_rollback_with_progress(rbd_image_t image, const char *snapname,
// librbd_progress_fn_t cb, void *cbdata);
func (snapshot *Snapshot) Rollback() error {
if snapshot.image.image == nil {
return RbdErrorImageNotOpen
}
c_snapname := C.CString(snapshot.name)
defer C.free(unsafe.Pointer(c_snapname))
return GetError(C.rbd_snap_rollback(snapshot.image.image, c_snapname))
}
// int rbd_snap_protect(rbd_image_t image, const char *snap_name);
func (snapshot *Snapshot) Protect() error {
if snapshot.image.image == nil {
return RbdErrorImageNotOpen
}
c_snapname := C.CString(snapshot.name)
defer C.free(unsafe.Pointer(c_snapname))
return GetError(C.rbd_snap_protect(snapshot.image.image, c_snapname))
}
// int rbd_snap_unprotect(rbd_image_t image, const char *snap_name);
func (snapshot *Snapshot) Unprotect() error {
if snapshot.image.image == nil {
return RbdErrorImageNotOpen
}
c_snapname := C.CString(snapshot.name)
defer C.free(unsafe.Pointer(c_snapname))
return GetError(C.rbd_snap_unprotect(snapshot.image.image, c_snapname))
}
// int rbd_snap_is_protected(rbd_image_t image, const char *snap_name,
// int *is_protected);
func (snapshot *Snapshot) IsProtected() (bool, error) {
if snapshot.image.image == nil {
return false, RbdErrorImageNotOpen
}
var c_is_protected C.int
c_snapname := C.CString(snapshot.name)
defer C.free(unsafe.Pointer(c_snapname))
ret := C.rbd_snap_is_protected(snapshot.image.image, c_snapname,
&c_is_protected)
if ret < 0 {
return false, RBDError(ret)
}
return c_is_protected != 0, nil
}
// int rbd_snap_set(rbd_image_t image, const char *snapname);
func (snapshot *Snapshot) Set() error {
if snapshot.image.image == nil {
return RbdErrorImageNotOpen
}
c_snapname := C.CString(snapshot.name)
defer C.free(unsafe.Pointer(c_snapname))
return GetError(C.rbd_snap_set(snapshot.image.image, c_snapname))
}
// GetTrashList returns a slice of TrashInfo structs, containing information about all RBD images
// currently residing in the trash.
func GetTrashList(ioctx *rados.IOContext) ([]TrashInfo, error) {
var num_entries C.size_t
// Call rbd_trash_list with nil pointer to get number of trash entries.
if C.rbd_trash_list(C.rados_ioctx_t(ioctx.Pointer()), nil, &num_entries); num_entries == 0 {
return nil, nil
}
c_entries := make([]C.rbd_trash_image_info_t, num_entries)
trashList := make([]TrashInfo, num_entries)
if ret := C.rbd_trash_list(C.rados_ioctx_t(ioctx.Pointer()), &c_entries[0], &num_entries); ret < 0 {
return nil, RBDError(ret)
}
for i, ti := range c_entries {
trashList[i] = TrashInfo{
Id: C.GoString(ti.id),
Name: C.GoString(ti.name),
DeletionTime: time.Unix(int64(ti.deletion_time), 0),
DefermentEndTime: time.Unix(int64(ti.deferment_end_time), 0),
}
}
// Free rbd_trash_image_info_t pointers
C.rbd_trash_list_cleanup(&c_entries[0], num_entries)
return trashList, nil
}
// TrashRemove permanently deletes the trashed RBD with the specified id.
func TrashRemove(ioctx *rados.IOContext, id string, force bool) error {
c_id := C.CString(id)
defer C.free(unsafe.Pointer(c_id))
return GetError(C.rbd_trash_remove(C.rados_ioctx_t(ioctx.Pointer()), c_id, C.bool(force)))
}
// TrashRestore restores the trashed RBD with the specified id back to the pool from whence it
// came, with the specified new name.
func TrashRestore(ioctx *rados.IOContext, id, name string) error {
c_id := C.CString(id)
c_name := C.CString(name)
defer C.free(unsafe.Pointer(c_id))
defer C.free(unsafe.Pointer(c_name))
return GetError(C.rbd_trash_restore(C.rados_ioctx_t(ioctx.Pointer()), c_id, c_name))
}
-23
View File
@@ -1,23 +0,0 @@
language: go
sudo: false
go:
- 1.7.x
- 1.8.x
- 1.9.x
- 1.10.x
- 1.11.x
- tip
matrix:
allow_failures:
- go: tip
fast_finish: true
env:
- GO111MODULE=on
before_install:
- go get golang.org/x/tools/cmd/cover
script:
- go test ./... -race -coverprofile=coverage.txt -covermode=atomic
after_success:
- bash <(curl -s https://codecov.io/bash)
notifications:
email: false
+1 -1
View File
@@ -12,7 +12,6 @@ and parsing of UUIDs in different formats.
This package supports the following UUID versions:
* Version 1, based on timestamp and MAC address (RFC-4122)
* Version 2, based on timestamp, MAC address and POSIX UID/GID (DCE 1.1)
* Version 3, based on MD5 hashing of a named value (RFC-4122)
* Version 4, based on random numbers (RFC-4122)
* Version 5, based on SHA-1 hashing of a named value (RFC-4122)
@@ -107,3 +106,4 @@ func main() {
* [RFC-4122](https://tools.ietf.org/html/rfc4122)
* [DCE 1.1: Authentication and Security Services](http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01)
* [New UUID Formats RFC Draft (Peabody) Rev 02](https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-02)
+5 -5
View File
@@ -114,7 +114,7 @@ func (u *UUID) UnmarshalText(text []byte) error {
case 41, 45:
return u.decodeURN(text)
default:
return fmt.Errorf("uuid: incorrect UUID length: %s", text)
return fmt.Errorf("uuid: incorrect UUID length %d in string %q", len(text), text)
}
}
@@ -122,7 +122,7 @@ func (u *UUID) UnmarshalText(text []byte) error {
// "6ba7b810-9dad-11d1-80b4-00c04fd430c8".
func (u *UUID) decodeCanonical(t []byte) error {
if t[8] != '-' || t[13] != '-' || t[18] != '-' || t[23] != '-' {
return fmt.Errorf("uuid: incorrect UUID format %s", t)
return fmt.Errorf("uuid: incorrect UUID format in string %q", t)
}
src := t
@@ -160,7 +160,7 @@ func (u *UUID) decodeBraced(t []byte) error {
l := len(t)
if t[0] != '{' || t[l-1] != '}' {
return fmt.Errorf("uuid: incorrect UUID format %s", t)
return fmt.Errorf("uuid: incorrect UUID format in string %q", t)
}
return u.decodePlain(t[1 : l-1])
@@ -175,7 +175,7 @@ func (u *UUID) decodeURN(t []byte) error {
urnUUIDPrefix := t[:9]
if !bytes.Equal(urnUUIDPrefix, urnPrefix) {
return fmt.Errorf("uuid: incorrect UUID format: %s", t)
return fmt.Errorf("uuid: incorrect UUID format in string %q", t)
}
return u.decodePlain(t[9:total])
@@ -191,7 +191,7 @@ func (u *UUID) decodePlain(t []byte) error {
case 36:
return u.decodeCanonical(t)
default:
return fmt.Errorf("uuid: incorrect UUID length: %s", t)
return fmt.Errorf("uuid: incorrect UUID length %d in string %q", len(t), t)
}
}
+309 -35
View File
@@ -26,11 +26,11 @@ import (
"crypto/rand"
"crypto/sha1"
"encoding/binary"
"errors"
"fmt"
"hash"
"io"
"net"
"os"
"sync"
"time"
)
@@ -47,21 +47,11 @@ type HWAddrFunc func() (net.HardwareAddr, error)
// DefaultGenerator is the default UUID Generator used by this package.
var DefaultGenerator Generator = NewGen()
var (
posixUID = uint32(os.Getuid())
posixGID = uint32(os.Getgid())
)
// NewV1 returns a UUID based on the current timestamp and MAC address.
func NewV1() (UUID, error) {
return DefaultGenerator.NewV1()
}
// NewV2 returns a DCE Security UUID based on the POSIX UID/GID.
func NewV2(domain byte) (UUID, error) {
return DefaultGenerator.NewV2(domain)
}
// NewV3 returns a UUID based on the MD5 hash of the namespace UUID and name.
func NewV3(ns UUID, name string) UUID {
return DefaultGenerator.NewV3(ns, name)
@@ -77,13 +67,45 @@ func NewV5(ns UUID, name string) UUID {
return DefaultGenerator.NewV5(ns, name)
}
// NewV6 returns a k-sortable UUID based on a timestamp and 48 bits of
// pseudorandom data. The timestamp in a V6 UUID is the same as V1, with the bit
// order being adjusted to allow the UUID to be k-sortable.
//
// This is implemented based on revision 02 of the Peabody UUID draft, and may
// be subject to change pending further revisions. Until the final specification
// revision is finished, changes required to implement updates to the spec will
// not be considered a breaking change. They will happen as a minor version
// releases until the spec is final.
func NewV6() (UUID, error) {
return DefaultGenerator.NewV6()
}
// NewV7 returns a k-sortable UUID based on the current UNIX epoch, with the
// ability to configure the timestamp's precision from millisecond all the way
// to nanosecond. The additional precision is supported by reducing the amount
// of pseudorandom data that makes up the rest of the UUID.
//
// If an unknown Precision argument is passed to this method it will panic. As
// such it's strongly encouraged to use the package-provided constants for this
// value.
//
// This is implemented based on revision 02 of the Peabody UUID draft, and may
// be subject to change pending further revisions. Until the final specification
// revision is finished, changes required to implement updates to the spec will
// not be considered a breaking change. They will happen as a minor version
// releases until the spec is final.
func NewV7(p Precision) (UUID, error) {
return DefaultGenerator.NewV7(p)
}
// Generator provides an interface for generating UUIDs.
type Generator interface {
NewV1() (UUID, error)
NewV2(domain byte) (UUID, error)
NewV3(ns UUID, name string) UUID
NewV4() (UUID, error)
NewV5(ns UUID, name string) UUID
NewV6() (UUID, error)
NewV7(Precision) (UUID, error)
}
// Gen is a reference UUID generator based on the specifications laid out in
@@ -109,6 +131,10 @@ type Gen struct {
lastTime uint64
clockSequence uint16
hardwareAddr [6]byte
v7LastTime uint64
v7LastSubsec uint64
v7ClockSequence uint16
}
// interface check -- build will fail if *Gen doesn't satisfy Generator
@@ -164,28 +190,6 @@ func (g *Gen) NewV1() (UUID, error) {
return u, nil
}
// NewV2 returns a DCE Security UUID based on the POSIX UID/GID.
func (g *Gen) NewV2(domain byte) (UUID, error) {
u, err := g.NewV1()
if err != nil {
return Nil, err
}
switch domain {
case DomainPerson:
binary.BigEndian.PutUint32(u[:], posixUID)
case DomainGroup:
binary.BigEndian.PutUint32(u[:], posixGID)
}
u[9] = domain
u.SetVersion(V2)
u.SetVariant(VariantRFC4122)
return u, nil
}
// NewV3 returns a UUID based on the MD5 hash of the namespace UUID and name.
func (g *Gen) NewV3(ns UUID, name string) UUID {
u := newFromHash(md5.New(), ns, name)
@@ -216,7 +220,39 @@ func (g *Gen) NewV5(ns UUID, name string) UUID {
return u
}
// Returns the epoch and clock sequence.
// NewV6 returns a k-sortable UUID based on a timestamp and 48 bits of
// pseudorandom data. The timestamp in a V6 UUID is the same as V1, with the bit
// order being adjusted to allow the UUID to be k-sortable.
//
// This is implemented based on revision 02 of the Peabody UUID draft, and may
// be subject to change pending further revisions. Until the final specification
// revision is finished, changes required to implement updates to the spec will
// not be considered a breaking change. They will happen as a minor version
// releases until the spec is final.
func (g *Gen) NewV6() (UUID, error) {
var u UUID
if _, err := io.ReadFull(g.rand, u[10:]); err != nil {
return Nil, err
}
timeNow, clockSeq, err := g.getClockSequence()
if err != nil {
return Nil, err
}
binary.BigEndian.PutUint32(u[0:], uint32(timeNow>>28)) // set time_high
binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>12)) // set time_mid
binary.BigEndian.PutUint16(u[6:], uint16(timeNow&0xfff)) // set time_low (minus four version bits)
binary.BigEndian.PutUint16(u[8:], clockSeq&0x3fff) // set clk_seq_hi_res (minus two variant bits)
u.SetVersion(V6)
u.SetVariant(VariantRFC4122)
return u, nil
}
// getClockSequence returns the epoch and clock sequence for V1 and V6 UUIDs.
func (g *Gen) getClockSequence() (uint64, uint16, error) {
var err error
g.clockSequenceOnce.Do(func() {
@@ -244,6 +280,244 @@ func (g *Gen) getClockSequence() (uint64, uint16, error) {
return timeNow, g.clockSequence, nil
}
// Precision is used to configure the V7 generator, to specify how precise the
// timestamp within the UUID should be.
type Precision byte
const (
NanosecondPrecision Precision = iota
MicrosecondPrecision
MillisecondPrecision
)
func (p Precision) String() string {
switch p {
case NanosecondPrecision:
return "nanosecond"
case MicrosecondPrecision:
return "microsecond"
case MillisecondPrecision:
return "millisecond"
default:
return "unknown"
}
}
// Duration returns the time.Duration for a specific precision. If the Precision
// value is not known, this returns 0.
func (p Precision) Duration() time.Duration {
switch p {
case NanosecondPrecision:
return time.Nanosecond
case MicrosecondPrecision:
return time.Microsecond
case MillisecondPrecision:
return time.Millisecond
default:
return 0
}
}
// NewV7 returns a k-sortable UUID based on the current UNIX epoch, with the
// ability to configure the timestamp's precision from millisecond all the way
// to nanosecond. The additional precision is supported by reducing the amount
// of pseudorandom data that makes up the rest of the UUID.
//
// If an unknown Precision argument is passed to this method it will panic. As
// such it's strongly encouraged to use the package-provided constants for this
// value.
//
// This is implemented based on revision 02 of the Peabody UUID draft, and may
// be subject to change pending further revisions. Until the final specification
// revision is finished, changes required to implement updates to the spec will
// not be considered a breaking change. They will happen as a minor version
// releases until the spec is final.
func (g *Gen) NewV7(p Precision) (UUID, error) {
var u UUID
var err error
switch p {
case NanosecondPrecision:
u, err = g.newV7Nano()
case MicrosecondPrecision:
u, err = g.newV7Micro()
case MillisecondPrecision:
u, err = g.newV7Milli()
default:
panic(fmt.Sprintf("unknown precision value %d", p))
}
if err != nil {
return Nil, err
}
u.SetVersion(V7)
u.SetVariant(VariantRFC4122)
return u, nil
}
func (g *Gen) newV7Milli() (UUID, error) {
var u UUID
if _, err := io.ReadFull(g.rand, u[8:]); err != nil {
return Nil, err
}
sec, nano, seq, err := g.getV7ClockSequence(MillisecondPrecision)
if err != nil {
return Nil, err
}
msec := (nano / 1000000) & 0xfff
d := (sec << 28) // set unixts field
d |= (msec << 16) // set msec field
d |= (uint64(seq) & 0xfff) // set seq field
binary.BigEndian.PutUint64(u[:], d)
return u, nil
}
func (g *Gen) newV7Micro() (UUID, error) {
var u UUID
if _, err := io.ReadFull(g.rand, u[10:]); err != nil {
return Nil, err
}
sec, nano, seq, err := g.getV7ClockSequence(MicrosecondPrecision)
if err != nil {
return Nil, err
}
usec := nano / 1000
usech := (usec << 4) & 0xfff0000
usecl := usec & 0xfff
d := (sec << 28) // set unixts field
d |= usech | usecl // set usec fields
binary.BigEndian.PutUint64(u[:], d)
binary.BigEndian.PutUint16(u[8:], seq)
return u, nil
}
func (g *Gen) newV7Nano() (UUID, error) {
var u UUID
if _, err := io.ReadFull(g.rand, u[11:]); err != nil {
return Nil, err
}
sec, nano, seq, err := g.getV7ClockSequence(NanosecondPrecision)
if err != nil {
return Nil, err
}
nano &= 0x3fffffffff
nanoh := nano >> 26
nanom := (nano >> 14) & 0xfff
nanol := uint16(nano & 0x3fff)
d := (sec << 28) // set unixts field
d |= (nanoh << 16) | nanom // set nsec high and med fields
binary.BigEndian.PutUint64(u[:], d)
binary.BigEndian.PutUint16(u[8:], nanol) // set nsec low field
u[10] = byte(seq) // set seq field
return u, nil
}
const (
maxSeq14 = (1 << 14) - 1
maxSeq12 = (1 << 12) - 1
maxSeq8 = (1 << 8) - 1
)
// getV7ClockSequence returns the unix epoch, nanoseconds of current second, and
// the sequence for V7 UUIDs.
func (g *Gen) getV7ClockSequence(p Precision) (epoch uint64, nano uint64, seq uint16, err error) {
g.storageMutex.Lock()
defer g.storageMutex.Unlock()
tn := g.epochFunc()
unix := uint64(tn.Unix())
nsec := uint64(tn.Nanosecond())
// V7 UUIDs have more precise requirements around how the clock sequence
// value is generated and used. Specifically they require that the sequence
// be zero, unless we've already generated a UUID within this unit of time
// (millisecond, microsecond, or nanosecond) at which point you should
// increment the sequence. Likewise if time has warped backwards for some reason (NTP
// adjustment?), we also increment the clock sequence to reduce the risk of a
// collision.
switch {
case unix < g.v7LastTime:
g.v7ClockSequence++
case unix > g.v7LastTime:
g.v7ClockSequence = 0
case unix == g.v7LastTime:
switch p {
case NanosecondPrecision:
if nsec <= g.v7LastSubsec {
if g.v7ClockSequence >= maxSeq8 {
return 0, 0, 0, errors.New("generating nanosecond precision UUIDv7s too fast: internal clock sequence would roll over")
}
g.v7ClockSequence++
} else {
g.v7ClockSequence = 0
}
case MicrosecondPrecision:
if nsec/1000 <= g.v7LastSubsec/1000 {
if g.v7ClockSequence >= maxSeq14 {
return 0, 0, 0, errors.New("generating microsecond precision UUIDv7s too fast: internal clock sequence would roll over")
}
g.v7ClockSequence++
} else {
g.v7ClockSequence = 0
}
case MillisecondPrecision:
if nsec/1000000 <= g.v7LastSubsec/1000000 {
if g.v7ClockSequence >= maxSeq12 {
return 0, 0, 0, errors.New("generating millisecond precision UUIDv7s too fast: internal clock sequence would roll over")
}
g.v7ClockSequence++
} else {
g.v7ClockSequence = 0
}
default:
panic(fmt.Sprintf("unknown precision value %d", p))
}
}
g.v7LastTime = unix
g.v7LastSubsec = nsec
return unix, nsec, g.v7ClockSequence, nil
}
// Returns the hardware address.
func (g *Gen) getHardwareAddr() ([]byte, error) {
var err error
+105 -7
View File
@@ -19,20 +19,33 @@
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// Package uuid provides implementations of the Universally Unique Identifier (UUID), as specified in RFC-4122 and DCE 1.1.
// Package uuid provides implementations of the Universally Unique Identifier
// (UUID), as specified in RFC-4122 and the Peabody RFC Draft (revision 02).
//
// RFC-4122[1] provides the specification for versions 1, 3, 4, and 5.
// RFC-4122[1] provides the specification for versions 1, 3, 4, and 5. The
// Peabody UUID RFC Draft[2] provides the specification for the new k-sortable
// UUIDs, versions 6 and 7.
//
// DCE 1.1[2] provides the specification for version 2.
// DCE 1.1[3] provides the specification for version 2, but version 2 support
// was removed from this package in v4 due to some concerns with the
// specification itself. Reading the spec, it seems that it would result in
// generating UUIDs that aren't very unique. In having read the spec it seemed
// that our implementation did not meet the spec. It also seems to be at-odds
// with RFC 4122, meaning we would need quite a bit of special code to support
// it. Lastly, there were no Version 2 implementations that we could find to
// ensure we were understanding the specification correctly.
//
// [1] https://tools.ietf.org/html/rfc4122
// [2] http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01
// [2] https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-02
// [3] http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01
package uuid
import (
"encoding/binary"
"encoding/hex"
"fmt"
"io"
"strings"
"time"
)
@@ -46,10 +59,13 @@ type UUID [Size]byte
const (
_ byte = iota
V1 // Version 1 (date-time and MAC address)
V2 // Version 2 (date-time and MAC address, DCE security version)
_ // Version 2 (date-time and MAC address, DCE security version) [removed]
V3 // Version 3 (namespace name-based)
V4 // Version 4 (random)
V5 // Version 5 (namespace name-based)
V6 // Version 6 (k-sortable timestamp and random data) [peabody draft]
V7 // Version 7 (k-sortable timestamp, with configurable precision, and random data) [peabody draft]
_ // Version 8 (k-sortable timestamp, meant for custom implementations) [peabody draft] [not implemented]
)
// UUID layout variants.
@@ -68,8 +84,8 @@ const (
)
// Timestamp is the count of 100-nanosecond intervals since 00:00:00.00,
// 15 October 1582 within a V1 UUID. This type has no meaning for V2-V5
// UUIDs since they don't have an embedded timestamp.
// 15 October 1582 within a V1 UUID. This type has no meaning for other
// UUID versions since they don't have an embedded timestamp.
type Timestamp uint64
const _100nsPerSecond = 10000000
@@ -78,6 +94,7 @@ const _100nsPerSecond = 10000000
func (t Timestamp) Time() (time.Time, error) {
secs := uint64(t) / _100nsPerSecond
nsecs := 100 * (uint64(t) % _100nsPerSecond)
return time.Unix(int64(secs)-(epochStart/_100nsPerSecond), int64(nsecs)), nil
}
@@ -88,12 +105,34 @@ func TimestampFromV1(u UUID) (Timestamp, error) {
err := fmt.Errorf("uuid: %s is version %d, not version 1", u, u.Version())
return 0, err
}
low := binary.BigEndian.Uint32(u[0:4])
mid := binary.BigEndian.Uint16(u[4:6])
hi := binary.BigEndian.Uint16(u[6:8]) & 0xfff
return Timestamp(uint64(low) + (uint64(mid) << 32) + (uint64(hi) << 48)), nil
}
// TimestampFromV6 returns the Timestamp embedded within a V6 UUID. This
// function returns an error if the UUID is any version other than 6.
//
// This is implemented based on revision 01 of the Peabody UUID draft, and may
// be subject to change pending further revisions. Until the final specification
// revision is finished, changes required to implement updates to the spec will
// not be considered a breaking change. They will happen as a minor version
// releases until the spec is final.
func TimestampFromV6(u UUID) (Timestamp, error) {
if u.Version() != 6 {
return 0, fmt.Errorf("uuid: %s is version %d, not version 6", u, u.Version())
}
hi := binary.BigEndian.Uint32(u[0:4])
mid := binary.BigEndian.Uint16(u[4:6])
low := binary.BigEndian.Uint16(u[6:8]) & 0xfff
return Timestamp(uint64(low) + (uint64(mid) << 12) + (uint64(hi) << 28)), nil
}
// String parse helpers.
var (
urnPrefix = []byte("urn:uuid:")
@@ -156,6 +195,65 @@ func (u UUID) String() string {
return string(buf)
}
// Format implements fmt.Formatter for UUID values.
//
// The behavior is as follows:
// The 'x' and 'X' verbs output only the hex digits of the UUID, using a-f for 'x' and A-F for 'X'.
// The 'v', '+v', 's' and 'q' verbs return the canonical RFC-4122 string representation.
// The 'S' verb returns the RFC-4122 format, but with capital hex digits.
// The '#v' verb returns the "Go syntax" representation, which is a 16 byte array initializer.
// All other verbs not handled directly by the fmt package (like '%p') are unsupported and will return
// "%!verb(uuid.UUID=value)" as recommended by the fmt package.
func (u UUID) Format(f fmt.State, c rune) {
switch c {
case 'x', 'X':
s := hex.EncodeToString(u.Bytes())
if c == 'X' {
s = strings.Map(toCapitalHexDigits, s)
}
_, _ = io.WriteString(f, s)
case 'v':
var s string
if f.Flag('#') {
s = fmt.Sprintf("%#v", [Size]byte(u))
} else {
s = u.String()
}
_, _ = io.WriteString(f, s)
case 's', 'S':
s := u.String()
if c == 'S' {
s = strings.Map(toCapitalHexDigits, s)
}
_, _ = io.WriteString(f, s)
case 'q':
_, _ = io.WriteString(f, `"`+u.String()+`"`)
default:
// invalid/unsupported format verb
fmt.Fprintf(f, "%%!%c(uuid.UUID=%s)", c, u.String())
}
}
func toCapitalHexDigits(ch rune) rune {
// convert a-f hex digits to A-F
switch ch {
case 'a':
return 'A'
case 'b':
return 'B'
case 'c':
return 'C'
case 'd':
return 'D'
case 'e':
return 'E'
case 'f':
return 'F'
default:
return ch
}
}
// SetVersion sets the version bits.
func (u *UUID) SetVersion(v byte) {
u[6] = (u[6] & 0x0f) | (v << 4)
+2 -5
View File
@@ -208,9 +208,6 @@ github.com/boombuler/barcode/utils
github.com/bradfitz/iter
# github.com/c-bata/go-prompt v0.2.1
github.com/c-bata/go-prompt
# github.com/ceph/go-ceph v0.0.0-20181217221554-e32f9f0f2e94 => github.com/yunionio/go-ceph v0.0.0-20190912101231-6f05a06b3859
github.com/ceph/go-ceph/rados
github.com/ceph/go-ceph/rbd
# github.com/cheggaaa/pb/v3 v3.0.8
github.com/cheggaaa/pb/v3
github.com/cheggaaa/pb/v3/termutil
@@ -333,7 +330,7 @@ github.com/go-playground/validator/v10
github.com/go-sql-driver/mysql
# github.com/go-yaml/yaml v2.1.0+incompatible
github.com/go-yaml/yaml
# github.com/gofrs/uuid v3.2.0+incompatible
# github.com/gofrs/uuid v4.1.0+incompatible
github.com/gofrs/uuid
# github.com/gogo/protobuf v1.3.1
github.com/gogo/protobuf/gogoproto
@@ -1172,7 +1169,7 @@ sigs.k8s.io/yaml
yunion.io/x/executor/apis
yunion.io/x/executor/client
yunion.io/x/executor/server
# yunion.io/x/jsonutils v0.0.0-20210709075951-798a67800349
# yunion.io/x/jsonutils v0.0.0-20211105163012-d846c05a3c9a
yunion.io/x/jsonutils
# yunion.io/x/log v0.0.0-20201210064738-43181789dc74
yunion.io/x/log
+120 -71
View File
@@ -33,13 +33,23 @@ import (
"yunion.io/x/pkg/util/timeutils"
)
func marshalSlice(val reflect.Value, info *reflectutils.SStructFieldInfo) JSONObject {
if val.Len() == 0 && info != nil && info.OmitEmpty {
return JSONNull
func marshalSlice(val reflect.Value, info *reflectutils.SStructFieldInfo, omitEmpty bool) JSONObject {
if val.Kind() == reflect.Slice && val.IsNil() {
if !omitEmpty {
return JSONNull
} else {
return nil
}
}
objs := make([]JSONObject, val.Len())
if val.Len() == 0 && info != nil && info.OmitEmpty && omitEmpty {
return nil
}
objs := make([]JSONObject, 0)
for i := 0; i < val.Len(); i += 1 {
objs[i] = marshalValue(val.Index(i), nil)
val := marshalValue(val.Index(i), nil, omitEmpty)
if val != nil {
objs = append(objs, val)
}
}
arr := NewArray(objs...)
if info != nil && info.ForceString {
@@ -49,16 +59,23 @@ func marshalSlice(val reflect.Value, info *reflectutils.SStructFieldInfo) JSONOb
}
}
func marshalMap(val reflect.Value, info *reflectutils.SStructFieldInfo) JSONObject {
func marshalMap(val reflect.Value, info *reflectutils.SStructFieldInfo, omitEmpty bool) JSONObject {
if val.IsNil() {
if !omitEmpty {
return JSONNull
} else {
return nil
}
}
keys := val.MapKeys()
if len(keys) == 0 && info != nil && info.OmitEmpty {
return JSONNull
if len(keys) == 0 && info != nil && info.OmitEmpty && omitEmpty {
return nil
}
objPairs := make([]JSONPair, 0)
for i := 0; i < len(keys); i += 1 {
key := keys[i]
val := marshalValue(val.MapIndex(key), nil)
if val != JSONNull {
val := marshalValue(val.MapIndex(key), nil, omitEmpty)
if val != nil {
objPairs = append(objPairs, JSONPair{key: fmt.Sprintf("%s", key), val: val})
}
}
@@ -70,10 +87,10 @@ func marshalMap(val reflect.Value, info *reflectutils.SStructFieldInfo) JSONObje
}
}
func marshalStruct(val reflect.Value, info *reflectutils.SStructFieldInfo) JSONObject {
objPairs := struct2JSONPairs(val)
if len(objPairs) == 0 && info != nil && info.OmitEmpty {
return JSONNull
func marshalStruct(val reflect.Value, info *reflectutils.SStructFieldInfo, omitEmpty bool) JSONObject {
objPairs := struct2JSONPairs(val, omitEmpty)
if len(objPairs) == 0 && info != nil && info.OmitEmpty && omitEmpty {
return nil
}
dict := NewDict(objPairs...)
if info != nil && info.ForceString {
@@ -92,7 +109,7 @@ func findValueByKey(pairs []JSONPair, key string) JSONObject {
return nil
}
func struct2JSONPairs(val reflect.Value) []JSONPair {
func struct2JSONPairs(val reflect.Value, omitEmpty bool) []JSONPair {
fields := reflectutils.FetchStructFieldValueSet(val)
objPairs := make([]JSONPair, 0, len(fields))
depFields := make(map[string]string)
@@ -106,8 +123,8 @@ func struct2JSONPairs(val reflect.Value) []JSONPair {
depFields[key] = deprecatedBy
continue
}
val := marshalValue(fields[i].Value, jsonInfo)
if val != nil && val != JSONNull {
val := marshalValue(fields[i].Value, jsonInfo, omitEmpty)
if val != nil {
objPair := JSONPair{key: key, val: val}
objPairs = append(objPairs, objPair)
}
@@ -140,9 +157,9 @@ func struct2JSONPairs(val reflect.Value) []JSONPair {
return objPairs
}
func marshalInt64(val int64, info *reflectutils.SStructFieldInfo) JSONObject {
if val == 0 && info != nil && info.OmitZero {
return JSONNull
func marshalInt64(val int64, info *reflectutils.SStructFieldInfo, omitEmpty bool) JSONObject {
if val == 0 && info != nil && info.OmitZero && omitEmpty {
return nil
} else if info != nil && info.ForceString {
return NewString(fmt.Sprintf("%d", val))
} else {
@@ -150,9 +167,9 @@ func marshalInt64(val int64, info *reflectutils.SStructFieldInfo) JSONObject {
}
}
func marshalFloat64(val float64, info *reflectutils.SStructFieldInfo, bit int) JSONObject {
if val == 0.0 && info != nil && info.OmitZero {
return JSONNull
func marshalFloat64(val float64, info *reflectutils.SStructFieldInfo, bit int, omitEmpty bool) JSONObject {
if val == 0.0 && info != nil && info.OmitZero && omitEmpty {
return nil
} else if info != nil && info.ForceString {
return NewString(fmt.Sprintf("%f", val))
} else {
@@ -160,9 +177,9 @@ func marshalFloat64(val float64, info *reflectutils.SStructFieldInfo, bit int) J
}
}
func marshalFloat32(val float32, info *reflectutils.SStructFieldInfo, bit int) JSONObject {
if val == 0.0 && info != nil && info.OmitZero {
return JSONNull
func marshalFloat32(val float32, info *reflectutils.SStructFieldInfo, bit int, omitEmpty bool) JSONObject {
if val == 0.0 && info != nil && info.OmitZero && omitEmpty {
return nil
} else if info != nil && info.ForceString {
return NewString(fmt.Sprintf("%f", val))
} else {
@@ -170,9 +187,9 @@ func marshalFloat32(val float32, info *reflectutils.SStructFieldInfo, bit int) J
}
}
func marshalBoolean(val bool, info *reflectutils.SStructFieldInfo) JSONObject {
if !val && info != nil && info.OmitFalse {
return JSONNull
func marshalBoolean(val bool, info *reflectutils.SStructFieldInfo, omitEmpty bool) JSONObject {
if !val && info != nil && info.OmitFalse && omitEmpty {
return nil
} else if info != nil && info.ForceString {
return NewString(fmt.Sprintf("%v", val))
} else {
@@ -184,28 +201,32 @@ func marshalBoolean(val bool, info *reflectutils.SStructFieldInfo) JSONObject {
}
}
func marshalTristate(val tristate.TriState, info *reflectutils.SStructFieldInfo) JSONObject {
func marshalTristate(val tristate.TriState, info *reflectutils.SStructFieldInfo, omitEmpty bool) JSONObject {
if val.IsTrue() {
return JSONTrue
} else if val.IsFalse() {
return JSONFalse
} else {
return JSONNull
if omitEmpty {
return nil
} else {
return JSONNull
}
}
}
func marshalString(val string, info *reflectutils.SStructFieldInfo) JSONObject {
if len(val) == 0 && info != nil && info.OmitEmpty {
return JSONNull
func marshalString(val string, info *reflectutils.SStructFieldInfo, omitEmpty bool) JSONObject {
if len(val) == 0 && info != nil && info.OmitEmpty && omitEmpty {
return nil
} else {
return NewString(val)
}
}
func marshalTime(val time.Time, info *reflectutils.SStructFieldInfo) JSONObject {
func marshalTime(val time.Time, info *reflectutils.SStructFieldInfo, omitEmpty bool) JSONObject {
if val.IsZero() {
if info != nil && info.OmitEmpty {
return JSONNull
if info != nil && info.OmitEmpty && omitEmpty {
return nil
}
return NewString("")
} else {
@@ -222,123 +243,151 @@ func Marshal(obj interface{}) JSONObject {
return JSONNull
}
objValue := reflect.Indirect(val)
return marshalValue(objValue, nil)
mval := marshalValue(objValue, nil, true)
if mval == nil {
return JSONNull
}
return mval
}
func marshalValue(objValue reflect.Value, info *reflectutils.SStructFieldInfo) JSONObject {
func marshalValue(objValue reflect.Value, info *reflectutils.SStructFieldInfo, omitEmpty bool) JSONObject {
switch objValue.Type() {
case JSONDictPtrType, JSONArrayPtrType, JSONBoolPtrType, JSONIntPtrType, JSONFloatPtrType, JSONStringPtrType, JSONObjectType:
if objValue.IsNil() {
return JSONNull
if omitEmpty {
return nil
} else {
return JSONNull
}
}
return objValue.Interface().(JSONObject)
case JSONDictType:
json, ok := objValue.Interface().(JSONDict)
if ok {
if len(json.data) == 0 && info != nil && info.OmitEmpty {
return JSONNull
if len(json.data) == 0 && info != nil && info.OmitEmpty && omitEmpty {
return nil
} else {
return &json
}
} else {
return JSONNull
return nil
}
case JSONArrayType:
json, ok := objValue.Interface().(JSONArray)
if ok {
if len(json.data) == 0 && info != nil && info.OmitEmpty {
return JSONNull
if len(json.data) == 0 && info != nil && info.OmitEmpty && omitEmpty {
return nil
} else {
return &json
}
} else {
return JSONNull
return nil
}
case JSONBoolType:
json, ok := objValue.Interface().(JSONBool)
if ok {
if !json.data && info != nil && info.OmitEmpty {
return JSONNull
if !json.data && info != nil && info.OmitEmpty && omitEmpty {
return nil
} else {
return &json
}
} else {
return JSONNull
return nil
}
case JSONIntType:
json, ok := objValue.Interface().(JSONInt)
if ok {
if json.data == 0 && info != nil && info.OmitEmpty {
return JSONNull
if json.data == 0 && info != nil && info.OmitEmpty && omitEmpty {
return nil
} else {
return &json
}
} else {
return JSONNull
return nil
}
case JSONFloatType:
json, ok := objValue.Interface().(JSONFloat)
if ok {
if json.data == 0.0 && info != nil && info.OmitEmpty {
return JSONNull
if json.data == 0.0 && info != nil && info.OmitEmpty && omitEmpty {
return nil
} else {
return &json
}
} else {
return JSONNull
return nil
}
case JSONStringType:
json, ok := objValue.Interface().(JSONString)
if ok {
if len(json.data) == 0 && info != nil && info.OmitEmpty {
return JSONNull
if len(json.data) == 0 && info != nil && info.OmitEmpty && omitEmpty {
return nil
} else {
return &json
}
} else {
return JSONNull
return nil
}
case tristate.TriStateType:
tri, ok := objValue.Interface().(tristate.TriState)
if ok {
return marshalTristate(tri, info)
return marshalTristate(tri, info, omitEmpty)
} else {
return JSONNull
return nil
}
}
switch objValue.Kind() {
case reflect.Slice, reflect.Array:
return marshalSlice(objValue, info)
return marshalSlice(objValue, info, omitEmpty)
case reflect.Struct:
if objValue.Type() == gotypes.TimeType {
return marshalTime(objValue.Interface().(time.Time), info)
return marshalTime(objValue.Interface().(time.Time), info, omitEmpty)
} else {
return marshalStruct(objValue, info)
return marshalStruct(objValue, info, omitEmpty)
}
case reflect.Map:
return marshalMap(objValue, info)
return marshalMap(objValue, info, omitEmpty)
case reflect.String:
strValue := objValue.Convert(gotypes.StringType)
return marshalString(strValue.Interface().(string), info)
return marshalString(strValue.Interface().(string), info, omitEmpty)
case reflect.Bool:
return marshalBoolean(objValue.Interface().(bool), info)
return marshalBoolean(objValue.Interface().(bool), info, omitEmpty)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
intValue := objValue.Convert(gotypes.Int64Type)
return marshalInt64(intValue.Interface().(int64), info)
return marshalInt64(intValue.Interface().(int64), info, omitEmpty)
case reflect.Float32:
floatVal := objValue.Convert(gotypes.Float32Type)
return marshalFloat32(floatVal.Interface().(float32), info, 32)
return marshalFloat32(floatVal.Interface().(float32), info, 32, omitEmpty)
case reflect.Float64:
floatVal := objValue.Convert(gotypes.Float64Type)
return marshalFloat64(floatVal.Interface().(float64), info, 64)
return marshalFloat64(floatVal.Interface().(float64), info, 64, omitEmpty)
case reflect.Interface, reflect.Ptr:
if objValue.IsNil() {
return JSONNull
if omitEmpty {
return nil
} else {
return JSONNull
}
}
return marshalValue(objValue.Elem(), info)
return marshalValue(objValue.Elem(), info, omitEmpty)
default:
log.Errorf("unsupport object %s %s", objValue.Type(), objValue.Interface())
return JSONNull
}
}
func MarshalAll(obj interface{}) JSONObject {
if obj == nil {
return JSONNull
}
val := reflect.ValueOf(obj)
if kind := val.Kind(); val.IsZero() && kind == reflect.Ptr {
return JSONNull
}
objValue := reflect.Indirect(val)
mval := marshalValue(objValue, nil, false)
if mval == nil {
return JSONNull
}
return mval
}