github.com/vmware/govmomi@v0.51.0/vapi/esx/settings/simulator/simulator.go (about) 1 // © Broadcom. All Rights Reserved. 2 // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. 3 // SPDX-License-Identifier: Apache-2.0 4 5 package simulator 6 7 import ( 8 "fmt" 9 "net/http" 10 "net/url" 11 "strings" 12 13 "github.com/vmware/govmomi/simulator" 14 "github.com/vmware/govmomi/vapi/esx/settings/clusters" 15 "github.com/vmware/govmomi/vapi/esx/settings/depots" 16 vapi "github.com/vmware/govmomi/vapi/simulator" 17 ) 18 19 func init() { 20 simulator.RegisterEndpoint(func(s *simulator.Service, r *simulator.Registry) { 21 New(s.Listen).Register(s, r) 22 }) 23 } 24 25 type Handler struct { 26 URL *url.URL 27 28 Depots map[string]depots.SettingsDepotsOfflineInfo 29 DepotComponents map[string]depots.SettingsDepotsOfflineContentInfo 30 BaseImages []depots.BaseImagesSummary 31 32 SoftwareDrafts map[string]clusters.SettingsClustersSoftwareDraftsMetadata 33 SoftwareComponents map[string]clusters.SettingsComponentInfo 34 ClusterImage *clusters.SettingsBaseImageInfo 35 36 depotCounter int 37 draftCounter int 38 39 vlcmEnabled bool 40 } 41 42 // New creates a Handler instance 43 func New(u *url.URL) *Handler { 44 return &Handler{ 45 URL: u, 46 Depots: make(map[string]depots.SettingsDepotsOfflineInfo), 47 DepotComponents: make(map[string]depots.SettingsDepotsOfflineContentInfo), 48 BaseImages: createMockBaseImages(), 49 SoftwareDrafts: make(map[string]clusters.SettingsClustersSoftwareDraftsMetadata), 50 SoftwareComponents: make(map[string]clusters.SettingsComponentInfo), 51 depotCounter: 0, 52 vlcmEnabled: false, 53 } 54 } 55 56 func (h *Handler) Register(s *simulator.Service, r *simulator.Registry) { 57 if r.IsVPX() { 58 s.HandleFunc(depots.DepotsOfflinePath, h.depotsOffline) 59 s.HandleFunc(depots.DepotsOfflinePath+"/", h.depotsOffline) 60 s.HandleFunc(depots.BaseImagesPath, h.baseImages) 61 s.HandleFunc("/api/esx/settings/clusters/", h.clusters) 62 } 63 } 64 65 func (h *Handler) depotsOffline(w http.ResponseWriter, r *http.Request) { 66 subpath := r.URL.Path[len(depots.DepotsOfflinePath):] 67 segments := strings.Split(subpath, "/") 68 69 switch r.Method { 70 case http.MethodGet: 71 if len(segments) > 1 { 72 if res, contains := h.DepotComponents[segments[1]]; !contains { 73 vapi.ApiErrorNotFound(w) 74 } else if len(segments) > 2 && segments[2] == "content" { 75 vapi.StatusOK(w, res) 76 } else { 77 vapi.ApiErrorUnsupported(w) 78 } 79 } 80 vapi.StatusOK(w, h.Depots) 81 case http.MethodDelete: 82 if _, contains := h.Depots[segments[1]]; !contains { 83 vapi.ApiErrorNotFound(w) 84 } else { 85 delete(h.Depots, segments[1]) 86 delete(h.DepotComponents, segments[1]) 87 vapi.StatusOK(w, "") 88 } 89 case http.MethodPost: 90 var spec depots.SettingsDepotsOfflineCreateSpec 91 if vapi.Decode(r, w, &spec) { 92 // Create depot 93 depot := depots.SettingsDepotsOfflineInfo{} 94 depot.SourceType = spec.SourceType 95 depot.FileId = spec.FileId 96 depot.Location = spec.Location 97 depot.OwnerData = spec.OwnerData 98 depot.Description = spec.Description 99 100 h.depotCounter += 1 101 depotId := fmt.Sprintf("depot-%d", h.depotCounter) 102 h.Depots[depotId] = depot 103 104 // Generate content 105 content := depots.SettingsDepotsOfflineContentInfo{} 106 content.MetadataBundles = make(map[string][]depots.SettingsDepotsMetadataInfo) 107 content.MetadataBundles["dummy-content"] = make([]depots.SettingsDepotsMetadataInfo, 1) 108 bundle := depots.SettingsDepotsMetadataInfo{} 109 bundle.IndependentComponents = make(map[string]depots.SettingsDepotsComponentSummary) 110 independentComp := depots.SettingsDepotsComponentSummary{} 111 independentComp.Versions = make([]depots.ComponentVersion, 1) 112 independentComp.Versions[0].Version = "1.0.0" 113 independentComp.Versions[0].DisplayVersion = "1.0.0" 114 independentComp.DisplayName = "DummyComponent" 115 bundle.IndependentComponents["dummy-component"] = independentComp 116 content.MetadataBundles["dummy-content"][0] = bundle 117 h.DepotComponents[depotId] = content 118 119 vapi.StatusOK(w, fmt.Sprintf("depot-task-%d", h.depotCounter)) 120 } 121 } 122 } 123 124 func (h *Handler) baseImages(w http.ResponseWriter, r *http.Request) { 125 switch r.Method { 126 case http.MethodGet: 127 vapi.StatusOK(w, h.BaseImages) 128 } 129 } 130 131 func (h *Handler) clusters(w http.ResponseWriter, r *http.Request) { 132 subpath := r.URL.Path[len("/api/esx/settings/clusters"):] 133 segments := strings.Split(subpath, "/") 134 135 if len(segments) > 3 && segments[2] == "software" && segments[3] == "drafts" { 136 segments = segments[4:] 137 if len(segments) > 2 && segments[1] == "software" && segments[2] == "components" { 138 h.clustersSoftwareDraftsComponents(w, r, segments) 139 return 140 } else if len(segments) > 2 && segments[1] == "software" && segments[2] == "base-image" { 141 h.clustersSoftwareDraftsBaseImage(w, r) 142 return 143 } else { 144 h.clustersSoftwareDrafts(w, r, segments) 145 return 146 } 147 } else if len(segments) > 3 && segments[2] == "enablement" && segments[3] == "software" { 148 h.clustersSoftwareEnablement(w, r) 149 return 150 } 151 152 vapi.ApiErrorUnsupported(w) 153 } 154 155 func (h *Handler) clustersSoftwareDrafts(w http.ResponseWriter, r *http.Request, subpath []string) { 156 var draftId *string 157 if len(subpath) > 0 { 158 draftId = &subpath[0] 159 } 160 161 switch r.Method { 162 case http.MethodGet: 163 if draftId != nil { 164 if draft, contains := h.SoftwareDrafts[*draftId]; !contains { 165 vapi.ApiErrorNotFound(w) 166 return 167 } else { 168 vapi.StatusOK(w, draft) 169 } 170 } else { 171 vapi.StatusOK(w, h.SoftwareDrafts) 172 } 173 case http.MethodDelete: 174 if draftId != nil { 175 if _, contains := h.SoftwareDrafts[*draftId]; !contains { 176 vapi.ApiErrorNotFound(w) 177 return 178 } else { 179 delete(h.SoftwareDrafts, *draftId) 180 vapi.StatusOK(w) 181 } 182 } 183 case http.MethodPost: 184 if strings.Contains(r.URL.RawQuery, "action=commit") { 185 if draftId != nil { 186 if _, contains := h.SoftwareDrafts[*draftId]; !contains { 187 vapi.ApiErrorNotFound(w) 188 return 189 } else { 190 delete(h.SoftwareDrafts, *draftId) 191 vapi.StatusOK(w) 192 } 193 } 194 } 195 // Only one active draft is permitted 196 if len(h.SoftwareDrafts) > 0 { 197 vapi.ApiErrorNotAllowedInCurrentState(w) 198 return 199 } 200 201 h.draftCounter += 1 202 draft := clusters.SettingsClustersSoftwareDraftsMetadata{} 203 newDraftId := fmt.Sprintf("%d", h.draftCounter) 204 h.SoftwareDrafts[newDraftId] = draft 205 vapi.StatusOK(w, newDraftId) 206 } 207 } 208 209 func (h *Handler) clustersSoftwareDraftsComponents(w http.ResponseWriter, r *http.Request, subpath []string) { 210 switch r.Method { 211 case http.MethodGet: 212 if len(subpath) > 3 { 213 if comp, contains := h.SoftwareComponents[subpath[3]]; contains { 214 vapi.StatusOK(w, comp) 215 } else { 216 vapi.ApiErrorNotFound(w) 217 } 218 } else { 219 vapi.StatusOK(w, h.SoftwareComponents) 220 } 221 case http.MethodDelete: 222 if len(subpath) > 3 { 223 compId := subpath[3] 224 if comp, contains := h.SoftwareComponents[compId]; contains { 225 delete(h.SoftwareComponents, compId) 226 vapi.StatusOK(w, comp) 227 } else { 228 vapi.ApiErrorNotFound(w) 229 } 230 } 231 case http.MethodPatch: 232 var spec clusters.SoftwareComponentsUpdateSpec 233 if vapi.Decode(r, w, &spec) { 234 for k, v := range spec.ComponentsToSet { 235 h.SoftwareComponents[k] = clusters.SettingsComponentInfo{ 236 Version: v, 237 Details: clusters.SettingsComponentDetails{DisplayName: "DummyComponent"}, 238 } 239 } 240 } 241 } 242 } 243 244 func (h *Handler) clustersSoftwareDraftsBaseImage(w http.ResponseWriter, r *http.Request) { 245 switch r.Method { 246 case http.MethodGet: 247 vapi.StatusOK(w, h.ClusterImage) 248 case http.MethodPut: 249 var spec clusters.SettingsBaseImageSpec 250 if vapi.Decode(r, w, &spec) { 251 h.ClusterImage = &clusters.SettingsBaseImageInfo{Version: spec.Version} 252 vapi.StatusOK(w) 253 } else { 254 vapi.ApiErrorInvalidArgument(w) 255 } 256 } 257 } 258 259 func (h *Handler) clustersSoftwareEnablement(w http.ResponseWriter, r *http.Request) { 260 switch r.Method { 261 case http.MethodGet: 262 vapi.StatusOK(w, clusters.SoftwareManagementInfo{Enabled: h.vlcmEnabled}) 263 case http.MethodPut: 264 h.vlcmEnabled = true 265 vapi.StatusOK(w) 266 } 267 } 268 269 func createMockBaseImages() []depots.BaseImagesSummary { 270 baseImage := depots.BaseImagesSummary{ 271 DisplayName: "DummyImage", 272 DisplayVersion: "0.0.1", 273 Version: "0.0.1", 274 } 275 return []depots.BaseImagesSummary{baseImage} 276 }