// node_capacity.go implements `orca node capacity` for v0.2 P02. // The capacity declaration is per-node (cpu_millicores, memory_mib, // disk_mib) and feeds the bin-packing scheduler. // // REQ-028: HCL/YAML schema for NodeCapacity — the CLI accepts the // three numeric flags and writes a row to the `node_capacity` table. // A future enhancement can read `~/.orca/node.hcl` at join time // (out of scope for P02). package cli import ( "context" "fmt" "time" "github.com/spf13/cobra" "git.cloudinit.dev/coreci/orca/internal/store" ) var ( capSetCPU int64 capSetMem int64 capSetDisk int64 capNodeID string ) var nodeCapacityCmd = &cobra.Command{ Use: "capacity", Short: "Manage node capacity declarations (P02 bin-packing input)", Long: "Read or write the per-node capacity used by the multi-node scheduler.", } var nodeCapacityShowCmd = &cobra.Command{ Use: "show [node-id]", Short: "Show capacity for a node (defaults to 'self')", Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { id := capNodeID if id == "" && len(args) > 0 { id = args[0] } if id == "" { id = "self" } ctx, cancel := context.WithTimeout(cmd.Context(), 5*time.Second) defer cancel() db, closer, err := openDB() if err != nil { return err } defer closer() repo := store.NewCapacityRepo(db) c, err := repo.Get(ctx, id) if err != nil { return fmt.Errorf("node %s: %w (use `orca node capacity --set` to declare)", id, err) } if jsonOutput { return printJSON(c) } fmt.Fprintf(cmd.OutOrStdout(), "Node: %s\n", c.NodeID) fmt.Fprintf(cmd.OutOrStdout(), "CPU: %d millicores\n", c.CPUMillicores) fmt.Fprintf(cmd.OutOrStdout(), "Memory: %d MiB\n", c.MemoryMiB) fmt.Fprintf(cmd.OutOrStdout(), "Disk: %d MiB\n", c.DiskMiB) fmt.Fprintf(cmd.OutOrStdout(), "Updated: %s\n", c.UpdatedAt.UTC().Format(time.RFC3339)) return nil }, } var nodeCapacitySetCmd = &cobra.Command{ Use: "set", Short: "Declare capacity for a node (used by bin-packing)", Long: "Write cpu_millicores, memory_mib, and disk_mib for the named node. Idempotent: subsequent calls overwrite.", RunE: func(cmd *cobra.Command, args []string) error { if capSetCPU <= 0 || capSetMem <= 0 || capSetDisk <= 0 { return fmt.Errorf("--cpu, --memory, and --disk must all be positive") } id := capNodeID if id == "" { id = "self" } ctx, cancel := context.WithTimeout(cmd.Context(), 5*time.Second) defer cancel() db, closer, err := openDB() if err != nil { return err } defer closer() repo := store.NewCapacityRepo(db) c := &store.NodeCapacity{ NodeID: id, CPUMillicores: capSetCPU, MemoryMiB: capSetMem, DiskMiB: capSetDisk, } if err := repo.Upsert(ctx, c); err != nil { return err } if jsonOutput { return printJSON(c) } fmt.Fprintf(cmd.OutOrStdout(), "✓ Capacity set for %s: cpu=%d mem=%d disk=%d\n", c.NodeID, c.CPUMillicores, c.MemoryMiB, c.DiskMiB) return nil }, } var nodeCapacityListCmd = &cobra.Command{ Use: "list", Short: "List all node capacity declarations", RunE: func(cmd *cobra.Command, args []string) error { ctx, cancel := context.WithTimeout(cmd.Context(), 5*time.Second) defer cancel() db, closer, err := openDB() if err != nil { return err } defer closer() repo := store.NewCapacityRepo(db) rows, err := repo.List(ctx) if err != nil { return err } if jsonOutput { return printJSON(rows) } if len(rows) == 0 { fmt.Fprintln(cmd.OutOrStdout(), "No capacity declarations. Use `orca node capacity --set` to add one.") return nil } fmt.Fprintf(cmd.OutOrStdout(), "%-20s %12s %12s %12s %s\n", "NODE", "CPU(mc)", "MEM(MiB)", "DISK(MiB)", "UPDATED") for _, c := range rows { fmt.Fprintf(cmd.OutOrStdout(), "%-20s %12d %12d %12d %s\n", c.NodeID, c.CPUMillicores, c.MemoryMiB, c.DiskMiB, c.UpdatedAt.UTC().Format(time.RFC3339)) } return nil }, } func init() { nodeCapacitySetCmd.Flags().Int64Var(&capSetCPU, "cpu", 0, "CPU capacity in millicores (1000 = 1 vCPU)") nodeCapacitySetCmd.Flags().Int64Var(&capSetMem, "memory", 0, "Memory capacity in MiB") nodeCapacitySetCmd.Flags().Int64Var(&capSetDisk, "disk", 0, "Disk capacity in MiB") nodeCapacitySetCmd.Flags().StringVar(&capNodeID, "node", "", "node id (defaults to 'self')") nodeCapacityShowCmd.Flags().StringVar(&capNodeID, "node", "", "node id (defaults to 'self')") nodeCapacityCmd.AddCommand(nodeCapacityShowCmd, nodeCapacitySetCmd, nodeCapacityListCmd) nodeCmd.AddCommand(nodeCapacityCmd) }