hardware.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. package ionet
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "strings"
  6. "github.com/samber/lo"
  7. )
  8. // GetAvailableReplicas retrieves available replicas per location for specified hardware
  9. func (c *Client) GetAvailableReplicas(hardwareID int, gpuCount int) (*AvailableReplicasResponse, error) {
  10. if hardwareID <= 0 {
  11. return nil, fmt.Errorf("hardware_id must be greater than 0")
  12. }
  13. if gpuCount < 1 {
  14. return nil, fmt.Errorf("gpu_count must be at least 1")
  15. }
  16. params := map[string]interface{}{
  17. "hardware_id": hardwareID,
  18. "hardware_qty": gpuCount,
  19. }
  20. endpoint := "/available-replicas" + buildQueryParams(params)
  21. resp, err := c.makeRequest("GET", endpoint, nil)
  22. if err != nil {
  23. return nil, fmt.Errorf("failed to get available replicas: %w", err)
  24. }
  25. type availableReplicaPayload struct {
  26. ID int `json:"id"`
  27. ISO2 string `json:"iso2"`
  28. Name string `json:"name"`
  29. AvailableReplicas int `json:"available_replicas"`
  30. }
  31. var payload []availableReplicaPayload
  32. if err := decodeData(resp.Body, &payload); err != nil {
  33. return nil, fmt.Errorf("failed to parse available replicas response: %w", err)
  34. }
  35. replicas := lo.Map(payload, func(item availableReplicaPayload, _ int) AvailableReplica {
  36. return AvailableReplica{
  37. LocationID: item.ID,
  38. LocationName: item.Name,
  39. HardwareID: hardwareID,
  40. HardwareName: "",
  41. AvailableCount: item.AvailableReplicas,
  42. MaxGPUs: gpuCount,
  43. }
  44. })
  45. return &AvailableReplicasResponse{Replicas: replicas}, nil
  46. }
  47. // GetMaxGPUsPerContainer retrieves the maximum number of GPUs available per hardware type
  48. func (c *Client) GetMaxGPUsPerContainer() (*MaxGPUResponse, error) {
  49. resp, err := c.makeRequest("GET", "/hardware/max-gpus-per-container", nil)
  50. if err != nil {
  51. return nil, fmt.Errorf("failed to get max GPUs per container: %w", err)
  52. }
  53. var maxGPUResp MaxGPUResponse
  54. if err := decodeData(resp.Body, &maxGPUResp); err != nil {
  55. return nil, fmt.Errorf("failed to parse max GPU response: %w", err)
  56. }
  57. return &maxGPUResp, nil
  58. }
  59. // ListHardwareTypes retrieves available hardware types using the max GPUs endpoint
  60. func (c *Client) ListHardwareTypes() ([]HardwareType, int, error) {
  61. maxGPUResp, err := c.GetMaxGPUsPerContainer()
  62. if err != nil {
  63. return nil, 0, fmt.Errorf("failed to list hardware types: %w", err)
  64. }
  65. mapped := lo.Map(maxGPUResp.Hardware, func(hw MaxGPUInfo, _ int) HardwareType {
  66. name := strings.TrimSpace(hw.HardwareName)
  67. if name == "" {
  68. name = fmt.Sprintf("Hardware %d", hw.HardwareID)
  69. }
  70. return HardwareType{
  71. ID: hw.HardwareID,
  72. Name: name,
  73. GPUType: "",
  74. GPUMemory: 0,
  75. MaxGPUs: hw.MaxGPUsPerContainer,
  76. CPU: "",
  77. Memory: 0,
  78. Storage: 0,
  79. HourlyRate: 0,
  80. Available: hw.Available > 0,
  81. BrandName: strings.TrimSpace(hw.BrandName),
  82. AvailableCount: hw.Available,
  83. }
  84. })
  85. totalAvailable := maxGPUResp.Total
  86. if totalAvailable == 0 {
  87. totalAvailable = lo.SumBy(maxGPUResp.Hardware, func(hw MaxGPUInfo) int {
  88. return hw.Available
  89. })
  90. }
  91. return mapped, totalAvailable, nil
  92. }
  93. // ListLocations retrieves available deployment locations (if supported by the API)
  94. func (c *Client) ListLocations() (*LocationsResponse, error) {
  95. resp, err := c.makeRequest("GET", "/locations", nil)
  96. if err != nil {
  97. return nil, fmt.Errorf("failed to list locations: %w", err)
  98. }
  99. var locations LocationsResponse
  100. if err := decodeData(resp.Body, &locations); err != nil {
  101. return nil, fmt.Errorf("failed to parse locations response: %w", err)
  102. }
  103. locations.Locations = lo.Map(locations.Locations, func(location Location, _ int) Location {
  104. location.ISO2 = strings.ToUpper(strings.TrimSpace(location.ISO2))
  105. return location
  106. })
  107. if locations.Total == 0 {
  108. locations.Total = lo.SumBy(locations.Locations, func(location Location) int {
  109. return location.Available
  110. })
  111. }
  112. return &locations, nil
  113. }
  114. // GetHardwareType retrieves details about a specific hardware type
  115. func (c *Client) GetHardwareType(hardwareID int) (*HardwareType, error) {
  116. if hardwareID <= 0 {
  117. return nil, fmt.Errorf("hardware ID must be greater than 0")
  118. }
  119. endpoint := fmt.Sprintf("/hardware/types/%d", hardwareID)
  120. resp, err := c.makeRequest("GET", endpoint, nil)
  121. if err != nil {
  122. return nil, fmt.Errorf("failed to get hardware type: %w", err)
  123. }
  124. // API response format not documented, assuming direct format
  125. var hardwareType HardwareType
  126. if err := json.Unmarshal(resp.Body, &hardwareType); err != nil {
  127. return nil, fmt.Errorf("failed to parse hardware type: %w", err)
  128. }
  129. return &hardwareType, nil
  130. }
  131. // GetLocation retrieves details about a specific location
  132. func (c *Client) GetLocation(locationID int) (*Location, error) {
  133. if locationID <= 0 {
  134. return nil, fmt.Errorf("location ID must be greater than 0")
  135. }
  136. endpoint := fmt.Sprintf("/locations/%d", locationID)
  137. resp, err := c.makeRequest("GET", endpoint, nil)
  138. if err != nil {
  139. return nil, fmt.Errorf("failed to get location: %w", err)
  140. }
  141. // API response format not documented, assuming direct format
  142. var location Location
  143. if err := json.Unmarshal(resp.Body, &location); err != nil {
  144. return nil, fmt.Errorf("failed to parse location: %w", err)
  145. }
  146. return &location, nil
  147. }
  148. // GetLocationAvailability retrieves real-time availability for a specific location
  149. func (c *Client) GetLocationAvailability(locationID int) (*LocationAvailability, error) {
  150. if locationID <= 0 {
  151. return nil, fmt.Errorf("location ID must be greater than 0")
  152. }
  153. endpoint := fmt.Sprintf("/locations/%d/availability", locationID)
  154. resp, err := c.makeRequest("GET", endpoint, nil)
  155. if err != nil {
  156. return nil, fmt.Errorf("failed to get location availability: %w", err)
  157. }
  158. // API response format not documented, assuming direct format
  159. var availability LocationAvailability
  160. if err := json.Unmarshal(resp.Body, &availability); err != nil {
  161. return nil, fmt.Errorf("failed to parse location availability: %w", err)
  162. }
  163. return &availability, nil
  164. }