feat: implement cmd management for targets, LUNs, and TPGTs (fixes #36)
- Fix target delete URL path mismatch (/targets/ -> /target/) - Implement target create/delete server handlers with proper validation - Add DeleteTarget method with force flag and mutex locking to SCSITargetService - Implement full LU management: create/list/delete through CLI, client, and server - Add TPGT list command to show target portal group tags - Add unit tests for target/LU router handlers and SCSI service Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -65,19 +65,24 @@ func newCreateTargetCmd(cli *client.Client) *cobra.Command {
|
||||
}
|
||||
|
||||
func newCreateLuCmd(cli *client.Client) *cobra.Command {
|
||||
opts := api.LuCreateRequest{}
|
||||
var cmd = &cobra.Command{
|
||||
Use: "lu",
|
||||
Short: "Create a new Lu into gotgt",
|
||||
Long: `All software has versions. This is Gotgt 's`,
|
||||
Short: "Create a new LU into gotgt",
|
||||
Long: `Create a new Logical Unit and map it to a target`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := NoArgs(cmd, args); err != nil {
|
||||
return err
|
||||
}
|
||||
return createLu(cli)
|
||||
return createLu(cli, opts)
|
||||
},
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
_ = flags
|
||||
flags.StringVar(&opts.TargetName, "target", "", "Specify target name")
|
||||
flags.Uint64Var(&opts.LUN, "lun", 0, "Specify LUN number")
|
||||
flags.Uint64Var(&opts.DeviceID, "device-id", 0, "Specify device ID")
|
||||
flags.StringVar(&opts.Path, "path", "", "Specify backing store path (e.g., file:/tmp/disk.img)")
|
||||
flags.UintVar(&opts.BlockShift, "block-shift", 9, "Specify block shift (default 9 = 512 bytes)")
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -93,6 +98,17 @@ func createTarget(cli *client.Client, opts api.TargetCreateRequest) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func createLu(cli *client.Client) error {
|
||||
func createLu(cli *client.Client, opts api.LuCreateRequest) error {
|
||||
if opts.TargetName == "" {
|
||||
return fmt.Errorf("target name is required (--target)")
|
||||
}
|
||||
if opts.Path == "" {
|
||||
return fmt.Errorf("backing store path is required (--path)")
|
||||
}
|
||||
err := cli.LuCreate(context.Background(), opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("LU %d successfully created on target %s\n", opts.LUN, opts.TargetName)
|
||||
return nil
|
||||
}
|
||||
|
||||
64
cmd/list.go
64
cmd/list.go
@@ -19,6 +19,7 @@ package cmd
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/gostor/gotgt/pkg/api"
|
||||
@@ -39,6 +40,7 @@ func newListCommand(cli *client.Client) *cobra.Command {
|
||||
cmd.AddCommand(
|
||||
newListTargetCmd(cli),
|
||||
newListLuCmd(cli),
|
||||
newListTpgtCmd(cli),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
@@ -68,19 +70,38 @@ func newListTargetCmd(cli *client.Client) *cobra.Command {
|
||||
}
|
||||
|
||||
func newListLuCmd(cli *client.Client) *cobra.Command {
|
||||
opts := api.LuListOptions{}
|
||||
var cmd = &cobra.Command{
|
||||
Use: "lu",
|
||||
Short: "List Lu(s) of gotgt",
|
||||
Long: `All software has versions. This is Gotgt 's`,
|
||||
Short: "List LU(s) of gotgt",
|
||||
Long: `List Logical Units for a target`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := NoArgs(cmd, args); err != nil {
|
||||
return err
|
||||
}
|
||||
return listLu(cli)
|
||||
return listLu(cli, opts)
|
||||
},
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
_ = flags
|
||||
flags.StringVar(&opts.TargetName, "target", "", "Specify target name")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newListTpgtCmd(cli *client.Client) *cobra.Command {
|
||||
opts := api.TpgtListOptions{}
|
||||
var cmd = &cobra.Command{
|
||||
Use: "tpgt",
|
||||
Short: "List TPGT(s) of gotgt",
|
||||
Long: `List Target Portal Group Tags for a target`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := NoArgs(cmd, args); err != nil {
|
||||
return err
|
||||
}
|
||||
return listTpgt(cli, opts)
|
||||
},
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&opts.TargetName, "target", "", "Specify target name")
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -106,6 +127,39 @@ func listTarget(cli *client.Client, opts api.TargetListOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func listLu(cli *client.Client) error {
|
||||
func listLu(cli *client.Client, opts api.LuListOptions) error {
|
||||
if opts.TargetName == "" {
|
||||
return fmt.Errorf("target name is required (--target)")
|
||||
}
|
||||
results, err := cli.LuList(context.Background(), opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 15, 1, 3, ' ', 0)
|
||||
fmt.Fprintln(w, "LUN\tPATH\tSIZE\tONLINE")
|
||||
for _, lu := range results {
|
||||
fmt.Fprintf(w, "%d\t%s\t%d\t%v\n", lu.LUN, lu.Path, lu.Size, lu.Online)
|
||||
}
|
||||
w.Flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
func listTpgt(cli *client.Client, opts api.TpgtListOptions) error {
|
||||
if opts.TargetName == "" {
|
||||
return fmt.Errorf("target name is required (--target)")
|
||||
}
|
||||
results, err := cli.TpgtList(context.Background(), opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 10, 1, 3, ' ', 0)
|
||||
fmt.Fprintln(w, "TPGT\tPORTALS")
|
||||
for _, tpgt := range results {
|
||||
portals := strings.Join(tpgt.Portals, ", ")
|
||||
fmt.Fprintf(w, "%d\t%s\n", tpgt.TPGT, portals)
|
||||
}
|
||||
w.Flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
func newRemoveCommand(cli *client.Client) *cobra.Command {
|
||||
var cmd = &cobra.Command{
|
||||
Use: "rm",
|
||||
Short: "remove a new object",
|
||||
Short: "Remove an object",
|
||||
Long: `All software has versions. This is Gotgt 's`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println(cmd.UsageString())
|
||||
@@ -45,7 +45,7 @@ func newRemoveTargetCmd(cli *client.Client) *cobra.Command {
|
||||
opts := api.TargetRemoveOptions{}
|
||||
var cmd = &cobra.Command{
|
||||
Use: "target",
|
||||
Short: "Remove a new target into gotgt",
|
||||
Short: "Remove a target from gotgt",
|
||||
Long: `All software has versions. This is Gotgt 's`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return removeTarget(cli, opts)
|
||||
@@ -53,23 +53,28 @@ func newRemoveTargetCmd(cli *client.Client) *cobra.Command {
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&opts.Name, "name", "", "Specify target name")
|
||||
flags.BoolVar(&opts.Force, "force", false, "Specify target name")
|
||||
flags.BoolVar(&opts.Force, "force", false, "Force removal even with active sessions")
|
||||
|
||||
return cmd
|
||||
|
||||
}
|
||||
|
||||
func newRemoveLuCmd(cli *client.Client) *cobra.Command {
|
||||
opts := api.LuRemoveOptions{}
|
||||
var cmd = &cobra.Command{
|
||||
Use: "lu",
|
||||
Short: "Remove a new Lu into gotgt",
|
||||
Long: `All software has versions. This is Gotgt 's`,
|
||||
Short: "Remove a LU from gotgt",
|
||||
Long: `Remove a Logical Unit from a target`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return removeLu(cli)
|
||||
if err := NoArgs(cmd, args); err != nil {
|
||||
return err
|
||||
}
|
||||
return removeLu(cli, opts)
|
||||
},
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
_ = flags
|
||||
flags.StringVar(&opts.TargetName, "target", "", "Specify target name")
|
||||
flags.Uint64Var(&opts.LUN, "lun", 0, "Specify LUN number")
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -82,6 +87,14 @@ func removeTarget(cli *client.Client, opts api.TargetRemoveOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeLu(cli *client.Client) error {
|
||||
func removeLu(cli *client.Client, opts api.LuRemoveOptions) error {
|
||||
if opts.TargetName == "" {
|
||||
return fmt.Errorf("target name is required (--target)")
|
||||
}
|
||||
err := cli.LuRemove(context.Background(), opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("LU %d successfully removed from target %s\n", opts.LUN, opts.TargetName)
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user