From 2788573601a3851d3859d2670f58b62a571be12b Mon Sep 17 00:00:00 2001 From: Scott Howe Date: Wed, 4 Dec 2024 09:10:41 -0700 Subject: [PATCH 1/5] fix(service): fix device discovery error handling during service starup (#59) --- cmd/cfm-service/main.go | 8 +++++--- pkg/api/api_default_service.go | 8 ++++---- pkg/common/status.go | 6 +++++- services/discovery.go | 10 ++++++---- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/cmd/cfm-service/main.go b/cmd/cfm-service/main.go index ef7f5b1..7a3502a 100644 --- a/cmd/cfm-service/main.go +++ b/cmd/cfm-service/main.go @@ -69,10 +69,12 @@ func main() { api.AddRedfishRouter(ctx, router, defaultRedfishController) // Discover devices before loading datastore - bladeDevices, _ := services.DiscoverDevices(ctx, defaultApiService, "blade") - hostDevices, _ := services.DiscoverDevices(ctx, defaultApiService, "cxl-host") + bladeDevices, errBlade := services.DiscoverDevices(ctx, defaultApiService, "blade") + hostDevices, errHost := services.DiscoverDevices(ctx, defaultApiService, "cxl-host") // Add the discovered devices into datastore - services.AddDiscoveredDevices(ctx, defaultApiService, bladeDevices, hostDevices) + if errBlade == nil && errHost == nil { + services.AddDiscoveredDevices(ctx, defaultApiService, bladeDevices, hostDevices) + } // Load datastore datastore.DStore().Restore() diff --git a/pkg/api/api_default_service.go b/pkg/api/api_default_service.go index 1fd2bfd..b704999 100644 --- a/pkg/api/api_default_service.go +++ b/pkg/api/api_default_service.go @@ -1498,7 +1498,7 @@ func (cfm *CfmApiService) RootGet(ctx context.Context) (openapi.ImplResponse, er func (cfm *CfmApiService) DiscoverDevices(ctx context.Context, deviceType string) (openapi.ImplResponse, error) { if deviceType != "blade" && deviceType != "cxl-host" { err := common.RequestError{ - StatusCode: http.StatusBadRequest, + StatusCode: common.StatusDeviceDiscoveryFailure, Err: fmt.Errorf("invalid type parameter"), } return formatErrorResp(ctx, &err) @@ -1513,7 +1513,7 @@ func (cfm *CfmApiService) DiscoverDevices(ctx context.Context, deviceType string conn, err := dbus.SystemBus() if err != nil { return formatErrorResp(ctx, &common.RequestError{ - StatusCode: http.StatusInternalServerError, + StatusCode: common.StatusDeviceDiscoveryFailure, Err: fmt.Errorf("cannot get system bus: %v", err), }) } @@ -1521,7 +1521,7 @@ func (cfm *CfmApiService) DiscoverDevices(ctx context.Context, deviceType string server, err := avahi.ServerNew(conn) if err != nil { return formatErrorResp(ctx, &common.RequestError{ - StatusCode: http.StatusInternalServerError, + StatusCode: common.StatusDeviceDiscoveryFailure, Err: fmt.Errorf("avahi new failed: %v", err), }) } @@ -1530,7 +1530,7 @@ func (cfm *CfmApiService) DiscoverDevices(ctx context.Context, deviceType string sb, err := server.ServiceBrowserNew(avahi.InterfaceUnspec, avahi.ProtoInet, "_obmc_redfish._tcp", "local", 0) if err != nil { return formatErrorResp(ctx, &common.RequestError{ - StatusCode: http.StatusInternalServerError, + StatusCode: common.StatusDeviceDiscoveryFailure, Err: fmt.Errorf("service browser new failed: %v", err), }) } diff --git a/pkg/common/status.go b/pkg/common/status.go index 3f067e6..3e3a600 100644 --- a/pkg/common/status.go +++ b/pkg/common/status.go @@ -76,6 +76,7 @@ const ( StatusBladeDeleteSessionFailure //500 StatusHostCreateSessionFailure //500 StatusHostDeleteSessionFailure //500 + StatusDeviceDiscoveryFailure //500 StatusApplianceIdDoesNotExist //404 StatusBladeIdDoesNotExist //404 @@ -186,6 +187,8 @@ func (e StatusCodeType) String() string { return "Rename Blade Failure" case StatusHostRenameFailure: return "Rename Host Failure" + case StatusDeviceDiscoveryFailure: + return "Device Discovery Failure" } return "Unknown" @@ -242,7 +245,8 @@ func (e StatusCodeType) HttpStatusCode() int { StatusHostDeleteSessionFailure, StatusApplianceCreateSessionFailure, StatusApplianceDeleteSessionFailure, - StatusManagerInitializationFailure: + StatusManagerInitializationFailure, + StatusDeviceDiscoveryFailure: return http.StatusInternalServerError // 500 case StatusAppliancesExceedMaximum, StatusBladesExceedMaximum, diff --git a/services/discovery.go b/services/discovery.go index df0955d..9a71542 100644 --- a/services/discovery.go +++ b/services/discovery.go @@ -1,6 +1,7 @@ package services import ( + "fmt" "log" "golang.org/x/net/context" @@ -12,12 +13,13 @@ import ( // discoverDevices function to call the DiscoverDevices API func DiscoverDevices(ctx context.Context, apiService openapi.DefaultAPIServicer, deviceType string) (openapi.ImplResponse, error) { - resp, err := apiService.DiscoverDevices(ctx, deviceType) - if err != nil { - log.Printf("Error discovering devices of type %s: %v", deviceType, err) + resp, _ := apiService.DiscoverDevices(ctx, deviceType) + if resp.Code >= 300 { + err := fmt.Errorf("error discovering devices of type %s: %+v", deviceType, resp) + log.Print(err) return resp, err } else { - log.Printf("Discovered devices of type %s: %v", deviceType, resp) + log.Printf("Discovered devices of type %s: %+v", deviceType, resp) return resp, nil } } From f6d7c55ecb8efdc9cc8ad5f4a3d721c5495967e0 Mon Sep 17 00:00:00 2001 From: Scott Howe Date: Wed, 4 Dec 2024 09:19:17 -0700 Subject: [PATCH 2/5] refactor(cli): command usage and example string generation refactor and update (#57) * refactor(cli): command usage and example string generation refactor and update * doc(cli): add docker-specific note about service-related cli options --- cli/pkg/serviceLib/flags/examples.go | 326 ++++++++++++++++++ cli/pkg/serviceLib/flags/flags.go | 75 +++- cli/pkg/serviceLib/flags/usages.go | 196 +++++++++++ .../serviceLib/serviceRequests/appliances.go | 4 +- cli/pkg/serviceLib/serviceRequests/blades.go | 4 +- cli/pkg/serviceLib/serviceRequests/hosts.go | 4 +- cmd/cfm-cli/cmd/addAppliance.go | 62 +++- cmd/cfm-cli/cmd/addBlade.go | 75 +++- cmd/cfm-cli/cmd/addHost.go | 70 +++- cmd/cfm-cli/cmd/assignBlade.go | 58 +++- cmd/cfm-cli/cmd/composeBlade.go | 74 +++- cmd/cfm-cli/cmd/deleteAppliance.go | 45 ++- cmd/cfm-cli/cmd/deleteBlade.go | 51 ++- cmd/cfm-cli/cmd/deleteHost.go | 45 ++- cmd/cfm-cli/cmd/freeBlade.go | 53 ++- cmd/cfm-cli/cmd/list.go | 6 +- cmd/cfm-cli/cmd/listAppliances.go | 40 ++- cmd/cfm-cli/cmd/listBladeMemory.go | 111 ++++-- cmd/cfm-cli/cmd/listBladePorts.go | 111 ++++-- cmd/cfm-cli/cmd/listBladeResources.go | 110 ++++-- cmd/cfm-cli/cmd/listBlades.go | 96 +++++- cmd/cfm-cli/cmd/listHostMemory.go | 94 ++++- cmd/cfm-cli/cmd/listHostMemoryDevices.go | 94 ++++- cmd/cfm-cli/cmd/listHostPorts.go | 94 ++++- cmd/cfm-cli/cmd/listHosts.go | 40 ++- cmd/cfm-cli/cmd/renameAppliance.go | 49 ++- cmd/cfm-cli/cmd/renameBlade.go | 54 ++- cmd/cfm-cli/cmd/renameHost.go | 49 ++- cmd/cfm-cli/cmd/resyncAppliance.go | 44 ++- cmd/cfm-cli/cmd/resyncBlade.go | 49 ++- cmd/cfm-cli/cmd/resyncHost.go | 44 ++- cmd/cfm-cli/cmd/root.go | 12 +- cmd/cfm-cli/cmd/unassignBlade.go | 58 +++- docs/DOCKER.md | 3 + 34 files changed, 1934 insertions(+), 366 deletions(-) create mode 100644 cli/pkg/serviceLib/flags/examples.go create mode 100644 cli/pkg/serviceLib/flags/usages.go diff --git a/cli/pkg/serviceLib/flags/examples.go b/cli/pkg/serviceLib/flags/examples.go new file mode 100644 index 0000000..eb5993e --- /dev/null +++ b/cli/pkg/serviceLib/flags/examples.go @@ -0,0 +1,326 @@ +// Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates +package flags + +import "fmt" + +// Family of getter functions to allow retrieval of consistent strings, for each option, when building the cobra.Command Example field string across multiple commands. + +func GetOptionExampleLhGroupServiceTcp() string { + return fmt.Sprintf("%s %s %s %s", + GetOptionExampleLhServiceIp(), + GetOptionExampleLhServicePort(), + GetOptionExampleLhServiceInsecure(), + GetOptionExampleLhServiceProtocol()) +} + +func GetOptionExampleLhServiceIp() string { + return fmt.Sprintf("--%s %s", SERVICE_NET_IP, SERVICE_NET_IP_DFLT) +} + +func GetOptionExampleLhServicePort() string { + return fmt.Sprintf("--%s %d", SERVICE_NET_PORT, SERVICE_NET_PORT_DFLT) +} + +func GetOptionExampleLhServiceInsecure() string { + return fmt.Sprintf("--%s", SERVICE_INSECURE) +} + +func GetOptionExampleLhServiceProtocol() string { + return fmt.Sprintf("--%s %s", SERVICE_PROTOCOL, SERVICE_PROTOCOL_DFLT) +} + +func GetOptionExampleLhApplianceId() string { + return fmt.Sprintf("--%s applianceId", APPLIANCE_ID) +} + +func GetOptionExampleLhBladeId() string { + return fmt.Sprintf("--%s bladeId", BLADE_ID) +} + +func GetOptionExampleLhHostId() string { + return fmt.Sprintf("--%s hostId", HOST_ID) +} + +func GetOptionExampleLhNewId() string { + return fmt.Sprintf("--%s newId", NEW_ID) +} + +func GetOptionExampleLhApplianceUsername() string { + return fmt.Sprintf("--%s %s", APPLIANCE_USERNAME, APPLIANCE_USERNAME_DFLT) +} + +func GetOptionExampleLhAppliancePassword() string { + return fmt.Sprintf("--%s %s", APPLIANCE_PASSWORD, APPLIANCE_PASSWORD_DFLT) +} + +func GetOptionExampleLhGroupApplianceTcp() string { + return fmt.Sprintf("%s %s %s %s", + GetOptionExampleLhApplianceIp(), + GetOptionExampleLhAppliancePort(), + GetOptionExampleLhApplianceInsecure(), + GetOptionExampleLhApplianceProtocol()) +} + +func GetOptionExampleLhApplianceIp() string { + return fmt.Sprintf("--%s %s", APPLIANCE_NET_IP, APPLIANCE_NET_IP_DFLT) +} + +func GetOptionExampleLhAppliancePort() string { + return fmt.Sprintf("--%s %d", APPLIANCE_NET_PORT, APPLIANCE_NET_PORT_DFLT) +} + +func GetOptionExampleLhApplianceInsecure() string { + return fmt.Sprintf("--%s", APPLIANCE_INSECURE) +} + +func GetOptionExampleLhApplianceProtocol() string { + return fmt.Sprintf("--%s %s", APPLIANCE_PROTOCOL, APPLIANCE_PROTOCOL_DFLT) +} + +func GetOptionExampleLhBladeUsername() string { + return fmt.Sprintf("--%s %s", BLADE_USERNAME, BLADE_USERNAME_DFLT) +} + +func GetOptionExampleLhBladePassword() string { + return fmt.Sprintf("--%s %s", BLADE_PASSWORD, BLADE_PASSWORD_DFLT) +} + +func GetOptionExampleLhGroupBladeTcp() string { + return fmt.Sprintf("%s %s %s %s", + GetOptionExampleLhBladeIp(), + GetOptionExampleLhBladePort(), + GetOptionExampleLhBladeInsecure(), + GetOptionExampleLhBladeProtocol()) +} + +func GetOptionExampleLhBladeIp() string { + return fmt.Sprintf("--%s %s", BLADE_NET_IP, BLADE_NET_IP_DFLT) +} + +func GetOptionExampleLhBladePort() string { + return fmt.Sprintf("--%s %d", BLADE_NET_PORT, BLADE_NET_PORT_DFLT) +} + +func GetOptionExampleLhBladeInsecure() string { + return fmt.Sprintf("--%s", BLADE_INSECURE) +} + +func GetOptionExampleLhBladeProtocol() string { + return fmt.Sprintf("--%s %s", BLADE_PROTOCOL, BLADE_PROTOCOL_DFLT) +} + +func GetOptionExampleLhHostUsername() string { + return fmt.Sprintf("--%s %s", HOST_USERNAME, HOST_USERNAME_DFLT) +} + +func GetOptionExampleLhHostPassword() string { + return fmt.Sprintf("--%s %s", HOST_PASSWORD, HOST_PASSWORD_DFLT) +} + +func GetOptionExampleLhGroupHostTcp() string { + return fmt.Sprintf("%s %s %s %s", + GetOptionExampleLhHostIp(), + GetOptionExampleLhHostPort(), + GetOptionExampleLhHostInsecure(), + GetOptionExampleLhHostProtocol()) +} + +func GetOptionExampleLhHostIp() string { + return fmt.Sprintf("--%s %s", HOST_NET_IP, HOST_NET_IP_DFLT) +} + +func GetOptionExampleLhHostPort() string { + return fmt.Sprintf("--%s %d", HOST_NET_PORT, HOST_NET_PORT_DFLT) +} + +func GetOptionExampleLhHostInsecure() string { + return fmt.Sprintf("--%s", HOST_INSECURE) +} + +func GetOptionExampleLhHostProtocol() string { + return fmt.Sprintf("--%s %s", HOST_PROTOCOL, HOST_PROTOCOL_DFLT) +} + +func GetOptionExampleLhMemoryId() string { + return fmt.Sprintf("--%s memoryId", MEMORY_ID) +} + +func GetOptionExampleLhMemoryQos() string { + return fmt.Sprintf("--%s %d", MEMORY_QOS, MEMORY_QOS_DFLT) +} + +func GetOptionExampleLhMemoryDeviceId() string { + return fmt.Sprintf("--%s memoryDeviceId", MEMORY_DEVICE_ID) +} + +func GetOptionExampleLhPortId() string { + return fmt.Sprintf("--%s portId", PORT_ID) +} + +func GetOptionExampleLhResourceId() string { + return fmt.Sprintf("--%s resourceId", RESOURCE_ID) +} + +func GetOptionExampleLhResourceSize() string { + return fmt.Sprintf("--%s %s", RESOURCE_SIZE, SIZE_DFLT) +} + +func GetOptionExampleShGroupServiceTcp() string { + return fmt.Sprintf("%s %s %s %s", + GetOptionExampleShServiceIp(), + GetOptionExampleShServicePort(), + GetOptionExampleShServiceInsecure(), + GetOptionExampleShServiceProtocol()) +} + +func GetOptionExampleShServiceIp() string { + return fmt.Sprintf("-%s %s", SERVICE_NET_IP_SH, SERVICE_NET_IP_DFLT) +} + +func GetOptionExampleShServicePort() string { + return fmt.Sprintf("-%s %d", SERVICE_NET_PORT_SH, SERVICE_NET_PORT_DFLT) +} + +func GetOptionExampleShServiceInsecure() string { + return fmt.Sprintf("-%s", SERVICE_INSECURE_SH) +} + +func GetOptionExampleShServiceProtocol() string { + return fmt.Sprintf("-%s %s", SERVICE_PROTOCOL_SH, SERVICE_PROTOCOL_DFLT) +} + +func GetOptionExampleShApplianceId() string { + return fmt.Sprintf("-%s applianceId", APPLIANCE_ID_SH) +} + +func GetOptionExampleShBladeId() string { + return fmt.Sprintf("-%s bladeId", BLADE_ID_SH) +} + +func GetOptionExampleShHostId() string { + return fmt.Sprintf("-%s hostId", HOST_ID_SH) +} + +func GetOptionExampleShNewId() string { + return fmt.Sprintf("-%s newId", NEW_ID_SH) +} + +func GetOptionExampleShApplianceUsername() string { + return fmt.Sprintf("-%s %s", APPLIANCE_USERNAME_SH, APPLIANCE_USERNAME_DFLT) +} + +func GetOptionExampleShAppliancePassword() string { + return fmt.Sprintf("-%s %s", APPLIANCE_PASSWORD_SH, APPLIANCE_PASSWORD_DFLT) +} + +func GetOptionExampleShGroupApplianceTcp() string { + return fmt.Sprintf("%s %s %s %s", + GetOptionExampleShApplianceIp(), + GetOptionExampleShAppliancePort(), + GetOptionExampleShApplianceInsecure(), + GetOptionExampleShApplianceProtocol()) +} + +func GetOptionExampleShApplianceIp() string { + return fmt.Sprintf("-%s %s", APPLIANCE_NET_IP_SH, APPLIANCE_NET_IP_DFLT) +} + +func GetOptionExampleShAppliancePort() string { + return fmt.Sprintf("-%s %d", APPLIANCE_NET_PORT_SH, APPLIANCE_NET_PORT_DFLT) +} + +func GetOptionExampleShApplianceInsecure() string { + return fmt.Sprintf("-%s", APPLIANCE_INSECURE_SH) +} + +func GetOptionExampleShApplianceProtocol() string { + return fmt.Sprintf("-%s %s", APPLIANCE_PROTOCOL_SH, APPLIANCE_PROTOCOL_DFLT) +} + +func GetOptionExampleShBladeUsername() string { + return fmt.Sprintf("-%s %s", BLADE_USERNAME_SH, BLADE_USERNAME_DFLT) +} + +func GetOptionExampleShBladePassword() string { + return fmt.Sprintf("-%s %s", BLADE_PASSWORD_SH, BLADE_PASSWORD_DFLT) +} + +func GetOptionExampleShGroupBladeTcp() string { + return fmt.Sprintf("%s %s %s %s", + GetOptionExampleShBladeIp(), + GetOptionExampleShBladePort(), + GetOptionExampleShBladeInsecure(), + GetOptionExampleShBladeProtocol()) +} + +func GetOptionExampleShBladeIp() string { + return fmt.Sprintf("-%s %s", BLADE_NET_IP_SH, BLADE_NET_IP_DFLT) +} + +func GetOptionExampleShBladePort() string { + return fmt.Sprintf("-%s %d", BLADE_NET_PORT_SH, BLADE_NET_PORT_DFLT) +} + +func GetOptionExampleShBladeInsecure() string { + return fmt.Sprintf("-%s", BLADE_INSECURE_SH) +} + +func GetOptionExampleShBladeProtocol() string { + return fmt.Sprintf("-%s %s", BLADE_PROTOCOL_SH, BLADE_PROTOCOL_DFLT) +} + +func GetOptionExampleShHostUsername() string { + return fmt.Sprintf("-%s %s", HOST_USERNAME_SH, HOST_USERNAME_DFLT) +} + +func GetOptionExampleShHostPassword() string { + return fmt.Sprintf("-%s %s", HOST_PASSWORD_SH, HOST_PASSWORD_DFLT) +} + +func GetOptionExampleShGroupHostTcp() string { + return fmt.Sprintf("%s %s %s %s", + GetOptionExampleShHostIp(), + GetOptionExampleShHostPort(), + GetOptionExampleShHostInsecure(), + GetOptionExampleShHostProtocol()) +} + +func GetOptionExampleShHostIp() string { + return fmt.Sprintf("-%s %s", HOST_NET_IP_SH, HOST_NET_IP_DFLT) +} + +func GetOptionExampleShHostPort() string { + return fmt.Sprintf("-%s %d", HOST_NET_PORT_SH, HOST_NET_PORT_DFLT) +} + +func GetOptionExampleShHostInsecure() string { + return fmt.Sprintf("-%s", HOST_INSECURE_SH) +} + +func GetOptionExampleShHostProtocol() string { + return fmt.Sprintf("-%s %s", HOST_PROTOCOL_SH, HOST_PROTOCOL_DFLT) +} + +func GetOptionExampleShMemoryId() string { + return fmt.Sprintf("-%s memoryId", MEMORY_ID_SH) +} + +func GetOptionExampleShMemoryQos() string { + return fmt.Sprintf("-%s %d", MEMORY_QOS_SH, MEMORY_QOS_DFLT) +} + +func GetOptionExampleShMemoryDeviceId() string { + return fmt.Sprintf("-%s memoryDeviceId", MEMORY_DEVICE_ID_SH) +} + +func GetOptionExampleShPortId() string { + return fmt.Sprintf("-%s portId", PORT_ID_SH) +} + +func GetOptionExampleShResourceId() string { + return fmt.Sprintf("-%s resourceId", RESOURCE_ID_SH) +} + +func GetOptionExampleShResourceSize() string { + return fmt.Sprintf("-%s %s", RESOURCE_SIZE_SH, SIZE_DFLT) +} diff --git a/cli/pkg/serviceLib/flags/flags.go b/cli/pkg/serviceLib/flags/flags.go index de259d3..d1576b3 100644 --- a/cli/pkg/serviceLib/flags/flags.go +++ b/cli/pkg/serviceLib/flags/flags.go @@ -4,8 +4,7 @@ package flags // CLI flag component descriptor const ( - SERVICE string = "serv" - DEVICE string = "dev" //generic "device" for appliance OR host - future deprecation + SERVICE string = "service" APPLIANCE string = "appliance" BLADE string = "blade" HOST string = "host" @@ -13,6 +12,13 @@ const ( MEMORY_DEVICE string = "memory-device" PORT string = "port" RESOURCE string = "resource" + + APPLIANCES string = "appliances" + BLADES string = "blades" + HOSTS string = "hosts" + MEMORY_DEVICES string = "memory-devices" + PORTS string = "ports" + RESOURCES string = "resources" ) // CLI flag detail descriptor @@ -20,7 +26,7 @@ const ( ID string = "id" USERNAME string = "username" PASSWORD string = "password" - NET_IP string = "ip" + NET_IP string = "net-ip" NET_PORT string = "net-port" INSECURE string = "insecure" PROTOCOL string = "protocol" @@ -40,9 +46,9 @@ const ( SERVICE_NET_PORT string = SERVICE + "-" + NET_PORT SERVICE_NET_PORT_SH string = "p" SERVICE_INSECURE string = SERVICE + "-" + INSECURE - SERVICE_INSECURE_SH string = "" //"s" + SERVICE_INSECURE_SH string = "s" SERVICE_PROTOCOL string = SERVICE + "-" + PROTOCOL - SERVICE_PROTOCOL_SH string = "" //"t" + SERVICE_PROTOCOL_SH string = "t" APPLIANCE_ID string = APPLIANCE + "-" + ID APPLIANCE_ID_SH string = "L" @@ -53,18 +59,51 @@ const ( NEW_ID string = NEW + "-" + ID NEW_ID_SH string = "N" - DEVICE_USERNAME string = DEVICE + "-" + USERNAME - DEVICE_USERNAME_SH string = "R" - DEVICE_PASSWORD string = DEVICE + "-" + PASSWORD - DEVICE_PASSWORD_SH string = "W" - DEVICE_NET_IP string = DEVICE + "-" + NET_IP - DEVICE_NET_IP_SH string = "A" - DEVICE_NET_PORT string = DEVICE + "-" + NET_PORT - DEVICE_NET_PORT_SH string = "P" - DEVICE_INSECURE string = DEVICE + "-" + INSECURE - DEVICE_INSECURE_SH string = "S" - DEVICE_PROTOCOL string = DEVICE + "-" + PROTOCOL - DEVICE_PROTOCOL_SH string = "T" + COMMON_USERNAME_SH string = "R" + COMMON_PASSWORD_SH string = "W" + COMMON_NET_IP_SH string = "A" + COMMON_NET_PORT_SH string = "P" + COMMON_INSECURE_SH string = "S" + COMMON_PROTOCOL_SH string = "T" + + APPLIANCE_USERNAME string = APPLIANCE + "-" + USERNAME + APPLIANCE_USERNAME_SH string = COMMON_USERNAME_SH + APPLIANCE_PASSWORD string = APPLIANCE + "-" + PASSWORD + APPLIANCE_PASSWORD_SH string = COMMON_PASSWORD_SH + APPLIANCE_NET_IP string = APPLIANCE + "-" + NET_IP + APPLIANCE_NET_IP_SH string = COMMON_NET_IP_SH + APPLIANCE_NET_PORT string = APPLIANCE + "-" + NET_PORT + APPLIANCE_NET_PORT_SH string = COMMON_NET_PORT_SH + APPLIANCE_INSECURE string = APPLIANCE + "-" + INSECURE + APPLIANCE_INSECURE_SH string = COMMON_INSECURE_SH + APPLIANCE_PROTOCOL string = APPLIANCE + "-" + PROTOCOL + APPLIANCE_PROTOCOL_SH string = COMMON_PROTOCOL_SH + + BLADE_USERNAME string = BLADE + "-" + USERNAME + BLADE_USERNAME_SH string = COMMON_USERNAME_SH + BLADE_PASSWORD string = BLADE + "-" + PASSWORD + BLADE_PASSWORD_SH string = COMMON_PASSWORD_SH + BLADE_NET_IP string = BLADE + "-" + NET_IP + BLADE_NET_IP_SH string = COMMON_NET_IP_SH + BLADE_NET_PORT string = BLADE + "-" + NET_PORT + BLADE_NET_PORT_SH string = COMMON_NET_PORT_SH + BLADE_INSECURE string = BLADE + "-" + INSECURE + BLADE_INSECURE_SH string = COMMON_INSECURE_SH + BLADE_PROTOCOL string = BLADE + "-" + PROTOCOL + BLADE_PROTOCOL_SH string = COMMON_PROTOCOL_SH + + HOST_USERNAME string = HOST + "-" + USERNAME + HOST_USERNAME_SH string = COMMON_USERNAME_SH + HOST_PASSWORD string = HOST + "-" + PASSWORD + HOST_PASSWORD_SH string = COMMON_PASSWORD_SH + HOST_NET_IP string = HOST + "-" + NET_IP + HOST_NET_IP_SH string = COMMON_NET_IP_SH + HOST_NET_PORT string = HOST + "-" + NET_PORT + HOST_NET_PORT_SH string = COMMON_NET_PORT_SH + HOST_INSECURE string = HOST + "-" + INSECURE + HOST_INSECURE_SH string = COMMON_INSECURE_SH + HOST_PROTOCOL string = HOST + "-" + PROTOCOL + HOST_PROTOCOL_SH string = COMMON_PROTOCOL_SH MEMORY_ID string = MEMORY + "-" + ID MEMORY_ID_SH string = "m" @@ -115,7 +154,7 @@ const ( HOST_USERNAME_DFLT string = "admin" HOST_PASSWORD_DFLT string = "admin12345" - SIZE_DFLT string = "0" + SIZE_DFLT string = "8g" MEMORY_QOS_DFLT = 4 diff --git a/cli/pkg/serviceLib/flags/usages.go b/cli/pkg/serviceLib/flags/usages.go new file mode 100644 index 0000000..1cafbea --- /dev/null +++ b/cli/pkg/serviceLib/flags/usages.go @@ -0,0 +1,196 @@ +// Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates +package flags + +import "fmt" + +// Family of getter functions to allow retrieval of consistent strings, for each option, when building the cobra.Command Usage field string across multiple commands. +// +// Usage syntax is defined as follows: +// 1.) Square Brackets [ ]: Indicate optional elements. For example, in command [option], the option is not required for the command to run. +// 2.) Angle Brackets < >: Often used to denote placeholders for user-supplied values, such as command . +// 3.) Curly Braces { }: Indicate a set of choices, where you must choose one. For example, command {start|stop|restart} means you must choose one of start, stop, or restart. +// 4.) Vertical Bar |: Used within curly braces to separate choices, as shown above. +// 5.) Ellipsis ...: Indicates that the preceding element can be repeated multiple times. For example, command [option]... means you can use multiple options. +// 6.) Parentheses ( ): Sometimes used to group elements together, though less common in man pages. +// 7.) Bold Text: Typically used to show the command itself or mandatory elements. +// 8.) Italic Text: Used for arguments or variables that the user must replace with actual values. + +// formatUsage - Generates a formatted usage string for a single command option. +// Used for every option in the cobra.Command.Use field. +// This function is meant to be called multiple times to consistently generate a complete string that represents the usage for a single cli command option. +// String format(using all available features): [--option | -o ] +func formatUsage(option, shorthand, entry string, optional bool) string { + usage := fmt.Sprintf("{--%s | -%s}", option, shorthand) + + if entry != "" { + usage = fmt.Sprintf("%s <%s>", usage, entry) + } + + if optional { + usage = fmt.Sprintf("[%s]", usage) + } // else { + // usage = fmt.Sprintf("(%s)", usage) + // } + + return usage +} + +func GetOptionUsageGroupServiceTcp(optional bool) string { + return fmt.Sprintf("%s %s %s %s", + GetOptionUsageServiceIp(optional), + GetOptionUsageServicePort(optional), + GetOptionUsageServiceInsecure(optional), + GetOptionUsageServiceProtocol(optional)) +} + +func GetOptionUsageServiceIp(optional bool) string { + return formatUsage(SERVICE_NET_IP, SERVICE_NET_IP_SH, "ip_address", optional) +} + +func GetOptionUsageServicePort(optional bool) string { + return formatUsage(SERVICE_NET_PORT, SERVICE_NET_PORT_SH, PORT, optional) +} + +func GetOptionUsageServiceInsecure(optional bool) string { + return formatUsage(SERVICE_INSECURE, SERVICE_INSECURE_SH, "", optional) +} + +func GetOptionUsageServiceProtocol(optional bool) string { + return formatUsage(SERVICE_PROTOCOL, SERVICE_PROTOCOL_SH, PROTOCOL, optional) +} + +func GetOptionUsageApplianceId(optional bool) string { + return formatUsage(APPLIANCE_ID, APPLIANCE_ID_SH, "applianceId", optional) +} + +func GetOptionUsageBladeId(optional bool) string { + return formatUsage(BLADE_ID, BLADE_ID_SH, "bladeId", optional) +} + +func GetOptionUsageHostId(optional bool) string { + return formatUsage(HOST_ID, HOST_ID_SH, "hostId", optional) +} + +func GetOptionUsageNewId(optional bool) string { + return formatUsage(NEW_ID, NEW_ID_SH, "newId", optional) +} + +func GetOptionUsageApplianceUsername(optional bool) string { + return formatUsage(APPLIANCE_USERNAME, APPLIANCE_USERNAME_SH, USERNAME, optional) +} + +func GetOptionUsageAppliancePassword(optional bool) string { + return formatUsage(APPLIANCE_PASSWORD, APPLIANCE_PASSWORD_SH, PASSWORD, optional) +} + +func GetOptionUsageGroupApplianceTcp(optional bool) string { + return fmt.Sprintf("%s %s %s %s", + GetOptionUsageApplianceIp(optional), + GetOptionUsageAppliancePort(optional), + GetOptionUsageApplianceInsecure(optional), + GetOptionUsageApplianceProtocol(optional)) +} + +func GetOptionUsageApplianceIp(optional bool) string { + return formatUsage(APPLIANCE_NET_IP, APPLIANCE_NET_IP_SH, "ip_address", optional) +} + +func GetOptionUsageAppliancePort(optional bool) string { + return formatUsage(APPLIANCE_NET_PORT, APPLIANCE_NET_PORT_SH, PORT, optional) +} + +func GetOptionUsageApplianceInsecure(optional bool) string { + return formatUsage(APPLIANCE_INSECURE, APPLIANCE_INSECURE_SH, "", optional) +} + +func GetOptionUsageApplianceProtocol(optional bool) string { + return formatUsage(APPLIANCE_PROTOCOL, APPLIANCE_PROTOCOL_SH, PROTOCOL, optional) +} + +func GetOptionUsageBladeUsername(optional bool) string { + return formatUsage(BLADE_USERNAME, BLADE_USERNAME_SH, USERNAME, optional) +} + +func GetOptionUsageBladePassword(optional bool) string { + return formatUsage(BLADE_PASSWORD, BLADE_PASSWORD_SH, PASSWORD, optional) +} + +func GetOptionUsageGroupBladeTcp(optional bool) string { + return fmt.Sprintf("%s %s %s %s", + GetOptionUsageBladeIp(optional), + GetOptionUsageBladePort(optional), + GetOptionUsageBladeInsecure(optional), + GetOptionUsageBladeProtocol(optional)) +} + +func GetOptionUsageBladeIp(optional bool) string { + return formatUsage(BLADE_NET_IP, BLADE_NET_IP_SH, "ip_address", optional) +} + +func GetOptionUsageBladePort(optional bool) string { + return formatUsage(BLADE_NET_PORT, BLADE_NET_PORT_SH, PORT, optional) +} + +func GetOptionUsageBladeInsecure(optional bool) string { + return formatUsage(BLADE_INSECURE, BLADE_INSECURE_SH, "", optional) +} + +func GetOptionUsageBladeProtocol(optional bool) string { + return formatUsage(BLADE_PROTOCOL, BLADE_PROTOCOL_SH, PROTOCOL, optional) +} + +func GetOptionUsageHostUsername(optional bool) string { + return formatUsage(HOST_USERNAME, HOST_USERNAME_SH, USERNAME, optional) +} + +func GetOptionUsageHostPassword(optional bool) string { + return formatUsage(HOST_PASSWORD, HOST_PASSWORD_SH, PASSWORD, optional) +} + +func GetOptionUsageGroupHostTcp(optional bool) string { + return fmt.Sprintf("%s %s %s %s", + GetOptionUsageHostIp(optional), + GetOptionUsageHostPort(optional), + GetOptionUsageHostInsecure(optional), + GetOptionUsageHostProtocol(optional)) +} + +func GetOptionUsageHostIp(optional bool) string { + return formatUsage(HOST_NET_IP, HOST_NET_IP_SH, "ip_address", optional) +} + +func GetOptionUsageHostPort(optional bool) string { + return formatUsage(HOST_NET_PORT, HOST_NET_PORT_SH, PORT, optional) +} + +func GetOptionUsageHostInsecure(optional bool) string { + return formatUsage(HOST_INSECURE, HOST_INSECURE_SH, "", optional) +} + +func GetOptionUsageHostProtocol(optional bool) string { + return formatUsage(HOST_PROTOCOL, HOST_PROTOCOL_SH, PROTOCOL, optional) +} + +func GetOptionUsageMemoryId(optional bool) string { + return formatUsage(MEMORY_ID, MEMORY_ID_SH, "memoryId", optional) +} + +func GetOptionUsageMemoryQos(optional bool) string { + return formatUsage(MEMORY_QOS, MEMORY_QOS_SH, QOS, optional) +} + +func GetOptionUsageMemoryDeviceId(optional bool) string { + return formatUsage(MEMORY_DEVICE_ID, MEMORY_DEVICE_ID_SH, "memdevId", optional) +} + +func GetOptionUsagePortId(optional bool) string { + return formatUsage(PORT_ID, PORT_ID_SH, "portId", optional) +} + +func GetOptionUsageResourceId(optional bool) string { + return formatUsage(RESOURCE_ID, RESOURCE_ID_SH, "resourceId", optional) +} + +func GetOptionUsageResourceSize(optional bool) string { + return formatUsage(RESOURCE_SIZE, RESOURCE_SIZE_SH, SIZE, optional) +} diff --git a/cli/pkg/serviceLib/serviceRequests/appliances.go b/cli/pkg/serviceLib/serviceRequests/appliances.go index 5adacb4..57f3228 100644 --- a/cli/pkg/serviceLib/serviceRequests/appliances.go +++ b/cli/pkg/serviceLib/serviceRequests/appliances.go @@ -28,8 +28,8 @@ func NewServiceRequestAddAppliance(cmd *cobra.Command) *ServiceRequestAddApplian return &ServiceRequestAddAppliance{ ServiceTcp: NewTcpInfo(cmd, flags.SERVICE), ApplianceId: NewId(cmd, flags.APPLIANCE), - ApplianceCred: NewDeviceCredentials(cmd, flags.DEVICE), - ApplianceTcp: NewTcpInfo(cmd, flags.DEVICE), + ApplianceCred: NewDeviceCredentials(cmd, flags.APPLIANCE), + ApplianceTcp: NewTcpInfo(cmd, flags.APPLIANCE), } } diff --git a/cli/pkg/serviceLib/serviceRequests/blades.go b/cli/pkg/serviceLib/serviceRequests/blades.go index 5b0911e..5591a1e 100644 --- a/cli/pkg/serviceLib/serviceRequests/blades.go +++ b/cli/pkg/serviceLib/serviceRequests/blades.go @@ -27,8 +27,8 @@ func NewServiceRequestAddBlade(cmd *cobra.Command) *ServiceRequestAddBlade { ServiceTcp: NewTcpInfo(cmd, flags.SERVICE), ApplianceId: NewId(cmd, flags.APPLIANCE), BladeId: NewId(cmd, flags.BLADE), - BladeCred: NewDeviceCredentials(cmd, flags.DEVICE), - BladeTcp: NewTcpInfo(cmd, flags.DEVICE), + BladeCred: NewDeviceCredentials(cmd, flags.BLADE), + BladeTcp: NewTcpInfo(cmd, flags.BLADE), } } diff --git a/cli/pkg/serviceLib/serviceRequests/hosts.go b/cli/pkg/serviceLib/serviceRequests/hosts.go index 2634bda..c3e41d4 100644 --- a/cli/pkg/serviceLib/serviceRequests/hosts.go +++ b/cli/pkg/serviceLib/serviceRequests/hosts.go @@ -25,8 +25,8 @@ func NewServiceRequestAddHost(cmd *cobra.Command) *ServiceRequestAddHost { return &ServiceRequestAddHost{ ServiceTcp: NewTcpInfo(cmd, flags.SERVICE), HostId: NewId(cmd, flags.HOST), - HostCred: NewDeviceCredentials(cmd, flags.DEVICE), - HostTcp: NewTcpInfo(cmd, flags.DEVICE), + HostCred: NewDeviceCredentials(cmd, flags.HOST), + HostTcp: NewTcpInfo(cmd, flags.HOST), } } diff --git a/cmd/cfm-cli/cmd/addAppliance.go b/cmd/cfm-cli/cmd/addAppliance.go index 0e0990c..45855cc 100644 --- a/cmd/cfm-cli/cmd/addAppliance.go +++ b/cmd/cfm-cli/cmd/addAppliance.go @@ -5,20 +5,18 @@ package cmd import ( "cfm/cli/pkg/serviceLib/flags" "cfm/cli/pkg/serviceLib/serviceRequests" + "fmt" "github.com/spf13/cobra" ) var addApplianceCmd = &cobra.Command{ - Use: `appliance [--serv-ip | -a] [--serv-net-port | -p] [--appliance-id | -L]`, + Use: GetCmdUsageAddAppliance(), Short: "Add a memory appliance to cfm-service", - Long: `Create a virtual memeory appliance within the cfm-service. + Long: `Create a virtual memory appliance within the cfm-service. Use "cfm add blade" to add composable memory to the appliance.`, - Example: ` - cfm add appliance --serv-ip 127.0.0.1 --serv-net-port 8080 --appliance-id userDefinedId - - cfm add appliance -a 127.0.0.1 -p 8080 -L userDefinedId`, - Args: cobra.MatchAll(cobra.NoArgs), + Example: GetCmdExampleAddAppliance(), + Args: cobra.MatchAll(cobra.NoArgs), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { initLogging(cmd) return nil @@ -40,17 +38,51 @@ func init() { initCommonPersistentFlags(addApplianceCmd) - // Unused by user, but some values are required by cfm-service frontend. - addApplianceCmd.Flags().StringP(flags.DEVICE_USERNAME, flags.DEVICE_USERNAME_SH, flags.APPLIANCE_USERNAME_DFLT, "Appliance username for authentication") - addApplianceCmd.Flags().StringP(flags.DEVICE_PASSWORD, flags.DEVICE_PASSWORD_SH, flags.APPLIANCE_PASSWORD_DFLT, "Appliance password for authentication") + addApplianceCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "User-defined ID for target appliance (Optional)\n (default: random w\\ format: memory-appliance-XXXX)") - addApplianceCmd.Flags().StringP(flags.DEVICE_NET_IP, flags.DEVICE_NET_IP_SH, flags.APPLIANCE_NET_IP_DFLT, "Appliance network IP address") - addApplianceCmd.Flags().Uint16P(flags.DEVICE_NET_PORT, flags.DEVICE_NET_PORT_SH, flags.APPLIANCE_NET_PORT_DFLT, "Appliance network port ") - addApplianceCmd.Flags().BoolP(flags.DEVICE_INSECURE, flags.DEVICE_INSECURE_SH, flags.APPLIANCE_INSECURE_DFLT, "Appliance insecure connection flag") - addApplianceCmd.Flags().StringP(flags.DEVICE_PROTOCOL, flags.DEVICE_PROTOCOL_SH, flags.APPLIANCE_PROTOCOL_DFLT, "Appliance network connection protocol (http/https)") + // Unused by user, but values are required by cfm-service client frontend. + addApplianceCmd.Flags().StringP(flags.APPLIANCE_USERNAME, flags.APPLIANCE_USERNAME_SH, flags.APPLIANCE_USERNAME_DFLT, "Appliance username for authentication\n") + addApplianceCmd.Flags().StringP(flags.APPLIANCE_PASSWORD, flags.APPLIANCE_PASSWORD_SH, flags.APPLIANCE_PASSWORD_DFLT, "Appliance password for authentication\n") + addApplianceCmd.Flags().MarkHidden(flags.APPLIANCE_USERNAME) + addApplianceCmd.Flags().MarkHidden(flags.APPLIANCE_PASSWORD) - addApplianceCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "User-defined ID for target appliance") + addApplianceCmd.Flags().StringP(flags.APPLIANCE_NET_IP, flags.APPLIANCE_NET_IP_SH, flags.APPLIANCE_NET_IP_DFLT, "Appliance network IP address\n") + addApplianceCmd.Flags().Uint16P(flags.APPLIANCE_NET_PORT, flags.APPLIANCE_NET_PORT_SH, flags.APPLIANCE_NET_PORT_DFLT, "Appliance network port\n") + addApplianceCmd.Flags().BoolP(flags.APPLIANCE_INSECURE, flags.APPLIANCE_INSECURE_SH, flags.APPLIANCE_INSECURE_DFLT, "Appliance insecure connection flag\n (default false)") + addApplianceCmd.Flags().StringP(flags.APPLIANCE_PROTOCOL, flags.APPLIANCE_PROTOCOL_SH, flags.APPLIANCE_PROTOCOL_DFLT, "Appliance network connection protocol (http/https)\n") + addApplianceCmd.Flags().MarkHidden(flags.APPLIANCE_NET_IP) + addApplianceCmd.Flags().MarkHidden(flags.APPLIANCE_NET_PORT) + addApplianceCmd.Flags().MarkHidden(flags.APPLIANCE_INSECURE) + addApplianceCmd.Flags().MarkHidden(flags.APPLIANCE_PROTOCOL) //Add command to parent addCmd.AddCommand(addApplianceCmd) } + +// GetCmdUsageAddAppliance - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageAddAppliance() string { + return fmt.Sprintf("%s %s %s", + flags.APPLIANCE, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false), + flags.GetOptionUsageApplianceId(true)) +} + +// GetCmdExampleAddAppliance - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleAddAppliance() string { + baseCmd := fmt.Sprintf("cfm add %s", flags.APPLIANCE) + + shorthandFormat := fmt.Sprintf("%s %s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp(), + flags.GetOptionExampleShApplianceId()) + + longhandFormat := fmt.Sprintf("%s %s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp(), + flags.GetOptionExampleLhApplianceId()) + + return fmt.Sprintf(` + %s + + %s`, shorthandFormat, longhandFormat) +} diff --git a/cmd/cfm-cli/cmd/addBlade.go b/cmd/cfm-cli/cmd/addBlade.go index 70e71d8..e22e965 100644 --- a/cmd/cfm-cli/cmd/addBlade.go +++ b/cmd/cfm-cli/cmd/addBlade.go @@ -5,20 +5,17 @@ package cmd import ( "cfm/cli/pkg/serviceLib/flags" "cfm/cli/pkg/serviceLib/serviceRequests" + "fmt" "github.com/spf13/cobra" ) var addBladeCmd = &cobra.Command{ - Use: `blade <--appliance-id | -L> [--dev-username | -R] [--dev-password | -W] [--serv-ip | -a] [--serv-net-port | -p] - [--blade-id | -B] [--dev-ip | -A] [--dev-net-port | -P] [--dev-insecure | -S] [--dev-protocol | -T]`, - Short: "Add a memory appliance blade connection to cfm-service", - Long: `Adds a netowrk connection from the cfm-service to an external memory appliance blade.`, - Example: ` - cfm add blade --serv-ip 127.0.0.1 --serv-net-port 8080 --appliance-id applId --blade-id userDefinedId --dev-username user --dev-password pswd --dev-ip 127.0.0.1 --dev-net-port 7443 --dev-insecure --dev-protocol https - - cfm add blade -a 127.0.0.1 -p 8080 -L applId -B userDefinedId -R user -W pswd -A 127.0.0.1 -P 7443 -S -T https`, - Args: cobra.MatchAll(cobra.NoArgs), + Use: GetCmdUsageAddBlade(), + Short: "Add a memory appliance blade connection to cfm-service", + Long: `Adds a netowrk connection from the cfm-service to an external memory appliance blade.`, + Example: GetCmdExampleAddBlade(), + Args: cobra.MatchAll(cobra.NoArgs), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { initLogging(cmd) return nil @@ -40,20 +37,60 @@ func init() { initCommonPersistentFlags(addBladeCmd) - addBladeCmd.Flags().StringP(flags.DEVICE_USERNAME, flags.DEVICE_USERNAME_SH, flags.BLADE_USERNAME_DFLT, "Blade username for authentication") - // addBladeCmd.MarkFlagRequired(flags.DEVICE_USERNAME) - addBladeCmd.Flags().StringP(flags.DEVICE_PASSWORD, flags.DEVICE_PASSWORD_SH, flags.BLADE_PASSWORD_DFLT, "Blade password for authentication") - // addBladeCmd.MarkFlagRequired(flags.DEVICE_PASSWORD) + addBladeCmd.Flags().StringP(flags.BLADE_USERNAME, flags.BLADE_USERNAME_SH, flags.BLADE_USERNAME_DFLT, "Blade username for authentication\n") + // addBladeCmd.MarkFlagRequired(flags.BLADE_USERNAME) + addBladeCmd.Flags().StringP(flags.BLADE_PASSWORD, flags.BLADE_PASSWORD_SH, flags.BLADE_PASSWORD_DFLT, "Blade password for authentication\n") + // addBladeCmd.MarkFlagRequired(flags.BLADE_PASSWORD) - addBladeCmd.Flags().StringP(flags.DEVICE_NET_IP, flags.DEVICE_NET_IP_SH, flags.BLADE_NET_IP_DFLT, "Blade network IP address") - addBladeCmd.Flags().Uint16P(flags.DEVICE_NET_PORT, flags.DEVICE_NET_PORT_SH, flags.BLADE_NET_PORT_DFLT, "Blade network port ") - addBladeCmd.Flags().BoolP(flags.DEVICE_INSECURE, flags.DEVICE_INSECURE_SH, flags.BLADE_INSECURE_DFLT, "Blade insecure connection flag") - addBladeCmd.Flags().StringP(flags.DEVICE_PROTOCOL, flags.DEVICE_PROTOCOL_SH, flags.BLADE_PROTOCOL_DFLT, "Blade network connection protocol (http/https)") + addBladeCmd.Flags().StringP(flags.BLADE_NET_IP, flags.BLADE_NET_IP_SH, flags.BLADE_NET_IP_DFLT, "Blade network IP address\n") + addBladeCmd.Flags().Uint16P(flags.BLADE_NET_PORT, flags.BLADE_NET_PORT_SH, flags.BLADE_NET_PORT_DFLT, "Blade network port\n") + addBladeCmd.Flags().BoolP(flags.BLADE_INSECURE, flags.BLADE_INSECURE_SH, flags.BLADE_INSECURE_DFLT, "Blade insecure connection flag\n (default false)") + addBladeCmd.Flags().StringP(flags.BLADE_PROTOCOL, flags.BLADE_PROTOCOL_SH, flags.BLADE_PROTOCOL_DFLT, "Blade network connection protocol (http/https)\n") - addBladeCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "Appliance ID of target blade") + addBladeCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "Appliance ID of target blade\n") addBladeCmd.MarkFlagRequired(flags.APPLIANCE_ID) - addBladeCmd.Flags().StringP(flags.BLADE_ID, flags.BLADE_ID_SH, flags.ID_DFLT, "User-defined ID for target blade") + addBladeCmd.Flags().StringP(flags.BLADE_ID, flags.BLADE_ID_SH, flags.ID_DFLT, "User-defined ID for target blade (Optional)\n (default: random w\\ format: blade-XXXX)") //Add command to parent addCmd.AddCommand(addBladeCmd) } + +// GetCmdUsageAddBlade - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageAddBlade() string { + return fmt.Sprintf("%s %s %s %s %s %s %s", + flags.BLADE, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false), + flags.GetOptionUsageApplianceId(false), + flags.GetOptionUsageBladeId(true), + flags.GetOptionUsageGroupBladeTcp(false), + flags.GetOptionUsageBladeUsername(false), + flags.GetOptionUsageBladePassword(false)) +} + +// GetCmdExampleAddBlade - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleAddBlade() string { + baseCmd := fmt.Sprintf("cfm add %s", flags.BLADE) + + shorthandFormat := fmt.Sprintf("%s %s %s %s %s %s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp(), + flags.GetOptionExampleShApplianceId(), + flags.GetOptionExampleShBladeId(), + flags.GetOptionExampleShGroupBladeTcp(), + flags.GetOptionExampleShBladeUsername(), + flags.GetOptionExampleShBladePassword()) + + longhandFormat := fmt.Sprintf("%s %s %s %s %s %s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp(), + flags.GetOptionExampleLhApplianceId(), + flags.GetOptionExampleLhBladeId(), + flags.GetOptionExampleLhGroupBladeTcp(), + flags.GetOptionExampleLhBladeUsername(), + flags.GetOptionExampleLhBladePassword()) + + return fmt.Sprintf(` + %s + + %s`, shorthandFormat, longhandFormat) +} diff --git a/cmd/cfm-cli/cmd/addHost.go b/cmd/cfm-cli/cmd/addHost.go index 75a6b1b..6d18840 100644 --- a/cmd/cfm-cli/cmd/addHost.go +++ b/cmd/cfm-cli/cmd/addHost.go @@ -5,21 +5,18 @@ package cmd import ( "cfm/cli/pkg/serviceLib/flags" "cfm/cli/pkg/serviceLib/serviceRequests" + "fmt" "github.com/spf13/cobra" ) // addHostCmd represents the addHost command var addHostCmd = &cobra.Command{ - Use: `host [--dev-username | -R] [--dev-password | -W] [--serv-ip | -a] [--serv-net-port | -p] - [--host-id | -H] [--dev-ip | -A] [--dev-net-port | -P] [--dev-insecure | -S] [--dev-protocol | -T]`, - Short: "Add a cxl host connection to cfm-service", - Long: `Adds a netowrk connection from the cfm-service to an external cxl host.`, - Example: ` - cfm add host --serv-ip 127.0.0.1 --serv-net-port 8080 --host-id userDefinedId --dev-username user --dev-password pswd --dev-ip 127.0.0.1 --dev-net-port 7443 --dev-insecure --dev-protocol https - - cfm add host -a 127.0.0.1 -p 8080 -H userDefinedId -R user -W pswd -A 127.0.0.1 -P 7443 -S -T https`, - Args: cobra.MatchAll(cobra.NoArgs), + Use: GetCmdUsageAddHost(), + Short: "Add a cxl host connection to cfm-service", + Long: `Adds a netowrk connection from the cfm-service to an external cxl host.`, + Example: GetCmdExampleAddHost(), + Args: cobra.MatchAll(cobra.NoArgs), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { initLogging(cmd) return nil @@ -41,18 +38,55 @@ func init() { initCommonPersistentFlags(addHostCmd) - addHostCmd.Flags().StringP(flags.DEVICE_USERNAME, flags.DEVICE_USERNAME_SH, flags.HOST_USERNAME_DFLT, "Host username for authentication") - // addHostCmd.MarkFlagRequired(flags.DEVICE_USERNAME) - addHostCmd.Flags().StringP(flags.DEVICE_PASSWORD, flags.DEVICE_PASSWORD_SH, flags.HOST_PASSWORD_DFLT, "Host password for authentication") - // addHostCmd.MarkFlagRequired(flags.DEVICE_PASSWORD) + addHostCmd.Flags().StringP(flags.HOST_USERNAME, flags.HOST_USERNAME_SH, flags.HOST_USERNAME_DFLT, "Host(CXL) username for authentication\n") + // addHostCmd.MarkFlagRequired(flags.HOST_USERNAME) + addHostCmd.Flags().StringP(flags.HOST_PASSWORD, flags.HOST_PASSWORD_SH, flags.HOST_PASSWORD_DFLT, "Host(CXL) password for authentication\n") + // addHostCmd.MarkFlagRequired(flags.HOST_PASSWORD) - addHostCmd.Flags().StringP(flags.DEVICE_NET_IP, flags.DEVICE_NET_IP_SH, flags.HOST_NET_IP_DFLT, "Host network IP address") - addHostCmd.Flags().Uint16P(flags.DEVICE_NET_PORT, flags.DEVICE_NET_PORT_SH, flags.HOST_NET_PORT_DFLT, "Host network port ") - addHostCmd.Flags().BoolP(flags.DEVICE_INSECURE, flags.DEVICE_INSECURE_SH, flags.HOST_INSECURE_DFLT, "Host insecure connection flag") - addHostCmd.Flags().StringP(flags.DEVICE_PROTOCOL, flags.DEVICE_PROTOCOL_SH, flags.HOST_PROTOCOL_DFLT, "Host network connection protocol (http/https)") + addHostCmd.Flags().StringP(flags.HOST_NET_IP, flags.HOST_NET_IP_SH, flags.HOST_NET_IP_DFLT, "Host(CXL) network IP address\n") + addHostCmd.Flags().Uint16P(flags.HOST_NET_PORT, flags.HOST_NET_PORT_SH, flags.HOST_NET_PORT_DFLT, "Host(CXL) network port\n") + addHostCmd.Flags().BoolP(flags.HOST_INSECURE, flags.HOST_INSECURE_SH, flags.HOST_INSECURE_DFLT, "Host(CXL) insecure connection flag\n (default false)") + addHostCmd.Flags().StringP(flags.HOST_PROTOCOL, flags.HOST_PROTOCOL_SH, flags.HOST_PROTOCOL_DFLT, "Host(CXL) network connection protocol (http/https)\n") - addHostCmd.Flags().StringP(flags.HOST_ID, flags.HOST_ID_SH, flags.ID_DFLT, "User-defined ID for target host") + addHostCmd.Flags().StringP(flags.HOST_ID, flags.HOST_ID_SH, flags.ID_DFLT, "User-defined ID for target host(CXL) (Optional)\n (default: random w\\ format: host-XXXX)") //Add command to parent addCmd.AddCommand(addHostCmd) } + +// GetCmdUsageAddHost - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageAddHost() string { + return fmt.Sprintf("%s %s %s %s %s %s", + flags.HOST, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false), + flags.GetOptionUsageHostId(true), + flags.GetOptionUsageGroupHostTcp(false), + flags.GetOptionUsageHostUsername(false), + flags.GetOptionUsageHostPassword(false)) +} + +// GetCmdExampleAddHost - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleAddHost() string { + baseCmd := fmt.Sprintf("cfm add %s", flags.HOST) + + shorthandFormat := fmt.Sprintf("%s %s %s %s %s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp(), + flags.GetOptionExampleShHostId(), + flags.GetOptionExampleShGroupHostTcp(), + flags.GetOptionExampleShHostUsername(), + flags.GetOptionExampleShHostPassword()) + + longhandFormat := fmt.Sprintf("%s %s %s %s %s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp(), + flags.GetOptionExampleLhHostId(), + flags.GetOptionExampleLhGroupHostTcp(), + flags.GetOptionExampleLhHostUsername(), + flags.GetOptionExampleLhHostPassword()) + + return fmt.Sprintf(` + %s + + %s`, shorthandFormat, longhandFormat) +} diff --git a/cmd/cfm-cli/cmd/assignBlade.go b/cmd/cfm-cli/cmd/assignBlade.go index 132d81e..05b926e 100644 --- a/cmd/cfm-cli/cmd/assignBlade.go +++ b/cmd/cfm-cli/cmd/assignBlade.go @@ -11,14 +11,11 @@ import ( ) var assignBladeCmd = &cobra.Command{ - Use: "blade [--serv-ip | -a] [--serv-net-port | -p] <--appliance-id | -L> <--blade-id | -B> <--memory-id | -m> <--port-id | -o>", - Short: "Assign an existing blade memory region to a blade port.", - Long: `Assign an existing blade memory region to a blade port. A physical connection must already exist between the appliance blade and the target cxl-host.`, - Example: ` - cfm assign blade --serv-ip 127.0.0.1 --serv-net-port 8080 --appliance-id applId --blade-id bladeId --memory-id memoryId --port-id portId - - cfm assign blade -a 127.0.0.1 -p 8080 -L applId -B bladeId -m memoryId -o portId`, - Args: cobra.MatchAll(cobra.NoArgs), + Use: GetCmdUsageAssignBlade(), + Short: "Assign an existing blade memory region to a blade port.", + Long: `Assign an existing blade memory region to a blade port. A physical connection must already exist between the appliance blade and the target cxl-host.`, + Example: GetCmdExampleAssignBlade(), + Args: cobra.MatchAll(cobra.NoArgs), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { initLogging(cmd) return nil @@ -44,13 +41,13 @@ var assignBladeCmd = &cobra.Command{ func init() { assignBladeCmd.DisableFlagsInUseLine = true - assignBladeCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "ID of appliance to interrogate") + assignBladeCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "ID of appliance to interrogate\n") assignBladeCmd.MarkFlagRequired(flags.APPLIANCE_ID) - assignBladeCmd.Flags().StringP(flags.BLADE_ID, flags.BLADE_ID_SH, flags.ID_DFLT, "ID of appliance blade to interrogate") + assignBladeCmd.Flags().StringP(flags.BLADE_ID, flags.BLADE_ID_SH, flags.ID_DFLT, "ID of appliance blade to interrogate\n") assignBladeCmd.MarkFlagRequired(flags.BLADE_ID) - assignBladeCmd.Flags().StringP(flags.MEMORY_ID, flags.MEMORY_ID_SH, flags.ID_DFLT, "ID of the appliance blade memory region to assign to the specified port.") + assignBladeCmd.Flags().StringP(flags.MEMORY_ID, flags.MEMORY_ID_SH, flags.ID_DFLT, "ID of the appliance blade memory region to assign to the specified port\n") assignBladeCmd.MarkFlagRequired(flags.MEMORY_ID) - assignBladeCmd.Flags().StringP(flags.PORT_ID, flags.PORT_ID_SH, flags.ID_DFLT, "ID of the appliance blade port to assign to the specified memory region.") + assignBladeCmd.Flags().StringP(flags.PORT_ID, flags.PORT_ID_SH, flags.ID_DFLT, "ID of the appliance blade port to assign to the specified memory region\n") assignBladeCmd.MarkFlagRequired(flags.PORT_ID) initCommonPersistentFlags(assignBladeCmd) @@ -58,3 +55,40 @@ func init() { //Add command to parent assignCmd.AddCommand(assignBladeCmd) } + +// GetCmdUsageAssignBlade - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageAssignBlade() string { + return fmt.Sprintf("%s %s %s %s %s %s", + flags.BLADE, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false), + flags.GetOptionUsageApplianceId(false), + flags.GetOptionUsageBladeId(false), + flags.GetOptionUsageMemoryId(false), + flags.GetOptionUsagePortId(false)) +} + +// GetCmdExampleAssignBlade - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleAssignBlade() string { + baseCmd := fmt.Sprintf("cfm assign %s", flags.BLADE) + + shorthandFormat := fmt.Sprintf("%s %s %s %s %s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp(), + flags.GetOptionExampleShApplianceId(), + flags.GetOptionExampleShBladeId(), + flags.GetOptionExampleShMemoryId(), + flags.GetOptionExampleShPortId()) + + longhandFormat := fmt.Sprintf("%s %s %s %s %s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp(), + flags.GetOptionExampleLhApplianceId(), + flags.GetOptionExampleLhBladeId(), + flags.GetOptionExampleLhMemoryId(), + flags.GetOptionExampleLhPortId()) + + return fmt.Sprintf(` + %s + + %s`, shorthandFormat, longhandFormat) +} diff --git a/cmd/cfm-cli/cmd/composeBlade.go b/cmd/cfm-cli/cmd/composeBlade.go index 242595c..f240d88 100644 --- a/cmd/cfm-cli/cmd/composeBlade.go +++ b/cmd/cfm-cli/cmd/composeBlade.go @@ -11,14 +11,11 @@ import ( ) var composeBladeCmd = &cobra.Command{ - Use: "blade [--serv-ip | -a] [--serv-net-port | -p] <--appliance-id | -L> <--blade-id | -B> [--port-id | -i] <--resource-size | -z> [--memory-qos | -q]", - Short: "Compose a new memory region on the specified memory appliance blade.", - Long: `Compose a new memory region on the specified memory appliance blade. The composed memory region can be optionally assigned to a specified memory appliance blade port for use by an external device (such as a cxl host).`, - Example: ` - cfm compose blade --serv-ip 127.0.0.1 --serv-net-port 8080 --appliance-id applId --blade-id bladeId --port-id portId --resource-size 32g --memory-qos 4 - - cfm compose blade -a 127.0.0.1 -p 8080 -L applId -B bladeId -o portId -z 32g -q 4`, - Args: cobra.MatchAll(cobra.NoArgs), + Use: GetCmdUsageComposeBlade(), + Short: "Compose a new memory region on the specified memory appliance blade.", + Long: `Compose a new memory region on the specified memory appliance blade. The composed memory region can be optionally assigned to a specified memory appliance blade port for use by an external device (such as a cxl host).`, + Example: GetCmdExampleComposeBlade(), + Args: cobra.MatchAll(cobra.NoArgs), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { initLogging(cmd) return nil @@ -47,17 +44,68 @@ var composeBladeCmd = &cobra.Command{ func init() { composeBladeCmd.DisableFlagsInUseLine = true - composeBladeCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "ID of appliance to interrogate") + composeBladeCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "ID of appliance to interrogate\n") composeBladeCmd.MarkFlagRequired(flags.APPLIANCE_ID) - composeBladeCmd.Flags().StringP(flags.BLADE_ID, flags.BLADE_ID_SH, flags.ID_DFLT, "ID of appliance blade to interrogate") + composeBladeCmd.Flags().StringP(flags.BLADE_ID, flags.BLADE_ID_SH, flags.ID_DFLT, "ID of appliance blade to interrogate\n") composeBladeCmd.MarkFlagRequired(flags.BLADE_ID) - composeBladeCmd.Flags().StringP(flags.PORT_ID, flags.PORT_ID_SH, flags.ID_DFLT, "ID of a specific appliance port.") - composeBladeCmd.Flags().StringP(flags.RESOURCE_SIZE, flags.RESOURCE_SIZE_SH, flags.SIZE_DFLT, "Total size of new, composed memory region(minimum of 1 GiB). Use 'G' or 'g' to specific GiB.") + composeBladeCmd.Flags().StringP(flags.PORT_ID, flags.PORT_ID_SH, flags.ID_DFLT, "ID of a specific appliance port\n") + composeBladeCmd.Flags().StringP(flags.RESOURCE_SIZE, flags.RESOURCE_SIZE_SH, flags.SIZE_DFLT, "Total size of new, composed memory region(minimum of 1 GiB). Use 'G' or 'g' to specify GiB\n") composeBladeCmd.MarkFlagRequired(flags.RESOURCE_SIZE) - composeBladeCmd.Flags().Int32P(flags.MEMORY_QOS, flags.MEMORY_QOS_SH, flags.MEMORY_QOS_DFLT, "NOT YET SUPPORTED, BUT, for now, cfm-service ***REQUIRES*** this to be 4: Quality of Service level.") + composeBladeCmd.Flags().Int32P(flags.MEMORY_QOS, flags.MEMORY_QOS_SH, flags.MEMORY_QOS_DFLT, "Quality of Service level (ie: channel bandwidth)\n") + composeBladeCmd.MarkFlagRequired(flags.MEMORY_QOS) initCommonPersistentFlags(composeBladeCmd) //Add command to parent composeCmd.AddCommand(composeBladeCmd) } + +// GetCmdUsageComposeBlade - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageComposeBlade() string { + return fmt.Sprintf("%s %s %s %s %s %s %s", + flags.BLADE, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false), + flags.GetOptionUsageApplianceId(false), + flags.GetOptionUsageBladeId(false), + flags.GetOptionUsagePortId(true), + flags.GetOptionUsageResourceSize(false), + flags.GetOptionUsageMemoryQos(false)) +} + +// GetCmdExampleComposeBlade - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleComposeBlade() string { + baseCmd := fmt.Sprintf("cfm compose %s", flags.BLADE) + + shorthandFormat := fmt.Sprintf("%s %s %s %s %s %s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp(), + flags.GetOptionExampleShApplianceId(), + flags.GetOptionExampleShBladeId(), + flags.GetOptionExampleShPortId(), + flags.GetOptionExampleShResourceSize(), + flags.GetOptionExampleShMemoryQos()) + + shorthandFormat2 := fmt.Sprintf("%s %s %s %s %s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp(), + flags.GetOptionExampleShApplianceId(), + flags.GetOptionExampleShBladeId(), + flags.GetOptionExampleShResourceSize(), + flags.GetOptionExampleShMemoryQos()) + + longhandFormat := fmt.Sprintf("%s %s %s %s %s %s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp(), + flags.GetOptionExampleLhApplianceId(), + flags.GetOptionExampleLhBladeId(), + flags.GetOptionExampleLhPortId(), + flags.GetOptionExampleLhResourceSize(), + flags.GetOptionExampleLhMemoryQos()) + + return fmt.Sprintf(` + %s + + %s + + %s`, shorthandFormat, shorthandFormat2, longhandFormat) +} diff --git a/cmd/cfm-cli/cmd/deleteAppliance.go b/cmd/cfm-cli/cmd/deleteAppliance.go index a683f06..e2af0d4 100644 --- a/cmd/cfm-cli/cmd/deleteAppliance.go +++ b/cmd/cfm-cli/cmd/deleteAppliance.go @@ -5,19 +5,17 @@ package cmd import ( "cfm/cli/pkg/serviceLib/flags" "cfm/cli/pkg/serviceLib/serviceRequests" + "fmt" "github.com/spf13/cobra" ) var deleteApplianceCmd = &cobra.Command{ - Use: `appliance [--serv-ip | -a] [--serv-net-port | -p] <--appliance-id | -L>`, - Short: "Delete a memory appliance connection from cfm-service", - Long: `Deletes a netowrk connection from the cfm-service to an external memory appliance.`, - Example: ` - cfm delete appliance --appliance-id applId --serv-ip 127.0.0.1 --serv-net-port 8080 - - cfm delete appliance -L applId -a 127.0.0.1 -p 8080`, - Args: cobra.MatchAll(cobra.NoArgs), + Use: GetCmdUsageDeleteAppliance(), + Short: "Delete a memory appliance connection from cfm-service", + Long: `Deletes a netowrk connection from the cfm-service to an external memory appliance.`, + Example: GetCmdExampleDeleteAppliance(), + Args: cobra.MatchAll(cobra.NoArgs), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { initLogging(cmd) return nil @@ -39,8 +37,37 @@ func init() { initCommonPersistentFlags(deleteApplianceCmd) - deleteApplianceCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "ID of memory appliance") + deleteApplianceCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "ID of memory appliance\n") + deleteApplianceCmd.MarkFlagRequired(flags.APPLIANCE_ID) //Add command to parent deleteCmd.AddCommand(deleteApplianceCmd) } + +// GetCmdUsageDeleteAppliance - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageDeleteAppliance() string { + return fmt.Sprintf("%s %s %s", + flags.APPLIANCE, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false), + flags.GetOptionUsageApplianceId(false)) +} + +// GetCmdExampleDeleteAppliance - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleDeleteAppliance() string { + baseCmd := fmt.Sprintf("cfm delete %s", flags.APPLIANCE) + + shorthandFormat := fmt.Sprintf("%s %s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp(), + flags.GetOptionExampleShApplianceId()) + + longhandFormat := fmt.Sprintf("%s %s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp(), + flags.GetOptionExampleLhApplianceId()) + + return fmt.Sprintf(` + %s + + %s`, shorthandFormat, longhandFormat) +} diff --git a/cmd/cfm-cli/cmd/deleteBlade.go b/cmd/cfm-cli/cmd/deleteBlade.go index c236b3e..f40e79a 100644 --- a/cmd/cfm-cli/cmd/deleteBlade.go +++ b/cmd/cfm-cli/cmd/deleteBlade.go @@ -5,19 +5,17 @@ package cmd import ( "cfm/cli/pkg/serviceLib/flags" "cfm/cli/pkg/serviceLib/serviceRequests" + "fmt" "github.com/spf13/cobra" ) var deleteBladeCmd = &cobra.Command{ - Use: `blade [--serv-ip | -a] [--serv-net-port | -p] <--appliance-id | -L> <--blade-id | -B>`, - Short: "Delete a memory appliance blade connection from cfm-service", - Long: `Deletes a netowrk connection from the cfm-service to an external memory appliance blade.`, - Example: ` - cfm delete blade --appliance-id applId --blade-id bladeId --serv-ip 127.0.0.1 --serv-net-port 8080 - - cfm delete blade -L applId -B bladeId -a 127.0.0.1 -p 8080`, - Args: cobra.MatchAll(cobra.NoArgs), + Use: GetCmdUsageDeleteBlade(), + Short: "Delete a memory appliance blade connection from cfm-service", + Long: `Deletes a netowrk connection from the cfm-service to an external memory appliance blade.`, + Example: GetCmdExampleDeleteBlade(), + Args: cobra.MatchAll(cobra.NoArgs), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { initLogging(cmd) return nil @@ -39,9 +37,42 @@ func init() { initCommonPersistentFlags(deleteBladeCmd) - deleteBladeCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "ID of blade's appliance") - deleteBladeCmd.Flags().StringP(flags.BLADE_ID, flags.BLADE_ID_SH, flags.ID_DFLT, "ID of blade to delete") + deleteBladeCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "ID of blade's appliance\n") + deleteBladeCmd.MarkFlagRequired(flags.APPLIANCE_ID) + deleteBladeCmd.Flags().StringP(flags.BLADE_ID, flags.BLADE_ID_SH, flags.ID_DFLT, "ID of blade to delete\n") + deleteBladeCmd.MarkFlagRequired(flags.BLADE_ID) //Add command to parent deleteCmd.AddCommand(deleteBladeCmd) } + +// GetCmdUsageDeleteBlade - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageDeleteBlade() string { + return fmt.Sprintf("%s %s %s %s", + flags.BLADE, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false), + flags.GetOptionUsageApplianceId(false), + flags.GetOptionUsageBladeId(false)) +} + +// GetCmdExampleDeleteBlade - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleDeleteBlade() string { + baseCmd := fmt.Sprintf("cfm delete %s", flags.BLADE) + + shorthandFormat := fmt.Sprintf("%s %s %s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp(), + flags.GetOptionExampleShApplianceId(), + flags.GetOptionExampleShBladeId()) + + longhandFormat := fmt.Sprintf("%s %s %s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp(), + flags.GetOptionExampleLhApplianceId(), + flags.GetOptionExampleLhBladeId()) + + return fmt.Sprintf(` + %s + + %s`, shorthandFormat, longhandFormat) +} diff --git a/cmd/cfm-cli/cmd/deleteHost.go b/cmd/cfm-cli/cmd/deleteHost.go index 5af6039..5c519a6 100644 --- a/cmd/cfm-cli/cmd/deleteHost.go +++ b/cmd/cfm-cli/cmd/deleteHost.go @@ -5,19 +5,17 @@ package cmd import ( "cfm/cli/pkg/serviceLib/flags" "cfm/cli/pkg/serviceLib/serviceRequests" + "fmt" "github.com/spf13/cobra" ) var deleteHostCmd = &cobra.Command{ - Use: `host [--serv-ip | -i] [--serv-net-port | -p] <--host-id | -H>`, - Short: `Delete a cxl host connection from cfm-service`, - Long: `Deletes a netowrk connection from the cfm-service to an external cxl host.`, - Example: ` - cfm delete host --host-id hostId --serv-ip 127.0.0.1 --serv-net-port 8080 - - cfm delete host -H hostId -a 127.0.0.1 -p 8080`, - Args: cobra.MatchAll(cobra.NoArgs), + Use: GetCmdUsageDeleteHost(), + Short: `Delete a cxl host connection from cfm-service`, + Long: `Deletes a netowrk connection from the cfm-service to an external cxl host.`, + Example: GetCmdExampleDeleteHost(), + Args: cobra.MatchAll(cobra.NoArgs), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { initLogging(cmd) return nil @@ -39,8 +37,37 @@ func init() { initCommonPersistentFlags(deleteHostCmd) - deleteHostCmd.Flags().StringP(flags.HOST_ID, flags.HOST_ID_SH, flags.ID_DFLT, "ID of CXL host") + deleteHostCmd.Flags().StringP(flags.HOST_ID, flags.HOST_ID_SH, flags.ID_DFLT, "ID of CXL host\n") + deleteHostCmd.MarkFlagRequired(flags.HOST_ID) //Add command to parent deleteCmd.AddCommand(deleteHostCmd) } + +// GetCmdUsageDeleteHost - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageDeleteHost() string { + return fmt.Sprintf("%s %s %s", + flags.HOST, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false), + flags.GetOptionUsageHostId(false)) +} + +// GetCmdExampleDeleteHost - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleDeleteHost() string { + baseCmd := fmt.Sprintf("cfm delete %s", flags.HOST) + + shorthandFormat := fmt.Sprintf("%s %s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp(), + flags.GetOptionExampleShHostId()) + + longhandFormat := fmt.Sprintf("%s %s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp(), + flags.GetOptionExampleLhHostId()) + + return fmt.Sprintf(` + %s + + %s`, shorthandFormat, longhandFormat) +} diff --git a/cmd/cfm-cli/cmd/freeBlade.go b/cmd/cfm-cli/cmd/freeBlade.go index d255fbf..2941405 100644 --- a/cmd/cfm-cli/cmd/freeBlade.go +++ b/cmd/cfm-cli/cmd/freeBlade.go @@ -11,14 +11,11 @@ import ( ) var freeBladeCmd = &cobra.Command{ - Use: "blade [--serv-ip | -a] [--serv-net-port | -p] <--appliance-id | -L> <--blade-id | -B> <--memory-id | -i>", - Short: "Free an existing memory region on the specified memory appliance blade.", - Long: `Free an existing memory region on the specified memory appliance blade. The blade port will be unassigned and the memory region's resource blocks will be deallocated.`, - Example: ` - cfm free --serv-ip 127.0.0.1 --serv-net-port 8080 --appliance-id applId --blade-id bladeId --memory-id memoryId - - cfm free -a 127.0.0.1 -p 8080 -L applId -B bladeId -m memoryId`, - Args: cobra.MatchAll(cobra.NoArgs), + Use: GetCmdUsageFreeBlade(), + Short: "Free an existing memory region on the specified memory appliance blade.", + Long: `Free an existing memory region on the specified memory appliance blade. The blade port (if present) will be unassigned and the memory region's resource blocks will be deallocated.`, + Example: GetCmdExampleFreeBlade(), + Args: cobra.MatchAll(cobra.NoArgs), Run: func(cmd *cobra.Command, args []string) { fmt.Println("Free Memory...") @@ -41,11 +38,11 @@ var freeBladeCmd = &cobra.Command{ func init() { freeBladeCmd.DisableFlagsInUseLine = true - freeBladeCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "ID of appliance to interrogate") + freeBladeCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "ID of appliance to interrogate\n") freeBladeCmd.MarkFlagRequired(flags.APPLIANCE_ID) - freeBladeCmd.Flags().StringP(flags.BLADE_ID, flags.BLADE_ID_SH, flags.ID_DFLT, "ID of appliance blade to interrogate") + freeBladeCmd.Flags().StringP(flags.BLADE_ID, flags.BLADE_ID_SH, flags.ID_DFLT, "ID of appliance blade to interrogate\n") freeBladeCmd.MarkFlagRequired(flags.BLADE_ID) - freeBladeCmd.Flags().StringP(flags.MEMORY_ID, flags.MEMORY_ID_SH, flags.ID_DFLT, "ID of a specific appliance blade memory region to free.") + freeBladeCmd.Flags().StringP(flags.MEMORY_ID, flags.MEMORY_ID_SH, flags.ID_DFLT, "ID of a specific appliance blade memory region to free\n") freeBladeCmd.MarkFlagRequired(flags.MEMORY_ID) initCommonPersistentFlags(freeBladeCmd) @@ -53,3 +50,37 @@ func init() { //Add command to parent freeCmd.AddCommand(freeBladeCmd) } + +// GetCmdUsageFreeBlade - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageFreeBlade() string { + return fmt.Sprintf("%s %s %s %s %s", + flags.BLADE, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false), + flags.GetOptionUsageApplianceId(false), + flags.GetOptionUsageBladeId(false), + flags.GetOptionUsageMemoryId(false)) +} + +// GetCmdExampleFreeBlade - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleFreeBlade() string { + baseCmd := fmt.Sprintf("cfm free %s", flags.BLADE) + + shorthandFormat := fmt.Sprintf("%s %s %s %s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp(), + flags.GetOptionExampleShApplianceId(), + flags.GetOptionExampleShBladeId(), + flags.GetOptionExampleShMemoryId()) + + longhandFormat := fmt.Sprintf("%s %s %s %s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp(), + flags.GetOptionExampleLhApplianceId(), + flags.GetOptionExampleLhBladeId(), + flags.GetOptionExampleLhMemoryId()) + + return fmt.Sprintf(` + %s + + %s`, shorthandFormat, longhandFormat) +} diff --git a/cmd/cfm-cli/cmd/list.go b/cmd/cfm-cli/cmd/list.go index a9f87d8..5fe0f89 100644 --- a/cmd/cfm-cli/cmd/list.go +++ b/cmd/cfm-cli/cmd/list.go @@ -31,8 +31,8 @@ func initCommonBladeListCmdFlags(cmd *cobra.Command) { initCommonPersistentFlags(cmd) - cmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "ID of appliance to interrogate") - cmd.Flags().StringP(flags.BLADE_ID, flags.BLADE_ID_SH, flags.ID_DFLT, "ID of blade to interrogate") + cmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "ID of appliance to interrogate\n (default \"all appliances listed\")") + cmd.Flags().StringP(flags.BLADE_ID, flags.BLADE_ID_SH, flags.ID_DFLT, "ID of blade to interrogate\n (default \"all blades listed\")") } // Add set of flags used by all the "list" commands @@ -42,5 +42,5 @@ func initCommonHostListCmdFlags(cmd *cobra.Command) { initCommonPersistentFlags(cmd) - cmd.Flags().StringP(flags.HOST_ID, flags.HOST_ID_SH, flags.ID_DFLT, "ID of appliance to interrogate") + cmd.Flags().StringP(flags.HOST_ID, flags.HOST_ID_SH, flags.ID_DFLT, "ID of appliance to interrogate\n (default \"all hosts listed\")") } diff --git a/cmd/cfm-cli/cmd/listAppliances.go b/cmd/cfm-cli/cmd/listAppliances.go index 39ead6f..58e1b8a 100644 --- a/cmd/cfm-cli/cmd/listAppliances.go +++ b/cmd/cfm-cli/cmd/listAppliances.go @@ -3,20 +3,19 @@ package cmd import ( + "cfm/cli/pkg/serviceLib/flags" "cfm/cli/pkg/serviceLib/serviceRequests" + "fmt" "github.com/spf13/cobra" ) var listAppliancesCmd = &cobra.Command{ - Use: `appliances [--serv-ip | -i] [--serv-net-port | -p]`, - Short: "List all recognized memory appliance(s)", - Long: `Queries the cfm-service for all recognized memory appliance(s) and outputs a summary to stdout.`, - Example: ` - cfm list appliances --serv-ip 127.0.0.1 --serv-net-port 8080 - - cfm list appliances -a 127.0.0.1 -p 8080`, - Args: cobra.MatchAll(cobra.NoArgs), + Use: GetCmdUsageListAppliances(), + Short: "List all recognized memory appliance(s)", + Long: `Queries the cfm-service for all recognized memory appliance(s) and outputs a summary to stdout.`, + Example: GetCmdExampleListAppliances(), + Args: cobra.MatchAll(cobra.NoArgs), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { initLogging(cmd) return nil @@ -42,3 +41,28 @@ func init() { //Add command to parent listCmd.AddCommand(listAppliancesCmd) } + +// GetCmdUsageListAppliances - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageListAppliances() string { + return fmt.Sprintf("%s %s", + flags.APPLIANCES, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false)) +} + +// GetCmdExampleListAppliances - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleListAppliances() string { + baseCmd := fmt.Sprintf("cfm list %s", flags.APPLIANCES) + + shorthandFormat := fmt.Sprintf("%s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp()) + + longhandFormat := fmt.Sprintf("%s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp()) + + return fmt.Sprintf(` + %s + + %s`, shorthandFormat, longhandFormat) +} diff --git a/cmd/cfm-cli/cmd/listBladeMemory.go b/cmd/cfm-cli/cmd/listBladeMemory.go index 27920e7..192b57a 100644 --- a/cmd/cfm-cli/cmd/listBladeMemory.go +++ b/cmd/cfm-cli/cmd/listBladeMemory.go @@ -6,35 +6,21 @@ import ( "cfm/cli/pkg/serviceLib/flags" "cfm/cli/pkg/serviceLib/serviceRequests" "fmt" + "strings" "github.com/spf13/cobra" ) var listBladesMemoryCmd = &cobra.Command{ - Use: `memory [--serv-ip | -a] [--serv-net-port | -p] [--appliance-id | -L] [--blade-id | -B] [--memory-id | -m]`, + Use: GetCmdUsageListBladesMemory(), Short: "List all available blade composed and\\or provisioned memory regions", Long: `Queries the cfm-service for available composed\\provisioned memory regions. Outputs a detailed summary of those memory regions to stdout. - Note that, for any given item ID, if it is omitted, ALL items are collected\searched.`, - Example: ` - cfm list blades memory --serv-ip 127.0.0.1 --serv-net-port 8080 --appliance-id applId --blade-id bladeId --memory-id memId - cfm list blades memory --serv-ip 127.0.0.1 --serv-net-port 8080 --appliance-id applId --blade-id bladeId - cfm list blades memory --serv-ip 127.0.0.1 --serv-net-port 8080 --appliance-id applId --memory-id memId - cfm list blades memory --serv-ip 127.0.0.1 --serv-net-port 8080 --appliance-id applId - cfm list blades memory --serv-ip 127.0.0.1 --serv-net-port 8080 --blade-id bladeId --memory-id memId - cfm list blades memory --serv-ip 127.0.0.1 --serv-net-port 8080 --blade-id bladeId - cfm list blades memory --serv-ip 127.0.0.1 --serv-net-port 8080 --memory-id memId - cfm list blades memory --serv-ip 127.0.0.1 --serv-net-port 8080 - - cfm list blades memory -a 127.0.0.1 -p 8080 -L applId -B bladeId -m memId - cfm list blades memory -a 127.0.0.1 -p 8080 -L applId -B bladeId - cfm list blades memory -a 127.0.0.1 -p 8080 -L applId -m memId - cfm list blades memory -a 127.0.0.1 -p 8080 -L applId - cfm list blades memory -a 127.0.0.1 -p 8080 -B bladeId -m memId - cfm list blades memory -a 127.0.0.1 -p 8080 -B bladeId - cfm list blades memory -a 127.0.0.1 -p 8080 -m memId - cfm list blades memory -a 127.0.0.1 -p 8080 `, - Args: cobra.MatchAll(cobra.NoArgs), + Note: For any given ID option: + If the option is included, ONLY THAT ID is searched. + If the option is omitted, ALL POSSIBLE IDs (within cfm-service) are searched.`, + Example: GetCmdExampleListBladesMemory(), + Args: cobra.MatchAll(cobra.NoArgs), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { initLogging(cmd) @@ -55,7 +41,7 @@ var listBladesMemoryCmd = &cobra.Command{ func init() { listBladesMemoryCmd.DisableFlagsInUseLine = true - listBladesMemoryCmd.Flags().StringP(flags.MEMORY_ID, flags.MEMORY_ID_SH, flags.ID_DFLT, "ID of a specific memory region. (default \"all memory regions returned\")") + listBladesMemoryCmd.Flags().StringP(flags.MEMORY_ID, flags.MEMORY_ID_SH, flags.ID_DFLT, "ID of a specific memory region\n (default \"all memory regions listed\")") initCommonBladeListCmdFlags(listBladesMemoryCmd) @@ -63,7 +49,82 @@ func init() { listBladesCmd.AddCommand(listBladesMemoryCmd) } -func testHelp(cmd *cobra.Command, test []string) { - fmt.Println("A new help line") - fmt.Println("Another new help line") +// GetCmdUsageListBladeMemory - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageListBladesMemory() string { + return fmt.Sprintf("%s %s %s %s %s", + flags.MEMORY, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false), + flags.GetOptionUsageApplianceId(true), + flags.GetOptionUsageBladeId(true), + flags.GetOptionUsageMemoryId(true)) +} + +// GetCmdExampleListBladesMemory - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleListBladesMemory() string { + baseCmd := fmt.Sprintf("cfm list %s %s", flags.BLADES, flags.MEMORY) + + baseCmdLoopSh := fmt.Sprintf("%s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp()) + + shIdExamplesMap := map[string]string{ + "applianceId": " " + flags.GetOptionExampleShApplianceId(), + "bladeId": " " + flags.GetOptionExampleShBladeId(), + "memoryId": " " + flags.GetOptionExampleShMemoryId(), + } + + shExampleLines := make([]string, 0, 8) + for i := 0; i < 8; i++ { + s := baseCmdLoopSh + if i&1 != 0 { + s += shIdExamplesMap["applianceId"] + } + if i&2 != 0 { + s += shIdExamplesMap["bladeId"] + } + if i&4 != 0 { + s += shIdExamplesMap["memoryId"] + } + shExampleLines = append(shExampleLines, s) + } + + var shorthandFormat strings.Builder + for _, line := range shExampleLines { + shorthandFormat.WriteString("\t" + line + "\n") + } + + baseCmdLoopLh := fmt.Sprintf("%s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp()) + + lhIdExamplesMap := map[string]string{ + "applianceId": " " + flags.GetOptionExampleLhApplianceId(), + "bladeId": " " + flags.GetOptionExampleLhBladeId(), + "memoryId": " " + flags.GetOptionExampleLhMemoryId(), + } + + lhExampleLines := make([]string, 0, 8) + for i := 0; i < 8; i++ { + s := baseCmdLoopLh + if i&1 != 0 { + s += lhIdExamplesMap["applianceId"] + } + if i&2 != 0 { + s += lhIdExamplesMap["bladeId"] + } + if i&4 != 0 { + s += lhIdExamplesMap["memoryId"] + } + lhExampleLines = append(lhExampleLines, s) + } + + var longhandFormat strings.Builder + for _, line := range lhExampleLines { + longhandFormat.WriteString("\t" + line + "\n") + } + + return fmt.Sprintf(` +%s + +%s`, shorthandFormat.String(), longhandFormat.String()) } diff --git a/cmd/cfm-cli/cmd/listBladePorts.go b/cmd/cfm-cli/cmd/listBladePorts.go index 60ddf50..5636080 100644 --- a/cmd/cfm-cli/cmd/listBladePorts.go +++ b/cmd/cfm-cli/cmd/listBladePorts.go @@ -5,35 +5,22 @@ package cmd import ( "cfm/cli/pkg/serviceLib/flags" "cfm/cli/pkg/serviceLib/serviceRequests" + "fmt" + "strings" "github.com/spf13/cobra" ) var listBladesPortsCmd = &cobra.Command{ - Use: `ports [--serv-ip | -a] [--serv-net-port | -p] [--appliance-id | -L] [--blade-id | -B] [--port-id | -o]`, + Use: GetCmdUsageListBladesPorts(), Short: "List all available blade ports", Long: `Queries the cfm-service for available appliance blade ports. Outputs a detailed summary of those ports to stdout. - Note that, for any given item ID, if it is omitted, ALL items are collected\searched.`, - Example: ` - cfm list blades ports --serv-ip 127.0.0.1 --serv-net-port 8080 --appliance-id applId --blade-id bladeId --port-id portId - cfm list blades ports --serv-ip 127.0.0.1 --serv-net-port 8080 --appliance-id applId --blade-id bladeId - cfm list blades ports --serv-ip 127.0.0.1 --serv-net-port 8080 --appliance-id applId --port-id portId - cfm list blades ports --serv-ip 127.0.0.1 --serv-net-port 8080 --appliance-id applId - cfm list blades ports --serv-ip 127.0.0.1 --serv-net-port 8080 --blade-id bladeId --port-id portId - cfm list blades ports --serv-ip 127.0.0.1 --serv-net-port 8080 --blade-id bladeId - cfm list blades ports --serv-ip 127.0.0.1 --serv-net-port 8080 --port-id portId - cfm list blades ports --serv-ip 127.0.0.1 --serv-net-port 8080 - - cfm list blades ports -a 127.0.0.1 -p 8080 -L applId -B bladeId -o portId - cfm list blades ports -a 127.0.0.1 -p 8080 -L applId -B bladeId - cfm list blades ports -a 127.0.0.1 -p 8080 -L applId -o portId - cfm list blades ports -a 127.0.0.1 -p 8080 -L applId - cfm list blades ports -a 127.0.0.1 -p 8080 -B bladeId -o portId - cfm list blades ports -a 127.0.0.1 -p 8080 -B bladeId - cfm list blades ports -a 127.0.0.1 -p 8080 -o portId - cfm list blades ports -a 127.0.0.1 -p 8080 `, - Args: cobra.MatchAll(cobra.NoArgs), + Note: For any given ID option: + If the option is included, ONLY THAT ID is searched. + If the option is omitted, ALL POSSIBLE IDs (within cfm-service) are searched.`, + Example: GetCmdExampleListBladesPorts(), + Args: cobra.MatchAll(cobra.NoArgs), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { initLogging(cmd) return nil @@ -53,10 +40,90 @@ var listBladesPortsCmd = &cobra.Command{ func init() { listBladesPortsCmd.DisableFlagsInUseLine = true - listBladesPortsCmd.Flags().StringP(flags.PORT_ID, flags.PORT_ID_SH, flags.ID_DFLT, "ID of a specific port. (default \"all ports returned.\")") + listBladesPortsCmd.Flags().StringP(flags.PORT_ID, flags.PORT_ID_SH, flags.ID_DFLT, "ID of a specific port\n (default \"all ports listed.\")") initCommonBladeListCmdFlags(listBladesPortsCmd) //Add command to parent listBladesCmd.AddCommand(listBladesPortsCmd) } + +// GetCmdUsageListBladesPorts - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageListBladesPorts() string { + return fmt.Sprintf("%s %s %s %s %s", + flags.PORTS, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false), + flags.GetOptionUsageApplianceId(true), + flags.GetOptionUsageBladeId(true), + flags.GetOptionUsagePortId(true)) +} + +// GetCmdExampleListBladesPorts - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleListBladesPorts() string { + baseCmd := fmt.Sprintf("cfm list %s %s", flags.BLADES, flags.PORTS) + + baseCmdLoopSh := fmt.Sprintf("%s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp()) + + shIdExamplesMap := map[string]string{ + "applianceId": " " + flags.GetOptionExampleShApplianceId(), + "bladeId": " " + flags.GetOptionExampleShBladeId(), + "portId": " " + flags.GetOptionExampleShPortId(), + } + + shExampleLines := make([]string, 0, 8) + for i := 0; i < 8; i++ { + s := baseCmdLoopSh + if i&1 != 0 { + s += shIdExamplesMap["applianceId"] + } + if i&2 != 0 { + s += shIdExamplesMap["bladeId"] + } + if i&4 != 0 { + s += shIdExamplesMap["portId"] + } + shExampleLines = append(shExampleLines, s) + } + + var shorthandFormat strings.Builder + for _, line := range shExampleLines { + shorthandFormat.WriteString("\t" + line + "\n") + } + + baseCmdLoopLh := fmt.Sprintf("%s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp()) + + lhIdExamplesMap := map[string]string{ + "applianceId": " " + flags.GetOptionExampleLhApplianceId(), + "bladeId": " " + flags.GetOptionExampleLhBladeId(), + "portId": " " + flags.GetOptionExampleLhPortId(), + } + + lhExampleLines := make([]string, 0, 8) + for i := 0; i < 8; i++ { + s := baseCmdLoopLh + if i&1 != 0 { + s += lhIdExamplesMap["applianceId"] + } + if i&2 != 0 { + s += lhIdExamplesMap["bladeId"] + } + if i&4 != 0 { + s += lhIdExamplesMap["portId"] + } + lhExampleLines = append(lhExampleLines, s) + } + + var longhandFormat strings.Builder + for _, line := range lhExampleLines { + longhandFormat.WriteString("\t" + line + "\n") + } + + return fmt.Sprintf(` +%s + +%s`, shorthandFormat.String(), longhandFormat.String()) +} diff --git a/cmd/cfm-cli/cmd/listBladeResources.go b/cmd/cfm-cli/cmd/listBladeResources.go index 0df414a..7eae392 100644 --- a/cmd/cfm-cli/cmd/listBladeResources.go +++ b/cmd/cfm-cli/cmd/listBladeResources.go @@ -5,35 +5,21 @@ package cmd import ( "cfm/cli/pkg/serviceLib/flags" "cfm/cli/pkg/serviceLib/serviceRequests" + "fmt" + "strings" "github.com/spf13/cobra" ) var listBladeResourcesCmd = &cobra.Command{ - Use: `resources [--serv-ip | -a] [--serv-net-port | -p] [--appliance-id | -L] [--blade-id | -B] [--resource-id | -i]`, + Use: GetCmdUsageListBladesResources(), Short: "List all available blade memory resources", Long: `Queries the cfm-service for existing memory resources. Outputs a detailed summary (including composition state) of those resources to stdout. - Note that, for any given item ID, if it is omitted, ALL items are collected\searched.`, - Example: ` - cfm list blades resources --serv-ip 127.0.0.1 --serv-net-port 8080 --appliance-id applId --blade-id bladeId --resource-id resId - cfm list blades resources --serv-ip 127.0.0.1 --serv-net-port 8080 --appliance-id applId --blade-id bladeId - cfm list blades resources --serv-ip 127.0.0.1 --serv-net-port 8080 --appliance-id applId --resource-id resId - cfm list blades resources --serv-ip 127.0.0.1 --serv-net-port 8080 --appliance-id applId - cfm list blades resources --serv-ip 127.0.0.1 --serv-net-port 8080 --blade-id bladeId --resource-id resId - cfm list blades resources --serv-ip 127.0.0.1 --serv-net-port 8080 --blade-id bladeId - cfm list blades resources --serv-ip 127.0.0.1 --serv-net-port 8080 --resource-id resId - cfm list blades resources --serv-ip 127.0.0.1 --serv-net-port 8080 - - cfm list blades resources -a 127.0.0.1 -p 8080 -L applId -B bladeId -r resId - cfm list blades resources -a 127.0.0.1 -p 8080 -L applId -B bladeId - cfm list blades resources -a 127.0.0.1 -p 8080 -L applId -r resId - cfm list blades resources -a 127.0.0.1 -p 8080 -L applId - cfm list blades resources -a 127.0.0.1 -p 8080 -B bladeId -r resId - cfm list blades resources -a 127.0.0.1 -p 8080 -B bladeId - cfm list blades resources -a 127.0.0.1 -p 8080 -r resId - cfm list blades resources -a 127.0.0.1 -p 8080 `, - Args: cobra.MatchAll(cobra.NoArgs), + Note: For any given ID option: + If the option is included, ONLY THAT ID is searched. + If the option is omitted, ALL POSSIBLE IDs (within cfm-service) are searched.`, + Example: GetCmdExampleListBladesResources(), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { initLogging(cmd) return nil @@ -53,10 +39,90 @@ var listBladeResourcesCmd = &cobra.Command{ func init() { listBladeResourcesCmd.DisableFlagsInUseLine = true - listBladeResourcesCmd.Flags().StringP(flags.RESOURCE_ID, flags.RESOURCE_ID_SH, flags.ID_DFLT, "ID of a specific resource block. (default \"all resource blocks returned\")") + listBladeResourcesCmd.Flags().StringP(flags.RESOURCE_ID, flags.RESOURCE_ID_SH, flags.ID_DFLT, "ID of a specific resource block\n (default \"all resource blocks listed\")") initCommonBladeListCmdFlags(listBladeResourcesCmd) //Add command to parent listBladesCmd.AddCommand(listBladeResourcesCmd) } + +// GetCmdUsageListBladeResources - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageListBladesResources() string { + return fmt.Sprintf("%s %s %s %s %s", + flags.RESOURCES, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false), + flags.GetOptionUsageApplianceId(true), + flags.GetOptionUsageBladeId(true), + flags.GetOptionUsageResourceId(true)) +} + +// GetCmdExampleListBladesResources - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleListBladesResources() string { + baseCmd := fmt.Sprintf("cfm list %s %s", flags.BLADES, flags.RESOURCES) + + baseCmdLoopSh := fmt.Sprintf("%s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp()) + + shIdExamplesMap := map[string]string{ + "applianceId": " " + flags.GetOptionExampleShApplianceId(), + "bladeId": " " + flags.GetOptionExampleShBladeId(), + "resourceId": " " + flags.GetOptionExampleShResourceId(), + } + + shExampleLines := make([]string, 0, 8) + for i := 0; i < 8; i++ { + s := baseCmdLoopSh + if i&1 != 0 { + s += shIdExamplesMap["applianceId"] + } + if i&2 != 0 { + s += shIdExamplesMap["bladeId"] + } + if i&4 != 0 { + s += shIdExamplesMap["resourceId"] + } + shExampleLines = append(shExampleLines, s) + } + + var shorthandFormat strings.Builder + for _, line := range shExampleLines { + shorthandFormat.WriteString("\t" + line + "\n") + } + + baseCmdLoopLh := fmt.Sprintf("%s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp()) + + lhIdExamplesMap := map[string]string{ + "applianceId": " " + flags.GetOptionExampleLhApplianceId(), + "bladeId": " " + flags.GetOptionExampleLhBladeId(), + "resourceId": " " + flags.GetOptionExampleLhResourceId(), + } + + lhExampleLines := make([]string, 0, 8) + for i := 0; i < 8; i++ { + s := baseCmdLoopLh + if i&1 != 0 { + s += lhIdExamplesMap["applianceId"] + } + if i&2 != 0 { + s += lhIdExamplesMap["bladeId"] + } + if i&4 != 0 { + s += lhIdExamplesMap["resourceId"] + } + lhExampleLines = append(lhExampleLines, s) + } + + var longhandFormat strings.Builder + for _, line := range lhExampleLines { + longhandFormat.WriteString("\t" + line + "\n") + } + + return fmt.Sprintf(` +%s + +%s`, shorthandFormat.String(), longhandFormat.String()) +} diff --git a/cmd/cfm-cli/cmd/listBlades.go b/cmd/cfm-cli/cmd/listBlades.go index 322c9ee..f4ecb3d 100644 --- a/cmd/cfm-cli/cmd/listBlades.go +++ b/cmd/cfm-cli/cmd/listBlades.go @@ -5,25 +5,18 @@ package cmd import ( "cfm/cli/pkg/serviceLib/flags" "cfm/cli/pkg/serviceLib/serviceRequests" + "fmt" + "strings" "github.com/spf13/cobra" ) var listBladesCmd = &cobra.Command{ - Use: `blades [--serv-ip | -a] [--serv-net-port | -p] [--appliance-id | -L] [--blade-id | -B]`, - Short: "List some or all recognized appliance blades", - Long: `Queries the cfm-service for some or all recognized appliance blades and outputs a detailed summary of the discovered blades to stdout.`, - Example: ` - cfm list blades --serv-ip 127.0.0.1 --serv-net-port 8080 --appliance-id applId --blade-id bladeId - cfm list blades --serv-ip 127.0.0.1 --serv-net-port 8080 --appliance-id applId - cfm list blades --serv-ip 127.0.0.1 --serv-net-port 8080 --blade-id bladeId - cfm list blades --serv-ip 127.0.0.1 --serv-net-port 8080 - - cfm list blades -a 127.0.0.1 -p 8080 -L applId -B bladeId - cfm list blades -a 127.0.0.1 -p 8080 -L applId - cfm list blades -a 127.0.0.1 -p 8080 -B bladeId - cfm list blades -a 127.0.0.1 -p 8080`, - Args: cobra.MatchAll(cobra.NoArgs), + Use: GetCmdUsageListBlades(), + Short: "List some or all recognized appliance blades", + Long: `Queries the cfm-service for some or all recognized appliance blades and outputs a detailed summary of the discovered blades to stdout.`, + Example: GetCmdExampleListBlades(), + Args: cobra.MatchAll(cobra.NoArgs), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { initLogging(cmd) return nil @@ -43,10 +36,79 @@ var listBladesCmd = &cobra.Command{ func init() { listBladesCmd.DisableFlagsInUseLine = true - initCommonPersistentFlags(listBladesCmd) - listBladesCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "ID of appliance to interrogate (default \"all appliances searched\")") - listBladesCmd.Flags().StringP(flags.BLADE_ID, flags.BLADE_ID_SH, flags.ID_DFLT, "ID of a specific appliance blade. (default \"all blades returned\")") + initCommonBladeListCmdFlags(listBladesCmd) //Add command to parent listCmd.AddCommand(listBladesCmd) } + +// GetCmdUsageListBlade - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageListBlades() string { + return fmt.Sprintf("%s %s %s %s", + flags.BLADES, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false), + flags.GetOptionUsageApplianceId(true), + flags.GetOptionUsageBladeId(true)) +} + +// GetCmdExampleListBlades - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleListBlades() string { + baseCmd := fmt.Sprintf("cfm list %s", flags.BLADES) + + baseCmdLoopSh := fmt.Sprintf("%s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp()) + + shIdExamplesMap := map[string]string{ + "applianceId": " " + flags.GetOptionExampleShApplianceId(), + "bladeId": " " + flags.GetOptionExampleShBladeId(), + } + + shExampleLines := make([]string, 0, 4) + for i := 0; i < 4; i++ { + s := baseCmdLoopSh + if i&1 != 0 { + s += shIdExamplesMap["applianceId"] + } + if i&2 != 0 { + s += shIdExamplesMap["bladeId"] + } + shExampleLines = append(shExampleLines, s) + } + + var shorthandFormat strings.Builder + for _, line := range shExampleLines { + shorthandFormat.WriteString("\t" + line + "\n") + } + + baseCmdLoopLh := fmt.Sprintf("%s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp()) + + lhIdExamplesMap := map[string]string{ + "applianceId": " " + flags.GetOptionExampleLhApplianceId(), + "bladeId": " " + flags.GetOptionExampleLhBladeId(), + } + + lhExampleLines := make([]string, 0, 4) + for i := 0; i < 4; i++ { + s := baseCmdLoopLh + if i&1 != 0 { + s += lhIdExamplesMap["applianceId"] + } + if i&2 != 0 { + s += lhIdExamplesMap["bladeId"] + } + lhExampleLines = append(lhExampleLines, s) + } + + var longhandFormat strings.Builder + for _, line := range lhExampleLines { + longhandFormat.WriteString("\t" + line + "\n") + } + + return fmt.Sprintf(` +%s + +%s`, shorthandFormat.String(), longhandFormat.String()) +} diff --git a/cmd/cfm-cli/cmd/listHostMemory.go b/cmd/cfm-cli/cmd/listHostMemory.go index 396d75c..9ba6cca 100644 --- a/cmd/cfm-cli/cmd/listHostMemory.go +++ b/cmd/cfm-cli/cmd/listHostMemory.go @@ -5,27 +5,22 @@ package cmd import ( "cfm/cli/pkg/serviceLib/flags" "cfm/cli/pkg/serviceLib/serviceRequests" + "fmt" + "strings" "github.com/spf13/cobra" ) var listHostsMemoryCmd = &cobra.Command{ - Use: `memory [--serv-ip | -a] [--serv-net-port | -p] [--host-id | -H] [--memory-id | -m]`, + Use: GetCmdUsageListHostsMemory(), Short: "List all available composed memory region(s) accessible to the host(s)", Long: `Queries the cfm-service for composed memory region(s) accessible to the host(s). Outputs a detailed summary of those memory regions to stdout. - Note that, for any given item ID, if it is omitted, ALL items are collected\searched.`, - Example: ` - cfm list hosts memory --serv-ip 127.0.0.1 --serv-net-port 8080 --host-id hostId --memory-id memId - cfm list hosts memory --serv-ip 127.0.0.1 --serv-net-port 8080 --host-id hostId - cfm list hosts memory --serv-ip 127.0.0.1 --serv-net-port 8080 --memory-id memId - cfm list hosts memory --serv-ip 127.0.0.1 --serv-net-port 8080 - - cfm list hosts memory -a 127.0.0.1 -p 8080 -B hostId -m memId - cfm list hosts memory -a 127.0.0.1 -p 8080 -B hostId - cfm list hosts memory -a 127.0.0.1 -p 8080 -m memId - cfm list hosts memory -a 127.0.0.1 -p 8080 `, - Args: cobra.MatchAll(cobra.NoArgs), + Note: For any given ID option: + If the option is included, ONLY THAT ID is searched. + If the option is omitted, ALL POSSIBLE IDs (within cfm-service) are searched.`, + Example: GetCmdExampleListHostsMemory(), + Args: cobra.MatchAll(cobra.NoArgs), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { initLogging(cmd) return nil @@ -47,8 +42,79 @@ func init() { initCommonHostListCmdFlags(listHostsMemoryCmd) - listHostsMemoryCmd.Flags().StringP(flags.MEMORY_ID, flags.MEMORY_ID_SH, flags.ID_DFLT, "ID of a specific memory region. (default \"all memory regions returned\")") + listHostsMemoryCmd.Flags().StringP(flags.MEMORY_ID, flags.MEMORY_ID_SH, flags.ID_DFLT, "ID of a specific memory region\n (default \"all memory regions listed\")") //Add command to parent listHostsCmd.AddCommand(listHostsMemoryCmd) } + +// GetCmdUsageListHostMemory - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageListHostsMemory() string { + return fmt.Sprintf("%s %s %s %s", + flags.MEMORY, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false), + flags.GetOptionUsageHostId(true), + flags.GetOptionUsageMemoryId(true)) +} + +// GetCmdExampleListHostsMemory - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleListHostsMemory() string { + baseCmd := fmt.Sprintf("cfm list %s %s", flags.HOSTS, flags.MEMORY) + + baseCmdLoopSh := fmt.Sprintf("%s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp()) + + shIdExamplesMap := map[string]string{ + "hostId": " " + flags.GetOptionExampleShHostId(), + "memoryId": " " + flags.GetOptionExampleShMemoryId(), + } + + shExampleLines := make([]string, 0, 4) + for i := 0; i < 4; i++ { + s := baseCmdLoopSh + if i&1 != 0 { + s += shIdExamplesMap["hostId"] + } + if i&2 != 0 { + s += shIdExamplesMap["memoryId"] + } + shExampleLines = append(shExampleLines, s) + } + + var shorthandFormat strings.Builder + for _, line := range shExampleLines { + shorthandFormat.WriteString("\t" + line + "\n") + } + + baseCmdLoopLh := fmt.Sprintf("%s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp()) + + lhIdExamplesMap := map[string]string{ + "hostId": " " + flags.GetOptionExampleShHostId(), + "memoryId": " " + flags.GetOptionExampleLhMemoryId(), + } + + lhExampleLines := make([]string, 0, 4) + for i := 0; i < 4; i++ { + s := baseCmdLoopLh + if i&1 != 0 { + s += shIdExamplesMap["hostId"] + } + if i&2 != 0 { + s += lhIdExamplesMap["memoryId"] + } + lhExampleLines = append(lhExampleLines, s) + } + + var longhandFormat strings.Builder + for _, line := range lhExampleLines { + longhandFormat.WriteString("\t" + line + "\n") + } + + return fmt.Sprintf(` +%s + +%s`, shorthandFormat.String(), longhandFormat.String()) +} diff --git a/cmd/cfm-cli/cmd/listHostMemoryDevices.go b/cmd/cfm-cli/cmd/listHostMemoryDevices.go index a33932a..72d522a 100644 --- a/cmd/cfm-cli/cmd/listHostMemoryDevices.go +++ b/cmd/cfm-cli/cmd/listHostMemoryDevices.go @@ -5,27 +5,22 @@ package cmd import ( "cfm/cli/pkg/serviceLib/flags" "cfm/cli/pkg/serviceLib/serviceRequests" + "fmt" + "strings" "github.com/spf13/cobra" ) var listHostsMemoryDevicesCmd = &cobra.Command{ - Use: `memory-devices [--serv-ip | -a] [--serv-net-port | -p] [--host-id | -H] [--memory-device-id | -d]`, + Use: GetCmdUsageListHostsMemoryDevices(), Short: "List all available logical memory device(s) accessible to the host(s)", Long: `Queries the cfm-service for logical memory device(s) accessible to the host(s). Outputs a detailed summary of those memory regions to stdout. - Note that, for any given item ID, if it is omitted, ALL items are collected\searched.`, - Example: ` - cfm list hosts memory-devices --serv-ip 127.0.0.1 --serv-net-port 8080 --host-id hostId --memory-device-id memdevId - cfm list hosts memory-devices --serv-ip 127.0.0.1 --serv-net-port 8080 --host-id hostId - cfm list hosts memory-devices --serv-ip 127.0.0.1 --serv-net-port 8080 --memory-device-id memdevId - cfm list hosts memory-devices --serv-ip 127.0.0.1 --serv-net-port 8080 - - cfm list hosts memory-devices -a 127.0.0.1 -p 8080 -B hostId -d memdevId - cfm list hosts memory-devices -a 127.0.0.1 -p 8080 -B hostId - cfm list hosts memory-devices -a 127.0.0.1 -p 8080 -d memdevId - cfm list hosts memory-devices -a 127.0.0.1 -p 8080 `, - Args: cobra.MatchAll(cobra.NoArgs), + Note: For any given ID option: + If the option is included, ONLY THAT ID is searched. + If the option is omitted, ALL POSSIBLE IDs (within cfm-service) are searched.`, + Example: GetCmdExampleListHostsMemoryDevices(), + Args: cobra.MatchAll(cobra.NoArgs), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { initLogging(cmd) return nil @@ -47,8 +42,79 @@ func init() { initCommonHostListCmdFlags(listHostsMemoryDevicesCmd) - listHostsMemoryDevicesCmd.Flags().StringP(flags.MEMORY_DEVICE_ID, flags.MEMORY_DEVICE_ID_SH, flags.ID_DFLT, "ID of a specific memory device. (default \"all memory devices returned\")") + listHostsMemoryDevicesCmd.Flags().StringP(flags.MEMORY_DEVICE_ID, flags.MEMORY_DEVICE_ID_SH, flags.ID_DFLT, "ID of a specific memory device\n (default \"all memory devices listsed\")") //Add command to parent listHostsCmd.AddCommand(listHostsMemoryDevicesCmd) } + +// GetCmdUsageListHostsMemoryDevices - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageListHostsMemoryDevices() string { + return fmt.Sprintf("%s %s %s %s", + flags.MEMORY_DEVICES, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false), + flags.GetOptionUsageHostId(true), + flags.GetOptionUsageMemoryDeviceId(true)) +} + +// GetCmdExampleListHostsMemoryDevices - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleListHostsMemoryDevices() string { + baseCmd := fmt.Sprintf("cfm list %s %s", flags.HOSTS, flags.MEMORY_DEVICES) + + baseCmdLoopSh := fmt.Sprintf("%s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp()) + + shIdExamplesMap := map[string]string{ + "hostId": " " + flags.GetOptionExampleShHostId(), + "memorydeviceId": " " + flags.GetOptionExampleShMemoryDeviceId(), + } + + shExampleLines := make([]string, 0, 4) + for i := 0; i < 4; i++ { + s := baseCmdLoopSh + if i&1 != 0 { + s += shIdExamplesMap["hostId"] + } + if i&2 != 0 { + s += shIdExamplesMap["memorydeviceId"] + } + shExampleLines = append(shExampleLines, s) + } + + var shorthandFormat strings.Builder + for _, line := range shExampleLines { + shorthandFormat.WriteString("\t" + line + "\n") + } + + baseCmdLoopLh := fmt.Sprintf("%s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp()) + + lhIdExamplesMap := map[string]string{ + "hostId": " " + flags.GetOptionExampleLhHostId(), + "memorydeviceId": " " + flags.GetOptionExampleLhMemoryDeviceId(), + } + + lhExampleLines := make([]string, 0, 4) + for i := 0; i < 4; i++ { + s := baseCmdLoopLh + if i&1 != 0 { + s += lhIdExamplesMap["hostId"] + } + if i&2 != 0 { + s += lhIdExamplesMap["memorydeviceId"] + } + lhExampleLines = append(lhExampleLines, s) + } + + var longhandFormat strings.Builder + for _, line := range lhExampleLines { + longhandFormat.WriteString("\t" + line + "\n") + } + + return fmt.Sprintf(` +%s + +%s`, shorthandFormat.String(), longhandFormat.String()) +} diff --git a/cmd/cfm-cli/cmd/listHostPorts.go b/cmd/cfm-cli/cmd/listHostPorts.go index 497d3b3..09adeb8 100644 --- a/cmd/cfm-cli/cmd/listHostPorts.go +++ b/cmd/cfm-cli/cmd/listHostPorts.go @@ -5,27 +5,22 @@ package cmd import ( "cfm/cli/pkg/serviceLib/flags" "cfm/cli/pkg/serviceLib/serviceRequests" + "fmt" + "strings" "github.com/spf13/cobra" ) var listHostsPortsCmd = &cobra.Command{ - Use: `ports [--serv-ip | -a] [--serv-net-port | -p] [-host-id | -H] [--port-id | -o]`, + Use: GetCmdUsageListHostsPorts(), Short: "List all available port(s) accessible to the host(s).", Long: `Queries the cfm-service for port(s) accessible to the host(s). Outputs a detailed summary of those ports to stdout. - Note that, for any given item ID, if it is omitted, ALL items are collected\searched.`, - Example: ` - cfm list hosts ports --serv-ip 127.0.0.1 --serv-net-port 8080 --host-id hostId --port-id portId - cfm list hosts ports --serv-ip 127.0.0.1 --serv-net-port 8080 --host-id hostId - cfm list hosts ports --serv-ip 127.0.0.1 --serv-net-port 8080 --port-id portId - cfm list hosts ports --serv-ip 127.0.0.1 --serv-net-port 8080 - - cfm list hosts ports -a 127.0.0.1 -p 8080 -B hostId -o portId - cfm list hosts ports -a 127.0.0.1 -p 8080 -B hostId - cfm list hosts ports -a 127.0.0.1 -p 8080 -o portId - cfm list hosts ports -a 127.0.0.1 -p 8080 `, - Args: cobra.MatchAll(cobra.NoArgs), + Note: For any given ID option: + If the option is included, ONLY THAT ID is searched. + If the option is omitted, ALL POSSIBLE IDs (within cfm-service) are searched.`, + Example: GetCmdExampleListHostsPorts(), + Args: cobra.MatchAll(cobra.NoArgs), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { initLogging(cmd) return nil @@ -47,8 +42,79 @@ func init() { initCommonHostListCmdFlags(listHostsPortsCmd) - listHostsPortsCmd.Flags().StringP(flags.PORT_ID, flags.PORT_ID_SH, flags.ID_DFLT, "ID of a specific port. (default \"all ports returned.\")") + listHostsPortsCmd.Flags().StringP(flags.PORT_ID, flags.PORT_ID_SH, flags.ID_DFLT, "ID of a specific port\n (default \"all ports listed.\")") //Add command to parent listHostsCmd.AddCommand(listHostsPortsCmd) } + +// GetCmdUsageListHostsPorts - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageListHostsPorts() string { + return fmt.Sprintf("%s %s %s %s", + flags.PORTS, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false), + flags.GetOptionUsageHostId(true), + flags.GetOptionUsagePortId(true)) +} + +// GetCmdExampleListHostsPorts - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleListHostsPorts() string { + baseCmd := fmt.Sprintf("cfm list %s %s", flags.HOSTS, flags.PORTS) + + baseCmdLoopSh := fmt.Sprintf("%s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp()) + + shIdExamplesMap := map[string]string{ + "hostId": " " + flags.GetOptionExampleShHostId(), + "portId": " " + flags.GetOptionExampleShPortId(), + } + + shExampleLines := make([]string, 0, 4) + for i := 0; i < 4; i++ { + s := baseCmdLoopSh + if i&1 != 0 { + s += shIdExamplesMap["hostId"] + } + if i&2 != 0 { + s += shIdExamplesMap["portId"] + } + shExampleLines = append(shExampleLines, s) + } + + var shorthandFormat strings.Builder + for _, line := range shExampleLines { + shorthandFormat.WriteString("\t" + line + "\n") + } + + baseCmdLoopLh := fmt.Sprintf("%s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp()) + + lhIdExamplesMap := map[string]string{ + "hostId": " " + flags.GetOptionExampleLhHostId(), + "portId": " " + flags.GetOptionExampleLhPortId(), + } + + lhExampleLines := make([]string, 0, 4) + for i := 0; i < 4; i++ { + s := baseCmdLoopLh + if i&1 != 0 { + s += lhIdExamplesMap["hostId"] + } + if i&2 != 0 { + s += lhIdExamplesMap["portId"] + } + lhExampleLines = append(lhExampleLines, s) + } + + var longhandFormat strings.Builder + for _, line := range lhExampleLines { + longhandFormat.WriteString("\t" + line + "\n") + } + + return fmt.Sprintf(` +%s + +%s`, shorthandFormat.String(), longhandFormat.String()) +} diff --git a/cmd/cfm-cli/cmd/listHosts.go b/cmd/cfm-cli/cmd/listHosts.go index e71982f..52c5f22 100644 --- a/cmd/cfm-cli/cmd/listHosts.go +++ b/cmd/cfm-cli/cmd/listHosts.go @@ -3,21 +3,20 @@ package cmd import ( + "cfm/cli/pkg/serviceLib/flags" "cfm/cli/pkg/serviceLib/serviceRequests" + "fmt" "github.com/spf13/cobra" ) // listHostsCmd represents the listHosts command var listHostsCmd = &cobra.Command{ - Use: `hosts [--serv-ip | -i] [--serv-net-port | -p]`, - Short: "List all recognized cxl host(s)", - Long: `Queries the cfm-service for all recognized cxl host(s) and outputs a summary to stdout.`, - Example: ` - cfm list hosts --serv-ip 127.0.0.1 --serv-net-port 8080 - - cfm list hosts -a 127.0.0.1 -p 8080`, - Args: cobra.MatchAll(cobra.NoArgs), + Use: GetCmdUsageListHosts(), + Short: "List all recognized cxl host(s)", + Long: `Queries the cfm-service for all recognized cxl host(s) and outputs a summary to stdout.`, + Example: GetCmdExampleListHosts(), + Args: cobra.MatchAll(cobra.NoArgs), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { initLogging(cmd) return nil @@ -43,3 +42,28 @@ func init() { //Add command to parent listCmd.AddCommand(listHostsCmd) } + +// GetCmdUsageListHost - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageListHosts() string { + return fmt.Sprintf("%s %s", + flags.HOSTS, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false)) +} + +// GetCmdExampleListHosts - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleListHosts() string { + baseCmd := fmt.Sprintf("cfm list %s", flags.HOSTS) + + shorthandFormat := fmt.Sprintf("%s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp()) + + longhandFormat := fmt.Sprintf("%s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp()) + + return fmt.Sprintf(` + %s + + %s`, shorthandFormat, longhandFormat) +} diff --git a/cmd/cfm-cli/cmd/renameAppliance.go b/cmd/cfm-cli/cmd/renameAppliance.go index 1936e92..fe67433 100644 --- a/cmd/cfm-cli/cmd/renameAppliance.go +++ b/cmd/cfm-cli/cmd/renameAppliance.go @@ -5,19 +5,17 @@ package cmd import ( "cfm/cli/pkg/serviceLib/flags" "cfm/cli/pkg/serviceLib/serviceRequests" + "fmt" "github.com/spf13/cobra" ) var renameApplianceCmd = &cobra.Command{ - Use: `appliance [--serv-ip | -a] [--serv-net-port | -p] <--appliance-id | -L> <--new-id | -N>`, - Short: "Rename a specific composable memory appliance (CMA) to a new ID", - Long: `Rename a specific composable memory appliance (CMA) to a new ID.`, - Example: ` - cfm rename appliance --appliance-id applId --new-id newId --serv-ip 127.0.0.1 --serv-net-port 8080 - - cfm rename appliance -L applId -N newId -a 127.0.0.1 -p 8080`, - Args: cobra.MatchAll(cobra.NoArgs), + Use: GetCmdUsageRenameAppliance(), + Short: "Rename a specific composable memory appliance (CMA) to a new ID", + Long: `Rename a specific composable memory appliance (CMA) to a new ID.`, + Example: GetCmdExampleRenameAppliance(), + Args: cobra.MatchAll(cobra.NoArgs), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { initLogging(cmd) return nil @@ -39,11 +37,42 @@ func init() { initCommonPersistentFlags(renameApplianceCmd) - renameApplianceCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "Current ID of composable memory appliance (CMA)") + renameApplianceCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "Current ID of composable memory appliance (CMA)\n") renameApplianceCmd.MarkFlagRequired(flags.APPLIANCE_ID) - renameApplianceCmd.Flags().StringP(flags.NEW_ID, flags.NEW_ID_SH, flags.ID_DFLT, "New ID of composable memory appliance (CMA)") + renameApplianceCmd.Flags().StringP(flags.NEW_ID, flags.NEW_ID_SH, flags.ID_DFLT, "New ID of composable memory appliance (CMA)\n") renameApplianceCmd.MarkFlagRequired(flags.NEW_ID) //Add command to parent renameCmd.AddCommand(renameApplianceCmd) } + +// GetCmdUsageRenameAppliance - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageRenameAppliance() string { + return fmt.Sprintf("%s %s %s %s", + flags.APPLIANCE, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false), + flags.GetOptionUsageApplianceId(false), + flags.GetOptionUsageNewId(false)) +} + +// GetCmdExampleRenameAppliance - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleRenameAppliance() string { + baseCmd := fmt.Sprintf("cfm rename %s", flags.APPLIANCE) + + shorthandFormat := fmt.Sprintf("%s %s %s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp(), + flags.GetOptionExampleShApplianceId(), + flags.GetOptionExampleShNewId()) + + longhandFormat := fmt.Sprintf("%s %s %s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp(), + flags.GetOptionExampleLhApplianceId(), + flags.GetOptionExampleLhNewId()) + + return fmt.Sprintf(` + %s + + %s`, shorthandFormat, longhandFormat) +} diff --git a/cmd/cfm-cli/cmd/renameBlade.go b/cmd/cfm-cli/cmd/renameBlade.go index 1403009..fd0b482 100644 --- a/cmd/cfm-cli/cmd/renameBlade.go +++ b/cmd/cfm-cli/cmd/renameBlade.go @@ -5,19 +5,17 @@ package cmd import ( "cfm/cli/pkg/serviceLib/flags" "cfm/cli/pkg/serviceLib/serviceRequests" + "fmt" "github.com/spf13/cobra" ) var renameBladeCmd = &cobra.Command{ - Use: `blade [--serv-ip | -a] [--serv-net-port | -p] <--appliance-id | -L> <--blade-id | -B> <--new-id | -N>`, - Short: "Rename a single blade ID on a specific composable memory appliance (CMA) to a new ID", - Long: `Rename a single blade ID on a specific composable memory appliance (CMA) to a new ID.`, - Example: ` - cfm rename blade --appliance-id applId --blade-id bladeId --new-id newId --serv-ip 127.0.0.1 --serv-net-port 8080 - - cfm rename blade -L applId -B bladeId -N newId -a 127.0.0.1 -p 8080`, - Args: cobra.MatchAll(cobra.NoArgs), + Use: GetCmdUsageRenameBlade(), + Short: "Rename a single blade ID on a specific composable memory appliance (CMA) to a new ID", + Long: `Rename a single blade ID on a specific composable memory appliance (CMA) to a new ID.`, + Example: GetCmdExampleRenameBlade(), + Args: cobra.MatchAll(cobra.NoArgs), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { initLogging(cmd) return nil @@ -39,13 +37,47 @@ func init() { initCommonPersistentFlags(renameBladeCmd) - renameBladeCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "ID of blade's composable memory appliance (CMA)") + renameBladeCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "ID of blade's composable memory appliance (CMA)\n") renameBladeCmd.MarkFlagRequired(flags.APPLIANCE_ID) - renameBladeCmd.Flags().StringP(flags.BLADE_ID, flags.BLADE_ID_SH, flags.ID_DFLT, "Current blade ID") + renameBladeCmd.Flags().StringP(flags.BLADE_ID, flags.BLADE_ID_SH, flags.ID_DFLT, "Current blade ID\n") renameBladeCmd.MarkFlagRequired(flags.BLADE_ID) - renameBladeCmd.Flags().StringP(flags.NEW_ID, flags.NEW_ID_SH, flags.ID_DFLT, "New blade ID") + renameBladeCmd.Flags().StringP(flags.NEW_ID, flags.NEW_ID_SH, flags.ID_DFLT, "New blade ID\n") renameBladeCmd.MarkFlagRequired(flags.NEW_ID) //Add command to parent renameCmd.AddCommand(renameBladeCmd) } + +// GetCmdUsageRenameBlade - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageRenameBlade() string { + return fmt.Sprintf("%s %s %s %s %s", + flags.BLADE, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false), + flags.GetOptionUsageApplianceId(false), + flags.GetOptionUsageBladeId(false), + flags.GetOptionUsageNewId(false)) +} + +// GetCmdExampleRenameBlade - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleRenameBlade() string { + baseCmd := fmt.Sprintf("cfm rename %s", flags.BLADE) + + shorthandFormat := fmt.Sprintf("%s %s %s %s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp(), + flags.GetOptionExampleShApplianceId(), + flags.GetOptionExampleShBladeId(), + flags.GetOptionExampleShNewId()) + + longhandFormat := fmt.Sprintf("%s %s %s %s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp(), + flags.GetOptionExampleLhApplianceId(), + flags.GetOptionExampleLhBladeId(), + flags.GetOptionExampleLhNewId()) + + return fmt.Sprintf(` + %s + + %s`, shorthandFormat, longhandFormat) +} diff --git a/cmd/cfm-cli/cmd/renameHost.go b/cmd/cfm-cli/cmd/renameHost.go index e826f76..cfcce31 100644 --- a/cmd/cfm-cli/cmd/renameHost.go +++ b/cmd/cfm-cli/cmd/renameHost.go @@ -5,19 +5,17 @@ package cmd import ( "cfm/cli/pkg/serviceLib/flags" "cfm/cli/pkg/serviceLib/serviceRequests" + "fmt" "github.com/spf13/cobra" ) var renameHostCmd = &cobra.Command{ - Use: `host [--serv-ip | -i] [--serv-net-port | -p] <--host-id | -H> <--new-id | -N>`, - Short: `Rename a specific cxl host to a new ID`, - Long: `Rename a specific cxl host to a new ID.`, - Example: ` - cfm rename host --host-id hostId --new-id newId --serv-ip 127.0.0.1 --serv-net-port 8080 - - cfm rename host -H hostId -N newId -a 127.0.0.1 -p 8080`, - Args: cobra.MatchAll(cobra.NoArgs), + Use: GetCmdUsageRenameHost(), + Short: `Rename a specific cxl host to a new ID`, + Long: `Rename a specific cxl host to a new ID.`, + Example: GetCmdExampleRenameHost(), + Args: cobra.MatchAll(cobra.NoArgs), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { initLogging(cmd) return nil @@ -39,11 +37,42 @@ func init() { initCommonPersistentFlags(renameHostCmd) - renameHostCmd.Flags().StringP(flags.HOST_ID, flags.HOST_ID_SH, flags.ID_DFLT, "Current CXL host ID") + renameHostCmd.Flags().StringP(flags.HOST_ID, flags.HOST_ID_SH, flags.ID_DFLT, "Current CXL host ID\n") renameHostCmd.MarkFlagRequired(flags.HOST_ID) - renameHostCmd.Flags().StringP(flags.NEW_ID, flags.NEW_ID_SH, flags.ID_DFLT, "New CXL host ID") + renameHostCmd.Flags().StringP(flags.NEW_ID, flags.NEW_ID_SH, flags.ID_DFLT, "New CXL host ID\n") renameHostCmd.MarkFlagRequired(flags.NEW_ID) //Add command to parent renameCmd.AddCommand(renameHostCmd) } + +// GetCmdUsageRenameHost - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageRenameHost() string { + return fmt.Sprintf("%s %s %s %s", + flags.HOST, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false), + flags.GetOptionUsageHostId(false), + flags.GetOptionUsageNewId(false)) +} + +// GetCmdExampleRenameHost - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleRenameHost() string { + baseCmd := fmt.Sprintf("cfm rename %s", flags.HOST) + + shorthandFormat := fmt.Sprintf("%s %s %s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp(), + flags.GetOptionExampleShHostId(), + flags.GetOptionExampleShNewId()) + + longhandFormat := fmt.Sprintf("%s %s %s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp(), + flags.GetOptionExampleLhHostId(), + flags.GetOptionExampleLhNewId()) + + return fmt.Sprintf(` + %s + + %s`, shorthandFormat, longhandFormat) +} diff --git a/cmd/cfm-cli/cmd/resyncAppliance.go b/cmd/cfm-cli/cmd/resyncAppliance.go index effe134..9242265 100644 --- a/cmd/cfm-cli/cmd/resyncAppliance.go +++ b/cmd/cfm-cli/cmd/resyncAppliance.go @@ -5,19 +5,17 @@ package cmd import ( "cfm/cli/pkg/serviceLib/flags" "cfm/cli/pkg/serviceLib/serviceRequests" + "fmt" "github.com/spf13/cobra" ) var resyncApplianceCmd = &cobra.Command{ - Use: `appliance [--serv-ip | -a] [--serv-net-port | -p] <--appliance-id | -L>`, - Short: "Resynchronize the cfm service to all the added blades for a specific composable memory appliance (CMA)", - Long: `Resynchronize the cfm service to all the added blades for a specific composable memory appliance (CMA).`, - Example: ` - cfm resync appliance --appliance-id applId --serv-ip 127.0.0.1 --serv-net-port 8080 - - cfm resync appliance -L applId -a 127.0.0.1 -p 8080`, - Args: cobra.MatchAll(cobra.NoArgs), + Use: GetCmdUsageResyncAppliance(), + Short: "Resynchronize the cfm service to all the added blades for a specific composable memory appliance (CMA)", + Long: `Resynchronize the cfm service to all the added blades for a specific composable memory appliance (CMA).`, + Example: GetCmdExampleResyncAppliance(), + Args: cobra.MatchAll(cobra.NoArgs), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { initLogging(cmd) return nil @@ -39,9 +37,37 @@ func init() { initCommonPersistentFlags(resyncApplianceCmd) - resyncApplianceCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "ID of composable memory appliance (CMA)") + resyncApplianceCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "ID of composable memory appliance (CMA)\n") resyncApplianceCmd.MarkFlagRequired(flags.APPLIANCE_ID) //Add command to parent resyncCmd.AddCommand(resyncApplianceCmd) } + +// GetCmdUsageResyncAppliance - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageResyncAppliance() string { + return fmt.Sprintf("%s %s %s", + flags.APPLIANCE, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false), + flags.GetOptionUsageApplianceId(false)) +} + +// GetCmdExampleResyncAppliance - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleResyncAppliance() string { + baseCmd := fmt.Sprintf("cfm resync %s", flags.APPLIANCE) + + shorthandFormat := fmt.Sprintf("%s %s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp(), + flags.GetOptionExampleShApplianceId()) + + longhandFormat := fmt.Sprintf("%s %s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp(), + flags.GetOptionExampleLhApplianceId()) + + return fmt.Sprintf(` + %s + + %s`, shorthandFormat, longhandFormat) +} diff --git a/cmd/cfm-cli/cmd/resyncBlade.go b/cmd/cfm-cli/cmd/resyncBlade.go index 3f55497..d46f800 100644 --- a/cmd/cfm-cli/cmd/resyncBlade.go +++ b/cmd/cfm-cli/cmd/resyncBlade.go @@ -5,19 +5,17 @@ package cmd import ( "cfm/cli/pkg/serviceLib/flags" "cfm/cli/pkg/serviceLib/serviceRequests" + "fmt" "github.com/spf13/cobra" ) var resyncBladeCmd = &cobra.Command{ - Use: `blade [--serv-ip | -a] [--serv-net-port | -p] <--appliance-id | -L> <--blade-id | -B>`, - Short: "Resynchronize the cfm service to a single blade on a specific composable memory appliance (CMA)", - Long: `Resynchronize the cfm service to a single blade on a specific composable memory appliance (CMA).`, - Example: ` - cfm resync blade --appliance-id applId --blade-id bladeId --serv-ip 127.0.0.1 --serv-net-port 8080 - - cfm resync blade -L applId -B bladeId -a 127.0.0.1 -p 8080`, - Args: cobra.MatchAll(cobra.NoArgs), + Use: GetCmdUsageResyncBlade(), + Short: "Resynchronize the cfm service to a single blade on a specific composable memory appliance (CMA)", + Long: `Resynchronize the cfm service to a single blade on a specific composable memory appliance (CMA).`, + Example: GetCmdExampleResyncBlade(), + Args: cobra.MatchAll(cobra.NoArgs), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { initLogging(cmd) return nil @@ -39,11 +37,42 @@ func init() { initCommonPersistentFlags(resyncBladeCmd) - resyncBladeCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "ID of blade's appliance") + resyncBladeCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "ID of blade's appliance\n") resyncBladeCmd.MarkFlagRequired(flags.APPLIANCE_ID) - resyncBladeCmd.Flags().StringP(flags.BLADE_ID, flags.BLADE_ID_SH, flags.ID_DFLT, "ID of blade") + resyncBladeCmd.Flags().StringP(flags.BLADE_ID, flags.BLADE_ID_SH, flags.ID_DFLT, "ID of blade\n") resyncBladeCmd.MarkFlagRequired(flags.BLADE_ID) //Add command to parent resyncCmd.AddCommand(resyncBladeCmd) } + +// GetCmdUsageResyncBlade - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageResyncBlade() string { + return fmt.Sprintf("%s %s %s %s", + flags.BLADE, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false), + flags.GetOptionUsageApplianceId(false), + flags.GetOptionUsageBladeId(false)) +} + +// GetCmdExampleResyncBlade - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleResyncBlade() string { + baseCmd := fmt.Sprintf("cfm resync %s", flags.BLADE) + + shorthandFormat := fmt.Sprintf("%s %s %s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp(), + flags.GetOptionExampleShApplianceId(), + flags.GetOptionExampleShBladeId()) + + longhandFormat := fmt.Sprintf("%s %s %s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp(), + flags.GetOptionExampleLhApplianceId(), + flags.GetOptionExampleLhBladeId()) + + return fmt.Sprintf(` + %s + + %s`, shorthandFormat, longhandFormat) +} diff --git a/cmd/cfm-cli/cmd/resyncHost.go b/cmd/cfm-cli/cmd/resyncHost.go index f39f461..ca4d581 100644 --- a/cmd/cfm-cli/cmd/resyncHost.go +++ b/cmd/cfm-cli/cmd/resyncHost.go @@ -5,19 +5,17 @@ package cmd import ( "cfm/cli/pkg/serviceLib/flags" "cfm/cli/pkg/serviceLib/serviceRequests" + "fmt" "github.com/spf13/cobra" ) var resyncHostCmd = &cobra.Command{ - Use: `host [--serv-ip | -i] [--serv-net-port | -p] <--host-id | -H>`, - Short: `Resynchronize the cfm service to a single cxl host`, - Long: `Resynchronize the cfm service to a single cxl host.`, - Example: ` - cfm resync host --host-id hostId --serv-ip 127.0.0.1 --serv-net-port 8080 - - cfm resync host -H hostId -a 127.0.0.1 -p 8080`, - Args: cobra.MatchAll(cobra.NoArgs), + Use: GetCmdUsageResyncHost(), + Short: `Resynchronize the cfm service to a single cxl host`, + Long: `Resynchronize the cfm service to a single cxl host.`, + Example: GetCmdExampleResyncHost(), + Args: cobra.MatchAll(cobra.NoArgs), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { initLogging(cmd) return nil @@ -39,9 +37,37 @@ func init() { initCommonPersistentFlags(resyncHostCmd) - resyncHostCmd.Flags().StringP(flags.HOST_ID, flags.HOST_ID_SH, flags.ID_DFLT, "ID of CXL host") + resyncHostCmd.Flags().StringP(flags.HOST_ID, flags.HOST_ID_SH, flags.ID_DFLT, "ID of CXL host\n") resyncHostCmd.MarkFlagRequired(flags.HOST_ID) //Add command to parent resyncCmd.AddCommand(resyncHostCmd) } + +// GetCmdUsageResyncHost - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageResyncHost() string { + return fmt.Sprintf("%s %s %s", + flags.HOST, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false), + flags.GetOptionUsageHostId(false)) +} + +// GetCmdExampleResyncHost - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleResyncHost() string { + baseCmd := fmt.Sprintf("cfm resync %s", flags.HOST) + + shorthandFormat := fmt.Sprintf("%s %s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp(), + flags.GetOptionExampleShHostId()) + + longhandFormat := fmt.Sprintf("%s %s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp(), + flags.GetOptionExampleLhHostId()) + + return fmt.Sprintf(` + %s + + %s`, shorthandFormat, longhandFormat) +} diff --git a/cmd/cfm-cli/cmd/root.go b/cmd/cfm-cli/cmd/root.go index 5624b4b..87d226e 100644 --- a/cmd/cfm-cli/cmd/root.go +++ b/cmd/cfm-cli/cmd/root.go @@ -67,12 +67,8 @@ func initLogging(cmd *cobra.Command) { // Need to ONLY add them in the subcommand init() func so these flags ONLY show up in the help output when they can actually be used. func initCommonPersistentFlags(cmd *cobra.Command) { //Add globally required cfm-service TCPIP connection flags - cmd.PersistentFlags().StringP(flags.SERVICE_NET_IP, flags.SERVICE_NET_IP_SH, flags.SERVICE_NET_IP_DFLT, "cfm-service network IP address") - cmd.PersistentFlags().Uint16P(flags.SERVICE_NET_PORT, flags.SERVICE_NET_PORT_SH, flags.SERVICE_NET_PORT_DFLT, "cfm-service network port") - - //Currently unused but need default values downstream - cmd.PersistentFlags().Bool(flags.SERVICE_INSECURE, flags.SERVICE_INSECURE_DFLT, "cfm-service insecure connection flag") - cmd.PersistentFlags().MarkHidden(flags.SERVICE_INSECURE) - cmd.PersistentFlags().String(flags.SERVICE_PROTOCOL, flags.SERVICE_PROTOCOL_DFLT, "cfm-service network connection protocol (http/https)") - cmd.PersistentFlags().MarkHidden(flags.SERVICE_PROTOCOL) + cmd.PersistentFlags().StringP(flags.SERVICE_NET_IP, flags.SERVICE_NET_IP_SH, flags.SERVICE_NET_IP_DFLT, "cfm-service network IP address\n") + cmd.PersistentFlags().Uint16P(flags.SERVICE_NET_PORT, flags.SERVICE_NET_PORT_SH, flags.SERVICE_NET_PORT_DFLT, "cfm-service network port\n") + cmd.PersistentFlags().BoolP(flags.SERVICE_INSECURE, flags.SERVICE_INSECURE_SH, flags.SERVICE_INSECURE_DFLT, "cfm-service insecure connection flag\n (default false)") + cmd.PersistentFlags().StringP(flags.SERVICE_PROTOCOL, flags.SERVICE_PROTOCOL_SH, flags.SERVICE_PROTOCOL_DFLT, "cfm-service network connection protocol (http/https)\n") } diff --git a/cmd/cfm-cli/cmd/unassignBlade.go b/cmd/cfm-cli/cmd/unassignBlade.go index bed75a6..3e176ae 100644 --- a/cmd/cfm-cli/cmd/unassignBlade.go +++ b/cmd/cfm-cli/cmd/unassignBlade.go @@ -11,14 +11,11 @@ import ( ) var unassignBladeCmd = &cobra.Command{ - Use: "blade [--serv-ip | -a] [--serv-net-port | -p] <--appliance-id | -L> <--blade-id | -B> <--memory-id | -m> <--port-id | -o>", - Short: "Unassign an existing blade memory region from a blade port.", - Long: `Unassign an existing blade memory region from a blade port.`, - Example: ` - cfm unassign blade --serv-ip 127.0.0.1 --serv-net-port 8080 --appliance-id applId --blade-id bladeId --memory-id memoryId --port-id portId - - cfm unassign blade -a 127.0.0.1 -p 8080 -L applId -B bladeId -m memoryId -o portId`, - Args: cobra.MatchAll(cobra.NoArgs), + Use: GetCmdUsageUnassignBlade(), + Short: "Unassign an existing blade memory region from a blade port.", + Long: `Unassign an existing blade memory region from a blade port.`, + Example: GetCmdExampleUnassignBlade(), + Args: cobra.MatchAll(cobra.NoArgs), PersistentPreRunE: func(cmd *cobra.Command, args []string) error { initLogging(cmd) return nil @@ -44,13 +41,13 @@ var unassignBladeCmd = &cobra.Command{ func init() { unassignBladeCmd.DisableFlagsInUseLine = true - unassignBladeCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "ID of appliance to interrogate") + unassignBladeCmd.Flags().StringP(flags.APPLIANCE_ID, flags.APPLIANCE_ID_SH, flags.ID_DFLT, "ID of appliance to interrogate\n") unassignBladeCmd.MarkFlagRequired(flags.APPLIANCE_ID) - unassignBladeCmd.Flags().StringP(flags.BLADE_ID, flags.BLADE_ID_SH, flags.ID_DFLT, "ID of appliance blade to interrogate") + unassignBladeCmd.Flags().StringP(flags.BLADE_ID, flags.BLADE_ID_SH, flags.ID_DFLT, "ID of appliance blade to interrogate\n") unassignBladeCmd.MarkFlagRequired(flags.BLADE_ID) - unassignBladeCmd.Flags().StringP(flags.MEMORY_ID, flags.MEMORY_ID_SH, flags.ID_DFLT, "ID of the appliance blade memory region to unassign from the specified port.") + unassignBladeCmd.Flags().StringP(flags.MEMORY_ID, flags.MEMORY_ID_SH, flags.ID_DFLT, "ID of the appliance blade memory region to unassign from the specified port\n") unassignBladeCmd.MarkFlagRequired(flags.MEMORY_ID) - unassignBladeCmd.Flags().StringP(flags.PORT_ID, flags.PORT_ID_SH, flags.ID_DFLT, "ID of the appliance blade port to unassign from the specified memory region.") + unassignBladeCmd.Flags().StringP(flags.PORT_ID, flags.PORT_ID_SH, flags.ID_DFLT, "ID of the appliance blade port to unassign from the specified memory region\n") unassignBladeCmd.MarkFlagRequired(flags.PORT_ID) initCommonPersistentFlags(unassignBladeCmd) @@ -58,3 +55,40 @@ func init() { //Add command to parent unassignCmd.AddCommand(unassignBladeCmd) } + +// GetCmdUsageUnassignBlade - Generates the command usage string for the cobra.Command.Use field. +func GetCmdUsageUnassignBlade() string { + return fmt.Sprintf("%s %s %s %s %s %s", + flags.BLADE, // Note: The first word in the Command.Use string is how Cobra defines the "name" of this "command". + flags.GetOptionUsageGroupServiceTcp(false), + flags.GetOptionUsageApplianceId(false), + flags.GetOptionUsageBladeId(false), + flags.GetOptionUsageMemoryId(false), + flags.GetOptionUsagePortId(false)) +} + +// GetCmdExampleUnassignBlade - Generates the command example string for the cobra.Command.Example field. +func GetCmdExampleUnassignBlade() string { + baseCmd := fmt.Sprintf("cfm unassign %s", flags.BLADE) + + shorthandFormat := fmt.Sprintf("%s %s %s %s %s %s", + baseCmd, + flags.GetOptionExampleShGroupServiceTcp(), + flags.GetOptionExampleShApplianceId(), + flags.GetOptionExampleShBladeId(), + flags.GetOptionExampleShMemoryId(), + flags.GetOptionExampleShPortId()) + + longhandFormat := fmt.Sprintf("%s %s %s %s %s %s", + baseCmd, + flags.GetOptionExampleLhGroupServiceTcp(), + flags.GetOptionExampleLhApplianceId(), + flags.GetOptionExampleLhBladeId(), + flags.GetOptionExampleLhMemoryId(), + flags.GetOptionExampleLhPortId()) + + return fmt.Sprintf(` + %s + + %s`, shorthandFormat, longhandFormat) +} diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 3ada062..5761b1f 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -51,6 +51,9 @@ docker exec -it cfm-container ./cfm-cli -h docker exec -it cfm-container ./cfm-cli list appliances ``` +NOTE: Currently, every cfm-cli command requires various tcpip options (e.g.: --service-net-ip) regarding the specific cfm-service that is being interacted with. +However, using the docker container, the user can rely on the default cli settings for these cfm-service options since they point to the cfm-service running within the same docker container. + ## Customization The developer could use the [DockerFile](../docker/Dockerfile) as a reference to build a new docker image using a local [cfm](https://github.com/Seagate/cfm) clone... From 5c63d28aa552b2ae9f0513a8627a155ea1459599 Mon Sep 17 00:00:00 2001 From: Mengling Ding <71745861+Meng-20@users.noreply.github.com> Date: Fri, 6 Dec 2024 12:32:11 -0700 Subject: [PATCH 3/5] feat(webui): device discovery in cfm webui (#58) * feat: set the nodes width based on the device id dimensions * feat: add ipaddress to the existed devices in dashboard * feat: add new button to discover new devices and the related interfaces * fix: fix the bug in addDiscoveredHosts interface * fix: fix bug in addDiscoveredBlades interface * feat: add function addDiscoveredDevices with output popup and waiting popup * style: separate lines for discovered blades and hosts * feat: add waiting progress for device discovery * feat: update the content after adding new discovered devices * feat: distinguish node status by border color * feat: prevent the user from manually adding blades to the CMA_Discovered_Blades appliance * style: remove unnecessary stype setup --- webui/src/components/Appliance/Appliances.vue | 14 +- webui/src/components/Dashboard/Dashboard.vue | 362 +++++++++++++++++- .../Dashboard/initial-control-elements.ts | 59 +-- .../Dashboard/initial-data-elements.ts | 61 +-- webui/src/components/Dashboard/useLayout.ts | 53 ++- webui/src/components/Stores/ApplianceStore.ts | 99 ++++- webui/src/components/Stores/HostStore.ts | 64 +++- 7 files changed, 625 insertions(+), 87 deletions(-) diff --git a/webui/src/components/Appliance/Appliances.vue b/webui/src/components/Appliance/Appliances.vue index 3651a4e..07fe502 100644 --- a/webui/src/components/Appliance/Appliances.vue +++ b/webui/src/components/Appliance/Appliances.vue @@ -93,6 +93,7 @@ variant="text" id="addBlade" @click="addNewBladeWindowButton" + :disabled="isAddBladeButtonDisabled" > mdi-plus-thick BLADE @@ -1524,6 +1525,12 @@ export default { ComposeMemoryButton, }, + computed: { + isAddBladeButtonDisabled() { + return this.selectedApplianceId === "CMA_Discovered_Blades"; + }, + }, + methods: { /* Open the add appliance popup */ addNewApplianceWindowButton() { @@ -1985,7 +1992,10 @@ export default { applianceStore.selectedApplianceId, newBladeId ), - bladeStore.fetchBladeById(applianceStore.selectedApplianceId, newBladeId), + bladeStore.fetchBladeById( + applianceStore.selectedApplianceId, + newBladeId + ), ]); // Update the URL with the new blade ID updateUrlWithBladeId(applianceStore.selectedApplianceId, newBladeId); @@ -2063,4 +2073,4 @@ export default { .highlighted-tab { font-weight: bold; } - \ No newline at end of file + diff --git a/webui/src/components/Dashboard/Dashboard.vue b/webui/src/components/Dashboard/Dashboard.vue index c0aa88c..07aaad5 100644 --- a/webui/src/components/Dashboard/Dashboard.vue +++ b/webui/src/components/Dashboard/Dashboard.vue @@ -3,7 +3,7 @@ - {{ buttonLabel }} - + + + + {{ buttonLabel }} + + + + + Click to discover new devices + +
+ + + + + mdi-magnify + + + + + + Devices + + + mdi-rectangle + CMA + + + + + mdi-rectangle + Blade + + + + + mdi-rectangle + Host + + +
Status + + + mdi-rectangle-outline + online + + + + + mdi-rectangle-outline + offline + + + + + mdi-rectangle-outline + unavailable + + +
+
+
- - - - mdi-magnify - - - + + + + + + New Discovered Blades: + +
+ New Discovered Hosts: + +
+ + + + Cancel + Add + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +

Congrats! New devices were added

+

+ New blades: +

    +
  • + {{ blade.id }} +
  • +

New hosts:
+
    +
  • + {{ host.id }} +
  • +
+

+ +
+ + Done + +
+
+
@@ -68,6 +265,110 @@ import { ControlButton, Controls } from "@vue-flow/controls"; export default { components: { VueFlow, ControlButton, Controls }, + data() { + return { + addNewDiscoveredDevicesProgressText: + "Adding the selected devices, please wait...", + discoverDevicesProgressText:"Discovering devices, please wait...", + + dialogNewDiscoveredDevices: false, + dialogAddNewDiscoveredDevicesWait: false, + dialogAddNewDiscoveredDevicesOutput: false, + dialogDiscoverDevicesWait: false, + + discoveredBlades: [], + discoveredHosts: [], + + selectedBlades: [], + selectedHosts: [], + + newBlades: [], + newHosts: [], + }; + }, + + methods: { + async discoverDevices() { + this.dialogDiscoverDevicesWait = true; + const applianceStore = useApplianceStore(); + const hostStore = useHostStore(); + + try { + const responseOfBlades = await applianceStore.discoverBlades(); + const responseOfHosts = await hostStore.discoverHosts(); + + const response = (responseOfBlades || []).concat(responseOfHosts || []); + this.discoveredBlades = responseOfBlades || []; + this.discoveredHosts = responseOfHosts || []; + + this.dialogNewDiscoveredDevices = true; + this.dialogDiscoverDevicesWait = false; + + return response.length ? response : []; + } catch (error) { + this.dialogDiscoverDevicesWait = false; + console.error("Error fetching data:", error); + return []; + } + }, + + async addDiscoveredDevices() { + this.dialogNewDiscoveredDevices = false; + this.dialogAddNewDiscoveredDevicesWait = true; + + const applianceStore = useApplianceStore(); + const hostStore = useHostStore(); + const bladePortStore = useBladePortStore(); + + if (this.selectedBlades.length === 0) { + console.log("No blades selected."); + } else { + for (var i = 0; i < this.selectedBlades.length; i++) { + try { + const newAddedBlade = await applianceStore.addDiscoveredBlades( + this.selectedBlades[i] + ); + + if (newAddedBlade) { + this.newBlades.push(newAddedBlade); + } + } catch (error) { + console.error("Error adding new discovered blade:", error); + } + } + } + + if (this.selectedHosts.length === 0) { + console.log("No hosts selected."); + } else { + for (var i = 0; i < this.selectedHosts.length; i++) { + try { + const newAddedHost = await hostStore.addDiscoveredHosts( + this.selectedHosts[i] + ); + if (newAddedHost) { + this.newHosts.push(newAddedHost); + } + } catch (error) { + console.error("Error adding new discovered host:", error); + } + } + } + + // Update the graph content + await applianceStore.fetchAppliances(); + await hostStore.fetchHosts(); + for (const appliance of applianceStore.applianceIds) { + for (const blade of appliance.blades) { + await bladePortStore.fetchBladePorts(appliance.id, blade.id); + } + } + + this.dialogAddNewDiscoveredDevicesWait = false; + this.dialogAddNewDiscoveredDevicesOutput = true; + }, + }, + setup() { const applianceStore = useApplianceStore(); const hostStore = useHostStore(); @@ -178,8 +479,8 @@ export default { await hostStore.fetchHosts(); // Ensure blade ports are fetched after appliances, this action will create the edges for dataPlane for (const appliance of applianceStore.applianceIds) { - for (const bladeId of appliance.bladeIds) { - await bladePortStore.fetchBladePorts(appliance.id, bladeId); + for (const blade of appliance.blades) { + await bladePortStore.fetchBladePorts(appliance.id, blade.id); } } }); @@ -200,3 +501,22 @@ export default { }, }; + + diff --git a/webui/src/components/Dashboard/initial-control-elements.ts b/webui/src/components/Dashboard/initial-control-elements.ts index 5ba362a..0d97900 100644 --- a/webui/src/components/Dashboard/initial-control-elements.ts +++ b/webui/src/components/Dashboard/initial-control-elements.ts @@ -23,7 +23,7 @@ export const useControlData = () => { id: "cfm-service", data: { label: "CFM Service", }, position: position, - style: { backgroundColor: "#6ebe4a", color: "#000" }, + style: { backgroundColor: useLayout().Colors.serviceColor, border: "none" }, type: serviceNodeType, }, ] @@ -35,26 +35,33 @@ export const useControlData = () => { id: `appliance-${appliance.id}`, data: { label: appliance.id, url: `/appliances/${appliance.id}` }, position: position, - style: { backgroundColor: "#f2ae72", color: "#000" }, + style: { backgroundColor: useLayout().Colors.applianceColor, border: "none" }, type: applianceNodeType, }, - ...appliance.bladeIds.map((bladeId, bladeIndex) => ({ - id: `blade-${bladeId}`, - data: { label: bladeId, url: `/appliances/${appliance.id}/blades/${bladeId}`, associatedAppliance: appliance.id }, - position: position, - style: { backgroundColor: "#f2e394", color: "#000" }, - type: bladeNodeType, - })), + ...appliance.blades.map((blade, bladeIndex) => { + const borderColor = useLayout().borderColorChange(blade.status); + return { + id: `blade-${blade.id}`, + data: { label: blade.id, url: `/appliances/${appliance.id}/blades/${blade.id}`, associatedAppliance: appliance.id }, + position: position, + style: { backgroundColor: useLayout().Colors.baldeColor, border: `3px solid ${borderColor}` }, + type: bladeNodeType, + } + }), ] ); - const hostNodes = hostStore.hostIds.map((host, index) => ({ - id: `host-${host}`, - data: { label: host, url: `/hosts/${host}` }, - position: position, - style: { backgroundColor: "#d9ecd0", color: "#000" }, - type: hostNodeType, - })); + const hostNodes = hostStore.hostIds.map((host, index) => { + const borderColor = useLayout().borderColorChange(host.status); + + return { + id: `host-${host.id}`, + data: { label: host.id, url: `/hosts/${host.id}` }, + position: position, + style: { backgroundColor: useLayout().Colors.hostColor, border: `3px solid ${borderColor}` }, + type: hostNodeType, + } + }); const allNodes = [...coreNode, ...applianceNodes, ...hostNodes]; @@ -67,18 +74,18 @@ export const useControlData = () => { source: "cfm-service", target: `appliance-${appliance.id}`, }, - ...appliance.bladeIds.map((bladeId) => ({ - id: `appliance-blade-${appliance.id}-${bladeId}`, + ...appliance.blades.map((blade) => ({ + id: `appliance-blade-${appliance.id}-${blade.id}`, source: `appliance-${appliance.id}`, - target: `blade-${bladeId}`, + target: `blade-${blade.id}`, })), ]) : []; const hostEdges = hostStore.hostIds.map((host) => ({ - id: `cfm-${host}`, + id: `cfm-${host.id}`, source: "cfm-service", - target: `host-${host}`, + target: `host-${host.id}`, })); return [...coreEdges, ...hostEdges]; @@ -97,19 +104,19 @@ export const useControlData = () => { target: `appliance-${appliance.id}`, animated: true, }, - ...appliance.bladeIds.map((bladeId) => ({ - id: `appliance-blade-${appliance.id}-${bladeId}`, + ...appliance.blades.map((blade) => ({ + id: `appliance-blade-${appliance.id}-${blade.id}`, source: `appliance-${appliance.id}`, - target: `blade-${bladeId}`, + target: `blade-${blade.id}`, animated: true, })), ]) : []; const hostEdges = hostStore.hostIds.map((host) => ({ - id: `cfm-${host}`, + id: `cfm-${host.id}`, source: "cfm-service", - target: `host-${host}`, + target: `host-${host.id}`, animated: true, })); diff --git a/webui/src/components/Dashboard/initial-data-elements.ts b/webui/src/components/Dashboard/initial-data-elements.ts index 28e4538..c4ec05e 100644 --- a/webui/src/components/Dashboard/initial-data-elements.ts +++ b/webui/src/components/Dashboard/initial-data-elements.ts @@ -3,6 +3,7 @@ import { computed } from "vue"; import { useApplianceStore } from "../Stores/ApplianceStore"; import { useHostStore } from "../Stores/HostStore"; import { useBladePortStore } from "../Stores/BladePortStore"; +import { useLayout } from "./useLayout"; export const useData = () => { const applianceStore = useApplianceStore(); @@ -17,7 +18,7 @@ export const useData = () => { let currentYPosition = 0; const applianceNodes = applianceStore.applianceIds.flatMap( (appliance, index) => { - const bladeCount = appliance.bladeIds.length; + const bladeCount = appliance.blades.length; const applianceHeight = 50 + bladeCount * 50; // Adjust height based on number of blades const applianceWidth = 270; // Width of the appliance node const bladeWidth = 250; // Width of the blade node @@ -27,24 +28,27 @@ export const useData = () => { id: `appliance-${appliance.id}`, data: { label: appliance.id, url: `/appliances/${appliance.id}` }, position: { x: 100, y: currentYPosition }, - style: { backgroundColor: "rgba(242, 174, 114, 0.5)", color: "#000", height: `${applianceHeight}px`, width: `${applianceWidth}px` }, + style: { backgroundColor: useLayout().Colors.applianceColor, height: `${applianceHeight}px`, width: `${applianceWidth}px`, border: "none" }, type: applianceNodeType, sourcePosition: 'right', targetPosition: 'left', }; - const bladeNodes = appliance.bladeIds.map((bladeId, bladeIndex) => ({ - id: `blade-${bladeId}`, - data: { label: bladeId, url: `/appliances/${appliance.id}/blades/${bladeId}`, associatedAppliance: appliance.id }, - position: { x: bladeXPosition, y: 50 + bladeIndex * 50 }, // Center blades within the appliance node - style: { backgroundColor: "#f2e394", color: "#000", width: `${bladeWidth}px` }, - type: bladeNodeType, - parentNode: `appliance-${appliance.id}`, - extent: 'parent', - expandParent: true, - sourcePosition: 'right', - targetPosition: 'left', - })); + const bladeNodes = appliance.blades.map((blade, bladeIndex) => { + const borderColor = useLayout().borderColorChange(blade.status); + return { + id: `blade-${blade.id}`, + data: { label: blade.id, url: `/appliances/${appliance.id}/blades/${blade.id}`, associatedAppliance: appliance.id }, + position: { x: bladeXPosition, y: 50 + bladeIndex * 50 }, // Center blades within the appliance node + style: { backgroundColor: useLayout().Colors.baldeColor, width: `${bladeWidth}px`, border: `3px solid ${borderColor}` }, + type: bladeNodeType, + parentNode: `appliance-${appliance.id}`, + extent: 'parent', + expandParent: true, + sourcePosition: 'right', + targetPosition: 'left', + } + }); currentYPosition += applianceHeight + 20; // Add some space between nodes @@ -52,15 +56,26 @@ export const useData = () => { } ); - const hostNodes = hostStore.hostIds.map((host, index) => ({ - id: `host-${host}`, - data: { label: host, url: `/hosts/${host}` }, - position: { x: 500, y: index * 200 }, - style: { backgroundColor: "#d9ecd0", color: "#000" }, - type: hostNodeType, - sourcePosition: 'right', - targetPosition: 'left', - })); + const hostNodes = hostStore.hostIds.map((host, index) => { + const { width, height } = useLayout().measureText(host.id); + const borderColor = useLayout().borderColorChange(host.status); + + return { + id: `host-${host.id}`, + data: { label: host.id, url: `/hosts/${host.id}` }, + position: { x: 500, y: index * 200 }, + style: { + backgroundColor: useLayout().Colors.hostColor, + color: "#000", + width: `${width + 20}px`, // Adding some padding + height: `${height + 20}px`, // Adding some padding + border: `3px solid ${borderColor}` + }, + type: hostNodeType, + sourcePosition: 'right', + targetPosition: 'left', + }; + }); return [...applianceNodes, ...hostNodes]; }); diff --git a/webui/src/components/Dashboard/useLayout.ts b/webui/src/components/Dashboard/useLayout.ts index acf3506..e6b6a4e 100644 --- a/webui/src/components/Dashboard/useLayout.ts +++ b/webui/src/components/Dashboard/useLayout.ts @@ -7,6 +7,39 @@ import { ref } from 'vue'; * Composable to run the layout algorithm on the graph. * It uses the `dagre` library to calculate the layout of the nodes and edges. */ + +export function measureText(text: string, font = '16px Arial') { + const canvas = document.createElement('canvas'); + const context = canvas.getContext('2d'); + context!.font = font; + const metrics = context!.measureText(text); + const width = Math.max(metrics.width, 220); + return { + width: width, + height: parseInt(font, 10) // Assuming height is roughly the font size + }; +}; + +export const Colors = { + applianceColor: '#f2ae72', + baldeColor: '#f2e394', + hostColor: '#d9ecd0', + serviceColor: '#6ebe4a', +}; + +export function borderColorChange(status: string | undefined) { + switch (status) { + case "online": + return "#6ebe4a"; // green + case "offline": + return "#b00020"; // Red + case "unavailable": + return "#ff9f40"; // Orange + default: + return "#ffffff"; // White + } +}; + export function useLayout() { const { findNode } = useVueFlow(); @@ -28,10 +61,13 @@ export function useLayout() { previousDirection.value = direction; for (const node of nodes) { - // Use the dimensions property of the internal node (`GraphNode` type) - const graphNode = findNode(node.id); + // Measure the text dimensions for dynamic sizing + const { width, height } = measureText(node.data.label); - dagreGraph.setNode(node.id, { width: graphNode?.dimensions.width || 150, height: graphNode?.dimensions.height || 50 }); + dagreGraph.setNode(node.id, { + width: width + 20, // Adding some padding + height: height + 20 // Adding some padding + }); } for (const edge of edges) { @@ -41,7 +77,7 @@ export function useLayout() { dagre.layout(dagreGraph); // Set nodes with updated positions - return nodes.map((node: { id: string | dagre.Label; }) => { + return nodes.map((node: { id: string | dagre.Label; style: any; }) => { const nodeWithPosition = dagreGraph.node(node.id); return { @@ -49,9 +85,14 @@ export function useLayout() { targetPosition: isHorizontal ? Position.Left : Position.Top, sourcePosition: isHorizontal ? Position.Right : Position.Bottom, position: { x: nodeWithPosition.x, y: nodeWithPosition.y }, + style: { + ...node.style, + width: `${nodeWithPosition.width}px`, + height: `${nodeWithPosition.height}px` + } }; }); } - return { graph, layout, previousDirection }; -} + return { graph, layout, previousDirection, measureText, borderColorChange, Colors }; +} \ No newline at end of file diff --git a/webui/src/components/Stores/ApplianceStore.ts b/webui/src/components/Stores/ApplianceStore.ts index 661f777..b1ad0d3 100644 --- a/webui/src/components/Stores/ApplianceStore.ts +++ b/webui/src/components/Stores/ApplianceStore.ts @@ -1,6 +1,6 @@ // Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates import { defineStore } from 'pinia' -import { Appliance, Credentials, DefaultApi } from "@/axios/api"; +import { Appliance, Credentials, DefaultApi, DiscoveredDevice } from "@/axios/api"; import { BASE_PATH } from "@/axios/base"; import axios from 'axios'; @@ -14,7 +14,30 @@ export const useApplianceStore = defineStore('appliance', { addApplianceError: null as unknown, deleteApplianceError: null as unknown, renameApplianceError: null as unknown, - applianceIds: [] as { id: string, bladeIds: string[] }[], + applianceIds: [] as { id: string, blades: { id: string, ipAddress: string, status: string | undefined }[] }[], + discoveredBlades: [] as DiscoveredDevice[], + + prefixBladeId: "Discoverd_Blade_", + newBladeCredentials: { + username: "root", + password: "0penBmc", + ipAddress: "127.0.0.1", + port: 443, + insecure: true, + protocol: "https", + customId: "", + }, + + defaultApplianceId: "CMA_Discovered_Blades", + newApplianceCredentials: { + username: "root", + password: "0penBmc", + ipAddress: "127.0.0.1", + port: 8443, + insecure: true, + protocol: "https", + customId: "", + }, }), actions: { @@ -72,7 +95,7 @@ export const useApplianceStore = defineStore('appliance', { const responseOfBlades = await defaultApi.bladesGet(applianceId); const bladeCount = responseOfBlades.data.memberCount; - const bladeIds = []; + const associatedBlades = []; for (let i = 0; i < bladeCount; i++) { // Extract the id for each blade @@ -80,10 +103,12 @@ export const useApplianceStore = defineStore('appliance', { const bladeId: string = JSON.stringify(uri).split("/").pop()?.slice(0, -2) as string; // Store blade in blades if (bladeId) { - bladeIds.push(bladeId); + const responseOfBlade = await defaultApi.bladesGetById(applianceId, bladeId); + const response = { id: responseOfBlade.data.id, ipAddress: responseOfBlade.data.ipAddress, status: responseOfBlade.data.status } + associatedBlades.push(response); } } - this.applianceIds.push({ id: detailsResponseOfAppliance.data.id, bladeIds }); + this.applianceIds.push({ id: detailsResponseOfAppliance.data.id, blades: associatedBlades }); } } } catch (error) { @@ -91,13 +116,75 @@ export const useApplianceStore = defineStore('appliance', { } }, + async discoverBlades() { + try { + // Get all the existed blades + const existedBladeIpAddress: (string | undefined)[] = [] + for (var i = 0; i < this.applianceIds.length; i++) { + for (var j = 0; j < this.applianceIds[i].blades.length; j++) { + existedBladeIpAddress.push(this.applianceIds[i].blades[j].ipAddress) + } + } + + const defaultApi = new DefaultApi(undefined, API_BASE_PATH); + this.discoveredBlades = []; + const responseOfBlade = await defaultApi.discoverDevices("blade"); + this.discoveredBlades = responseOfBlade.data; + + // Remove the existed blades from the discovered blades + for (var k = 0; k < this.discoveredBlades.length; k++) { + for (var m = 0; m < existedBladeIpAddress.length; m++) { + this.discoveredBlades = this.discoveredBlades.filter( + (discoveredBlade) => discoveredBlade.address !== existedBladeIpAddress[m] + ); + } + } + + return this.discoveredBlades + } catch (error) { + console.error("Error discovering new devices:", error); + } + }, + + async addDiscoveredBlades(blade: DiscoveredDevice) { + const defaultApi = new DefaultApi(undefined, API_BASE_PATH); + const responseOfApplianceExist = await defaultApi.appliancesGetById(this.defaultApplianceId) + + // If there is no default appliance, add one + if (!responseOfApplianceExist) { + this.newApplianceCredentials.customId = this.defaultApplianceId; + const responseOfAppliance = await defaultApi.appliancesPost(this.newApplianceCredentials); + + // Add the new appliance to the appliances and applianceIds array + if (responseOfAppliance) { + this.appliances.push(responseOfAppliance.data); + const newAppliance = { id: responseOfAppliance.data.id, blades: [] } + this.applianceIds.push(newAppliance) + } + } + + // Add the new discovered blade to the default appliance + let appliance = this.applianceIds.find(appliance => appliance.id === this.defaultApplianceId); + + this.newBladeCredentials.customId = this.prefixBladeId + blade.address; + this.newBladeCredentials.ipAddress = blade.address + ""; + + const responseOfBlade = await defaultApi.bladesPost(this.defaultApplianceId, this.newBladeCredentials); + + if (responseOfBlade) { + const response = { id: responseOfBlade.data.id, ipAddress: responseOfBlade.data.ipAddress, status: responseOfBlade.data.status }; + appliance!.blades.push(response); + } + + return responseOfBlade.data; + }, + async addNewAppliance(newAppliance: Credentials) { this.addApplianceError = ""; try { const defaultApi = new DefaultApi(undefined, API_BASE_PATH); const response = await defaultApi.appliancesPost(newAppliance); const addedAppliance = response.data; - console.log("added appliance", addedAppliance) // Add the new appliance to the appliances array this.appliances.push(addedAppliance); return addedAppliance; diff --git a/webui/src/components/Stores/HostStore.ts b/webui/src/components/Stores/HostStore.ts index 5051c54..ae3e44a 100644 --- a/webui/src/components/Stores/HostStore.ts +++ b/webui/src/components/Stores/HostStore.ts @@ -1,6 +1,6 @@ // Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates import { defineStore } from 'pinia' -import { Host, Credentials, DefaultApi } from "@/axios/api"; +import { Host, Credentials, DefaultApi, DiscoveredDevice } from "@/axios/api"; import { BASE_PATH } from "@/axios/base"; import axios from 'axios'; @@ -19,7 +19,19 @@ export const useHostStore = defineStore('host', { deleteHostError: null as unknown, resyncHostError: null as unknown, renameHostError: null as unknown, - hostIds: [] as string[], + hostIds: [] as { id: string, ipAddress: string, status: string | undefined }[], + discoveredHosts: [] as DiscoveredDevice[], + + prefixHostId: "Discoverd_Host_", + newHostCredentials: { + username: "admin", + password: "admin12345", + ipAddress: "127.0.0.1", + port: 8082, + insecure: true, + protocol: "http", + customId: "", + }, }), actions: { @@ -44,7 +56,8 @@ export const useHostStore = defineStore('host', { // Store host in hosts if (detailsResponseOfHost) { this.hosts.push(detailsResponseOfHost.data); - this.hostIds.push(detailsResponseOfHost.data.id) + const host = { id: detailsResponseOfHost.data.id, ipAddress: detailsResponseOfHost.data.ipAddress, status: detailsResponseOfHost.data.status } + this.hostIds.push(host) } } } catch (error) { @@ -68,6 +81,51 @@ export const useHostStore = defineStore('host', { } }, + async discoverHosts() { + try { + // Get all the existed hosts + const existedHostIpAddress: (string | undefined)[] = [] + for (var i = 0; i < this.hostIds.length; i++) { + existedHostIpAddress.push(this.hostIds[i].ipAddress) + } + + const defaultApi = new DefaultApi(undefined, API_BASE_PATH); + this.discoveredHosts = []; + const responseOfHost = await defaultApi.discoverDevices("cxl-host"); + this.discoveredHosts = responseOfHost.data; + + // Remove the existed hosts from the discovered hosts + for (var k = 0; k < this.discoveredHosts.length; k++) { + for (var m = 0; m < existedHostIpAddress.length; m++) { + this.discoveredHosts = this.discoveredHosts.filter( + (discoveredHost) => discoveredHost.address !== existedHostIpAddress[m] + ); + } + } + + return this.discoveredHosts + } catch (error) { + console.error("Error discovering new devices:", error); + } + }, + + async addDiscoveredHosts(host: DiscoveredDevice) { + const defaultApi = new DefaultApi(undefined, API_BASE_PATH); + // Add all the new didcovered hosts + this.newHostCredentials.customId = this.prefixHostId + host.address; + this.newHostCredentials.ipAddress = host.address + ""; + + const responseOfHost = await defaultApi.hostsPost(this.newHostCredentials); + + // Update the applianceIds and appliances + if (responseOfHost) { + const response = { id: responseOfHost.data.id, ipAddress: responseOfHost.data.ipAddress, status: responseOfHost.data.status } + this.hosts.push(responseOfHost.data); + this.hostIds.push(response) + } + return responseOfHost.data; + }, + async addNewHost(newHost: Credentials) { this.addHostError = ""; try { From 5a6dc7e02745e9750c088235e205c0fac950234e Mon Sep 17 00:00:00 2001 From: Mengling Ding <71745861+Meng-20@users.noreply.github.com> Date: Fri, 6 Dec 2024 15:23:22 -0700 Subject: [PATCH 4/5] fix(docker): update the docker running command to make D-bus available using privileged mode (#60) * feat: update the docker running command to make D-bus available ay priviledged mode * fix: update the docker running command to make D-bus available using privileged mode --- docs/DOCKER.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 5761b1f..75816a0 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -17,7 +17,7 @@ If desired, the user can add `:vX.X.X` to the end of the command to obtain an ol To enable the webui launching during cfm-service startup, the user must provide the `-webui` flag in the command below. ```bash -docker run --restart unless-stopped --network=host --name --detach ghcr.io/seagate/cfm -webui -verbosity 4 +docker run --restart unless-stopped --network=host --name --detach --privileged -v /var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket ghcr.io/seagate/cfm -webui -verbosity 4 ``` By default, the cfm-service will be hosted at port 8080 and the webui will be hosted at port 3000. The user could change the port by input argument -Port and/or -webuiPort. The webui only works with --network=host mode. @@ -66,5 +66,5 @@ docker build --no-cache -t -f docker/Dockerfile . ...and then run those changes ```bash -docker run --restart unless-stopped --network=host --name --detach -webui -verbosity 4 +docker run --restart unless-stopped --network=host --name --detach --privileged -v /var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket -webui -verbosity 4 ``` From 4faa2261c60276d9db734dc831eb21b53e0722dd Mon Sep 17 00:00:00 2001 From: Scott Howe Date: Fri, 6 Dec 2024 16:51:09 -0700 Subject: [PATCH 5/5] fix(service): fix error handling bugs during add blade\host (#61) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Found a couple bugs in appliance.AddBlade() and manager.AddHost(). (1) corner case where an empty bladeId is being allowed, when it shouldn't (2) Not enough information is being saved to new blade objects during error handling.  This was causing a panic during the delete because the backendOps variable was a nil point being used to call a backend function. --- pkg/manager/appliance.go | 22 ++++++++++++++++++++-- pkg/manager/manager.go | 35 +++++++++++++++++++++++++++-------- 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/pkg/manager/appliance.go b/pkg/manager/appliance.go index e52c672..f35e057 100644 --- a/pkg/manager/appliance.go +++ b/pkg/manager/appliance.go @@ -5,6 +5,7 @@ package manager import ( "context" "fmt" + "strings" "github.com/google/uuid" "k8s.io/klog/v2" @@ -97,12 +98,27 @@ func (a *Appliance) AddBlade(ctx context.Context, c *openapi.Credentials) (*Blad newErr := fmt.Errorf("create session failure at [%s:%d] using interface [%s]: %w", c.IpAddress, c.Port, backendName, err) logger.Error(newErr, "failure: add blade") + bladeId := c.CustomId + if bladeId == "" { // Order CustomeId > BladeSN > UUID + bladeId = response.ChassisSN + if bladeId == "" { + // Generate default id using last N digits of a randomly generated uuid combined with the default prefix + // Example uuid: ee0328d9-258a-4e81-976e-b75aa4a2d8f5 + uuid := uuid.New().String() + uuid = strings.ReplaceAll(uuid, "-", "") + bladeId = fmt.Sprintf("%s-%s", ID_PREFIX_BLADE_DFLT, uuid[(len(uuid)-common.NumUuidCharsForId):]) + } + c.CustomId = bladeId + } + // Continue adding the failed blade to the datastore, but update the connection status to unavailable newBlade := &Blade{ Id: c.CustomId, Uri: GetCfmUriBladeId(a.Id, c.CustomId), Status: common.UNAVAILABLE, ApplianceId: a.Id, + backendOps: ops, + creds: c, } a.Blades[newBlade.Id] = newBlade @@ -171,6 +187,8 @@ func (a *Appliance) AddBlade(ctx context.Context, c *openapi.Credentials) (*Blad Uri: GetCfmUriBladeId(a.Id, c.CustomId), Status: common.UNAVAILABLE, ApplianceId: a.Id, + backendOps: ops, + creds: c, } a.Blades[newBlade.Id] = newBlade @@ -295,11 +313,11 @@ func (a *Appliance) DeleteBladeById(ctx context.Context, bladeId string) (*Blade blade, err := a.DeleteBladeByIdBackend(ctx, bladeId) if err != nil || blade == nil { - logger.V(2).Info("success: delete blade by id after backend session failure", "bladeId", blade.Id, "applianceId", a.Id) + logger.V(2).Info("success: delete blade by id after backend session failure", "bladeId", bladeId, "applianceId", a.Id) return blade, err } - logger.V(2).Info("success: delete blade by id", "bladeId", blade.Id, "applianceId", a.Id) + logger.V(2).Info("success: delete blade by id", "bladeId", bladeId, "applianceId", a.Id) return blade, nil } diff --git a/pkg/manager/manager.go b/pkg/manager/manager.go index ac26076..0e94041 100644 --- a/pkg/manager/manager.go +++ b/pkg/manager/manager.go @@ -5,7 +5,9 @@ package manager import ( "context" "fmt" + "strings" + "github.com/google/uuid" "k8s.io/klog/v2" "cfm/pkg/backend" @@ -281,11 +283,26 @@ func AddHost(ctx context.Context, c *openapi.Credentials) (*Host, error) { newErr := fmt.Errorf("create session failure at [%s:%d] using interface [%s]: %w", c.IpAddress, c.Port, backendName, err) logger.Error(newErr, "failure: add host") + hostId := c.CustomId + if hostId == "" { // Order CustomeId > HostSN > UUID + hostId = response.ChassisSN + if hostId == "" { + // Generate default id using last N digits of the session id combined with the default prefix + // Example uuid: ee0328d9-258a-4e81-976e-b75aa4a2d8f5 + uuid := uuid.New().String() + uuid = strings.ReplaceAll(uuid, "-", "") + hostId = fmt.Sprintf("%s-%s", ID_PREFIX_HOST_DFLT, uuid[(len(uuid)-common.NumUuidCharsForId):]) + } + c.CustomId = hostId + } + // Continue adding the failed host to the datastore, but update the connection status to unavailable host := &Host{ - Id: c.CustomId, - Uri: GetCfmUriHostId(c.CustomId), - Status: common.UNAVAILABLE, + Id: c.CustomId, + Uri: GetCfmUriHostId(c.CustomId), + Status: common.UNAVAILABLE, + backendOps: ops, + creds: c, } deviceCache.AddHost(host, false) @@ -348,9 +365,11 @@ func AddHost(ctx context.Context, c *openapi.Credentials) (*Host, error) { // Continue adding the failed host to the datastore, but update the connection status to unavailable host := &Host{ - Id: c.CustomId, - Uri: GetCfmUriHostId(c.CustomId), - Status: common.UNAVAILABLE, + Id: c.CustomId, + Uri: GetCfmUriHostId(c.CustomId), + Status: common.UNAVAILABLE, + backendOps: ops, + creds: c, } deviceCache.AddHost(host, false) @@ -487,11 +506,11 @@ func DeleteHostById(ctx context.Context, hostId string) (*Host, error) { host, err := DeleteHostByIdBackend(ctx, hostId) if err != nil || host == nil { - logger.V(2).Info("success: delete host by id after backend session failure", "hostId", host.Id) + logger.V(2).Info("success: delete host by id after backend session failure", "hostId", hostId) return host, err } - logger.V(2).Info("success: delete host by id", "hostId", host.Id) + logger.V(2).Info("success: delete host by id", "hostId", hostId) return host, nil }