github.com/vmware/govmomi@v0.51.0/vapi/appliance/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  	"encoding/json"
     9  	"log"
    10  	"net/http"
    11  	"net/url"
    12  	"time"
    13  
    14  	"github.com/vmware/govmomi/simulator"
    15  	"github.com/vmware/govmomi/vapi/appliance/access/consolecli"
    16  	"github.com/vmware/govmomi/vapi/appliance/access/dcui"
    17  	"github.com/vmware/govmomi/vapi/appliance/access/shell"
    18  	"github.com/vmware/govmomi/vapi/appliance/access/ssh"
    19  	"github.com/vmware/govmomi/vapi/appliance/shutdown"
    20  	vapi "github.com/vmware/govmomi/vapi/simulator"
    21  )
    22  
    23  func init() {
    24  	simulator.RegisterEndpoint(func(s *simulator.Service, r *simulator.Registry) {
    25  		New(s.Listen).Register(s, r)
    26  	})
    27  }
    28  
    29  // Handler implements the Appliance API simulator
    30  type Handler struct {
    31  	URL            *url.URL
    32  	consolecli     consolecli.Access
    33  	dcui           dcui.Access
    34  	ssh            ssh.Access
    35  	shell          shell.Access
    36  	shutdownConfig shutdown.Config
    37  }
    38  
    39  // New creates a Handler instance
    40  func New(u *url.URL) *Handler {
    41  	return &Handler{
    42  		URL:            nil,
    43  		consolecli:     consolecli.Access{Enabled: false},
    44  		dcui:           dcui.Access{Enabled: false},
    45  		ssh:            ssh.Access{Enabled: false},
    46  		shell:          shell.Access{Enabled: false, Timeout: 0},
    47  		shutdownConfig: shutdown.Config{},
    48  	}
    49  }
    50  
    51  // Register Appliance Management API paths with the vapi simulator's http.ServeMux
    52  func (h *Handler) Register(s *simulator.Service, r *simulator.Registry) {
    53  	s.HandleFunc(consolecli.Path, h.consoleCLIAccess)
    54  	s.HandleFunc(dcui.Path, h.dcuiAccess)
    55  	s.HandleFunc(ssh.Path, h.sshAccess)
    56  	s.HandleFunc(shell.Path, h.shellAccess)
    57  	s.HandleFunc(shutdown.Path, h.shutdown)
    58  }
    59  
    60  func (h *Handler) decode(r *http.Request, w http.ResponseWriter, val any) bool {
    61  	return Decode(r, w, val)
    62  }
    63  
    64  // Decode decodes the request Body into val, returns true on success, otherwise false.
    65  func Decode(request *http.Request, writer http.ResponseWriter, val any) bool {
    66  	defer request.Body.Close()
    67  	err := json.NewDecoder(request.Body).Decode(val)
    68  	if err != nil {
    69  		log.Printf("%s %s: %s", request.Method, request.RequestURI, err)
    70  		return false
    71  	}
    72  	return true
    73  }
    74  
    75  func (h *Handler) consoleCLIAccess(writer http.ResponseWriter, request *http.Request) {
    76  	switch request.Method {
    77  	case http.MethodGet:
    78  		vapi.StatusOK(writer, h.consolecli.Enabled)
    79  	case http.MethodPut:
    80  		var input consolecli.Access
    81  		if h.decode(request, writer, &input) {
    82  			h.consolecli.Enabled = input.Enabled
    83  			writer.WriteHeader(http.StatusNoContent)
    84  		} else {
    85  			writer.WriteHeader(http.StatusInternalServerError)
    86  		}
    87  	default:
    88  		http.NotFound(writer, request)
    89  	}
    90  }
    91  
    92  func (h *Handler) dcuiAccess(writer http.ResponseWriter, request *http.Request) {
    93  	switch request.Method {
    94  	case http.MethodGet:
    95  		vapi.StatusOK(writer, h.dcui.Enabled)
    96  	case http.MethodPut:
    97  		var input dcui.Access
    98  		if h.decode(request, writer, &input) {
    99  			h.dcui.Enabled = input.Enabled
   100  			writer.WriteHeader(http.StatusNoContent)
   101  		} else {
   102  			writer.WriteHeader(http.StatusInternalServerError)
   103  		}
   104  	default:
   105  		http.NotFound(writer, request)
   106  	}
   107  }
   108  
   109  func (h *Handler) sshAccess(writer http.ResponseWriter, request *http.Request) {
   110  	switch request.Method {
   111  	case http.MethodGet:
   112  		vapi.StatusOK(writer, h.ssh.Enabled)
   113  	case http.MethodPut:
   114  		var input ssh.Access
   115  		if h.decode(request, writer, &input) {
   116  			h.ssh.Enabled = input.Enabled
   117  			writer.WriteHeader(http.StatusNoContent)
   118  		} else {
   119  			writer.WriteHeader(http.StatusInternalServerError)
   120  		}
   121  	default:
   122  		http.NotFound(writer, request)
   123  	}
   124  }
   125  
   126  func (h *Handler) shellAccess(writer http.ResponseWriter, request *http.Request) {
   127  	switch request.Method {
   128  	case http.MethodGet:
   129  		vapi.StatusOK(writer, h.shell)
   130  	case http.MethodPut:
   131  		var input shell.Access
   132  		if h.decode(request, writer, &input) {
   133  			h.shell.Enabled = input.Enabled
   134  			h.shell.Timeout = input.Timeout
   135  			writer.WriteHeader(http.StatusNoContent)
   136  		} else {
   137  			writer.WriteHeader(http.StatusInternalServerError)
   138  		}
   139  	default:
   140  		http.NotFound(writer, request)
   141  	}
   142  }
   143  
   144  func (h *Handler) shutdown(w http.ResponseWriter, r *http.Request) {
   145  	switch r.Method {
   146  	case http.MethodGet:
   147  		vapi.StatusOK(w, h.shutdownConfig)
   148  	case http.MethodPost:
   149  		switch r.URL.Query().Get(shutdown.Action) {
   150  		case shutdown.Cancel:
   151  			h.shutdownConfig.ShutdownTime = ""
   152  			h.shutdownConfig.Action = ""
   153  			h.shutdownConfig.Reason = ""
   154  			w.WriteHeader(http.StatusNoContent)
   155  		case shutdown.Reboot:
   156  			var spec shutdown.Spec
   157  			if h.decode(r, w, &spec) {
   158  				h.shutdownConfig.ShutdownTime = time.Now().UTC().Add(time.Duration(spec.Delay) * time.Minute).String()
   159  				h.shutdownConfig.Reason = spec.Reason
   160  				h.shutdownConfig.Action = shutdown.Reboot
   161  				w.WriteHeader(http.StatusNoContent)
   162  			}
   163  		case shutdown.PowerOff:
   164  			var spec shutdown.Spec
   165  			if h.decode(r, w, &spec) {
   166  				h.shutdownConfig.ShutdownTime = time.Now().UTC().Add(time.Duration(spec.Delay) * time.Minute).String()
   167  				h.shutdownConfig.Reason = spec.Reason
   168  				h.shutdownConfig.Action = shutdown.PowerOff
   169  				w.WriteHeader(http.StatusNoContent)
   170  			}
   171  		default:
   172  			http.NotFound(w, r)
   173  		}
   174  	default:
   175  		http.NotFound(w, r)
   176  	}
   177  }