mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-19 02:37:24 +08:00
Merge pull request #10771 from ioito/hotfix/qx-rbdcli
fix: add rbdcli commond for debug
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// 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
|
||||
})
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// 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
|
||||
})
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// 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)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package rbdutils // import "yunion.io/x/onecloud/pkg/util/rbdutils"
|
||||
@@ -0,0 +1,154 @@
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user