diff --git a/cli/command/client/status.go b/cli/command/client/status.go index d9a66b571..444a9934f 100644 --- a/cli/command/client/status.go +++ b/cli/command/client/status.go @@ -57,6 +57,9 @@ func NewStatusCommand(curveadm *cli.CurveAdm) *cobra.Command { DisableFlagsInUseLine: true, } + flags := cmd.Flags() + flags.BoolVarP(&options.verbose, "verbose", "v", false, "Verbose output for status") + return cmd } @@ -74,6 +77,9 @@ func genStatusPlaybook(curveadm *cli.CurveAdm, pb.AddStep(&playbook.PlaybookStep{ Type: step, Configs: config, + Options: map[string]interface{}{ + comm.KEY_CLIENT_STATUS_VERBOSE: options.verbose, + }, ExecOptions: playbook.ExecOptions{ SilentSubBar: true, }, diff --git a/internal/build/debug.go b/internal/build/debug.go index 48559377e..1c028c9b3 100644 --- a/internal/build/debug.go +++ b/internal/build/debug.go @@ -38,7 +38,7 @@ const ( DEBUG_TOOL = "DEBUG_TOOL" DEBUG_CLUSTER = "DEBUG_CLUSTER" DEBUG_CREATE_POOL = "DEBUG_CREATE_POOL" - DEBUG_CLIENT_CONFIGURE = "DEBUG_CLIENT_CONFIGURE" + DEBUG_CLIENT_CONFIGURE = "DEBUG_CLIENT_CONFIGURE" ) type Field struct { diff --git a/internal/common/common.go b/internal/common/common.go index f5e9a088f..4cf89e25b 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -77,14 +77,15 @@ const ( CLEANED_CONTAINER_ID = "-" // client - KEY_CLIENT_HOST = "CLIENT_HOST" - KEY_CLIENT_KIND = "CLIENT_KIND" - KEY_ALL_CLIENT_STATUS = "ALL_CLIENT_STATUS" - KEY_MAP_OPTIONS = "MAP_OPTIONS" - KEY_MOUNT_OPTIONS = "MOUNT_OPTIONS" - CLIENT_STATUS_LOSED = "Losed" - KERNERL_MODULE_NBD = "nbd" - KERNERL_MODULE_FUSE = "fuse" + KEY_CLIENT_HOST = "CLIENT_HOST" + KEY_CLIENT_KIND = "CLIENT_KIND" + KEY_ALL_CLIENT_STATUS = "ALL_CLIENT_STATUS" + KEY_CLIENT_STATUS_VERBOSE = "CLIENT_STATUS_VERBOSE" + KEY_MAP_OPTIONS = "MAP_OPTIONS" + KEY_MOUNT_OPTIONS = "MOUNT_OPTIONS" + CLIENT_STATUS_LOSED = "Losed" + KERNERL_MODULE_NBD = "nbd" + KERNERL_MODULE_FUSE = "fuse" // polarfs KEY_POLARFS_HOST = "POLARFS_HOST" diff --git a/internal/configure/client.go b/internal/configure/client.go index 607cb7638..7d0de47ac 100644 --- a/internal/configure/client.go +++ b/internal/configure/client.go @@ -144,19 +144,36 @@ func ParseClientCfg(data string) (*ClientConfig, error) { } func ParseClientConfig(filename string) (*ClientConfig, error) { + // 1. read file content + data, err := utils.ReadFile(filename) + if err != nil { + return nil, errno.ERR_PARSE_CLIENT_CONFIGURE_FAILED.E(err) + } + + // 2. new parser parser := viper.NewWithOptions(viper.KeyDelimiter("::")) parser.SetConfigFile(filename) parser.SetConfigType("yaml") - if err := parser.ReadInConfig(); err != nil { + err = parser.ReadInConfig() + if err != nil { return nil, errno.ERR_PARSE_CLIENT_CONFIGURE_FAILED.E(err) } - config := map[string]interface{}{} - if err := parser.Unmarshal(&config); err != nil { + // 3. parse + m := map[string]interface{}{} + err = parser.Unmarshal(&m) + if err != nil { return nil, errno.ERR_PARSE_CLIENT_CONFIGURE_FAILED.E(err) } - build.DEBUG(build.DEBUG_CLIENT_CONFIGURE, config) - return NewClientConfig(config) + + // 4. new config + cfg, err := NewClientConfig(m) + if err != nil { + return nil, err + } + + cfg.data = data + return cfg, nil } func (cc *ClientConfig) getString(key string) string { @@ -186,6 +203,7 @@ func (cc *ClientConfig) GetS3BucketName() string { return cc.getStri func (cc *ClientConfig) GetContainerPid() string { return cc.getString(KEY_CONTAINER_PID) } func (cc *ClientConfig) GetEnvironments() string { return cc.getString(KEY_ENVIRONMENT) } func (cc *ClientConfig) GetCoreLocateDir() string { return DEFAULT_CORE_LOCATE_DIR } +func (cc *ClientConfig) GetData() string { return cc.data } func (cc *ClientConfig) GetServiceConfig() map[string]string { return cc.serviceConfig } func (cc *ClientConfig) GetVariables() *variable.Variables { return cc.variables } func (cc *ClientConfig) GetContainerImage() string { diff --git a/internal/errno/errno.go b/internal/errno/errno.go index 54c412ce9..b7b479539 100644 --- a/internal/errno/errno.go +++ b/internal/errno/errno.go @@ -126,6 +126,7 @@ func (e *ErrorCode) Error() string { * * 113: clients table * * 114: plauground table * * 115: audit table + * * 116: any table * * 2xx: command options * 20*: hosts @@ -223,6 +224,10 @@ var ( ERR_DELETE_PLAYGROUND_FAILED = EC(114003, "execute SQL failed which delete playground") // 115: database/SQL (execute SQL statement: audit table) ERR_GET_AUDIT_LOGS_FAILE = EC(115000, "execute SQL failed which get audit logs") + // 116: database/SQL (execute SQL statement: any table) + ERR_INSERT_CLIENT_CONFIG_FAILED = EC(116000, "execute SQL failed which insert client config") + ERR_SELECT_CLIENT_CONFIG_FAILED = EC(116001, "execute SQL failed which select client config") + ERR_DELETE_CLIENT_CONFIG_FAILED = EC(116002, "execute SQL failed which delete client config") // 200: command options (hosts) diff --git a/internal/storage/sql.go b/internal/storage/sql.go index 5706c63fd..bd8468277 100644 --- a/internal/storage/sql.go +++ b/internal/storage/sql.go @@ -297,6 +297,34 @@ var ( SelectAuditLogById = `SELECT * FROM audit WHERE id = ?` ) +// any: we can store anything +type Any struct { + Id string + Data string +} + +var ( + // table: any + CreateAnyTable = ` + CREATE TABLE IF NOT EXISTS any ( + id TEXT PRIMARY KEY, + data TEXT NOT NULL + ) + ` + + // insert item + InsertAnyItem = `INSERT INTO any(id, data) VALUES(?, ?)` + + // set item + SetAnyItem = `UPDATE any SET data = ? WHERE id = ?` + + // select item by id + SelectAnyItem = `SELECT * FROM any WHERE id = ?` + + // delete item + DeleteAnyItem = `DELETE from any WHERE id = ?` +) + var ( // check pool column CheckPoolColumn = ` diff --git a/internal/storage/storage.go b/internal/storage/storage.go index e1dacdc07..88fa1045d 100644 --- a/internal/storage/storage.go +++ b/internal/storage/storage.go @@ -75,6 +75,7 @@ func (s *Storage) init() error { CreateClientsTable, CreatePlaygroundTable, CreateAuditTable, + CreateAnyTable, } for _, sql := range sqls { @@ -403,3 +404,43 @@ func (s *Storage) GetAuditLogs() ([]AuditLog, error) { func (s *Storage) GetAuditLog(id int64) ([]AuditLog, error) { return s.getAuditLogs(SelectAuditLogById, id) } + +// any item prefix +const ( + PREFIX_CLIENT_CONFIG = 0x01 +) + +func (s *Storage) realId(prefix int, id string) string { + return fmt.Sprintf("%d:%s", prefix, id) +} + +func (s *Storage) InsertClientConfig(id, data string) error { + id = s.realId(PREFIX_CLIENT_CONFIG, id) + return s.write(InsertAnyItem, id, data) +} + +func (s *Storage) GetClientConfig(id string) ([]Any, error) { + id = s.realId(PREFIX_CLIENT_CONFIG, id) + result, err := s.db.Query(SelectAnyItem, id) + if err != nil { + return nil, err + } + defer result.Close() + + items := []Any{} + var item Any + for result.Next() { + err = result.Scan(&item.Id, &item.Data) + if err != nil { + return nil, err + } + items = append(items, item) + } + + return items, nil +} + +func (s *Storage) DeleteClientConfig(id string) error { + id = s.realId(PREFIX_CLIENT_CONFIG, id) + return s.write(DeleteAnyItem, id) +} diff --git a/internal/task/task/bs/start_nebd.go b/internal/task/task/bs/start_nebd.go index e1bbf725e..a521493ff 100644 --- a/internal/task/task/bs/start_nebd.go +++ b/internal/task/task/bs/start_nebd.go @@ -137,6 +137,12 @@ func (s *step2InsertClient) Execute(ctx *context.Context) error { if err != nil { return errno.ERR_INSERT_CLIENT_FAILED.E(err) } + + err = curveadm.Storage().InsertClientConfig(volumeId, config.GetData()) + if err != nil { + return errno.ERR_INSERT_CLIENT_CONFIG_FAILED.E(err) + } + return nil } diff --git a/internal/task/task/bs/unmap.go b/internal/task/task/bs/unmap.go index 33a596373..3e1dee29b 100644 --- a/internal/task/task/bs/unmap.go +++ b/internal/task/task/bs/unmap.go @@ -121,6 +121,12 @@ func (s *step2DeleteClient) Execute(ctx *context.Context) error { if err != nil { return errno.ERR_DELETE_CLIENT_FAILED.E(err) } + + err = s.curveadm.Storage().DeleteClientConfig(s.volumeId) + if err != nil { + return errno.ERR_DELETE_CLIENT_CONFIG_FAILED.E(err) + } + return nil } diff --git a/internal/task/task/common/client_status.go b/internal/task/task/common/client_status.go index 4d4d9a581..b7b4d28a7 100644 --- a/internal/task/task/common/client_status.go +++ b/internal/task/task/common/client_status.go @@ -27,6 +27,7 @@ import ( "github.com/opencurve/curveadm/cli/cli" comm "github.com/opencurve/curveadm/internal/common" + "github.com/opencurve/curveadm/internal/errno" "github.com/opencurve/curveadm/internal/storage" "github.com/opencurve/curveadm/internal/task/context" "github.com/opencurve/curveadm/internal/task/step" @@ -35,11 +36,16 @@ import ( "github.com/opencurve/curveadm/internal/utils" ) +const ( + MISSING_CLIENT_CONFIG = "-" +) + type ( step2FormatClientStatus struct { client storage.Client containerId string status *string + cfgPath *string memStorage *utils.SafeMap } @@ -50,9 +56,32 @@ type ( ContainerId string Status string AuxInfo string + CfgPath string } ) +func dumpCfg(curveadm *cli.CurveAdm, id string, cfgPath *string) step.LambdaType { + return func(ctx *context.Context) error { + *cfgPath = MISSING_CLIENT_CONFIG + cfgs, err := curveadm.Storage().GetClientConfig(id) + if err != nil { + return errno.ERR_SELECT_CLIENT_CONFIG_FAILED.E(err) + } else if len(cfgs) == 0 { + return nil + } + + data := cfgs[0].Data + path := utils.RandFilename("/tmp") + err = utils.WriteFile(path, data, 0644) + if err != nil { + return errno.ERR_WRITE_FILE_FAILED.E(err) + } + + *cfgPath = path + return nil + } +} + // TODO(P0): init client status func setClientStatus(memStorage *utils.SafeMap, id string, status ClientStatus) { memStorage.TX(func(kv *utils.SafeMap) error { @@ -82,6 +111,7 @@ func (s *step2FormatClientStatus) Execute(ctx *context.Context) error { ContainerId: s.containerId, Status: status, AuxInfo: client.AuxInfo, + CfgPath: *s.cfgPath, }) return nil } @@ -99,7 +129,7 @@ func NewGetClientStatusTask(curveadm *cli.CurveAdm, v interface{}) (*task.Task, t := task.NewTask("Get Client Status", subname, hc.GetSSHConfig()) // add step - var status string + var status, cfgPath string t.AddStep(&step.ListContainers{ ShowAll: true, Format: `"{{.Status}}"`, @@ -107,10 +137,14 @@ func NewGetClientStatusTask(curveadm *cli.CurveAdm, v interface{}) (*task.Task, Out: &status, ExecOptions: curveadm.ExecOptions(), }) + t.AddStep(&step.Lambda{ + Lambda: dumpCfg(curveadm, client.Id, &cfgPath), + }) t.AddStep(&step2FormatClientStatus{ client: client, containerId: containerId, status: &status, + cfgPath: &cfgPath, memStorage: curveadm.MemStorage(), }) diff --git a/internal/task/task/fs/mount.go b/internal/task/task/fs/mount.go index 7d2410a98..9e855541b 100644 --- a/internal/task/task/fs/mount.go +++ b/internal/task/task/fs/mount.go @@ -257,6 +257,12 @@ func (s *step2InsertClient) Execute(ctx *context.Context) error { if err != nil { return errno.ERR_INSERT_CLIENT_FAILED.E(err) } + + err = curveadm.Storage().InsertClientConfig(fsId, config.GetData()) + if err != nil { + return errno.ERR_INSERT_CLIENT_CONFIG_FAILED.E(err) + } + return nil } diff --git a/internal/task/task/fs/umount.go b/internal/task/task/fs/umount.go index 10cd425ff..9a1275cae 100644 --- a/internal/task/task/fs/umount.go +++ b/internal/task/task/fs/umount.go @@ -83,6 +83,12 @@ func (s *step2DeleteClient) Execute(ctx *context.Context) error { if err != nil { return errno.ERR_DELETE_CLIENT_FAILED.E(err) } + + err = s.curveadm.Storage().DeleteClientConfig(s.fsId) + if err != nil { + return errno.ERR_DELETE_CLIENT_CONFIG_FAILED.E(err) + } + return nil } diff --git a/internal/tui/client/status.go b/internal/tui/client/status.go index b61ed0c31..c80ff3ac9 100644 --- a/internal/tui/client/status.go +++ b/internal/tui/client/status.go @@ -61,6 +61,9 @@ func FormatStatus(statuses []task.ClientStatus, verbose bool) string { "Status", "Aux Info", } + if verbose { + title = append(title, "Config Dumpfile") + } first, second := tui.FormatTitle(title) lines = append(lines, first) lines = append(lines, second) @@ -68,14 +71,21 @@ func FormatStatus(statuses []task.ClientStatus, verbose bool) string { // status sortStatues(statuses) for _, status := range statuses { - lines = append(lines, []interface{}{ + // line + line := []interface{}{ status.Id, status.Kind, status.Host, tui.TrimContainerId(status.ContainerId), tui.DecorateMessage{Message: status.Status, Decorate: statusDecorate}, status.AuxInfo, - }) + } + if verbose { + line = append(line, status.CfgPath) + } + + // lines + lines = append(lines, line) } output := tui.FixedFormat(lines, 2)