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  }