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 }