mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-05-01 21:24:36 +00:00
console-watcher: use console watcher to monitor guest console outputs
Import new console watcher to monitor guest console outputs, and will be only effective when we turn on enable_debug option. Guest console outputs may include guest kernel debug info, agent debug info, etc. Fixes: #389 Signed-off-by: Penny Zheng penny.zheng@arm.com
This commit is contained in:
parent
1099a28830
commit
7f3e8959c5
@ -254,7 +254,7 @@ func (a *Acrn) buildDevices(imagePath string) ([]Device, error) {
|
|||||||
return nil, fmt.Errorf("Image Path should not be empty: %s", imagePath)
|
return nil, fmt.Errorf("Image Path should not be empty: %s", imagePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
console, err := a.getSandboxConsole(a.id)
|
_, console, err := a.getSandboxConsole(a.id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -643,11 +643,16 @@ func (a *Acrn) addDevice(devInfo interface{}, devType deviceType) error {
|
|||||||
|
|
||||||
// getSandboxConsole builds the path of the console where we can read
|
// getSandboxConsole builds the path of the console where we can read
|
||||||
// logs coming from the sandbox.
|
// logs coming from the sandbox.
|
||||||
func (a *Acrn) getSandboxConsole(id string) (string, error) {
|
func (a *Acrn) getSandboxConsole(id string) (string, string, error) {
|
||||||
span, _ := a.trace("getSandboxConsole")
|
span, _ := a.trace("getSandboxConsole")
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
|
|
||||||
return utils.BuildSocketPath(a.store.RunVMStoragePath(), id, acrnConsoleSocket)
|
consoleURL, err := utils.BuildSocketPath(a.store.RunVMStoragePath(), id, acrnConsoleSocket)
|
||||||
|
if err != nil {
|
||||||
|
return consoleProtoUnix, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return consoleProtoUnix, consoleURL, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Acrn) saveSandbox() error {
|
func (a *Acrn) saveSandbox() error {
|
||||||
|
@ -205,9 +205,10 @@ func TestAcrnGetSandboxConsole(t *testing.T) {
|
|||||||
sandboxID := "testSandboxID"
|
sandboxID := "testSandboxID"
|
||||||
expected := filepath.Join(a.store.RunVMStoragePath(), sandboxID, consoleSocket)
|
expected := filepath.Join(a.store.RunVMStoragePath(), sandboxID, consoleSocket)
|
||||||
|
|
||||||
result, err := a.getSandboxConsole(sandboxID)
|
proto, result, err := a.getSandboxConsole(sandboxID)
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
assert.Equal(result, expected)
|
assert.Equal(result, expected)
|
||||||
|
assert.Equal(proto, consoleProtoUnix)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAcrnCreateSandbox(t *testing.T) {
|
func TestAcrnCreateSandbox(t *testing.T) {
|
||||||
|
@ -9,8 +9,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -21,6 +19,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/containerd/console"
|
||||||
persistapi "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/persist/api"
|
persistapi "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/persist/api"
|
||||||
chclient "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/cloud-hypervisor/client"
|
chclient "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/cloud-hypervisor/client"
|
||||||
"github.com/opencontainers/selinux/go-selinux/label"
|
"github.com/opencontainers/selinux/go-selinux/label"
|
||||||
@ -123,6 +122,7 @@ type cloudHypervisor struct {
|
|||||||
vmconfig chclient.VmConfig
|
vmconfig chclient.VmConfig
|
||||||
virtiofsd Virtiofsd
|
virtiofsd Virtiofsd
|
||||||
store persistapi.PersistDriver
|
store persistapi.PersistDriver
|
||||||
|
console console.Console
|
||||||
}
|
}
|
||||||
|
|
||||||
var clhKernelParams = []Param{
|
var clhKernelParams = []Param{
|
||||||
@ -357,13 +357,12 @@ func (clh *cloudHypervisor) startSandbox(timeout int) error {
|
|||||||
return errors.New("cloud-hypervisor only supports virtio based file sharing")
|
return errors.New("cloud-hypervisor only supports virtio based file sharing")
|
||||||
}
|
}
|
||||||
|
|
||||||
var strErr string
|
pid, err := clh.LaunchClh()
|
||||||
strErr, pid, err := clh.LaunchClh()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if shutdownErr := clh.virtiofsd.Stop(); shutdownErr != nil {
|
if shutdownErr := clh.virtiofsd.Stop(); shutdownErr != nil {
|
||||||
clh.Logger().WithField("error", shutdownErr).Warn("error shutting down Virtiofsd")
|
clh.Logger().WithField("error", shutdownErr).Warn("error shutting down Virtiofsd")
|
||||||
}
|
}
|
||||||
return fmt.Errorf("failed to launch cloud-hypervisor: %q, hypervisor output:\n%s", err, strErr)
|
return fmt.Errorf("failed to launch cloud-hypervisor: %q", err)
|
||||||
}
|
}
|
||||||
clh.state.PID = pid
|
clh.state.PID = pid
|
||||||
|
|
||||||
@ -377,9 +376,16 @@ func (clh *cloudHypervisor) startSandbox(timeout int) error {
|
|||||||
|
|
||||||
// getSandboxConsole builds the path of the console where we can read
|
// getSandboxConsole builds the path of the console where we can read
|
||||||
// logs coming from the sandbox.
|
// logs coming from the sandbox.
|
||||||
func (clh *cloudHypervisor) getSandboxConsole(id string) (string, error) {
|
func (clh *cloudHypervisor) getSandboxConsole(id string) (string, string, error) {
|
||||||
clh.Logger().WithField("function", "getSandboxConsole").WithField("id", id).Info("Get Sandbox Console")
|
clh.Logger().WithField("function", "getSandboxConsole").WithField("id", id).Info("Get Sandbox Console")
|
||||||
return "", nil
|
master, slave, err := console.NewPty()
|
||||||
|
if err != nil {
|
||||||
|
clh.Logger().Debugf("Error create pseudo tty: %v", err)
|
||||||
|
return consoleProtoPty, "", err
|
||||||
|
}
|
||||||
|
clh.console = master
|
||||||
|
|
||||||
|
return consoleProtoPty, slave, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (clh *cloudHypervisor) disconnect() {
|
func (clh *cloudHypervisor) disconnect() {
|
||||||
@ -885,11 +891,11 @@ func (clh *cloudHypervisor) getAvailableVersion() error {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (clh *cloudHypervisor) LaunchClh() (string, int, error) {
|
func (clh *cloudHypervisor) LaunchClh() (int, error) {
|
||||||
|
|
||||||
clhPath, err := clh.clhPath()
|
clhPath, err := clh.clhPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
|
||||||
args := []string{cscAPIsocket, clh.state.apiSocket}
|
args := []string{cscAPIsocket, clh.state.apiSocket}
|
||||||
@ -913,14 +919,12 @@ func (clh *cloudHypervisor) LaunchClh() (string, int, error) {
|
|||||||
clh.Logger().WithField("args", strings.Join(args, " ")).Info()
|
clh.Logger().WithField("args", strings.Join(args, " ")).Info()
|
||||||
|
|
||||||
cmdHypervisor := exec.Command(clhPath, args...)
|
cmdHypervisor := exec.Command(clhPath, args...)
|
||||||
var hypervisorOutput io.ReadCloser
|
|
||||||
if clh.config.Debug {
|
if clh.config.Debug {
|
||||||
cmdHypervisor.Env = os.Environ()
|
cmdHypervisor.Env = os.Environ()
|
||||||
cmdHypervisor.Env = append(cmdHypervisor.Env, "RUST_BACKTRACE=full")
|
cmdHypervisor.Env = append(cmdHypervisor.Env, "RUST_BACKTRACE=full")
|
||||||
// Get StdoutPipe only for debug, without debug golang will redirect to /dev/null
|
if clh.console != nil {
|
||||||
hypervisorOutput, err = cmdHypervisor.StdoutPipe()
|
cmdHypervisor.Stderr = clh.console
|
||||||
if err != nil {
|
cmdHypervisor.Stdout = clh.console
|
||||||
return "", -1, err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -928,37 +932,15 @@ func (clh *cloudHypervisor) LaunchClh() (string, int, error) {
|
|||||||
|
|
||||||
err = utils.StartCmd(cmdHypervisor)
|
err = utils.StartCmd(cmdHypervisor)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := clh.waitVMM(clhTimeout); err != nil {
|
if err := clh.waitVMM(clhTimeout); err != nil {
|
||||||
clh.Logger().WithField("error", err).Warn("cloud-hypervisor init failed")
|
clh.Logger().WithField("error", err).Warn("cloud-hypervisor init failed")
|
||||||
var output string
|
return -1, err
|
||||||
|
|
||||||
if hypervisorOutput != nil {
|
|
||||||
b, errRead := ioutil.ReadAll(hypervisorOutput)
|
|
||||||
if errRead != nil {
|
|
||||||
output = "failed to read hypervisor output to get error information"
|
|
||||||
} else {
|
|
||||||
output = string(b)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
output = "Please enable hypervisor logging to get stdout information"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return output, -1, err
|
return cmdHypervisor.Process.Pid, nil
|
||||||
}
|
|
||||||
|
|
||||||
if clh.config.Debug {
|
|
||||||
cmdLogger := utils.NewProgramLogger("kata-hypervisor")
|
|
||||||
clh.Logger().Debugf("Starting process logger(%s) for hypervisor", cmdLogger)
|
|
||||||
if err := cmdLogger.StartLogger(hypervisorOutput); err != nil {
|
|
||||||
// Not critical to run a container, but output wont be logged
|
|
||||||
clh.Logger().Warnf("Failed start process logger(%s) %s", cmdLogger, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", cmdHypervisor.Process.Pid, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//###########################################################################
|
//###########################################################################
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -25,7 +24,6 @@ import (
|
|||||||
|
|
||||||
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/device/config"
|
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/device/config"
|
||||||
persistapi "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/persist/api"
|
persistapi "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/persist/api"
|
||||||
kataclient "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/agent/protocols/client"
|
|
||||||
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/firecracker/client"
|
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/firecracker/client"
|
||||||
models "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/firecracker/client/models"
|
models "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/firecracker/client/models"
|
||||||
ops "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/firecracker/client/operations"
|
ops "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/firecracker/client/operations"
|
||||||
@ -154,6 +152,8 @@ type firecracker struct {
|
|||||||
|
|
||||||
fcConfigPath string
|
fcConfigPath string
|
||||||
fcConfig *types.FcConfig // Parameters configured before VM starts
|
fcConfig *types.FcConfig // Parameters configured before VM starts
|
||||||
|
|
||||||
|
console console.Console
|
||||||
}
|
}
|
||||||
|
|
||||||
type firecrackerDevice struct {
|
type firecrackerDevice struct {
|
||||||
@ -398,13 +398,8 @@ func (fc *firecracker) fcInit(timeout int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if fc.config.Debug {
|
if fc.config.Debug {
|
||||||
stdin, err := fc.watchConsole()
|
cmd.Stderr = fc.console
|
||||||
if err != nil {
|
cmd.Stdout = fc.console
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Stderr = stdin
|
|
||||||
cmd.Stdout = stdin
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fc.Logger().WithField("hypervisor args", args).Debug()
|
fc.Logger().WithField("hypervisor args", args).Debug()
|
||||||
@ -1101,8 +1096,15 @@ func (fc *firecracker) hotplugRemoveDevice(devInfo interface{}, devType deviceTy
|
|||||||
|
|
||||||
// getSandboxConsole builds the path of the console where we can read
|
// getSandboxConsole builds the path of the console where we can read
|
||||||
// logs coming from the sandbox.
|
// logs coming from the sandbox.
|
||||||
func (fc *firecracker) getSandboxConsole(id string) (string, error) {
|
func (fc *firecracker) getSandboxConsole(id string) (string, string, error) {
|
||||||
return fmt.Sprintf("%s://%s:%d", kataclient.HybridVSockScheme, filepath.Join(fc.jailerRoot, defaultHybridVSocketName), vSockLogsPort), nil
|
master, slave, err := console.NewPty()
|
||||||
|
if err != nil {
|
||||||
|
fc.Logger().Debugf("Error create pseudo tty: %v", err)
|
||||||
|
return consoleProtoPty, "", err
|
||||||
|
}
|
||||||
|
fc.console = master
|
||||||
|
|
||||||
|
return consoleProtoPty, slave, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fc *firecracker) disconnect() {
|
func (fc *firecracker) disconnect() {
|
||||||
@ -1214,40 +1216,6 @@ func (fc *firecracker) generateSocket(id string) (interface{}, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fc *firecracker) watchConsole() (*os.File, error) {
|
|
||||||
master, slave, err := console.NewPty()
|
|
||||||
if err != nil {
|
|
||||||
fc.Logger().WithField("Error create pseudo tty", err).Debug()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
stdio, err := os.OpenFile(slave, syscall.O_RDWR, 0700)
|
|
||||||
if err != nil {
|
|
||||||
fc.Logger().WithError(err).Debugf("open pseudo tty %s", slave)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
scanner := bufio.NewScanner(master)
|
|
||||||
for scanner.Scan() {
|
|
||||||
fc.Logger().WithFields(logrus.Fields{
|
|
||||||
"sandbox": fc.id,
|
|
||||||
"vmconsole": scanner.Text(),
|
|
||||||
}).Infof("reading guest console")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
fc.Logger().Info("console watcher quits")
|
|
||||||
} else {
|
|
||||||
fc.Logger().WithError(err).Error("Failed to read guest console")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return stdio, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fc *firecracker) isRateLimiterBuiltin() bool {
|
func (fc *firecracker) isRateLimiterBuiltin() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -764,7 +764,7 @@ type hypervisor interface {
|
|||||||
hotplugRemoveDevice(devInfo interface{}, devType deviceType) (interface{}, error)
|
hotplugRemoveDevice(devInfo interface{}, devType deviceType) (interface{}, error)
|
||||||
resizeMemory(memMB uint32, memoryBlockSizeMB uint32, probe bool) (uint32, memoryDevice, error)
|
resizeMemory(memMB uint32, memoryBlockSizeMB uint32, probe bool) (uint32, memoryDevice, error)
|
||||||
resizeVCPUs(vcpus uint32) (uint32, uint32, error)
|
resizeVCPUs(vcpus uint32) (uint32, uint32, error)
|
||||||
getSandboxConsole(sandboxID string) (string, error)
|
getSandboxConsole(sandboxID string) (string, string, error)
|
||||||
disconnect()
|
disconnect()
|
||||||
capabilities() types.Capabilities
|
capabilities() types.Capabilities
|
||||||
hypervisorConfig() HypervisorConfig
|
hypervisorConfig() HypervisorConfig
|
||||||
|
@ -82,8 +82,8 @@ func (m *mockHypervisor) hotplugRemoveDevice(devInfo interface{}, devType device
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockHypervisor) getSandboxConsole(sandboxID string) (string, error) {
|
func (m *mockHypervisor) getSandboxConsole(sandboxID string) (string, string, error) {
|
||||||
return "", nil
|
return "", "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockHypervisor) resizeMemory(memMB uint32, memorySectionSizeMB uint32, probe bool) (uint32, memoryDevice, error) {
|
func (m *mockHypervisor) resizeMemory(memMB uint32, memorySectionSizeMB uint32, probe bool) (uint32, memoryDevice, error) {
|
||||||
|
@ -66,9 +66,11 @@ func TestMockHypervisorGetSandboxConsole(t *testing.T) {
|
|||||||
var m *mockHypervisor
|
var m *mockHypervisor
|
||||||
|
|
||||||
expected := ""
|
expected := ""
|
||||||
result, err := m.getSandboxConsole("testSandboxID")
|
expectedProto := ""
|
||||||
|
proto, result, err := m.getSandboxConsole("testSandboxID")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, result, expected)
|
assert.Equal(t, result, expected)
|
||||||
|
assert.Equal(t, proto, expectedProto)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMockHypervisorSaveSandbox(t *testing.T) {
|
func TestMockHypervisorSaveSandbox(t *testing.T) {
|
||||||
|
@ -380,7 +380,7 @@ func (q *qemu) createQmpSocket() ([]govmmQemu.QMPSocket, error) {
|
|||||||
func (q *qemu) buildDevices(initrdPath string) ([]govmmQemu.Device, *govmmQemu.IOThread, error) {
|
func (q *qemu) buildDevices(initrdPath string) ([]govmmQemu.Device, *govmmQemu.IOThread, error) {
|
||||||
var devices []govmmQemu.Device
|
var devices []govmmQemu.Device
|
||||||
|
|
||||||
console, err := q.getSandboxConsole(q.id)
|
_, console, err := q.getSandboxConsole(q.id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@ -1764,11 +1764,16 @@ func (q *qemu) addDevice(devInfo interface{}, devType deviceType) error {
|
|||||||
|
|
||||||
// getSandboxConsole builds the path of the console where we can read
|
// getSandboxConsole builds the path of the console where we can read
|
||||||
// logs coming from the sandbox.
|
// logs coming from the sandbox.
|
||||||
func (q *qemu) getSandboxConsole(id string) (string, error) {
|
func (q *qemu) getSandboxConsole(id string) (string, string, error) {
|
||||||
span, _ := q.trace("getSandboxConsole")
|
span, _ := q.trace("getSandboxConsole")
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
|
|
||||||
return utils.BuildSocketPath(q.store.RunVMStoragePath(), id, consoleSocket)
|
consoleURL, err := utils.BuildSocketPath(q.store.RunVMStoragePath(), id, consoleSocket)
|
||||||
|
if err != nil {
|
||||||
|
return consoleProtoUnix, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return consoleProtoUnix, consoleURL, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *qemu) saveSandbox() error {
|
func (q *qemu) saveSandbox() error {
|
||||||
|
@ -314,9 +314,10 @@ func TestQemuGetSandboxConsole(t *testing.T) {
|
|||||||
sandboxID := "testSandboxID"
|
sandboxID := "testSandboxID"
|
||||||
expected := filepath.Join(q.store.RunVMStoragePath(), sandboxID, consoleSocket)
|
expected := filepath.Join(q.store.RunVMStoragePath(), sandboxID, consoleSocket)
|
||||||
|
|
||||||
result, err := q.getSandboxConsole(sandboxID)
|
proto, result, err := q.getSandboxConsole(sandboxID)
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
assert.Equal(result, expected)
|
assert.Equal(result, expected)
|
||||||
|
assert.Equal(proto, consoleProtoUnix)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestQemuCapabilities(t *testing.T) {
|
func TestQemuCapabilities(t *testing.T) {
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
package virtcontainers
|
package virtcontainers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -189,6 +190,8 @@ type Sandbox struct {
|
|||||||
cgroupMgr *vccgroups.Manager
|
cgroupMgr *vccgroups.Manager
|
||||||
|
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
|
||||||
|
cw *consoleWatcher
|
||||||
}
|
}
|
||||||
|
|
||||||
// ID returns the sandbox identifier string.
|
// ID returns the sandbox identifier string.
|
||||||
@ -929,6 +932,103 @@ func (s *Sandbox) ListRoutes() ([]*pbTypes.Route, error) {
|
|||||||
return s.agent.listRoutes()
|
return s.agent.listRoutes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// unix socket type of console
|
||||||
|
consoleProtoUnix = "unix"
|
||||||
|
|
||||||
|
// pty type of console.
|
||||||
|
consoleProtoPty = "pty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// console watcher is designed to monitor guest console output.
|
||||||
|
type consoleWatcher struct {
|
||||||
|
proto string
|
||||||
|
consoleURL string
|
||||||
|
conn net.Conn
|
||||||
|
ptyConsole *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConsoleWatcher(s *Sandbox) (*consoleWatcher, error) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
cw consoleWatcher
|
||||||
|
)
|
||||||
|
|
||||||
|
cw.proto, cw.consoleURL, err = s.hypervisor.getSandboxConsole(s.id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cw, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// start the console watcher
|
||||||
|
func (cw *consoleWatcher) start(s *Sandbox) (err error) {
|
||||||
|
if cw.consoleWatched() {
|
||||||
|
return fmt.Errorf("console watcher has already watched for sandbox %s", s.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
var scanner *bufio.Scanner
|
||||||
|
|
||||||
|
switch cw.proto {
|
||||||
|
case consoleProtoUnix:
|
||||||
|
cw.conn, err = net.Dial("unix", cw.consoleURL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
scanner = bufio.NewScanner(cw.conn)
|
||||||
|
case consoleProtoPty:
|
||||||
|
// read-only
|
||||||
|
cw.ptyConsole, err = os.Open(cw.consoleURL)
|
||||||
|
scanner = bufio.NewScanner(cw.ptyConsole)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown console proto %s", cw.proto)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for scanner.Scan() {
|
||||||
|
s.Logger().WithFields(logrus.Fields{
|
||||||
|
"console-protocol": cw.proto,
|
||||||
|
"console-url": cw.consoleURL,
|
||||||
|
"sandbox": s.id,
|
||||||
|
"vmconsole": scanner.Text(),
|
||||||
|
}).Debug("reading guest console")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
s.Logger().Info("console watcher quits")
|
||||||
|
} else {
|
||||||
|
s.Logger().WithError(err).WithFields(logrus.Fields{
|
||||||
|
"console-protocol": cw.proto,
|
||||||
|
"console-url": cw.consoleURL,
|
||||||
|
"sandbox": s.id,
|
||||||
|
}).Error("Failed to read guest console logs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the console watcher has already watched the vm console.
|
||||||
|
func (cw *consoleWatcher) consoleWatched() bool {
|
||||||
|
return cw.conn != nil || cw.ptyConsole != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop the console watcher.
|
||||||
|
func (cw *consoleWatcher) stop() {
|
||||||
|
if cw.conn != nil {
|
||||||
|
cw.conn.Close()
|
||||||
|
cw.conn = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if cw.ptyConsole != nil {
|
||||||
|
cw.ptyConsole.Close()
|
||||||
|
cw.ptyConsole = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// startVM starts the VM.
|
// startVM starts the VM.
|
||||||
func (s *Sandbox) startVM() (err error) {
|
func (s *Sandbox) startVM() (err error) {
|
||||||
span, ctx := s.trace("startVM")
|
span, ctx := s.trace("startVM")
|
||||||
@ -936,6 +1036,15 @@ func (s *Sandbox) startVM() (err error) {
|
|||||||
|
|
||||||
s.Logger().Info("Starting VM")
|
s.Logger().Info("Starting VM")
|
||||||
|
|
||||||
|
if s.config.HypervisorConfig.Debug {
|
||||||
|
// create console watcher
|
||||||
|
consoleWatcher, err := newConsoleWatcher(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.cw = consoleWatcher
|
||||||
|
}
|
||||||
|
|
||||||
if err := s.network.Run(s.networkNS.NetNsPath, func() error {
|
if err := s.network.Run(s.networkNS.NetNsPath, func() error {
|
||||||
if s.factory != nil {
|
if s.factory != nil {
|
||||||
vm, err := s.factory.GetVM(ctx, VMConfig{
|
vm, err := s.factory.GetVM(ctx, VMConfig{
|
||||||
@ -980,6 +1089,14 @@ func (s *Sandbox) startVM() (err error) {
|
|||||||
|
|
||||||
s.Logger().Info("VM started")
|
s.Logger().Info("VM started")
|
||||||
|
|
||||||
|
if s.cw != nil {
|
||||||
|
s.Logger().Debug("console watcher starts")
|
||||||
|
if err := s.cw.start(s); err != nil {
|
||||||
|
s.cw.stop()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Once the hypervisor is done starting the sandbox,
|
// Once the hypervisor is done starting the sandbox,
|
||||||
// we want to guarantee that it is manageable.
|
// we want to guarantee that it is manageable.
|
||||||
// For that we need to ask the agent to start the
|
// For that we need to ask the agent to start the
|
||||||
@ -1478,6 +1595,12 @@ func (s *Sandbox) Stop(force bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// shutdown console watcher if exists
|
||||||
|
if s.cw != nil {
|
||||||
|
s.Logger().Debug("stop the sandbox")
|
||||||
|
s.cw.stop()
|
||||||
|
}
|
||||||
|
|
||||||
if err := s.setSandboxState(types.StateStopped); err != nil {
|
if err := s.setSandboxState(types.StateStopped); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -303,26 +302,6 @@ const (
|
|||||||
GiB = MiB << 10
|
GiB = MiB << 10
|
||||||
)
|
)
|
||||||
|
|
||||||
// Binary to use to log program output
|
|
||||||
const LoggerBinaryName = "systemd-cat"
|
|
||||||
|
|
||||||
type ProgramLogger struct {
|
|
||||||
cmd *exec.Cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewProgramLogger(loggerLabel string) ProgramLogger {
|
|
||||||
return ProgramLogger{cmd: exec.Command(LoggerBinaryName, "-t", loggerLabel)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ProgramLogger) StartLogger(output io.ReadCloser) error {
|
|
||||||
p.cmd.Stdin = output
|
|
||||||
return StartCmd(p.cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p ProgramLogger) String() string {
|
|
||||||
return p.cmd.Path
|
|
||||||
}
|
|
||||||
|
|
||||||
func ConvertNetlinkFamily(netlinkFamily int32) pbTypes.IPFamily {
|
func ConvertNetlinkFamily(netlinkFamily int32) pbTypes.IPFamily {
|
||||||
switch netlinkFamily {
|
switch netlinkFamily {
|
||||||
case netlink.FAMILY_V6:
|
case netlink.FAMILY_V6:
|
||||||
|
Loading…
Reference in New Issue
Block a user