Skip to content

Commit

Permalink
Merge pull request #632 from rgooch/master
Browse files Browse the repository at this point in the history
Add remote console (VNC) support for VMs.
  • Loading branch information
rgooch committed Aug 1, 2019
2 parents 89ee803 + 16119fa commit d8a2919
Show file tree
Hide file tree
Showing 19 changed files with 430 additions and 14 deletions.
3 changes: 3 additions & 0 deletions cmd/vm-control/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@ vm-control -h
Some of the sub-commands available are:

- **become-primary-vm-owner**: become the primary owner of a VM
- **change-vm-console-type**: change the console type for a VM
- **change-vm-destroy-protection**: enable/disable destroy protect for a VM
- **change-vm-owner-users**: change the extra owners for a VM
- **change-vm-tags**: change the tags for a VM
- **connect-to-vm-console**: connect to the Virtual Network Console for the
specified VM
- **connect-to-vm-serial-port**: connect to the specified VM serial port
- **copy-vm**: make a copy of a VM
- **create-vm**: create a VM
Expand Down
47 changes: 47 additions & 0 deletions cmd/vm-control/changeVmConsoleType.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package main

import (
"fmt"
"net"

"github.com/Symantec/Dominator/lib/errors"
"github.com/Symantec/Dominator/lib/log"
proto "github.com/Symantec/Dominator/proto/hypervisor"
)

func changeVmConsoleTypeSubcommand(args []string,
logger log.DebugLogger) error {
if err := changeVmConsoleType(args[0], logger); err != nil {
return fmt.Errorf("Error changing VM console type: %s", err)
}
return nil
}

func changeVmConsoleType(vmHostname string,
logger log.DebugLogger) error {
if vmIP, hypervisor, err := lookupVmAndHypervisor(vmHostname); err != nil {
return err
} else {
return changeVmConsoleTypeOnHypervisor(hypervisor, vmIP, logger)
}
}

func changeVmConsoleTypeOnHypervisor(hypervisor string, ipAddr net.IP,
logger log.DebugLogger) error {
request := proto.ChangeVmConsoleTypeRequest{
ConsoleType: consoleType,
IpAddress: ipAddr,
}
client, err := dialHypervisor(hypervisor)
if err != nil {
return err
}
defer client.Close()
var reply proto.ChangeVmOwnerUsersResponse
err = client.RequestReply("Hypervisor.ChangeVmConsoleType",
request, &reply)
if err != nil {
return err
}
return errors.New(reply.Error)
}
99 changes: 99 additions & 0 deletions cmd/vm-control/connectToVmConsole.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package main

import (
"fmt"
"io"
"net"
"os"
"os/exec"

"github.com/Symantec/Dominator/lib/bufwriter"
"github.com/Symantec/Dominator/lib/errors"
"github.com/Symantec/Dominator/lib/log"
proto "github.com/Symantec/Dominator/proto/hypervisor"
)

func connectToVmConsoleSubcommand(args []string,
logger log.DebugLogger) error {
if err := connectToVmConsole(args[0], logger); err != nil {
return fmt.Errorf("Error connecting to VM console: %s", err)
}
return nil
}

func connectToVmConsole(vmHostname string, logger log.DebugLogger) error {
if vmIP, hypervisor, err := lookupVmAndHypervisor(vmHostname); err != nil {
return err
} else {
return connectToVmConsoleOnHypervisor(hypervisor, vmIP, logger)
}
}

func connectToVmConsoleOnHypervisor(hypervisor string, ipAddr net.IP,
logger log.DebugLogger) error {
client, err := dialHypervisor(hypervisor)
if err != nil {
return err
}
defer client.Close()
serverConn, err := client.Call("Hypervisor.ConnectToVmConsole")
if err != nil {
return err
}
defer serverConn.Close()
request := proto.ConnectToVmConsoleRequest{
IpAddress: ipAddr,
}
if err := serverConn.Encode(request); err != nil {
return err
}
if err := serverConn.Flush(); err != nil {
return err
}
var response proto.ConnectToVmConsoleResponse
if err := serverConn.Decode(&response); err != nil {
return err
}
if err := errors.New(response.Error); err != nil {
return err
}
listener, err := net.Listen("tcp", "localhost:")
if err != nil {
return err
}
defer listener.Close()
_, port, err := net.SplitHostPort(listener.Addr().String())
if err != nil {
return err
}
if *vncViewer == "" {
fmt.Fprintf(os.Stderr, "Listening on port %s for VNC connection\n",
port)
} else {
cmd := exec.Command(*vncViewer, "::"+port)
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
logger.Println(err)
} else {
fmt.Fprintf(os.Stderr, "Listening on port %s for VNC connection\n",
port)
}
}
clientConn, err := listener.Accept()
if err != nil {
return err
}
listener.Close()
closed := false
go func() { // Copy from server to client.
_, err := io.Copy(clientConn, serverConn)
if err != nil && !closed {
logger.Fatalln(err)
}
os.Exit(0)
}()
// Copy from client to server.
_, err = io.Copy(bufwriter.NewAutoFlushWriter(serverConn), clientConn)
closed = true
return err
}
2 changes: 1 addition & 1 deletion cmd/vm-control/connectToVmSerialPort.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func connectToVmSerialPortOnHypervisor(hypervisor string, ipAddr net.IP,
if err := conn.Flush(); err != nil {
return err
}
var response proto.ChangeVmTagsResponse
var response proto.ConnectToVmSerialPortResponse
if err := conn.Decode(&response); err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions cmd/vm-control/copyVm.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ func copyVmFromHypervisor(sourceHypervisorAddress string, vmIP net.IP,
return err
}
vmInfo := createVmInfoFromFlags()
vmInfo.ConsoleType = sourceVmInfo.ConsoleType
vmInfo.DestroyProtection = vmInfo.DestroyProtection ||
sourceVmInfo.DestroyProtection
if vmInfo.Hostname == "" {
Expand Down
1 change: 1 addition & 0 deletions cmd/vm-control/createVm.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ func createVm(logger log.DebugLogger) error {

func createVmInfoFromFlags() hyper_proto.VmInfo {
return hyper_proto.VmInfo{
ConsoleType: consoleType,
DestroyProtection: *destroyProtection,
Hostname: *vmHostname,
MemoryInMiB: uint64(memory >> 20),
Expand Down
1 change: 1 addition & 0 deletions cmd/vm-control/importVirshVm.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ func importVirshVm(macAddr, domainName string, sAddrs []proto.Address,
tags["Name"] = domainName
}
request := proto.ImportLocalVmRequest{VmInfo: proto.VmInfo{
ConsoleType: consoleType,
Hostname: domainName,
OwnerGroups: ownerGroups,
OwnerUsers: ownerUsers,
Expand Down
14 changes: 12 additions & 2 deletions cmd/vm-control/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ import (
"github.com/Symantec/Dominator/lib/net/rrdialer"
"github.com/Symantec/Dominator/lib/srpc/setupclient"
"github.com/Symantec/Dominator/lib/tags"
hyper_proto "github.com/Symantec/Dominator/proto/hypervisor"
)

var (
adjacentVM = flag.String("adjacentVM", "",
"IP address of VM adjacent (same Hypervisor) to VM being created")
consoleType hyper_proto.ConsoleType
destroyProtection = flag.Bool("destroyProtection", false,
"If true, do not destroy running VM")
dhcpTimeout = flag.Duration("dhcpTimeout", time.Minute,
Expand Down Expand Up @@ -75,8 +77,10 @@ var (
"If true, trace metadata calls until interrupted")
userDataFile = flag.String("userDataFile", "",
"Name file containing user-data accessible from the metadata server")
vmHostname = flag.String("vmHostname", "", "Hostname for VM")
vmTags tags.Tags
vmHostname = flag.String("vmHostname", "", "Hostname for VM")
vmTags tags.Tags
vncViewer = flag.String("vncViewer", defaultVncViewer,
"Path to VNC viewer")
volumeFilename = flag.String("volumeFilename", "",
"Name of file to write volume data to")
volumeIndex = flag.Uint("volumeIndex", 0,
Expand All @@ -87,6 +91,8 @@ var (
)

func init() {
flag.Var(&consoleType, "consoleType",
"type of graphical console (default none)")
flag.Var(&memory, "memory", "memory (default 1GiB)")
flag.Var(&minFreeBytes, "minFreeBytes",
"minimum number of free bytes in root volume")
Expand All @@ -106,9 +112,11 @@ func printUsage() {
flag.PrintDefaults()
fmt.Fprintln(os.Stderr, "Commands:")
fmt.Fprintln(os.Stderr, " become-primary-vm-owner IPaddr")
fmt.Fprintln(os.Stderr, " change-vm-console-type IPaddr")
fmt.Fprintln(os.Stderr, " change-vm-destroy-protection IPaddr")
fmt.Fprintln(os.Stderr, " change-vm-owner-users IPaddr")
fmt.Fprintln(os.Stderr, " change-vm-tags IPaddr")
fmt.Fprintln(os.Stderr, " connect-to-vm-console IPaddr")
fmt.Fprintln(os.Stderr, " connect-to-vm-serial-port IPaddr")
fmt.Fprintln(os.Stderr, " copy-vm IPaddr")
fmt.Fprintln(os.Stderr, " create-vm")
Expand Down Expand Up @@ -154,9 +162,11 @@ type subcommand struct {

var subcommands = []subcommand{
{"become-primary-vm-owner", 1, 1, becomePrimaryVmOwnerSubcommand},
{"change-vm-console-type", 1, 1, changeVmConsoleTypeSubcommand},
{"change-vm-destroy-protection", 1, 1, changeVmDestroyProtectionSubcommand},
{"change-vm-owner-users", 1, 1, changeVmOwnerUsersSubcommand},
{"change-vm-tags", 1, 1, changeVmTagsSubcommand},
{"connect-to-vm-console", 1, 1, connectToVmConsoleSubcommand},
{"connect-to-vm-serial-port", 1, 1, connectToVmSerialPortSubcommand},
{"copy-vm", 1, 1, copyVmSubcommand},
{"create-vm", 0, 0, createVmSubcommand},
Expand Down
3 changes: 3 additions & 0 deletions cmd/vm-control/variables_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package main

const defaultVncViewer = ""
3 changes: 3 additions & 0 deletions cmd/vm-control/variables_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package main

const defaultVncViewer = "vncviewer"
10 changes: 10 additions & 0 deletions hypervisor/manager/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ func (m *Manager) ChangeOwners(ownerGroups, ownerUsers []string) error {
return m.changeOwners(ownerGroups, ownerUsers)
}

func (m *Manager) ChangeVmConsoleType(ipAddr net.IP,
authInfo *srpc.AuthInformation, consoleType proto.ConsoleType) error {
return m.changeVmConsoleType(ipAddr, authInfo, consoleType)
}

func (m *Manager) ChangeVmDestroyProtection(ipAddr net.IP,
authInfo *srpc.AuthInformation, destroyProtection bool) error {
return m.changeVmDestroyProtection(ipAddr, authInfo, destroyProtection)
Expand Down Expand Up @@ -133,6 +138,11 @@ func (m *Manager) CommitImportedVm(ipAddr net.IP,
return m.commitImportedVm(ipAddr, authInfo)
}

func (m *Manager) ConnectToVmConsole(ipAddr net.IP,
authInfo *srpc.AuthInformation) (net.Conn, error) {
return m.connectToVmConsole(ipAddr, authInfo)
}

func (m *Manager) ConnectToVmSerialPort(ipAddr net.IP,
authInfo *srpc.AuthInformation,
portNumber uint) (chan<- byte, <-chan byte, error) {
Expand Down
57 changes: 53 additions & 4 deletions hypervisor/manager/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@ func (m *Manager) acknowledgeVm(ipAddr net.IP,

func (m *Manager) allocateVm(req proto.CreateVmRequest,
authInfo *srpc.AuthInformation) (*vmInfoType, error) {
if err := req.ConsoleType.CheckValid(); err != nil {
return nil, err
}
if req.MemoryInMiB < 1 {
return nil, errors.New("no memory specified")
}
Expand Down Expand Up @@ -276,6 +279,7 @@ func (m *Manager) allocateVm(req proto.CreateVmRequest,
LocalVmInfo: proto.LocalVmInfo{
VmInfo: proto.VmInfo{
Address: address,
ConsoleType: req.ConsoleType,
DestroyProtection: req.DestroyProtection,
Hostname: req.Hostname,
ImageName: req.ImageName,
Expand Down Expand Up @@ -328,6 +332,24 @@ func (m *Manager) becomePrimaryVmOwner(ipAddr net.IP,
return nil
}

func (m *Manager) changeVmConsoleType(ipAddr net.IP,
authInfo *srpc.AuthInformation, consoleType proto.ConsoleType) error {
if err := consoleType.CheckValid(); err != nil {
return err
}
vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, nil)
if err != nil {
return err
}
defer vm.mutex.Unlock()
if vm.State != proto.StateStopped {
return errors.New("VM is not stopped")
}
vm.ConsoleType = consoleType
vm.writeAndSendInfo()
return nil
}

func (m *Manager) changeVmDestroyProtection(ipAddr net.IP,
authInfo *srpc.AuthInformation, destroyProtection bool) error {
vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, nil)
Expand Down Expand Up @@ -403,6 +425,26 @@ func (m *Manager) commitImportedVm(ipAddr net.IP,
return nil
}

func (m *Manager) connectToVmConsole(ipAddr net.IP,
authInfo *srpc.AuthInformation) (net.Conn, error) {
vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, nil)
if err != nil {
return nil, err
}
defer vm.mutex.Unlock()
if vm.State != proto.StateRunning {
return nil, errors.New("VM is not running")
}
if vm.ConsoleType != proto.ConsoleVNC {
return nil, errors.New("VNC console is not enabled")
}
console, err := net.Dial("unix", filepath.Join(vm.dirname, "vnc"))
if err != nil {
return nil, err
}
return console, nil
}

func (m *Manager) connectToVmSerialPort(ipAddr net.IP,
authInfo *srpc.AuthInformation,
portNumber uint) (chan<- byte, <-chan byte, error) {
Expand Down Expand Up @@ -2819,10 +2861,17 @@ func (vm *vmInfoType) startVm(haveManagerLock bool) error {
cmd.Args = append(cmd.Args, netOptions...)
if vm.manager.ShowVgaConsole {
cmd.Args = append(cmd.Args, "-vga", "std")
} else if vm.getActiveKernelPath() == "" { // Have a bootloader.
cmd.Args = append(cmd.Args, "-display", "none", "-vga", "std")
} else { // No bootloader: go minimalist.
cmd.Args = append(cmd.Args, "-nographic")
} else {
switch vm.ConsoleType {
case proto.ConsoleNone:
cmd.Args = append(cmd.Args, "-nographic")
case proto.ConsoleDummy:
cmd.Args = append(cmd.Args, "-display", "none", "-vga", "std")
case proto.ConsoleVNC:
cmd.Args = append(cmd.Args,
"-display", "vnc=unix:"+filepath.Join(vm.dirname, "vnc"),
"-vga", "std")
}
}
for index, volume := range vm.VolumeLocations {
var volumeFormat proto.VolumeFormat
Expand Down
2 changes: 2 additions & 0 deletions hypervisor/rpcd/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,12 @@ func Setup(manager *manager.Manager, dhcpServer DhcpServer,
PublicMethods: []string{
"AcknowledgeVm",
"BecomePrimaryVmOwner",
"ChangeVmConsoleType",
"ChangeVmDestroyProtection",
"ChangeVmOwnerUsers",
"ChangeVmTags",
"CommitImportedVm",
"ConnectToVmConsole",
"ConnectToVmSerialPort",
"CopyVm",
"CreateVm",
Expand Down
Loading

0 comments on commit d8a2919

Please sign in to comment.