From fb2736dd0ad4fe96a6184e5526ffe73cf639c76f Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Thu, 9 Apr 2020 13:59:50 -0400 Subject: [PATCH] rados: add MgrCommand and MgrCommandWithInputBuffer functions Add functions for issuing commands to ceph mgrs with MgrCommand and MgrCommandWithInputBuffer, implemented simliarly to the other *Command style functions. Signed-off-by: John Mulligan --- rados/command.go | 62 +++++++++++++++++++++++++++++++++++++ rados/command_mimic_test.go | 37 ++++++++++++++++++++++ rados/command_test.go | 42 +++++++++++++++++++++++++ 3 files changed, 141 insertions(+) create mode 100644 rados/command_mimic_test.go diff --git a/rados/command.go b/rados/command.go index b5b314c..ec41d1c 100644 --- a/rados/command.go +++ b/rados/command.go @@ -130,3 +130,65 @@ func (c *Conn) pgCommand(pgid []byte, args [][]byte, inputBuffer []byte) ([]byte return buffer, info, nil } + +// MgrCommand sends a command to a ceph-mgr. +func (c *Conn) MgrCommand(args [][]byte) ([]byte, string, error) { + return c.mgrCommand(args, nil) +} + +// MgrCommandWithInputBuffer sends a command, with an input buffer, to a ceph-mgr. +func (c *Conn) MgrCommandWithInputBuffer(args [][]byte, inputBuffer []byte) ([]byte, string, error) { + return c.mgrCommand(args, inputBuffer) +} + +// Implements: +// int rados_mgr_command(rados_t cluster, const char **cmd, +// size_t cmdlen, const char *inbuf, +// size_t inbuflen, char **outbuf, +// size_t *outbuflen, char **outs, +// size_t *outslen); +func (c *Conn) mgrCommand(args [][]byte, inputBuffer []byte) ([]byte, string, error) { + argc := len(args) + argv := make([]*C.char, argc) + + for i, arg := range args { + argv[i] = C.CString(string(arg)) + defer C.free(unsafe.Pointer(argv[i])) + } + + var ( + info string + buffer []byte + 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_mgr_command( + c.cluster, + &argv[0], + C.size_t(argc), + inbuf, + C.size_t(inbufLen), + &outbuf, + &outbuflen, + &outs, + &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 := getError(ret) + return nil, info, err + } + + return buffer, info, nil +} diff --git a/rados/command_mimic_test.go b/rados/command_mimic_test.go new file mode 100644 index 0000000..aaa93da --- /dev/null +++ b/rados/command_mimic_test.go @@ -0,0 +1,37 @@ +// +build !luminous + +package rados + +import ( + "encoding/json" + + "github.com/stretchr/testify/assert" +) + +// A real test using input buffer is hard to find for mgr. +// The simplest does not work on luminous, so we simply don't +// provide the test for luminous. + +func (suite *RadosTestSuite) TestMgrCommandWithInputBuffer() { + suite.SetupConnection() + + command, err := json.Marshal( + map[string]string{"prefix": "crash post", "format": "json"}) + assert.NoError(suite.T(), err) + + buf, info, err := suite.conn.MgrCommandWithInputBuffer( + [][]byte{command}, []byte(`{"crash_id": "foobar", "timestamp": "2020-04-10 15:08:34.659679Z"}`)) + assert.NoError(suite.T(), err) + assert.Equal(suite.T(), info, "") + assert.Len(suite.T(), buf, 0) + + command, err = json.Marshal( + map[string]string{"prefix": "crash rm", "id": "foobar", "format": "json"}) + assert.NoError(suite.T(), err) + + buf, info, err = suite.conn.MgrCommandWithInputBuffer( + [][]byte{command}, nil) + assert.NoError(suite.T(), err) + assert.Equal(suite.T(), info, "") + assert.Len(suite.T(), buf, 0) +} diff --git a/rados/command_test.go b/rados/command_test.go index 6b2100b..6924741 100644 --- a/rados/command_test.go +++ b/rados/command_test.go @@ -87,3 +87,45 @@ func (suite *RadosTestSuite) TestPGCommand() { err = json.Unmarshal(buf, &message) assert.NoError(suite.T(), err) } + +func (suite *RadosTestSuite) TestMgrCommandDescrptions() { + suite.SetupConnection() + + command, err := json.Marshal( + map[string]string{"prefix": "get_command_descriptions", "format": "json"}) + assert.NoError(suite.T(), err) + + buf, info, err := suite.conn.MgrCommand([][]byte{command}) + assert.NoError(suite.T(), err) + assert.Equal(suite.T(), info, "") + + var message map[string]interface{} + err = json.Unmarshal(buf, &message) + assert.NoError(suite.T(), err) +} + +func (suite *RadosTestSuite) TestMgrCommand() { + suite.SetupConnection() + + command, err := json.Marshal( + map[string]string{"prefix": "balancer status", "format": "json"}) + assert.NoError(suite.T(), err) + + buf, info, err := suite.conn.MgrCommand([][]byte{command}) + assert.NoError(suite.T(), err) + assert.Equal(suite.T(), info, "") + + var message map[string]interface{} + err = json.Unmarshal(buf, &message) + assert.NoError(suite.T(), err) +} + +func (suite *RadosTestSuite) TestMgrCommandMalformedCommand() { + suite.SetupConnection() + + command := []byte("JUNK!") + buf, info, err := suite.conn.MgrCommand([][]byte{command}) + assert.Error(suite.T(), err) + assert.NotEqual(suite.T(), info, "") + assert.Len(suite.T(), buf, 0) +}