github.com/vmware/govmomi@v0.37.1/vapi/appliance/simulator/simulator.go (about) 1 /* 2 Copyright (c) 2022 VMware, Inc. All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package simulator 18 19 import ( 20 "encoding/json" 21 "log" 22 "net/http" 23 "net/url" 24 "time" 25 26 "github.com/vmware/govmomi/simulator" 27 "github.com/vmware/govmomi/vapi/appliance/access/consolecli" 28 "github.com/vmware/govmomi/vapi/appliance/access/dcui" 29 "github.com/vmware/govmomi/vapi/appliance/access/shell" 30 "github.com/vmware/govmomi/vapi/appliance/access/ssh" 31 "github.com/vmware/govmomi/vapi/appliance/shutdown" 32 vapi "github.com/vmware/govmomi/vapi/simulator" 33 ) 34 35 func init() { 36 simulator.RegisterEndpoint(func(s *simulator.Service, r *simulator.Registry) { 37 New(s.Listen).Register(s, r) 38 }) 39 } 40 41 // Handler implements the Appliance API simulator 42 type Handler struct { 43 URL *url.URL 44 consolecli consolecli.Access 45 dcui dcui.Access 46 ssh ssh.Access 47 shell shell.Access 48 shutdownConfig shutdown.Config 49 } 50 51 // New creates a Handler instance 52 func New(u *url.URL) *Handler { 53 return &Handler{ 54 URL: nil, 55 consolecli: consolecli.Access{Enabled: false}, 56 dcui: dcui.Access{Enabled: false}, 57 ssh: ssh.Access{Enabled: false}, 58 shell: shell.Access{Enabled: false, Timeout: 0}, 59 shutdownConfig: shutdown.Config{}, 60 } 61 } 62 63 // Register Appliance Management API paths with the vapi simulator's http.ServeMux 64 func (h *Handler) Register(s *simulator.Service, r *simulator.Registry) { 65 s.HandleFunc(consolecli.Path, h.consoleCLIAccess) 66 s.HandleFunc(dcui.Path, h.dcuiAccess) 67 s.HandleFunc(ssh.Path, h.sshAccess) 68 s.HandleFunc(shell.Path, h.shellAccess) 69 s.HandleFunc(shutdown.Path, h.shutdown) 70 } 71 72 func (h *Handler) decode(r *http.Request, w http.ResponseWriter, val interface{}) bool { 73 return Decode(r, w, val) 74 } 75 76 // Decode decodes the request Body into val, returns true on success, otherwise false. 77 func Decode(request *http.Request, writer http.ResponseWriter, val interface{}) bool { 78 defer request.Body.Close() 79 err := json.NewDecoder(request.Body).Decode(val) 80 if err != nil { 81 log.Printf("%s %s: %s", request.Method, request.RequestURI, err) 82 return false 83 } 84 return true 85 } 86 87 func (h *Handler) consoleCLIAccess(writer http.ResponseWriter, request *http.Request) { 88 switch request.Method { 89 case http.MethodGet: 90 vapi.StatusOK(writer, h.consolecli.Enabled) 91 case http.MethodPut: 92 var input consolecli.Access 93 if h.decode(request, writer, &input) { 94 h.consolecli.Enabled = input.Enabled 95 writer.WriteHeader(http.StatusNoContent) 96 } else { 97 writer.WriteHeader(http.StatusInternalServerError) 98 } 99 default: 100 http.NotFound(writer, request) 101 } 102 } 103 104 func (h *Handler) dcuiAccess(writer http.ResponseWriter, request *http.Request) { 105 switch request.Method { 106 case http.MethodGet: 107 vapi.StatusOK(writer, h.dcui.Enabled) 108 case http.MethodPut: 109 var input dcui.Access 110 if h.decode(request, writer, &input) { 111 h.dcui.Enabled = input.Enabled 112 writer.WriteHeader(http.StatusNoContent) 113 } else { 114 writer.WriteHeader(http.StatusInternalServerError) 115 } 116 default: 117 http.NotFound(writer, request) 118 } 119 } 120 121 func (h *Handler) sshAccess(writer http.ResponseWriter, request *http.Request) { 122 switch request.Method { 123 case http.MethodGet: 124 vapi.StatusOK(writer, h.ssh.Enabled) 125 case http.MethodPut: 126 var input ssh.Access 127 if h.decode(request, writer, &input) { 128 h.ssh.Enabled = input.Enabled 129 writer.WriteHeader(http.StatusNoContent) 130 } else { 131 writer.WriteHeader(http.StatusInternalServerError) 132 } 133 default: 134 http.NotFound(writer, request) 135 } 136 } 137 138 func (h *Handler) shellAccess(writer http.ResponseWriter, request *http.Request) { 139 switch request.Method { 140 case http.MethodGet: 141 vapi.StatusOK(writer, h.shell) 142 case http.MethodPut: 143 var input shell.Access 144 if h.decode(request, writer, &input) { 145 h.shell.Enabled = input.Enabled 146 h.shell.Timeout = input.Timeout 147 writer.WriteHeader(http.StatusNoContent) 148 } else { 149 writer.WriteHeader(http.StatusInternalServerError) 150 } 151 default: 152 http.NotFound(writer, request) 153 } 154 } 155 156 func (h *Handler) shutdown(w http.ResponseWriter, r *http.Request) { 157 switch r.Method { 158 case http.MethodGet: 159 vapi.StatusOK(w, h.shutdownConfig) 160 case http.MethodPost: 161 switch r.URL.Query().Get(shutdown.Action) { 162 case shutdown.Cancel: 163 h.shutdownConfig.ShutdownTime = "" 164 h.shutdownConfig.Action = "" 165 h.shutdownConfig.Reason = "" 166 w.WriteHeader(http.StatusNoContent) 167 case shutdown.Reboot: 168 var spec shutdown.Spec 169 if h.decode(r, w, &spec) { 170 h.shutdownConfig.ShutdownTime = time.Now().UTC().Add(time.Duration(spec.Delay) * time.Minute).String() 171 h.shutdownConfig.Reason = spec.Reason 172 h.shutdownConfig.Action = shutdown.Reboot 173 w.WriteHeader(http.StatusNoContent) 174 } 175 case shutdown.PowerOff: 176 var spec shutdown.Spec 177 if h.decode(r, w, &spec) { 178 h.shutdownConfig.ShutdownTime = time.Now().UTC().Add(time.Duration(spec.Delay) * time.Minute).String() 179 h.shutdownConfig.Reason = spec.Reason 180 h.shutdownConfig.Action = shutdown.PowerOff 181 w.WriteHeader(http.StatusNoContent) 182 } 183 default: 184 http.NotFound(w, r) 185 } 186 default: 187 http.NotFound(w, r) 188 } 189 }