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  }