github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/cmd/hyper-control/netbootVm.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  
     6  	hyperclient "github.com/Cloud-Foundations/Dominator/hypervisor/client"
     7  	"github.com/Cloud-Foundations/Dominator/lib/errors"
     8  	"github.com/Cloud-Foundations/Dominator/lib/log"
     9  	"github.com/Cloud-Foundations/Dominator/lib/srpc"
    10  	fm_proto "github.com/Cloud-Foundations/Dominator/proto/fleetmanager"
    11  	hyper_proto "github.com/Cloud-Foundations/Dominator/proto/hypervisor"
    12  )
    13  
    14  func netbootVmSubcommand(args []string, logger log.DebugLogger) error {
    15  	err := netbootVm(logger)
    16  	if err != nil {
    17  		return fmt.Errorf("Error netbooting VM: %s", err)
    18  	}
    19  	return nil
    20  }
    21  
    22  func netbootVm(logger log.DebugLogger) error {
    23  	if len(subnetIDs) < 1 {
    24  		return errors.New("no subnetIDs specified")
    25  	}
    26  	fmCR := srpc.NewClientResource("tcp",
    27  		fmt.Sprintf("%s:%d", *fleetManagerHostname, *fleetManagerPortNum))
    28  	defer fmCR.ScheduleClose()
    29  	imageClient, err := srpc.DialHTTP("tcp", fmt.Sprintf("%s:%d",
    30  		*imageServerHostname, *imageServerPortNum), 0)
    31  	if err != nil {
    32  		return fmt.Errorf("%s: %s", *imageServerHostname, err)
    33  	}
    34  	defer imageClient.Close()
    35  	var hypervisorAddresses []string
    36  	if *hypervisorHostname != "" {
    37  		hypervisorAddresses = append(hypervisorAddresses,
    38  			fmt.Sprintf("%s:%d", *hypervisorHostname, *hypervisorPortNum))
    39  	} else {
    40  		hypervisorAddresses, err = listConnectedHypervisorsInLocation(fmCR,
    41  			*location)
    42  		if err != nil {
    43  			return err
    44  		}
    45  	}
    46  	if len(hypervisorAddresses) < 1 {
    47  		return errors.New("no nearby Hypervisors available")
    48  	}
    49  	logger.Debugf(0, "Selected %s as boot server on subnet: %s\n",
    50  		hypervisorAddresses[0], subnetIDs[0])
    51  	hyperCR := srpc.NewClientResource("tcp", hypervisorAddresses[0])
    52  	defer hyperCR.ScheduleClose()
    53  	client, err := hyperCR.GetHTTP(nil, 0)
    54  	if err != nil {
    55  		return err
    56  	}
    57  	defer client.Put()
    58  	hypervisorSubnets, err := hyperclient.ListSubnets(client, false)
    59  	if err != nil {
    60  		return err
    61  	}
    62  	subnetTable := make(map[string]hyper_proto.Subnet, len(hypervisorSubnets))
    63  	for _, subnet := range hypervisorSubnets {
    64  		subnetTable[subnet.Id] = subnet
    65  	}
    66  	var subnets []*hyper_proto.Subnet
    67  	for _, subnetId := range subnetIDs {
    68  		if subnet, ok := subnetTable[subnetId]; !ok {
    69  			return fmt.Errorf("subnet: %s not available on: %s",
    70  				subnetId, hypervisorAddresses[0])
    71  		} else {
    72  			subnets = append(subnets, &subnet)
    73  		}
    74  	}
    75  	info := fm_proto.GetMachineInfoResponse{Subnets: subnets}
    76  	createRequest := hyper_proto.CreateVmRequest{
    77  		DhcpTimeout:      -1,
    78  		EnableNetboot:    true,
    79  		MinimumFreeBytes: uint64(volumeSizes[0]),
    80  		VmInfo: hyper_proto.VmInfo{
    81  			ConsoleType:        hyper_proto.ConsoleVNC,
    82  			Hostname:           "netboot-test",
    83  			MemoryInMiB:        uint64(memory >> 20),
    84  			MilliCPUs:          1000,
    85  			SecondarySubnetIDs: subnetIDs[1:],
    86  			SubnetId:           subnetIDs[0],
    87  		},
    88  	}
    89  	if createRequest.VmInfo.MemoryInMiB < 1 {
    90  		createRequest.VmInfo.MemoryInMiB = 1024
    91  	}
    92  	for _, size := range volumeSizes[1:] {
    93  		createRequest.SecondaryVolumes = append(createRequest.SecondaryVolumes,
    94  			hyper_proto.Volume{Size: uint64(size)})
    95  	}
    96  	var createResponse hyper_proto.CreateVmResponse
    97  	err = hyperclient.CreateVm(client, createRequest, &createResponse, logger)
    98  	if err != nil {
    99  		return err
   100  	}
   101  	err = hyperclient.AcknowledgeVm(client, createResponse.IpAddress)
   102  	if err != nil {
   103  		return err
   104  	}
   105  	logger.Printf("created VM: %s\n", createResponse.IpAddress)
   106  	vmInfo, err := hyperclient.GetVmInfo(client, createResponse.IpAddress)
   107  	if err != nil {
   108  		return err
   109  	}
   110  	vncErrChannel := make(chan error, 1)
   111  	if *vncViewer == "" {
   112  		vncErrChannel <- nil
   113  	} else {
   114  		defer hyperclient.DestroyVm(client, createResponse.IpAddress, nil)
   115  		client, err := srpc.DialHTTP("tcp", hypervisorAddresses[0], 0)
   116  		if err != nil {
   117  			return err
   118  		}
   119  		go func() {
   120  			defer client.Close()
   121  			vncErrChannel <- hyperclient.ConnectToVmConsole(client,
   122  				vmInfo.Address.IpAddress, *vncViewer, logger)
   123  		}()
   124  	}
   125  	info.Machine.NetworkEntry = fm_proto.NetworkEntry{
   126  		Hostname:      vmInfo.Hostname,
   127  		HostIpAddress: vmInfo.Address.IpAddress,
   128  		SubnetId:      subnetIDs[0],
   129  	}
   130  	err = info.Machine.HostMacAddress.UnmarshalText(
   131  		[]byte(vmInfo.Address.MacAddress))
   132  	if err != nil {
   133  		return err
   134  	}
   135  	for index, subnetId := range subnetIDs {
   136  		if index < 1 {
   137  			continue
   138  		}
   139  		address := vmInfo.SecondaryAddresses[index-1]
   140  		var hwAddr fm_proto.HardwareAddr
   141  		if err := hwAddr.UnmarshalText([]byte(address.MacAddress)); err != nil {
   142  			return err
   143  		}
   144  		info.Machine.SecondaryNetworkEntries = append(
   145  			info.Machine.SecondaryNetworkEntries, fm_proto.NetworkEntry{
   146  				HostIpAddress:  address.IpAddress,
   147  				HostMacAddress: hwAddr,
   148  				SubnetId:       subnetId,
   149  			})
   150  	}
   151  	configFiles, err := makeConfigFiles(info, "", getNetworkEntries(info),
   152  		false)
   153  	netbootRequest := hyper_proto.NetbootMachineRequest{
   154  		Address:                      vmInfo.Address,
   155  		Files:                        configFiles,
   156  		FilesExpiration:              *netbootFilesTimeout,
   157  		Hostname:                     vmInfo.Hostname,
   158  		NumAcknowledgementsToWaitFor: *numAcknowledgementsToWaitFor,
   159  		OfferExpiration:              *offerTimeout,
   160  		WaitTimeout:                  *netbootTimeout,
   161  	}
   162  	var netbootResponse hyper_proto.NetbootMachineResponse
   163  	err = client.RequestReply("Hypervisor.NetbootMachine", netbootRequest,
   164  		&netbootResponse)
   165  	if err != nil {
   166  		return err
   167  	}
   168  	if err := errors.New(netbootResponse.Error); err != nil {
   169  		return err
   170  	}
   171  	logger.Println("waiting for console exit")
   172  	return <-vncErrChannel
   173  }