diff --git a/cli/cli/cli.go b/cli/cli/cli.go index 26a516b79..0b19d48da 100644 --- a/cli/cli/cli.go +++ b/cli/cli/cli.go @@ -31,6 +31,7 @@ import ( "strings" "time" + "github.com/kpango/glg" comm "github.com/opencurve/curveadm/internal/common" configure "github.com/opencurve/curveadm/internal/configure/curveadm" "github.com/opencurve/curveadm/internal/configure/hosts" @@ -94,11 +95,11 @@ func NewCurveAdm() (*CurveAdm, error) { rootDir := fmt.Sprintf("%s/.curveadm", home) curveadm := &CurveAdm{ - rootDir: rootDir, - dataDir: path.Join(rootDir, "data"), - pluginDir: path.Join(rootDir, "plugins"), - logDir: path.Join(rootDir, "logs"), - tempDir: path.Join(rootDir, "temp"), + rootDir: rootDir, + dataDir: path.Join(rootDir, "data"), + pluginDir: path.Join(rootDir, "plugins"), + logDir: path.Join(rootDir, "logs"), + tempDir: path.Join(rootDir, "temp"), httpConfPath: path.Join(rootDir, "http/conf"), httpLogPath: path.Join(rootDir, "http/logs"), } @@ -308,6 +309,7 @@ func (curveadm *CurveAdm) ClusterName() string { return curveadm.c func (curveadm *CurveAdm) ClusterTopologyData() string { return curveadm.clusterTopologyData } func (curveadm *CurveAdm) ClusterPoolData() string { return curveadm.clusterPoolData } func (curveadm *CurveAdm) Monitor() storage.Monitor { return curveadm.monitor } +func (curveadm *CurveAdm) SetDebugLevel() { glg.Get().SetLevel(glg.DEBG) } func (curveadm *CurveAdm) GetHost(host string) (*hosts.HostConfig, error) { if len(curveadm.Hosts()) == 0 { diff --git a/cli/command/deploy.go b/cli/command/deploy.go index b2d3bc00c..c7530a059 100644 --- a/cli/command/deploy.go +++ b/cli/command/deploy.go @@ -111,6 +111,7 @@ type deployOptions struct { insecure bool poolset string poolsetDiskType string + debug bool } func checkDeployOptions(options deployOptions) error { @@ -145,6 +146,7 @@ func NewDeployCommand(curveadm *cli.CurveAdm) *cobra.Command { flags.BoolVarP(&options.insecure, "insecure", "k", false, "Deploy without precheck") flags.StringVar(&options.poolset, "poolset", "default", "poolset name") flags.StringVar(&options.poolsetDiskType, "poolset-disktype", "ssd", "Specify the disk type of physical pool") + flags.BoolVar(&options.debug, "debug", false, "Debug deploy progress") return cmd } @@ -299,36 +301,43 @@ func displayDeployTitle(curveadm *cli.CurveAdm, dcs []*topology.DeployConfig) { * 6) balance leader rapidly */ func runDeploy(curveadm *cli.CurveAdm, options deployOptions) error { - // 1) parse cluster topology + + // 1) check debug mode + debug := options.debug + if debug { + curveadm.SetDebugLevel() + } + + // 2) parse cluster topology dcs, err := curveadm.ParseTopology() if err != nil { return err } - // 2) skip service role + // 3) skip service role dcs = skipServiceRole(dcs, options) - // 3) precheck before deploy + // 4) precheck before deploy err = precheckBeforeDeploy(curveadm, dcs, options) if err != nil { return err } - // 4) generate deploy playbook + // 5) generate deploy playbook pb, err := genDeployPlaybook(curveadm, dcs, options) if err != nil { return err } - // 5) display title + // 6) display title displayDeployTitle(curveadm, dcs) - // 6) run playground + // 7) run playground if err = pb.Run(); err != nil { return err } - // 7) print success prompt + // 8) print success prompt curveadm.WriteOutln("") curveadm.WriteOutln(color.GreenString("Cluster '%s' successfully deployed ^_^."), curveadm.ClusterName()) return nil diff --git a/cli/command/format.go b/cli/command/format.go index be6bb3c03..51e788cfa 100644 --- a/cli/command/format.go +++ b/cli/command/format.go @@ -41,7 +41,9 @@ const ( FORMAT_EXAMPLE = `Examples: $ curveadm format -f /path/to/format.yaml # Format chunkfile pool with specified configure file $ curveadm format --status -f /path/to/format.yaml # Display formatting status - $ curveadm format --stop -f /path/to/format.yaml # Stop formatting progress` + $ curveadm format --stop -f /path/to/format.yaml # Stop formatting progress + $ curveadm format --debug -f /path/to/format.yaml # Format chunkfile with debug mode + $ curveadm format --clean -f /path/to/format.yaml # clean the container left by debug mode` ) var ( @@ -56,12 +58,39 @@ var ( FORMAT_STOP_PLAYBOOK_STEPS = []int{ playbook.STOP_FORMAT, } + + FORMAT_CLEAN_PLAYBOOK_STEPS = []int{ + playbook.CLEAN_FORMAT, + } ) type formatOptions struct { filename string showStatus bool stopFormat bool + debug bool + clean bool +} + +func checkFormatOptions(options formatOptions) error { + opts := []bool{ + options.showStatus, + options.stopFormat, + options.debug, + options.clean, + } + + trueCount := 0 + for _, opt := range opts { + if opt { + trueCount++ + if trueCount > 1 { + return errno.ERR_UNSUPPORT_CONFIGURE_VALUE_TYPE + } + } + } + + return nil } func NewFormatCommand(curveadm *cli.CurveAdm) *cobra.Command { @@ -72,6 +101,9 @@ func NewFormatCommand(curveadm *cli.CurveAdm) *cobra.Command { Short: "Format chunkfile pool", Args: cliutil.NoArgs, Example: FORMAT_EXAMPLE, + PreRunE: func(cmd *cobra.Command, args []string) error { + return checkFormatOptions(options) + }, RunE: func(cmd *cobra.Command, args []string) error { return runFormat(curveadm, options) }, @@ -82,6 +114,8 @@ func NewFormatCommand(curveadm *cli.CurveAdm) *cobra.Command { flags.StringVarP(&options.filename, "formatting", "f", "format.yaml", "Specify the configure file for formatting chunkfile pool") flags.BoolVar(&options.showStatus, "status", false, "Show formatting status") flags.BoolVar(&options.stopFormat, "stop", false, "Stop formatting progress") + flags.BoolVar(&options.debug, "debug", false, "Debug formatting progress") + flags.BoolVar(&options.clean, "clean", false, "Clean the Container") return cmd } @@ -93,25 +127,36 @@ func genFormatPlaybook(curveadm *cli.CurveAdm, return nil, errno.ERR_NO_DISK_FOR_FORMATTING } - if options.showStatus && options.stopFormat { - return nil, errno.ERR_UNSUPPORT_CONFIGURE_VALUE_TYPE - } + showStatus := options.showStatus + stopFormat := options.stopFormat + debug := options.debug + clean := options.clean steps := FORMAT_PLAYBOOK_STEPS - if options.showStatus { + if showStatus { steps = FORMAT_STATUS_PLAYBOOK_STEPS } - if options.stopFormat { + if stopFormat { steps = FORMAT_STOP_PLAYBOOK_STEPS } + if clean { + steps = FORMAT_CLEAN_PLAYBOOK_STEPS + } + pb := playbook.NewPlaybook(curveadm) for _, step := range steps { + // options + options := map[string]interface{}{} + if step == playbook.FORMAT_CHUNKFILE_POOL { + options[comm.DEBUG_MODE] = debug + } pb.AddStep(&playbook.PlaybookStep{ Type: step, Configs: fcs, ExecOptions: playbook.ExecOptions{ - SilentSubBar: options.showStatus, + SilentSubBar: showStatus, }, + Options: options, }) } return pb, nil @@ -133,6 +178,10 @@ func runFormat(curveadm *cli.CurveAdm, options formatOptions) error { var err error var fcs []*configure.FormatConfig diskRecords := curveadm.DiskRecords() + debug := options.debug + if debug { + curveadm.SetDebugLevel() + } // 1) parse format config from yaml file or database if len(diskRecords) == 0 { diff --git a/http/manager/manager.go b/http/manager/manager.go index b6099b00a..e919830f2 100644 --- a/http/manager/manager.go +++ b/http/manager/manager.go @@ -308,7 +308,7 @@ func GetClusterServicesAddr(r *pigeon.Request, ctx *Context) bool { pigeon.Field("error", err)) return core.ExitSuccessWithData(r, servicesAddr) } - } + } servicesAddr.ClusterId = adm.ClusterId() servicesAddr.Addrs = getServicesAddrFromConf(dcs, mcs) return core.ExitSuccessWithData(r, servicesAddr) diff --git a/internal/common/common.go b/internal/common/common.go index c12b801d0..d5e3ff147 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -47,6 +47,7 @@ const ( POOL_TYPE_PHYSICAL = "physicalpool" POOLSET = "poolset" POOLSET_DISK_TYPE = "poolset-disktype" + DEBUG_MODE = "DEBUG_MODE" // disk DISK_DEFAULT_NULL_SIZE = "-" diff --git a/internal/playbook/factory.go b/internal/playbook/factory.go index 68ce8993d..0749e719a 100644 --- a/internal/playbook/factory.go +++ b/internal/playbook/factory.go @@ -90,6 +90,7 @@ const ( CREATE_VOLUME MAP_IMAGE UNMAP_IMAGE + CLEAN_FORMAT // monitor PULL_MONITOR_IMAGE @@ -265,6 +266,8 @@ func (p *Playbook) createTasks(step *PlaybookStep) (*tasks.Tasks, error) { t, err = bs.NewGetFormatStatusTask(curveadm, config.GetFC(i)) case STOP_FORMAT: t, err = bs.NewStopFormatTask(curveadm, config.GetFC(i)) + case CLEAN_FORMAT: + t, err = bs.NewCleanFormatTask(curveadm, config.GetFC(i)) case BALANCE_LEADER: t, err = bs.NewBalanceTask(curveadm, config.GetDC(i)) case START_NEBD_SERVICE: diff --git a/internal/task/scripts/monitor.go b/internal/task/scripts/monitor.go index cf6f25302..824513a76 100644 --- a/internal/task/scripts/monitor.go +++ b/internal/task/scripts/monitor.go @@ -51,4 +51,4 @@ datasources: is_default: true version: 1 editable: true -` \ No newline at end of file +` diff --git a/internal/task/task/bs/format.go b/internal/task/task/bs/format.go index e9a04aafc..016826b71 100644 --- a/internal/task/task/bs/format.go +++ b/internal/task/task/bs/format.go @@ -29,6 +29,7 @@ import ( "time" "github.com/opencurve/curveadm/cli/cli" + comm "github.com/opencurve/curveadm/internal/common" "github.com/opencurve/curveadm/internal/configure" "github.com/opencurve/curveadm/internal/configure/disks" os "github.com/opencurve/curveadm/internal/configure/os" @@ -247,6 +248,7 @@ func NewFormatChunkfilePoolTask(curveadm *cli.CurveAdm, fc *configure.FormatConf formatScriptPath := fmt.Sprintf("%s/format.sh", layout.ToolsBinDir) formatCommand := fmt.Sprintf("%s %s %d %d %s %s", formatScriptPath, layout.FormatBinaryPath, usagePercent, DEFAULT_CHUNKFILE_SIZE, layout.ChunkfilePoolDir, layout.ChunkfilePoolMetaPath) + debug := curveadm.MemStorage().Get(comm.DEBUG_MODE).(bool) // 1: skip if formating container exist t.AddStep(&step.ListContainers{ @@ -319,7 +321,7 @@ func NewFormatChunkfilePoolTask(curveadm *cli.CurveAdm, fc *configure.FormatConf Command: formatCommand, Entrypoint: "/bin/bash", Name: containerName, - Remove: true, + Remove: !debug, Volumes: []step.Volume{{HostPath: mountPoint, ContainerPath: chunkfilePoolRootDir}}, Out: &containerId, ExecOptions: curveadm.ExecOptions(), diff --git a/internal/task/task/bs/format_clean.go b/internal/task/task/bs/format_clean.go new file mode 100644 index 000000000..2bff89e86 --- /dev/null +++ b/internal/task/task/bs/format_clean.go @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2022 NetEase Inc. + * + * 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. + */ + +/* + * Project: CurveAdm + * Created Date: 2023-10-05 + * Author: junfan song (Sonjf-ttk) + */ + +package bs + +import ( + "fmt" + + "github.com/opencurve/curveadm/cli/cli" + "github.com/opencurve/curveadm/internal/configure" + "github.com/opencurve/curveadm/internal/task/context" + "github.com/opencurve/curveadm/internal/task/step" + "github.com/opencurve/curveadm/internal/task/task" +) + +type step2FormatClean struct { + containerId *string + fc *configure.FormatConfig + curveadm *cli.CurveAdm +} + +func (s *step2FormatClean) Execute(ctx *context.Context) error { + if len(*s.containerId) == 0 { + return nil + } + + var success bool + steps := []task.Step{} + steps = append(steps, &step.StopContainer{ + ContainerId: *s.containerId, + Time: 1, + ExecOptions: s.curveadm.ExecOptions(), + }) + steps = append(steps, &step.RemoveContainer{ + Success: &success, + ContainerId: *s.containerId, + ExecOptions: s.curveadm.ExecOptions(), + }) + + for _, step := range steps { + err := step.Execute(ctx) + if err != nil { + return err + } + } + + return nil +} + +func NewCleanFormatTask(curveadm *cli.CurveAdm, fc *configure.FormatConfig) (*task.Task, error) { + hc, err := curveadm.GetHost(fc.GetHost()) + if err != nil { + return nil, err + } + + // new task + device := fc.GetDevice() + mountPoint := fc.GetMountPoint() + containerName := device2ContainerName(device) + subname := fmt.Sprintf("host=%s device=%s mountPoint=%s containerName=%s", + fc.GetHost(), device, mountPoint, containerName) + t := task.NewTask("Clean Format Container", subname, hc.GetSSHConfig()) + + // add step to task + var out string + t.AddStep(&step.ListContainers{ + ShowAll: true, + Format: `"{{.ID}}"`, + Filter: fmt.Sprintf("name=%s", containerName), + Out: &out, + ExecOptions: curveadm.ExecOptions(), + }) + t.AddStep(&step2FormatClean{ + containerId: &out, + fc: fc, + curveadm: curveadm, + }) + + return t, nil +} diff --git a/internal/task/task/bs/format_status.go b/internal/task/task/bs/format_status.go index d09c27eca..68b3a3265 100644 --- a/internal/task/task/bs/format_status.go +++ b/internal/task/task/bs/format_status.go @@ -95,7 +95,7 @@ func (s *step2FormatStatus) Execute(ctx *context.Context) error { } if usage == 0 { status = "Mounting" - } else if len(*s.containerStatus) > 1 { + } else if len(*s.containerStatus) > 1 && !strings.Contains(*s.containerStatus, "Exited") { status = "Formatting" } else if usage < s.config.GetFormatPercent() { status = "Pulling image" diff --git a/internal/task/task/checker/network.go b/internal/task/task/checker/network.go index 0edcf9142..e1493f0f8 100644 --- a/internal/task/task/checker/network.go +++ b/internal/task/task/checker/network.go @@ -303,7 +303,7 @@ func (s *step2StopContainer) Execute(ctx *context.Context) error { ExecOptions: s.curveadm.ExecOptions(), }) steps = append(steps, &step.RemoveContainer{ - Success: &success, // FIXME(P1): rmeove iff container exist + Success: &success, // FIXME(P1): remove if container exist ContainerId: *s.containerId, ExecOptions: s.curveadm.ExecOptions(), })