package resources import ( "fmt" "strings" "xcecli/common/output" "xcecli/common/request" "xcecli/config" "xcecli/models" logger "xcecli/config" resource_filter "xcecli/filters" "github.com/spf13/cobra" "go.uber.org/zap" ) type ResourcesDistQuerier struct { configManager *config.ConfigManager filter *resource_filter.ResourceDistFilter fields string distType string jsonOutput bool noHeader bool verbose bool wideFormat bool // wide format } func NewResourcesDistQuerier(configManager *config.ConfigManager) *ResourcesDistQuerier { return &ResourcesDistQuerier{ configManager: configManager, filter: &resource_filter.ResourceDistFilter{}, } } func (rq *ResourcesDistQuerier) ParseCmdArgs(cmd *cobra.Command, args []string) error { // No additional processing needed, all parameters are bound in flags if len(args) > 0 { rq.filter.ResName = strings.Join(args, ",") } // Define mutual exclusions for verbose flag if rq.verbose { conflicts := make([]string, 0) if rq.wideFormat { conflicts = append(conflicts, "--wided (-w)") } if len(conflicts) > 0 { return fmt.Errorf("flag --verbose (-l) cannot be used with: %s\n\n%s", strings.Join(conflicts, ", "), cmd.UsageString()) } } return nil } // ResourcesDefQuerier handles querying resource definition information type ResourcesDefQuerier struct { configManager *config.ConfigManager filter *resource_filter.ResourceDefFilter fields string jsonOutput bool noHeader bool verbose bool wideFormat bool // wide format } func NewResourcesDefQuerier(configManager *config.ConfigManager) *ResourcesDefQuerier { return &ResourcesDefQuerier{ configManager: configManager, filter: &resource_filter.ResourceDefFilter{}, } } func (rq *ResourcesDefQuerier) ParseCmdArgs(cmd *cobra.Command, args []string) error { // No additional processing needed, all parameters are bound in flags if len(args) > 0 { rq.filter.ResName = strings.Join(args, ",") } // Define mutual exclusions for verbose flag if rq.verbose { conflicts := make([]string, 0) if rq.wideFormat { conflicts = append(conflicts, "--wided (-w)") } if len(conflicts) > 0 { return fmt.Errorf("flag --verbose (-l) cannot be used with: %s\n\n%s", strings.Join(conflicts, ", "), cmd.UsageString()) } } return nil } // NewListResourcesDefCmd creates a command for listing resource definition information func NewListResourcesDefCmd(configManager *config.ConfigManager) *cobra.Command { resourcesDefQuerier := NewResourcesDefQuerier(configManager) cmd := &cobra.Command{ Use: "list", Short: "Query the resources definition info", Long: `Query the resources definition info. Example: xce resources definition list # query the definition info`, RunE: func(cmd *cobra.Command, args []string) error { if err := resourcesDefQuerier.ParseCmdArgs(cmd, args); err != nil { fmt.Println(strings.ToLower(err.Error())) return nil } if err := resourcesDefQuerier.RunResourcesDefList(); err != nil { fmt.Println(strings.ToLower(err.Error())) return nil } return nil }, } // Add command line parameters flags := cmd.Flags() flags.BoolVarP(&resourcesDefQuerier.verbose, "verbose", "l", false, "show resource detail") flags.BoolVarP(&resourcesDefQuerier.wideFormat, "wided", "w", false, "output in wide format") flags.BoolVarP(&resourcesDefQuerier.jsonOutput, "json", "", false, "output in JSON format") flags.BoolVarP(&resourcesDefQuerier.noHeader, "noheader", "n", false, "output no header") return cmd } func (rq *ResourcesDefQuerier) RunResourcesDefList() error { // Get configuration // Get server information serverInfo, _ := rq.configManager.GetServerInfo() // create API client apiClient := request.NewAPIClient(serverInfo.URL) apiClient.SetRootCAs(serverInfo.CACert) // Create query parameters params := make(map[string]string) // Get filter string filterStr, err := rq.filter.HandleResourceDefFilter() if err != nil { errMsg := request.ExtractErrorMessage(err) return fmt.Errorf("failed to get filter: [%s]", errMsg) } else if filterStr != "" { params["filter"] = filterStr } // Field mapping fieldsMap := models.ResourceDefFieldsMap if rq.verbose && rq.fields == "" { for header := range fieldsMap { rq.fields += header + "," } rq.fields = strings.TrimSuffix(rq.fields, ",") } // Process field selection if rq.fields != "" { rq.fields = request.CleanFields(rq.fields) mappedFields, err := request.CheckAndMapFields(rq.fields, fieldsMap) if err != nil { return err } params["fields"] = strings.Join(mappedFields, ",") } // create page printer pagePrinter := output.NewPagePrinter(output.DataTypeResourcesDef, output.WithFields(rq.fields), output.WithFormatJson(rq.jsonOutput), output.WithNoHeader(rq.noHeader), output.WithVerbose(rq.verbose), output.WithWideFormat(rq.wideFormat)) // Record if the first page has been processed isFirstPage := true var ( resp models.ResourceDefResponse loopCount int pageToken string ) // Loop to get all pages of data for { if loopCount > 0 && pageToken == "" { break } err = apiClient.GetRequestFromAPIServer(serverInfo.Token, pageToken, serverInfo.CACert, "/xce/v1/resources/definition", &resp, params) if err != nil { return err } if len(resp.Data.Rows) == 0 { if isFirstPage { fmt.Println("no resource definition found") return nil } } // Process the first page if isFirstPage { // Start printing process pagePrinter.StartPrinting() // Prepare for printing, calculate column width and table header, etc. pagePrinter.PrepareForPrinting(&resp) isFirstPage = false } // Print the current page pagePrinter.PrintPage(&resp) // Update page token and loop counter pageToken = resp.Data.Page_token loopCount++ } // Finish printing pagePrinter.Finish() logger.Debug("resource definition response completed", zap.Int("pages", loopCount)) return nil } // NewListResourcesDistCmd creates a command for listing resource distribution information func NewListResourcesDistCmd(configManager *config.ConfigManager) *cobra.Command { resourcesDistQuerier := NewResourcesDistQuerier(configManager) cmd := &cobra.Command{ Use: "list", Short: "Query the resources distribution info", Long: `Query the resources info, support static and dynamic type. Example: xce resources distribution list # query the distribution info`, RunE: func(cmd *cobra.Command, args []string) error { if err := resourcesDistQuerier.ParseCmdArgs(cmd, args); err != nil { fmt.Println(strings.ToLower(err.Error())) return nil } if err := resourcesDistQuerier.RunResourcesDistList(); err != nil { fmt.Println(strings.ToLower(err.Error())) return nil } return nil }, } // Add command line parameters flags := cmd.Flags() flags.StringVarP(&resourcesDistQuerier.distType, "type", "t", "", "static or dynamic") flags.BoolVarP(&resourcesDistQuerier.wideFormat, "wided", "w", false, "output in wide format") flags.BoolVarP(&resourcesDistQuerier.jsonOutput, "json", "", false, "output in JSON format") flags.BoolVarP(&resourcesDistQuerier.noHeader, "noheader", "n", false, "output no header") return cmd } func (rq *ResourcesDistQuerier) RunResourcesDistList() error { // Get configuration // Get server information serverInfo, _ := rq.configManager.GetServerInfo() // create API client apiClient := request.NewAPIClient(serverInfo.URL) apiClient.SetRootCAs(serverInfo.CACert) // Create query parameters params := make(map[string]string) // Get filter string filterStr, err := rq.filter.HandleResourceDistFilter() if rq.distType != "" { if rq.distType != "static" && rq.distType != "dynamic" { return fmt.Errorf("invalid type, must be 'static' or 'dynamic'") } else { params["type"] = rq.distType } } if err != nil { errMsg := request.ExtractErrorMessage(err) return fmt.Errorf("failed to get filter: [%s]", errMsg) } else if filterStr != "" { params["filter"] = filterStr } // Field mapping fieldsMap := models.ResourceDistFieldsMap if rq.verbose && rq.fields == "" { for header := range fieldsMap { rq.fields += header + "," } rq.fields = strings.TrimSuffix(rq.fields, ",") } // Process field selection if rq.fields != "" { rq.fields = request.CleanFields(rq.fields) mappedFields, err := request.CheckAndMapFields(rq.fields, fieldsMap) if err != nil { return err } params["fields"] = strings.Join(mappedFields, ",") } // create page printer pagePrinter := output.NewPagePrinter(output.DataTypeResourcesDist, output.WithFields(rq.fields), output.WithFormatJson(rq.jsonOutput), output.WithNoHeader(rq.noHeader), output.WithVerbose(rq.verbose), output.WithWideFormat(rq.wideFormat)) // Record if the first page has been processed isFirstPage := true var ( resp models.ResourceDistResponse loopCount int pageToken string ) // Loop to get all pages of data for { if loopCount > 0 && pageToken == "" { break } err = apiClient.GetRequestFromAPIServer(serverInfo.Token, pageToken, serverInfo.CACert, "/xce/v1/resources/distribution", &resp, params) if err != nil { return err } if len(resp.Data.Rows) == 0 { if isFirstPage { fmt.Println("no resource found") return nil } } // Process the first page if isFirstPage { // Start printing process pagePrinter.StartPrinting() // Prepare for printing, calculate column width and table header, etc. pagePrinter.PrepareForPrinting(&resp) isFirstPage = false } // Print the current page pagePrinter.PrintPage(&resp) // Update page token and loop counter pageToken = resp.Data.Page_token loopCount++ } // Finish printing pagePrinter.Finish() logger.Debug("resource dirstribution response completed", zap.Int("pages", loopCount)) return nil }