github.com/vmware/govmomi@v0.37.1/vcsim/main.go (about) 1 /* 2 Copyright (c) 2017-2023 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 main 18 19 import ( 20 "crypto/tls" 21 "expvar" 22 "flag" 23 "fmt" 24 "log" 25 "net" 26 "net/http" 27 "net/url" 28 "os" 29 "os/signal" 30 "runtime" 31 "strconv" 32 "strings" 33 "syscall" 34 35 "github.com/google/uuid" 36 37 "github.com/vmware/govmomi/session" 38 "github.com/vmware/govmomi/simulator" 39 "github.com/vmware/govmomi/simulator/esx" 40 "github.com/vmware/govmomi/vim25/types" 41 42 // Register vcsim optional endpoints 43 _ "github.com/vmware/govmomi/cns/simulator" 44 _ "github.com/vmware/govmomi/eam/simulator" 45 _ "github.com/vmware/govmomi/lookup/simulator" 46 _ "github.com/vmware/govmomi/pbm/simulator" 47 _ "github.com/vmware/govmomi/ssoadmin/simulator" 48 _ "github.com/vmware/govmomi/sts/simulator" 49 _ "github.com/vmware/govmomi/vapi/appliance/simulator" 50 _ "github.com/vmware/govmomi/vapi/cis/tasks/simulator" 51 _ "github.com/vmware/govmomi/vapi/cluster/simulator" 52 _ "github.com/vmware/govmomi/vapi/esx/settings/simulator" 53 _ "github.com/vmware/govmomi/vapi/namespace/simulator" 54 _ "github.com/vmware/govmomi/vapi/simulator" 55 _ "github.com/vmware/govmomi/vapi/vm/simulator" 56 _ "github.com/vmware/govmomi/vsan/simulator" 57 ) 58 59 var ( 60 buildVersion string 61 buildCommit string 62 buildDate string 63 ) 64 65 func main() { 66 model := simulator.VPX() 67 68 flag.IntVar(&model.Datacenter, "dc", model.Datacenter, "Number of datacenters") 69 flag.IntVar(&model.Cluster, "cluster", model.Cluster, "Number of clusters") 70 flag.IntVar(&model.ClusterHost, "host", model.ClusterHost, "Number of hosts per cluster") 71 flag.IntVar(&model.Host, "standalone-host", model.Host, "Number of standalone hosts") 72 flag.IntVar(&model.Datastore, "ds", model.Datastore, "Number of local datastores") 73 flag.IntVar(&model.Machine, "vm", model.Machine, "Number of virtual machines per resource pool") 74 flag.IntVar(&model.Pool, "pool", model.Pool, "Number of resource pools per compute resource") 75 flag.IntVar(&model.App, "app", model.App, "Number of virtual apps per compute resource") 76 flag.IntVar(&model.Pod, "pod", model.Pod, "Number of storage pods per datacenter") 77 flag.IntVar(&model.Portgroup, "pg", model.Portgroup, "Number of port groups") 78 flag.IntVar(&model.PortgroupNSX, "pg-nsx", model.PortgroupNSX, "Number of NSX backed port groups") 79 flag.IntVar(&model.OpaqueNetwork, "nsx", model.OpaqueNetwork, "Number of NSX backed opaque networks") 80 flag.IntVar(&model.Folder, "folder", model.Folder, "Number of folders") 81 flag.BoolVar(&model.Autostart, "autostart", model.Autostart, "Autostart model created VMs") 82 v := &model.ServiceContent.About.ApiVersion 83 flag.StringVar(v, "api-version", *v, "API version") 84 85 isESX := flag.Bool("esx", false, "Simulate standalone ESX") 86 isTLS := flag.Bool("tls", true, "Enable TLS") 87 cert := flag.String("tlscert", "", "Path to TLS certificate file") 88 key := flag.String("tlskey", "", "Path to TLS key file") 89 env := flag.String("E", "-", "Output vcsim variables to the given fifo or stdout") 90 listen := flag.String("l", "127.0.0.1:8989", "Listen address for vcsim") 91 user := flag.String("username", "", "Login username for vcsim (any username allowed by default)") 92 pass := flag.String("password", "", "Login password for vcsim (any password allowed by default)") 93 tunnel := flag.Int("tunnel", -1, "SDK tunnel port") 94 flag.BoolVar(&simulator.Trace, "trace", simulator.Trace, "Trace SOAP to -trace-file") 95 trace := flag.String("trace-file", "", "Trace output file (defaults to stderr)") 96 stdinExit := flag.Bool("stdinexit", false, "Press any key to exit") 97 dir := flag.String("load", "", "Load model from directory") 98 99 flag.IntVar(&model.DelayConfig.Delay, "delay", model.DelayConfig.Delay, "Method response delay across all methods") 100 methodDelayP := flag.String("method-delay", "", "Delay per method on the form 'method1:delay1,method2:delay2...'") 101 flag.Float64Var(&model.DelayConfig.DelayJitter, "delay-jitter", model.DelayConfig.DelayJitter, "Delay jitter coefficient of variation (tip: 0.5 is a good starting value)") 102 103 flag.Parse() 104 105 if *trace != "" { 106 var err error 107 simulator.TraceFile, err = os.Create(*trace) 108 if err != nil { 109 log.Fatal(err) 110 } 111 simulator.Trace = true 112 } 113 114 methodDelay := *methodDelayP 115 u := &url.URL{Host: *listen} 116 if *user != "" { 117 u.User = url.UserPassword(secret(user), secret(pass)) 118 } 119 120 switch flag.Arg(0) { 121 case "uuidgen": 122 fmt.Println(uuid.New().String()) 123 os.Exit(0) 124 case "version": 125 fmt.Printf("Build Version: %s\n", buildVersion) 126 fmt.Printf("Build Commit: %s\n", buildCommit) 127 fmt.Printf("Build Date: %s\n", buildDate) 128 os.Exit(0) 129 } 130 131 if methodDelay != "" { 132 m := make(map[string]int) 133 for _, s := range strings.Split(methodDelay, ",") { 134 s = strings.TrimSpace(s) 135 tuples := strings.Split(s, ":") 136 if len(tuples) == 2 { 137 key := tuples[0] 138 value, err := strconv.Atoi(tuples[1]) 139 if err != nil { 140 log.Fatalf("Incorrect format of method-delay argument: %s", err) 141 } 142 m[key] = value 143 } else { 144 log.Fatal("Incorrect method delay format.") 145 } 146 } 147 model.DelayConfig.MethodDelay = m 148 simulator.TaskDelay.MethodDelay = m 149 } 150 151 var err error 152 153 if err = updateHostTemplate(u.Host); err != nil { 154 log.Fatal(err) 155 } 156 157 if *isESX { 158 opts := model 159 model = simulator.ESX() 160 // Preserve options that also apply to ESX 161 model.Datastore = opts.Datastore 162 model.Machine = opts.Machine 163 model.Autostart = opts.Autostart 164 model.DelayConfig.Delay = opts.DelayConfig.Delay 165 model.DelayConfig.MethodDelay = opts.DelayConfig.MethodDelay 166 model.DelayConfig.DelayJitter = opts.DelayConfig.DelayJitter 167 } 168 169 tag := " (govmomi simulator)" 170 model.ServiceContent.About.FullName += tag 171 model.ServiceContent.About.OsType = runtime.GOOS + "-" + runtime.GOARCH 172 173 esx.HostSystem.Summary.Hardware.Vendor += tag 174 175 if *dir == "" { 176 err = model.Create() 177 } else { 178 err = model.Load(*dir) 179 } 180 if err != nil { 181 log.Fatal(err) 182 } 183 184 model.Service.RegisterEndpoints = true 185 model.Service.Listen = u 186 if *isTLS { 187 model.Service.TLS = new(tls.Config) 188 if *cert != "" { 189 c, err := tls.LoadX509KeyPair(*cert, *key) 190 if err != nil { 191 log.Fatal(err) 192 } 193 194 model.Service.TLS.Certificates = []tls.Certificate{c} 195 } 196 } 197 198 expvar.Publish("vcsim", expvar.Func(func() interface{} { 199 count := model.Count() 200 201 return struct { 202 Registry *simulator.Registry `json:"registry"` 203 Model *simulator.Model `json:"model"` 204 }{ 205 simulator.Map, 206 &count, 207 } 208 })) 209 210 model.Service.ServeMux = http.DefaultServeMux // expvar.init registers "/debug/vars" with the DefaultServeMux 211 212 s := model.Service.NewServer() 213 214 if *tunnel >= 0 { 215 s.Tunnel = *tunnel 216 if err := s.StartTunnel(); err != nil { 217 log.Fatal(err) 218 } 219 } 220 221 out := os.Stdout 222 223 if *env != "-" { 224 out, err = os.OpenFile(*env, os.O_WRONLY, 0) 225 if err != nil { 226 log.Fatal(err) 227 } 228 } 229 230 _, err = fmt.Fprintf(out, "export GOVC_URL=%s GOVC_SIM_PID=%d\n", s.URL, os.Getpid()) 231 if err != nil { 232 log.Fatal(err) 233 } 234 if out != os.Stdout { 235 err = out.Close() 236 if err != nil { 237 log.Fatal(err) 238 } 239 } 240 241 sig := make(chan os.Signal, 1) 242 signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) 243 if *stdinExit { 244 fmt.Println("Press any key to exit") 245 go func() { 246 os.Stdin.Read(make([]byte, 1)) 247 sig <- syscall.SIGTERM 248 }() 249 } 250 251 <-sig 252 253 model.Remove() 254 255 if *trace != "" { 256 _ = simulator.TraceFile.Close() 257 } 258 } 259 260 func updateHostTemplate(ip string) error { 261 addr, port, err := net.SplitHostPort(ip) 262 if err != nil { 263 return err 264 } 265 if port != "0" { // server starts after the model is created, skipping auto-selected ports for now 266 n, err := strconv.Atoi(port) 267 if err != nil { 268 return err 269 } 270 esx.HostSystem.Summary.Config.Port = int32(n) 271 } 272 273 nics := [][]types.HostVirtualNic{ 274 esx.HostConfigInfo.Network.Vnic, 275 esx.HostConfigInfo.Vmotion.NetConfig.CandidateVnic, 276 } 277 278 for _, nic := range esx.HostConfigInfo.VirtualNicManagerInfo.NetConfig { 279 nics = append(nics, nic.CandidateVnic) 280 } 281 282 for _, nic := range nics { 283 for i := range nic { 284 nic[i].Spec.Ip.IpAddress = addr // replace "127.0.0.1" with $addr 285 } 286 } 287 288 return nil 289 } 290 291 func secret(s *string) string { 292 val, err := session.Secret(*s) 293 if err != nil { 294 log.Fatal(err) 295 } 296 return val 297 }