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