github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/cmd/hypervisor/main.go (about)

     1  package main
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  	"syscall"
    10  
    11  	"github.com/Cloud-Foundations/Dominator/hypervisor/dhcpd"
    12  	"github.com/Cloud-Foundations/Dominator/hypervisor/httpd"
    13  	"github.com/Cloud-Foundations/Dominator/hypervisor/manager"
    14  	"github.com/Cloud-Foundations/Dominator/hypervisor/metadatad"
    15  	"github.com/Cloud-Foundations/Dominator/hypervisor/rpcd"
    16  	"github.com/Cloud-Foundations/Dominator/hypervisor/tftpbootd"
    17  	"github.com/Cloud-Foundations/Dominator/lib/constants"
    18  	"github.com/Cloud-Foundations/Dominator/lib/flags/loadflags"
    19  	"github.com/Cloud-Foundations/Dominator/lib/flagutil"
    20  	"github.com/Cloud-Foundations/Dominator/lib/log/serverlogger"
    21  	"github.com/Cloud-Foundations/Dominator/lib/net"
    22  	"github.com/Cloud-Foundations/Dominator/lib/srpc/setupserver"
    23  	"github.com/Cloud-Foundations/tricorder/go/tricorder"
    24  )
    25  
    26  const (
    27  	dirPerms = syscall.S_IRWXU | syscall.S_IRGRP | syscall.S_IXGRP |
    28  		syscall.S_IROTH | syscall.S_IXOTH
    29  )
    30  
    31  var (
    32  	dhcpServerOnBridgesOnly = flag.Bool("dhcpServerOnBridgesOnly", false,
    33  		"If true, run the DHCP server on bridge interfaces only")
    34  	imageServerHostname = flag.String("imageServerHostname", "localhost",
    35  		"Hostname of image server")
    36  	imageServerPortNum = flag.Uint("imageServerPortNum",
    37  		constants.ImageServerPortNumber,
    38  		"Port number of image server")
    39  	networkBootImage = flag.String("networkBootImage", "pxelinux.0",
    40  		"Name of boot image passed via DHCP option")
    41  	objectCacheSize = flagutil.Size(10 << 30)
    42  	portNum         = flag.Uint("portNum", constants.HypervisorPortNumber,
    43  		"Port number to allocate and listen on for HTTP/RPC")
    44  	showVGA  = flag.Bool("showVGA", false, "If true, show VGA console")
    45  	stateDir = flag.String("stateDir", "/var/lib/hypervisor",
    46  		"Name of state directory")
    47  	testMemoryAvailable = flag.Uint64("testMemoryAvailable", 0,
    48  		"test if memory is allocatable and exit (units of MiB)")
    49  	tftpbootImageStream = flag.String("tftpbootImageStream", "",
    50  		"Name of default image stream for network booting")
    51  	username = flag.String("username", "nobody",
    52  		"Name of user to run VMs")
    53  	volumeDirectories flagutil.StringList
    54  )
    55  
    56  func init() {
    57  	flag.Var(&objectCacheSize, "objectCacheSize",
    58  		"maximum size of object cache")
    59  	flag.Var(&volumeDirectories, "volumeDirectories",
    60  		"Comma separated list of volume directories. If empty, scan for space")
    61  }
    62  
    63  func printUsage() {
    64  	fmt.Fprintln(os.Stderr,
    65  		"Usage: hypervisor [flags...] [run|stop|stop-vms-on-next-stop]")
    66  	fmt.Fprintln(os.Stderr, "Common flags:")
    67  	flag.PrintDefaults()
    68  }
    69  
    70  func processCommand(args []string) {
    71  	if len(args) < 1 {
    72  		return
    73  	} else if len(args) > 1 {
    74  		printUsage()
    75  		os.Exit(2)
    76  	}
    77  	switch args[0] {
    78  	case "run":
    79  		return
    80  	case "stop":
    81  		requestStop()
    82  	case "stop-vms-on-next-stop":
    83  		configureVMsToStopOnNextStop()
    84  	default:
    85  		printUsage()
    86  		os.Exit(2)
    87  	}
    88  }
    89  
    90  func main() {
    91  	if err := loadflags.LoadForDaemon("hypervisor"); err != nil {
    92  		fmt.Fprintln(os.Stderr, err)
    93  		os.Exit(1)
    94  	}
    95  	flag.Usage = printUsage
    96  	flag.Parse()
    97  	processCommand(flag.Args())
    98  	if *testMemoryAvailable > 0 {
    99  		nBytes := *testMemoryAvailable << 20
   100  		mem := make([]byte, nBytes)
   101  		for pos := uint64(0); pos < nBytes; pos += 4096 {
   102  			mem[pos] = 0
   103  		}
   104  		os.Exit(0)
   105  	}
   106  	tricorder.RegisterFlags()
   107  	if os.Geteuid() != 0 {
   108  		fmt.Fprintln(os.Stderr, "Must run the Hypervisor as root")
   109  		os.Exit(1)
   110  	}
   111  	logger := serverlogger.New("")
   112  	if err := setupserver.SetupTls(); err != nil {
   113  		logger.Fatalln(err)
   114  	}
   115  	if err := os.MkdirAll(*stateDir, dirPerms); err != nil {
   116  		logger.Fatalf("Cannot create state directory: %s\n", err)
   117  	}
   118  	bridges, bridgeMap, err := net.ListBroadcastInterfaces(
   119  		net.InterfaceTypeBridge, logger)
   120  	if err != nil {
   121  		logger.Fatalf("Cannot list bridges: %s\n", err)
   122  	}
   123  	dhcpInterfaces := make([]string, 0, len(bridges))
   124  	vlanIdToBridge := make(map[uint]string)
   125  	for _, bridge := range bridges {
   126  		if vlanId, err := net.GetBridgeVlanId(bridge.Name); err != nil {
   127  			logger.Fatalf("Cannot get VLAN Id for bridge: %s: %s\n",
   128  				bridge.Name, err)
   129  		} else if vlanId < 0 {
   130  			logger.Printf("Bridge: %s has no EtherNet port, ignoring\n",
   131  				bridge.Name)
   132  		} else {
   133  			if *dhcpServerOnBridgesOnly {
   134  				dhcpInterfaces = append(dhcpInterfaces, bridge.Name)
   135  			}
   136  			if !strings.HasPrefix(bridge.Name, "br@") {
   137  				vlanIdToBridge[uint(vlanId)] = bridge.Name
   138  				logger.Printf("Bridge: %s, VLAN Id: %d\n", bridge.Name, vlanId)
   139  			}
   140  		}
   141  	}
   142  	dhcpServer, err := dhcpd.New(dhcpInterfaces,
   143  		filepath.Join(*stateDir, "dynamic-leases.json"), logger)
   144  	if err != nil {
   145  		logger.Fatalf("Cannot start DHCP server: %s\n", err)
   146  	}
   147  	if err := dhcpServer.SetNetworkBootImage(*networkBootImage); err != nil {
   148  		logger.Fatalf("Cannot set NetworkBootImage name: %s\n", err)
   149  	}
   150  	imageServerAddress := fmt.Sprintf("%s:%d",
   151  		*imageServerHostname, *imageServerPortNum)
   152  	tftpbootServer, err := tftpbootd.New(imageServerAddress,
   153  		*tftpbootImageStream, logger)
   154  	if err != nil {
   155  		logger.Fatalf("Cannot start tftpboot server: %s\n", err)
   156  	}
   157  	managerObj, err := manager.New(manager.StartOptions{
   158  		BridgeMap:          bridgeMap,
   159  		DhcpServer:         dhcpServer,
   160  		ImageServerAddress: imageServerAddress,
   161  		Logger:             logger,
   162  		ObjectCacheBytes:   uint64(objectCacheSize),
   163  		ShowVgaConsole:     *showVGA,
   164  		StateDir:           *stateDir,
   165  		Username:           *username,
   166  		VlanIdToBridge:     vlanIdToBridge,
   167  		VolumeDirectories:  volumeDirectories,
   168  	})
   169  	if err != nil {
   170  		logger.Fatalf("Cannot start hypervisor: %s\n", err)
   171  	}
   172  	if err := listenForControl(managerObj, logger); err != nil {
   173  		logger.Fatalf("Cannot listen for control: %s\n", err)
   174  	}
   175  	httpd.AddHtmlWriter(managerObj)
   176  	if len(bridges) < 1 {
   177  		logger.Println("No bridges found: entering log-only mode")
   178  	} else {
   179  		rpcHtmlWriter, err := rpcd.Setup(managerObj, dhcpServer, tftpbootServer,
   180  			logger)
   181  		if err != nil {
   182  			logger.Fatalf("Cannot start rpcd: %s\n", err)
   183  		}
   184  		httpd.AddHtmlWriter(rpcHtmlWriter)
   185  	}
   186  	httpd.AddHtmlWriter(logger)
   187  	err = metadatad.StartServer(*portNum, bridges, managerObj, logger)
   188  	if err != nil {
   189  		logger.Fatalf("Cannot start metadata server: %s\n", err)
   190  	}
   191  	if err := httpd.StartServer(*portNum, managerObj, false); err != nil {
   192  		logger.Fatalf("Unable to create http server: %s\n", err)
   193  	}
   194  }