mirror of
https://github.com/clearlinux/docker.git
synced 2026-09-05 13:11:30 +00:00
722 lines
17 KiB
Go
722 lines
17 KiB
Go
// +build linux
|
|
|
|
package clr
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"math"
|
|
"net/http"
|
|
"os"
|
|
"os/exec"
|
|
"net"
|
|
"path"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"syscall"
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
"github.com/docker/docker/daemon/execdriver"
|
|
"github.com/docker/docker/pkg/mount"
|
|
sysinfo "github.com/docker/docker/pkg/system"
|
|
"github.com/docker/docker/pkg/term"
|
|
"github.com/docker/libnetwork/netlabel"
|
|
"github.com/kr/pty"
|
|
"github.com/opencontainers/runc/libcontainer/configs"
|
|
)
|
|
|
|
const (
|
|
// Clear Linux for Intel(R) Architecture
|
|
driverName = "clr"
|
|
|
|
envVarPrefix = "CLR_"
|
|
|
|
// Command used for lkvm control
|
|
lkvmName = "lkvm"
|
|
|
|
// local "latest" information
|
|
clrFile = "latest"
|
|
|
|
// upstream base URL
|
|
clrURL = "https://download.clearlinux.org"
|
|
|
|
// upstream latest release file
|
|
latestFile = "https://download.clearlinux.org/latest"
|
|
|
|
// clr kernel (not bzimage)
|
|
clrKernel = "/usr/lib/kernel/vmlinux.container"
|
|
)
|
|
|
|
type driver struct {
|
|
root string // root path for the driver to use
|
|
libPath string
|
|
initPath string
|
|
version string
|
|
apparmor bool
|
|
sharedRoot bool
|
|
activeContainers map[string]*activeContainer
|
|
machineMemory int64
|
|
containerPid int
|
|
sync.Mutex
|
|
}
|
|
|
|
type activeContainer struct {
|
|
container *configs.Config
|
|
cmd *exec.Cmd
|
|
}
|
|
|
|
func getTapIf(c *execdriver.Command) string {
|
|
return fmt.Sprintf("tb-%s", c.ID[:12])
|
|
}
|
|
|
|
func getClrVersion(libPath string) string {
|
|
txt, err := ioutil.ReadFile(path.Join(libPath, clrFile))
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return strings.Split(string(txt), "\n")[0]
|
|
}
|
|
|
|
func fetchLatest(libPath string) error {
|
|
out, err := os.Create(path.Join(libPath, clrFile))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer out.Close()
|
|
|
|
resp, err := http.Get(latestFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
_, err = io.Copy(out, resp.Body)
|
|
|
|
return err
|
|
}
|
|
|
|
func fetchImage(version, libPath string) error {
|
|
// TODO: Add checksum validation
|
|
outfile := fmt.Sprintf("clear-%s-containers.img.xz", version)
|
|
url := fmt.Sprintf("%s/releases/%s/clear/%s", clrURL, version, outfile)
|
|
outpath := path.Join(libPath, outfile)
|
|
var output []byte
|
|
|
|
logrus.Debugf("Fetching clr version: %s, %s", version, outpath)
|
|
out, err := os.Create(outpath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer out.Close()
|
|
|
|
resp, err := http.Get(url)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// Consider progress feedback ?
|
|
_, err = io.Copy(out, resp.Body)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// decompress the file
|
|
cmd := exec.Command("unxz", outpath)
|
|
cmd.Dir = libPath
|
|
|
|
if output, err = cmd.CombinedOutput(); err != nil {
|
|
logrus.Debugf("Unable to extract image %s: %s", version, output)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// NewDriver creates a new clear linux execution driver.
|
|
func NewDriver(root, libPath, initPath string, apparmor bool) (*driver, error) {
|
|
if err := os.MkdirAll(root, 0700); err != nil {
|
|
return nil, err
|
|
}
|
|
meminfo, err := sysinfo.ReadMemInfo()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
version, err := prepareClr(libPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &driver{
|
|
apparmor: apparmor,
|
|
root: root,
|
|
libPath: libPath,
|
|
initPath: initPath,
|
|
version: version,
|
|
sharedRoot: false,
|
|
activeContainers: make(map[string]*activeContainer),
|
|
// FIXME:
|
|
machineMemory: meminfo.MemTotal,
|
|
}, nil
|
|
}
|
|
|
|
func prepareClr(libPath string) (string, error) {
|
|
var version = getClrVersion(libPath)
|
|
var nversion string
|
|
logrus.Debugf("%s preparing environment", driverName)
|
|
|
|
err := fetchLatest(libPath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
nversion = getClrVersion(libPath)
|
|
if nversion != version && version != "" {
|
|
logrus.Debugf("Updating to clr version: %s", nversion)
|
|
err = fetchImage(nversion, libPath)
|
|
} else if version == "" {
|
|
logrus.Debugf("Installing clr version: %s", nversion)
|
|
err = fetchImage(nversion, libPath)
|
|
} else {
|
|
logrus.Debugf("Using clr version: %s", nversion)
|
|
}
|
|
|
|
return nversion, nil
|
|
}
|
|
|
|
func (d *driver) Name() string {
|
|
return fmt.Sprintf("%s-%s", driverName, d.version)
|
|
}
|
|
|
|
func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (execdriver.ExitStatus, error) {
|
|
var (
|
|
term execdriver.Terminal
|
|
err error
|
|
)
|
|
|
|
container, err := d.createContainer(c)
|
|
if err != nil {
|
|
return execdriver.ExitStatus{ExitCode: -1}, err
|
|
}
|
|
|
|
memoryMiB := c.HostConfig.Memory
|
|
if memoryMiB == 0 {
|
|
memoryMiB = 1024
|
|
} else {
|
|
// docker passes the value as bytes
|
|
memoryMiB = memoryMiB / int64(math.Pow(2, 20))
|
|
}
|
|
|
|
workingDirVar := fmt.Sprintf("%s%s=%q", envVarPrefix, "WORKINGDIR", c.WorkingDir)
|
|
c.ProcessConfig.Cmd.Env = append(c.ProcessConfig.Cmd.Env, workingDirVar)
|
|
|
|
userVar := fmt.Sprintf("%s%s=%q", envVarPrefix, "USER", c.ProcessConfig.User)
|
|
c.ProcessConfig.Cmd.Env = append(c.ProcessConfig.Cmd.Env, userVar)
|
|
|
|
if err := d.setupNetwork(c); err != nil {
|
|
return execdriver.ExitStatus{ExitCode: -1}, err
|
|
}
|
|
|
|
if c.ProcessConfig.Tty {
|
|
term, err = NewTtyConsole(&c.ProcessConfig, pipes)
|
|
} else {
|
|
term, err = execdriver.NewStdConsole(&c.ProcessConfig, pipes)
|
|
}
|
|
if err != nil {
|
|
return execdriver.ExitStatus{ExitCode: -1}, err
|
|
}
|
|
c.ProcessConfig.Terminal = term
|
|
|
|
d.Lock()
|
|
d.activeContainers[c.ID] = &activeContainer{
|
|
container: container,
|
|
cmd: &c.ProcessConfig.Cmd,
|
|
}
|
|
d.Unlock()
|
|
|
|
if err := d.generateEnvConfig(c); err != nil {
|
|
return execdriver.ExitStatus{ExitCode: -1}, err
|
|
}
|
|
|
|
if err := d.generateDockerInit(c); err != nil {
|
|
return execdriver.ExitStatus{ExitCode: -1}, err
|
|
}
|
|
|
|
for _, m := range c.Mounts {
|
|
dest := path.Join(c.Rootfs, m.Destination)
|
|
|
|
if m.Destination == "/etc/hostname" {
|
|
continue
|
|
}
|
|
|
|
if !pathExists(m.Source) {
|
|
continue
|
|
}
|
|
|
|
opts := "bind"
|
|
|
|
if m.Private {
|
|
opts = opts + ",rprivate"
|
|
}
|
|
if m.Slave {
|
|
opts = opts + ",rslave"
|
|
}
|
|
|
|
// This may look racy, but it isn't since the VM isn't
|
|
// running yet.
|
|
//
|
|
// The check is necessary to handle bind mounting of
|
|
// regular files correctly since without it we may be
|
|
// attempting to create a directory where there already
|
|
// exists a normal file.
|
|
if !pathExists(dest) {
|
|
if err := os.MkdirAll(dest, 0750); err != nil {
|
|
return execdriver.ExitStatus{ExitCode: -1}, err
|
|
}
|
|
}
|
|
|
|
if err := mount.Mount(m.Source, dest, "", opts); err != nil {
|
|
return execdriver.ExitStatus{ExitCode: -1}, err
|
|
}
|
|
|
|
if !m.Writable {
|
|
if err := mount.Mount("", dest, "", "bind,remount,ro"); err != nil {
|
|
return execdriver.ExitStatus{ExitCode: -1}, err
|
|
}
|
|
}
|
|
defer mount.Unmount(dest)
|
|
}
|
|
|
|
var args []string
|
|
// various things for lkvm
|
|
ifname := getTapIf(c)
|
|
// FIXME: Should be real hostname from like process/container struct
|
|
hostname := c.ID[0:12]
|
|
img := fmt.Sprintf("%s/clear-%s-containers.img", d.libPath, d.version)
|
|
memory := fmt.Sprintf("%d", memoryMiB)
|
|
// FIXME: Locked cores to 6 ?
|
|
cores := fmt.Sprintf("%d", 6)
|
|
ipaddr := c.NetworkSettings.IPAddress
|
|
gateway := c.NetworkSettings.Gateway
|
|
macaddr := c.NetworkSettings.MacAddress
|
|
|
|
args = append(args, c.ProcessConfig.Entrypoint)
|
|
args = append(args, c.ProcessConfig.Arguments...)
|
|
|
|
rootParams := fmt.Sprintf("root=/dev/plkvm0p1 rootfstype=ext4 rootflags=dax,data=ordered "+
|
|
"init=/usr/lib/systemd/systemd systemd.unit=container.target rw tsc=reliable "+
|
|
"systemd.show_status=false "+
|
|
"no_timer_check rcupdate.rcu_expedited=1 console=hvc0 quiet ip=%s::%s::%s::off",
|
|
ipaddr, gateway, hostname)
|
|
|
|
params := []string{
|
|
lkvmName, "run", "-c", cores, "-m", memory,
|
|
"--name", c.ID, "--console", "virtio",
|
|
"--kernel", clrKernel,
|
|
"--params", rootParams,
|
|
"--shmem", fmt.Sprintf("0x200000000:0:file=%s:private", img),
|
|
"--network", fmt.Sprintf("mode=tap,script=none,tapif=%s,guest_mac=%s", ifname, macaddr),
|
|
"--9p", fmt.Sprintf("%s,rootfs", c.Rootfs),
|
|
}
|
|
|
|
logrus.Debugf("%s params %s", driverName, params)
|
|
var (
|
|
name = params[0]
|
|
arg = params[1:]
|
|
)
|
|
aname, err := exec.LookPath(name)
|
|
if err != nil {
|
|
aname = name
|
|
}
|
|
c.ProcessConfig.Path = aname
|
|
c.ProcessConfig.Args = append([]string{name}, arg...)
|
|
c.ProcessConfig.Env = []string{fmt.Sprintf("HOME=%s", d.root)}
|
|
|
|
// Start the container. Since it runs synchronously, we don't Wait()
|
|
// for it since we need to check the status to determine if it did
|
|
// actually start successfully.
|
|
if err := c.ProcessConfig.Start(); err != nil {
|
|
return execdriver.ExitStatus{ExitCode: -1}, err
|
|
}
|
|
|
|
var (
|
|
waitErr error
|
|
waitLock = make(chan struct{})
|
|
)
|
|
go func() {
|
|
if err := c.ProcessConfig.Wait(); err != nil {
|
|
if _, ok := err.(*exec.ExitError); !ok { // Do not propagate the error if it's simply a status code != 0
|
|
waitErr = err
|
|
}
|
|
}
|
|
close(waitLock)
|
|
}()
|
|
|
|
// FIXME: need to create state.json for Stats() to work.
|
|
c.ContainerPid = c.ProcessConfig.Process.Pid
|
|
d.containerPid = c.ProcessConfig.Process.Pid
|
|
|
|
if startCallback != nil {
|
|
logrus.Debugf("Invoking startCallback")
|
|
startCallback(&c.ProcessConfig, c.ProcessConfig.Process.Pid)
|
|
}
|
|
|
|
// FIXME:
|
|
oomKill := false
|
|
|
|
// Wait for the VM to shutdown
|
|
<-waitLock
|
|
exitCode := getExitCode(c)
|
|
|
|
cExitStatus, cerr := d.cleanupVM(c)
|
|
|
|
if cerr != nil {
|
|
waitErr = cerr
|
|
exitCode = cExitStatus
|
|
}
|
|
|
|
// check oom error
|
|
if oomKill {
|
|
exitCode = 137
|
|
}
|
|
|
|
return execdriver.ExitStatus{ExitCode: exitCode, OOMKilled: false}, waitErr
|
|
}
|
|
|
|
func pathExists(path string) bool {
|
|
if _, err := os.Stat(path); err == nil {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func pathExecutable(path string) bool {
|
|
s, err := os.Stat(path)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
mode := s.Mode()
|
|
if mode&0111 != 0 {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (d *driver) cleanupVM(c *execdriver.Command) (exitStatus int, err error) {
|
|
cmd := exec.Command("ip", "tuntap", "del", "dev", getTapIf(c), "mode", "tap")
|
|
var output []byte
|
|
|
|
if output, err = cmd.CombinedOutput(); err != nil {
|
|
if exitError, ok := err.(*exec.ExitError); ok {
|
|
waitStatus := exitError.Sys().(syscall.WaitStatus)
|
|
exitStatus = waitStatus.ExitStatus()
|
|
}
|
|
logrus.Debugf("teardown failed for vm %s: %s (%s)", c.ID, string(output), err.Error())
|
|
}
|
|
|
|
// doesn't matter if this fails
|
|
// lkvm could have removed it, and stale sockets are not fatal
|
|
_ = os.Remove(fmt.Sprintf("%s/.lkvm/%s.sock", d.root, c.ID))
|
|
|
|
return exitStatus, err
|
|
}
|
|
|
|
// createContainer populates and configures the container type with the
|
|
// data provided by the execdriver.Command
|
|
func (d *driver) createContainer(c *execdriver.Command) (*configs.Config, error) {
|
|
return execdriver.InitContainer(c), nil
|
|
}
|
|
|
|
/// Return the exit code of the process
|
|
// if the process has not exited -1 will be returned
|
|
func getExitCode(c *execdriver.Command) int {
|
|
if c.ProcessConfig.ProcessState == nil {
|
|
return -1
|
|
}
|
|
return c.ProcessConfig.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
|
|
}
|
|
|
|
func (d *driver) lkvmCommand(c *execdriver.Command, arg string) ([]byte, error) {
|
|
args := append([]string{lkvmName}, arg)
|
|
if c != nil {
|
|
args = append(args, "--name", c.ID)
|
|
}
|
|
cmd := exec.Command(lkvmName, args...)
|
|
cmd.Env = []string{fmt.Sprintf("HOME=%s", d.root)}
|
|
return cmd.Output()
|
|
}
|
|
|
|
// Kill sends a signal to workload
|
|
func (d *driver) Kill(c *execdriver.Command, sig int) error {
|
|
// Not supported
|
|
return nil
|
|
}
|
|
|
|
func (d *driver) Pause(c *execdriver.Command) error {
|
|
_, err := d.lkvmCommand(c, "pause")
|
|
return err
|
|
}
|
|
|
|
func (d *driver) Unpause(c *execdriver.Command) error {
|
|
_, err := d.lkvmCommand(c, "resume")
|
|
return err
|
|
}
|
|
|
|
// Terminate forcibly stops a container
|
|
func (d *driver) Terminate(c *execdriver.Command) error {
|
|
_, err := d.lkvmCommand(c, "stop")
|
|
return err
|
|
}
|
|
|
|
func (d *driver) containerDir(containerID string) string {
|
|
return path.Join(d.libPath, "containers", containerID)
|
|
}
|
|
|
|
// isDigit returns true if s can be represented as an integer
|
|
func isDigit(s string) bool {
|
|
if _, err := strconv.Atoi(s); err == nil {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (d *driver) getInfo(id string) ([]byte, error) {
|
|
output, err := d.lkvmCommand(nil, "list")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for i, line := range strings.Split(string(output), "\n") {
|
|
if i < 2 {
|
|
continue
|
|
}
|
|
|
|
fields := strings.Fields(strings.TrimSpace(line))
|
|
|
|
if len(fields) != 3 {
|
|
continue
|
|
}
|
|
|
|
if !isDigit(fields[0]) {
|
|
continue
|
|
}
|
|
|
|
if fields[1] != id {
|
|
continue
|
|
}
|
|
|
|
return []byte(line), nil
|
|
}
|
|
|
|
return []byte(fmt.Sprintf("-1 %s stopped", id)), nil
|
|
}
|
|
|
|
type info struct {
|
|
ID string
|
|
driver *driver
|
|
}
|
|
|
|
func (i *info) IsRunning() bool {
|
|
output, err := i.driver.getInfo(i.ID)
|
|
if err != nil {
|
|
logrus.Errorf("Error getting info for %s container %s: %s (%s)",
|
|
driverName, i.ID, err, output)
|
|
return false
|
|
}
|
|
|
|
clrInfo, err := parseClrInfo(i.ID, string(output))
|
|
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
return clrInfo.Running
|
|
}
|
|
|
|
func (d *driver) Info(id string) execdriver.Info {
|
|
return &info{
|
|
ID: id,
|
|
driver: d,
|
|
}
|
|
}
|
|
|
|
func (d *driver) GetPidsForContainer(id string) ([]int, error) {
|
|
// The VM doesn't expose the worload pid(s), so the only meaningful
|
|
// pid is that of the VM
|
|
return []int{d.containerPid}, nil
|
|
}
|
|
|
|
// TtyConsole is a type to represent a pseud-oterminal (see pty(7))
|
|
type TtyConsole struct {
|
|
MasterPty *os.File
|
|
SlavePty *os.File
|
|
}
|
|
|
|
// NewTtyConsole returns a new TtyConsole object.
|
|
func NewTtyConsole(processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes) (*TtyConsole, error) {
|
|
// lxc is special in that we cannot create the master outside of the container without
|
|
// opening the slave because we have nothing to provide to the cmd. We have to open both then do
|
|
// the crazy setup on command right now instead of passing the console path to lxc and telling it
|
|
// to open up that console. we save a couple of openfiles in the native driver because we can do
|
|
// this.
|
|
ptyMaster, ptySlave, err := pty.Open()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
tty := &TtyConsole{
|
|
MasterPty: ptyMaster,
|
|
SlavePty: ptySlave,
|
|
}
|
|
|
|
if err := tty.AttachPipes(&processConfig.Cmd, pipes); err != nil {
|
|
tty.Close()
|
|
return nil, err
|
|
}
|
|
|
|
processConfig.Console = tty.SlavePty.Name()
|
|
|
|
return tty, nil
|
|
}
|
|
|
|
// Master returns the master end of the pty
|
|
func (t *TtyConsole) Master() *os.File {
|
|
return t.MasterPty
|
|
}
|
|
|
|
// Resize modifies the size of the pty terminal being used.
|
|
func (t *TtyConsole) Resize(h, w int) error {
|
|
return term.SetWinsize(t.MasterPty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
|
|
}
|
|
|
|
// AttachPipes associates the specified pipes with the pty master.
|
|
func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *execdriver.Pipes) error {
|
|
command.Stdout = t.SlavePty
|
|
command.Stderr = t.SlavePty
|
|
|
|
go func() {
|
|
if wb, ok := pipes.Stdout.(interface {
|
|
CloseWriters() error
|
|
}); ok {
|
|
defer wb.CloseWriters()
|
|
}
|
|
|
|
io.Copy(pipes.Stdout, t.MasterPty)
|
|
}()
|
|
|
|
if pipes.Stdin != nil {
|
|
command.Stdin = t.SlavePty
|
|
command.SysProcAttr.Setctty = true
|
|
|
|
go func() {
|
|
io.Copy(t.MasterPty, pipes.Stdin)
|
|
|
|
pipes.Stdin.Close()
|
|
}()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Close closes both ends of the pty.
|
|
func (t *TtyConsole) Close() error {
|
|
t.SlavePty.Close()
|
|
return t.MasterPty.Close()
|
|
}
|
|
|
|
func (d *driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
|
return -1, fmt.Errorf("Unsupported: Exec is not supported by the %q driver", driverName)
|
|
|
|
}
|
|
|
|
// Clean up after an Exec
|
|
func (d *driver) Clean(id string) error {
|
|
return nil
|
|
}
|
|
|
|
func (d *driver) generateEnvConfig(c *execdriver.Command) error {
|
|
data := []byte(strings.Join(c.ProcessConfig.Env, "\n"))
|
|
|
|
p := path.Join(d.libPath, "containers", c.ID, "config.env")
|
|
c.Mounts = append(c.Mounts, execdriver.Mount{
|
|
Source: p,
|
|
Destination: "/.dockerenv",
|
|
Writable: false,
|
|
Private: true,
|
|
})
|
|
|
|
return ioutil.WriteFile(p, data, 0600)
|
|
}
|
|
|
|
func (d *driver) generateDockerInit(c *execdriver.Command) error {
|
|
p := fmt.Sprintf("%s/.containerexec", c.Rootfs)
|
|
var args []string
|
|
|
|
if pathExecutable(p) {
|
|
return nil
|
|
}
|
|
|
|
args = append(args, c.ProcessConfig.Entrypoint)
|
|
args = append(args, c.ProcessConfig.Arguments...)
|
|
|
|
data := []byte(fmt.Sprintf("#!/bin/sh\n%s\n", strings.Join(args, " ")))
|
|
|
|
return ioutil.WriteFile(p, data, 0755)
|
|
}
|
|
|
|
func (d *driver) setupNetwork(c *execdriver.Command) error {
|
|
ifname := getTapIf(c)
|
|
|
|
var bridgeName string
|
|
var bridgeLinkName string
|
|
var output []byte
|
|
var err error
|
|
|
|
for _, info := range c.EndpointInfo {
|
|
if mac, ok := info[netlabel.MacAddress].(net.HardwareAddr); ok {
|
|
if mac.String() == c.NetworkSettings.MacAddress {
|
|
bridgeName = info[netlabel.BridgeName].(string)
|
|
bridgeLinkName = info[netlabel.BridgeLinkName].(string)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Strip existing veth
|
|
cmd := exec.Command("ip", "link", "del", bridgeLinkName)
|
|
if output, err = cmd.CombinedOutput(); err != nil {
|
|
logrus.Debugf("%s setupNetwork error: %v, %s", driverName, cmd.Args, output)
|
|
return err
|
|
}
|
|
|
|
cmd = exec.Command("ip", "tuntap", "add", "dev", ifname, "mode", "tap", "vnet_hdr")
|
|
if output, err = cmd.CombinedOutput(); err != nil {
|
|
logrus.Debugf("%s setupNetwork error: %v, %s", driverName, cmd.Args, output)
|
|
return err
|
|
}
|
|
cmd = exec.Command("ip", "link", "set", "dev", ifname, "master", bridgeName)
|
|
if output, err = cmd.CombinedOutput(); err != nil {
|
|
logrus.Debugf("%s setupNetwork error: %v, %s", driverName, cmd.Args, output)
|
|
return err
|
|
}
|
|
|
|
cmd = exec.Command("ip", "link", "set", "dev", ifname, "up")
|
|
if output, err = cmd.CombinedOutput(); err != nil {
|
|
logrus.Debugf("%s setupNetwork error: %v, %s", driverName, cmd.Args, output)
|
|
return err
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (d *driver) Stats(id string) (*execdriver.ResourceStats, error) {
|
|
if _, ok := d.activeContainers[id]; !ok {
|
|
return nil, fmt.Errorf("%s is not a key in active containers", id)
|
|
}
|
|
// FIXME:
|
|
return execdriver.Stats(d.containerDir(id), d.activeContainers[id].container.Cgroups.Memory, d.machineMemory)
|
|
}
|