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