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  }