2013-10-28 23:32:39 -04:00
|
|
|
// Copyright © 2013, S.Çağlar Onur
|
|
|
|
|
// Use of this source code is governed by a LGPLv2.1
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
//
|
|
|
|
|
// Authors:
|
|
|
|
|
// S.Çağlar Onur <caglar@10ur.org>
|
2013-09-06 01:36:09 -04:00
|
|
|
|
2013-10-28 23:32:39 -04:00
|
|
|
// +build linux
|
2013-03-30 12:14:57 -04:00
|
|
|
|
|
|
|
|
package lxc
|
|
|
|
|
|
|
|
|
|
// #include <lxc/lxccontainer.h>
|
|
|
|
|
// #include "lxc.h"
|
|
|
|
|
import "C"
|
|
|
|
|
|
|
|
|
|
import (
|
2013-09-09 00:40:18 -04:00
|
|
|
"fmt"
|
2013-11-22 00:21:37 -05:00
|
|
|
"os/exec"
|
2013-03-30 14:22:08 -04:00
|
|
|
"strconv"
|
2013-03-30 12:14:57 -04:00
|
|
|
"strings"
|
2013-04-03 12:38:41 -04:00
|
|
|
"sync"
|
2013-04-26 15:18:31 -04:00
|
|
|
"time"
|
2013-03-30 12:14:57 -04:00
|
|
|
"unsafe"
|
|
|
|
|
)
|
|
|
|
|
|
2013-09-09 00:40:18 -04:00
|
|
|
// Container struct
|
2013-03-30 12:14:57 -04:00
|
|
|
type Container struct {
|
|
|
|
|
container *C.struct_lxc_container
|
2013-12-07 17:18:36 -05:00
|
|
|
mu sync.RWMutex
|
2014-01-26 14:52:11 -05:00
|
|
|
|
|
|
|
|
name string
|
|
|
|
|
verbosity Verbosity
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2013-10-11 17:16:41 -04:00
|
|
|
// Snapshot struct
|
|
|
|
|
type Snapshot struct {
|
|
|
|
|
Name string
|
|
|
|
|
CommentPath string
|
|
|
|
|
Timestamp string
|
|
|
|
|
Path string
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) makeSure(flags int) error {
|
|
|
|
|
if flags&isDefined != 0 && !c.Defined() {
|
|
|
|
|
return fmt.Errorf(errNotDefined, c.name)
|
2013-09-27 16:34:05 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if flags&isNotDefined != 0 && c.Defined() {
|
|
|
|
|
return fmt.Errorf(errAlreadyDefined, c.name)
|
2013-09-27 16:34:05 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if flags&isRunning != 0 && !c.Running() {
|
|
|
|
|
return fmt.Errorf(errNotRunning, c.name)
|
2013-09-27 16:34:05 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if flags&isNotRunning != 0 && c.Running() {
|
|
|
|
|
return fmt.Errorf(errAlreadyRunning, c.name)
|
2013-09-27 16:34:05 -04:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// Name returns the name of the container.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) Name() string {
|
|
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
return C.GoString(c.container.name)
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// Defined returns true if the container is already defined.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) Defined() bool {
|
|
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
return bool(C.lxc_defined(c.container))
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// Running returns true if the container is already running.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) Running() bool {
|
|
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
return bool(C.lxc_running(c.container))
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// Controllable returns true if the caller can control the container.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) Controllable() bool {
|
|
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2013-09-30 16:41:17 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
return bool(C.lxc_may_control(c.container))
|
2013-09-30 16:41:17 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// CreateSnapshot creates a new snapshot.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) CreateSnapshot() (*Snapshot, error) {
|
|
|
|
|
if err := c.makeSure(isDefined | isNotRunning); err != nil {
|
2014-01-12 14:19:25 -05:00
|
|
|
return nil, err
|
2013-10-11 17:16:41 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-10-11 17:16:41 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
ret := int(C.lxc_snapshot(c.container))
|
2014-01-12 14:19:25 -05:00
|
|
|
if ret < 0 {
|
2014-01-26 14:52:11 -05:00
|
|
|
return nil, fmt.Errorf(errCreateSnapshotFailed, c.name)
|
2013-10-11 17:16:41 -04:00
|
|
|
}
|
2014-01-12 14:19:25 -05:00
|
|
|
return &Snapshot{Name: fmt.Sprintf("snap%d", ret)}, nil
|
2013-10-11 17:16:41 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// RestoreSnapshot creates a new container based on a snapshot.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) RestoreSnapshot(snapshot Snapshot, name string) error {
|
|
|
|
|
if err := c.makeSure(isDefined); err != nil {
|
|
|
|
|
return err
|
2013-10-11 17:16:41 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-10-11 17:16:41 -04:00
|
|
|
|
|
|
|
|
cname := C.CString(name)
|
|
|
|
|
defer C.free(unsafe.Pointer(cname))
|
|
|
|
|
|
|
|
|
|
csnapname := C.CString(snapshot.Name)
|
|
|
|
|
defer C.free(unsafe.Pointer(csnapname))
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_snapshot_restore(c.container, csnapname, cname)) {
|
|
|
|
|
return fmt.Errorf(errRestoreSnapshotFailed, c.name)
|
2013-10-11 17:16:41 -04:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// DestroySnapshot destroys the specified snapshot.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) DestroySnapshot(snapshot Snapshot) error {
|
|
|
|
|
if err := c.makeSure(isDefined); err != nil {
|
|
|
|
|
return err
|
2013-10-19 11:57:44 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-10-19 11:57:44 -04:00
|
|
|
|
|
|
|
|
csnapname := C.CString(snapshot.Name)
|
|
|
|
|
defer C.free(unsafe.Pointer(csnapname))
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_snapshot_destroy(c.container, csnapname)) {
|
|
|
|
|
return fmt.Errorf(errDestroySnapshotFailed, c.name)
|
2013-10-19 11:57:44 -04:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// Snapshots returns the list of container snapshots.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) Snapshots() ([]Snapshot, error) {
|
|
|
|
|
if err := c.makeSure(isDefined); err != nil {
|
|
|
|
|
return nil, err
|
2013-10-11 17:16:41 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-10-15 18:06:42 -04:00
|
|
|
|
|
|
|
|
var csnapshots *C.struct_lxc_snapshot
|
2013-10-11 17:16:41 -04:00
|
|
|
var snapshots []Snapshot
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
size := int(C.lxc_snapshot_list(c.container, &csnapshots))
|
2013-10-15 18:06:42 -04:00
|
|
|
defer freeSnapshots(csnapshots, size)
|
|
|
|
|
|
2013-10-11 17:16:41 -04:00
|
|
|
if size < 1 {
|
2014-01-26 14:52:11 -05:00
|
|
|
return nil, fmt.Errorf("%s has no snapshots", c.name)
|
2013-10-11 17:16:41 -04:00
|
|
|
}
|
|
|
|
|
|
2013-10-15 18:06:42 -04:00
|
|
|
p := uintptr(unsafe.Pointer(csnapshots))
|
2013-10-11 17:16:41 -04:00
|
|
|
for i := 0; i < size; i++ {
|
|
|
|
|
z := (*C.struct_lxc_snapshot)(unsafe.Pointer(p))
|
2014-01-26 14:52:11 -05:00
|
|
|
s := &Snapshot{Name: C.GoString(z.name), Timestamp: C.GoString(z.timestamp),
|
|
|
|
|
CommentPath: C.GoString(z.comment_pathname), Path: C.GoString(z.lxcpath)}
|
2013-10-11 17:16:41 -04:00
|
|
|
snapshots = append(snapshots, *s)
|
2013-10-15 18:06:42 -04:00
|
|
|
p += unsafe.Sizeof(*csnapshots)
|
2013-10-11 17:16:41 -04:00
|
|
|
}
|
|
|
|
|
return snapshots, nil
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// State returns the state of the container.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) State() State {
|
|
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
return stateMap[C.GoString(C.lxc_state(c.container))]
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// InitPID returns the process ID of the container's init process
|
|
|
|
|
// seen from outside the container.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) InitPID() int {
|
|
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
return int(C.lxc_init_pid(c.container))
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// Daemonize returns true if the container wished to be daemonized.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) Daemonize() bool {
|
|
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
return bool(c.container.daemonize)
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// WantDaemonize determines if the container wants to run daemonized.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) WantDaemonize(state bool) error {
|
|
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_want_daemonize(c.container, C.bool(state))) {
|
|
|
|
|
return fmt.Errorf(errDaemonizeFailed, c.name)
|
2013-09-24 15:47:23 -04:00
|
|
|
}
|
|
|
|
|
return nil
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// WantCloseAllFds determines whether container wishes all file descriptors
|
|
|
|
|
// to be closed on startup.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) WantCloseAllFds(state bool) error {
|
|
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-09-22 12:12:40 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_want_close_all_fds(c.container, C.bool(state))) {
|
|
|
|
|
return fmt.Errorf(errCloseAllFdsFailed, c.name)
|
2013-09-22 12:12:40 -04:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-09 00:40:18 -04:00
|
|
|
// SetVerbosity sets the verbosity level of some API calls
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) SetVerbosity(verbosity Verbosity) {
|
|
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.verbosity = verbosity
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// Freeze freezes the running container.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) Freeze() error {
|
|
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-09-27 16:34:05 -04:00
|
|
|
return err
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if c.State() == FROZEN {
|
|
|
|
|
return fmt.Errorf(errAlreadyFrozen, c.name)
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_freeze(c.container)) {
|
|
|
|
|
return fmt.Errorf(errFreezeFailed, c.name)
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// Unfreeze thaws the frozen container.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) Unfreeze() error {
|
|
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-09-27 16:34:05 -04:00
|
|
|
return err
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if c.State() != FROZEN {
|
|
|
|
|
return fmt.Errorf(errNotFrozen, c.name)
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_unfreeze(c.container)) {
|
|
|
|
|
return fmt.Errorf(errUnfreezeFailed, c.name)
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// CreateUsing creates the container using given arguments with specified backend.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) CreateUsing(template string, backend BackendStore, args ...string) error {
|
2013-11-10 22:30:29 -05:00
|
|
|
// FIXME: Support bdevtype and bdev_specs
|
|
|
|
|
// bdevtypes:
|
|
|
|
|
// "btrfs", "zfs", "lvm", "dir"
|
|
|
|
|
//
|
2013-11-18 00:50:34 -05:00
|
|
|
// best tries to find the best backing store type
|
2013-11-10 22:30:29 -05:00
|
|
|
//
|
|
|
|
|
// bdev_specs:
|
|
|
|
|
// zfs requires zfsroot
|
|
|
|
|
// lvm requires lvname/vgname/thinpool as well as fstype and fssize
|
|
|
|
|
// btrfs requires nothing
|
|
|
|
|
// dir requires nothing
|
2014-01-26 14:52:11 -05:00
|
|
|
if err := c.makeSure(isNotDefined); err != nil {
|
|
|
|
|
return err
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-04-03 12:38:41 -04:00
|
|
|
|
2013-03-30 12:14:57 -04:00
|
|
|
ctemplate := C.CString(template)
|
|
|
|
|
defer C.free(unsafe.Pointer(ctemplate))
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-14 23:15:12 -05:00
|
|
|
cbackend := C.CString(backend.String())
|
|
|
|
|
defer C.free(unsafe.Pointer(cbackend))
|
2013-11-10 22:30:29 -05:00
|
|
|
|
2013-09-09 00:40:18 -04:00
|
|
|
ret := false
|
2013-03-30 12:14:57 -04:00
|
|
|
if args != nil {
|
2013-11-18 00:50:34 -05:00
|
|
|
cargs := makeNullTerminatedArgs(args)
|
|
|
|
|
defer freeNullTerminatedArgs(cargs, len(args))
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
ret = bool(C.lxc_create(c.container, ctemplate, cbackend, C.int(c.verbosity), cargs))
|
2013-09-09 00:40:18 -04:00
|
|
|
} else {
|
2014-01-26 14:52:11 -05:00
|
|
|
ret = bool(C.lxc_create(c.container, ctemplate, cbackend, C.int(c.verbosity), nil))
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !ret {
|
2014-01-26 14:52:11 -05:00
|
|
|
return fmt.Errorf(errCreateFailed, c.name)
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
2013-09-09 00:40:18 -04:00
|
|
|
return nil
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// Create creates the container using the Directory backendstore.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) Create(template string, args ...string) error {
|
|
|
|
|
return c.CreateUsing(template, Directory, args...)
|
2014-01-14 23:15:12 -05:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// CreateAsUser creates the container as "unprivileged user" using the Directory backendstore.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) CreateAsUser(distro string, release string, arch string, args ...string) error {
|
2014-01-17 01:03:58 -05:00
|
|
|
// required parameters
|
|
|
|
|
nargs := []string{"-d", distro, "-r", release, "-a", arch}
|
|
|
|
|
// optional arguments
|
|
|
|
|
nargs = append(nargs, args...)
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
return c.CreateUsing("download", Directory, nargs...)
|
2014-01-17 01:03:58 -05:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// Start starts the container.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) Start() error {
|
|
|
|
|
if err := c.makeSure(isDefined | isNotRunning); err != nil {
|
2013-09-27 16:34:05 -04:00
|
|
|
return err
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-04-03 12:38:41 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_start(c.container, 0, nil)) {
|
|
|
|
|
return fmt.Errorf(errStartFailed, c.name)
|
2013-11-18 00:50:34 -05:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// Execute executes the given command in a temporary container.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) Execute(args ...string) ([]byte, error) {
|
|
|
|
|
if err := c.makeSure(isNotDefined); err != nil {
|
|
|
|
|
return nil, err
|
2013-11-22 00:21:37 -05:00
|
|
|
}
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
cargs := []string{"lxc-execute", "-n", c.Name(), "-P", c.ConfigPath(), "--"}
|
2013-11-22 00:21:37 -05:00
|
|
|
cargs = append(cargs, args...)
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-11-22 00:21:37 -05:00
|
|
|
|
|
|
|
|
/*
|
2014-01-26 15:55:41 -05:00
|
|
|
* FIXME: Go runtime and src/lxc/start.c signal_handler are not playing nice together so use lxc-execute for now
|
2013-11-22 00:21:37 -05:00
|
|
|
*/
|
|
|
|
|
output, err := exec.Command(cargs[0], cargs[1:]...).CombinedOutput()
|
|
|
|
|
if err != nil {
|
2014-01-26 14:52:11 -05:00
|
|
|
return nil, fmt.Errorf(errExecuteFailed, c.name)
|
2013-11-22 00:21:37 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return output, nil
|
|
|
|
|
/*
|
2013-11-18 15:49:26 -05:00
|
|
|
cargs := makeNullTerminatedArgs(args)
|
|
|
|
|
defer freeNullTerminatedArgs(cargs, len(args))
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_start(c.container, 1, cargs)) {
|
|
|
|
|
return fmt.Errorf(errExecuteFailed, c.name)
|
2013-11-18 15:49:26 -05:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
*/
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// Stop stops the container.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) Stop() error {
|
|
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-09-27 16:34:05 -04:00
|
|
|
return err
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_stop(c.container)) {
|
|
|
|
|
return fmt.Errorf(errStopFailed, c.name)
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
return nil
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// Reboot reboots the container.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) Reboot() error {
|
|
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-09-27 16:34:05 -04:00
|
|
|
return err
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_reboot(c.container)) {
|
|
|
|
|
return fmt.Errorf(errRebootFailed, c.name)
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
return nil
|
2013-06-24 13:18:26 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// Shutdown shuts down the container.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) Shutdown(timeout int) error {
|
|
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-09-27 16:34:05 -04:00
|
|
|
return err
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_shutdown(c.container, C.int(timeout))) {
|
|
|
|
|
return fmt.Errorf(errShutdownFailed, c.name)
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
return nil
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// Destroy destroys the container.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) Destroy() error {
|
|
|
|
|
if err := c.makeSure(isDefined | isNotRunning); err != nil {
|
2013-09-27 16:34:05 -04:00
|
|
|
return err
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_destroy(c.container)) {
|
|
|
|
|
return fmt.Errorf(errDestroyFailed, c.name)
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
return nil
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// CloneUsing clones the container using given arguments with specified backend.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) CloneUsing(name string, backend BackendStore, flags CloneFlags) error {
|
2013-10-25 19:28:05 -04:00
|
|
|
// FIXME: support lxcpath, bdevtype, bdevdata, newsize and hookargs
|
2013-11-10 22:30:29 -05:00
|
|
|
//
|
|
|
|
|
// bdevtypes:
|
|
|
|
|
// "btrfs", "zfs", "lvm", "dir" "overlayfs"
|
|
|
|
|
//
|
|
|
|
|
// bdevdata:
|
|
|
|
|
// zfs requires zfsroot
|
|
|
|
|
// lvm requires lvname/vgname/thinpool as well as fstype and fssize
|
|
|
|
|
// btrfs requires nothing
|
|
|
|
|
// dir requires nothing
|
|
|
|
|
//
|
|
|
|
|
// newsize: for blockdev-backed backingstores
|
|
|
|
|
//
|
|
|
|
|
// hookargs: additional arguments to pass to the clone hook script
|
2014-01-26 14:52:11 -05:00
|
|
|
if err := c.makeSure(isDefined | isNotRunning); err != nil {
|
2013-09-27 16:34:05 -04:00
|
|
|
return err
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-04-24 19:32:25 -04:00
|
|
|
|
|
|
|
|
cname := C.CString(name)
|
|
|
|
|
defer C.free(unsafe.Pointer(cname))
|
|
|
|
|
|
2014-01-14 23:15:12 -05:00
|
|
|
cbackend := C.CString(backend.String())
|
|
|
|
|
defer C.free(unsafe.Pointer(cbackend))
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_clone(c.container, cname, C.int(flags), cbackend)) {
|
|
|
|
|
return fmt.Errorf(errCloneFailed, c.name)
|
2013-04-24 19:32:25 -04:00
|
|
|
}
|
2013-09-09 00:40:18 -04:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// Clone clones the container using the Directory backendstore.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) Clone(name string) error {
|
|
|
|
|
return c.CloneUsing(name, Directory, 0)
|
2013-04-25 14:05:11 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// Rename renames the container.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) Rename(name string) error {
|
|
|
|
|
if err := c.makeSure(isDefined | isNotRunning); err != nil {
|
2013-12-18 21:15:42 -05:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-12-18 21:15:42 -05:00
|
|
|
|
|
|
|
|
cname := C.CString(name)
|
|
|
|
|
defer C.free(unsafe.Pointer(cname))
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_rename(c.container, cname)) {
|
|
|
|
|
return fmt.Errorf(errRenameFailed, c.name)
|
2013-12-18 21:15:42 -05:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// Wait waits for container to reach a given state until it timeouts.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) Wait(state State, timeout int) bool {
|
|
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2013-03-30 12:14:57 -04:00
|
|
|
cstate := C.CString(state.String())
|
|
|
|
|
defer C.free(unsafe.Pointer(cstate))
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
return bool(C.lxc_wait(c.container, cstate, C.int(timeout)))
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// ConfigFileName returns the container's configuration file's name.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) ConfigFileName() string {
|
|
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 15:55:41 -05:00
|
|
|
// allocated in lxc.c
|
2014-01-26 14:52:11 -05:00
|
|
|
configFileName := C.lxc_config_file_name(c.container)
|
2013-09-17 21:29:29 -04:00
|
|
|
defer C.free(unsafe.Pointer(configFileName))
|
|
|
|
|
|
|
|
|
|
return C.GoString(configFileName)
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// ConfigItem returns the value of the given config item.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) ConfigItem(key string) []string {
|
|
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2013-03-30 12:14:57 -04:00
|
|
|
ckey := C.CString(key)
|
|
|
|
|
defer C.free(unsafe.Pointer(ckey))
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 15:55:41 -05:00
|
|
|
// allocated in lxc.c
|
2014-01-26 14:52:11 -05:00
|
|
|
configItem := C.lxc_get_config_item(c.container, ckey)
|
2013-09-17 21:29:29 -04:00
|
|
|
defer C.free(unsafe.Pointer(configItem))
|
|
|
|
|
|
|
|
|
|
ret := strings.TrimSpace(C.GoString(configItem))
|
2013-03-30 12:14:57 -04:00
|
|
|
return strings.Split(ret, "\n")
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// SetConfigItem sets the value of the given config item.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) SetConfigItem(key string, value string) error {
|
|
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2013-03-30 12:14:57 -04:00
|
|
|
ckey := C.CString(key)
|
|
|
|
|
defer C.free(unsafe.Pointer(ckey))
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2013-03-30 12:14:57 -04:00
|
|
|
cvalue := C.CString(value)
|
|
|
|
|
defer C.free(unsafe.Pointer(cvalue))
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_set_config_item(c.container, ckey, cvalue)) {
|
|
|
|
|
return fmt.Errorf(errSettingConfigItemFailed, c.name, key, value)
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
return nil
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-27 23:41:27 -05:00
|
|
|
// RunningConfigItem returns the value of the given config item.
|
|
|
|
|
func (c *Container) RunningConfigItem(key string) []string {
|
|
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
|
|
|
|
|
|
|
|
|
ckey := C.CString(key)
|
|
|
|
|
defer C.free(unsafe.Pointer(ckey))
|
|
|
|
|
|
|
|
|
|
// allocated in lxc.c
|
|
|
|
|
configItem := C.lxc_get_running_config_item(c.container, ckey)
|
|
|
|
|
defer C.free(unsafe.Pointer(configItem))
|
|
|
|
|
|
|
|
|
|
ret := strings.TrimSpace(C.GoString(configItem))
|
|
|
|
|
return strings.Split(ret, "\n")
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// CgroupItem returns the value of the given cgroup subsystem value.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) CgroupItem(key string) []string {
|
|
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2013-03-30 12:14:57 -04:00
|
|
|
ckey := C.CString(key)
|
|
|
|
|
defer C.free(unsafe.Pointer(ckey))
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 15:55:41 -05:00
|
|
|
// allocated in lxc.c
|
2014-01-26 14:52:11 -05:00
|
|
|
cgroupItem := C.lxc_get_cgroup_item(c.container, ckey)
|
2013-09-17 21:29:29 -04:00
|
|
|
defer C.free(unsafe.Pointer(cgroupItem))
|
|
|
|
|
|
|
|
|
|
ret := strings.TrimSpace(C.GoString(cgroupItem))
|
2013-03-30 12:14:57 -04:00
|
|
|
return strings.Split(ret, "\n")
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// SetCgroupItem sets the value of given cgroup subsystem value.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) SetCgroupItem(key string, value string) error {
|
|
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2013-03-30 12:14:57 -04:00
|
|
|
ckey := C.CString(key)
|
|
|
|
|
defer C.free(unsafe.Pointer(ckey))
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2013-03-30 12:14:57 -04:00
|
|
|
cvalue := C.CString(value)
|
|
|
|
|
defer C.free(unsafe.Pointer(cvalue))
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_set_cgroup_item(c.container, ckey, cvalue)) {
|
|
|
|
|
return fmt.Errorf(errSettingCgroupItemFailed, c.name, key, value)
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
return nil
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// ClearConfigItem clears the value of given config item.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) ClearConfigItem(key string) error {
|
|
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2013-03-30 12:14:57 -04:00
|
|
|
ckey := C.CString(key)
|
|
|
|
|
defer C.free(unsafe.Pointer(ckey))
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_clear_config_item(c.container, ckey)) {
|
|
|
|
|
return fmt.Errorf(errClearingCgroupItemFailed, c.name, key)
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
return nil
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// ConfigKeys returns the names of the config items.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) ConfigKeys(key ...string) []string {
|
|
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2013-11-10 22:30:29 -05:00
|
|
|
var keys *_Ctype_char
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2013-11-10 22:30:29 -05:00
|
|
|
if key != nil && len(key) == 1 {
|
|
|
|
|
ckey := C.CString(key[0])
|
|
|
|
|
defer C.free(unsafe.Pointer(ckey))
|
2013-09-17 21:29:29 -04:00
|
|
|
|
2014-01-26 15:55:41 -05:00
|
|
|
// allocated in lxc.c
|
2014-01-26 14:52:11 -05:00
|
|
|
keys = C.lxc_get_keys(c.container, ckey)
|
2013-11-10 22:30:29 -05:00
|
|
|
defer C.free(unsafe.Pointer(keys))
|
|
|
|
|
} else {
|
2014-01-26 15:55:41 -05:00
|
|
|
// allocated in lxc.c
|
2014-01-26 14:52:11 -05:00
|
|
|
keys = C.lxc_get_keys(c.container, nil)
|
2013-11-10 22:30:29 -05:00
|
|
|
defer C.free(unsafe.Pointer(keys))
|
|
|
|
|
}
|
2013-09-17 21:29:29 -04:00
|
|
|
ret := strings.TrimSpace(C.GoString(keys))
|
2013-03-30 12:14:57 -04:00
|
|
|
return strings.Split(ret, "\n")
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// LoadConfigFile loads the configuration file from given path.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) LoadConfigFile(path string) error {
|
|
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2013-03-30 12:14:57 -04:00
|
|
|
cpath := C.CString(path)
|
|
|
|
|
defer C.free(unsafe.Pointer(cpath))
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_load_config(c.container, cpath)) {
|
|
|
|
|
return fmt.Errorf(errLoadConfigFailed, c.name, path)
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
return nil
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// SaveConfigFile saves the configuration file to given path.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) SaveConfigFile(path string) error {
|
|
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2013-03-30 12:14:57 -04:00
|
|
|
cpath := C.CString(path)
|
|
|
|
|
defer C.free(unsafe.Pointer(cpath))
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_save_config(c.container, cpath)) {
|
|
|
|
|
return fmt.Errorf(errSaveConfigFailed, c.name, path)
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
return nil
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// ConfigPath returns the configuration file's path.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) ConfigPath() string {
|
|
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
return C.GoString(C.lxc_get_config_path(c.container))
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// SetConfigPath sets the configuration file's path.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) SetConfigPath(path string) error {
|
|
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2013-03-30 12:14:57 -04:00
|
|
|
cpath := C.CString(path)
|
|
|
|
|
defer C.free(unsafe.Pointer(cpath))
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_set_config_path(c.container, cpath)) {
|
|
|
|
|
return fmt.Errorf(errSettingConfigPathFailed, c.name, path)
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
return nil
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// MemoryUsage returns memory usage of the container in bytes.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) MemoryUsage() (ByteSize, error) {
|
|
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-09-27 16:34:05 -04:00
|
|
|
return -1, err
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
memUsed, err := strconv.ParseFloat(c.CgroupItem("memory.usage_in_bytes")[0], 64)
|
2013-09-09 00:40:18 -04:00
|
|
|
if err != nil {
|
2013-10-25 19:28:05 -04:00
|
|
|
return -1, fmt.Errorf(errMemLimit)
|
2013-03-30 14:22:08 -04:00
|
|
|
}
|
2014-01-26 23:56:04 -05:00
|
|
|
return ByteSize(memUsed), nil
|
2013-03-30 14:22:08 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 23:56:04 -05:00
|
|
|
// MemoryLimit returns memory limit of the container in bytes.
|
|
|
|
|
func (c *Container) MemoryLimit() (ByteSize, error) {
|
2014-01-26 14:52:11 -05:00
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2014-01-18 16:42:23 -05:00
|
|
|
return -1, err
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2014-01-18 16:42:23 -05:00
|
|
|
|
2014-01-26 23:56:04 -05:00
|
|
|
memLimit, err := strconv.ParseFloat(c.CgroupItem("memory.limit_in_bytes")[0], 64)
|
2014-01-18 16:42:23 -05:00
|
|
|
if err != nil {
|
2014-01-26 23:56:04 -05:00
|
|
|
return -1, fmt.Errorf(errMemLimit)
|
2014-01-18 16:42:23 -05:00
|
|
|
}
|
2014-01-26 23:56:04 -05:00
|
|
|
return ByteSize(memLimit), nil
|
2014-01-18 16:42:23 -05:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 23:56:04 -05:00
|
|
|
// SetMemoryLimit sets memory limit of the container in bytes.
|
|
|
|
|
func (c *Container) SetMemoryLimit(limit ByteSize) error {
|
2014-01-26 14:52:11 -05:00
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2014-01-26 23:56:04 -05:00
|
|
|
return err
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 23:56:04 -05:00
|
|
|
if err := c.SetCgroupItem("memory.limit_in_bytes", fmt.Sprintf("%.f", limit)); err != nil {
|
|
|
|
|
return fmt.Errorf(errSettingMemoryLimitFailed, c.name)
|
2013-03-30 14:22:08 -04:00
|
|
|
}
|
2014-01-26 23:56:04 -05:00
|
|
|
return nil
|
2013-03-30 14:22:08 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 23:56:04 -05:00
|
|
|
// KernelMemoryUsage returns current kernel memory allocation of the container in bytes.
|
|
|
|
|
func (c *Container) KernelMemoryUsage() (ByteSize, error) {
|
2014-01-26 14:52:11 -05:00
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2014-01-18 16:42:23 -05:00
|
|
|
return -1, err
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2014-01-18 16:42:23 -05:00
|
|
|
|
2014-01-26 23:56:04 -05:00
|
|
|
kmemUsed, err := strconv.ParseFloat(c.CgroupItem("memory.kmem.usage_in_bytes")[0], 64)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return -1, fmt.Errorf(errKMemLimit)
|
2014-01-18 16:42:23 -05:00
|
|
|
}
|
2014-01-26 23:56:04 -05:00
|
|
|
return ByteSize(kmemUsed), nil
|
2014-01-18 16:42:23 -05:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 23:56:04 -05:00
|
|
|
// KernelMemoryLimit returns kernel memory limit of the container in bytes.
|
|
|
|
|
func (c *Container) KernelMemoryLimit() (ByteSize, error) {
|
2014-01-26 14:52:11 -05:00
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-09-27 16:34:05 -04:00
|
|
|
return -1, err
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 23:56:04 -05:00
|
|
|
kmemLimit, err := strconv.ParseFloat(c.CgroupItem("memory.kmem.limit_in_bytes")[0], 64)
|
2013-09-09 00:40:18 -04:00
|
|
|
if err != nil {
|
2014-01-26 23:56:04 -05:00
|
|
|
return -1, fmt.Errorf(errKMemLimit)
|
2013-03-30 14:22:08 -04:00
|
|
|
}
|
2014-01-26 23:56:04 -05:00
|
|
|
return ByteSize(kmemLimit), nil
|
2013-03-30 14:22:08 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 23:56:04 -05:00
|
|
|
// SetKernelMemoryLimit sets kernel memory limit of the container in bytes.
|
|
|
|
|
func (c *Container) SetKernelMemoryLimit(limit ByteSize) error {
|
2014-01-26 14:52:11 -05:00
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-09-28 21:10:16 -04:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 23:56:04 -05:00
|
|
|
if err := c.SetCgroupItem("memory.kmem.limit_in_bytes", fmt.Sprintf("%.f", limit)); err != nil {
|
|
|
|
|
return fmt.Errorf(errSettingKMemoryLimitFailed, c.name)
|
2014-01-18 16:42:23 -05:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 23:56:04 -05:00
|
|
|
// MemorySwapUsage returns memory+swap usage of the container in bytes.
|
|
|
|
|
func (c *Container) MemorySwapUsage() (ByteSize, error) {
|
2014-01-26 14:52:11 -05:00
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2014-01-18 16:42:23 -05:00
|
|
|
return -1, err
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2014-01-18 16:42:23 -05:00
|
|
|
|
2014-01-26 23:56:04 -05:00
|
|
|
memorySwapUsed, err := strconv.ParseFloat(c.CgroupItem("memory.memsw.usage_in_bytes")[0], 64)
|
2014-01-18 16:42:23 -05:00
|
|
|
if err != nil {
|
2014-01-26 23:56:04 -05:00
|
|
|
return -1, fmt.Errorf(errMemorySwapLimit)
|
2014-01-18 16:42:23 -05:00
|
|
|
}
|
2014-01-26 23:56:04 -05:00
|
|
|
return ByteSize(memorySwapUsed), nil
|
2013-09-28 21:10:16 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 23:56:04 -05:00
|
|
|
// MemorySwapLimit returns the memory+swap limit of the container in bytes.
|
|
|
|
|
func (c *Container) MemorySwapLimit() (ByteSize, error) {
|
2014-01-26 14:52:11 -05:00
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-09-27 16:34:05 -04:00
|
|
|
return -1, err
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 23:56:04 -05:00
|
|
|
memorySwapLimit, err := strconv.ParseFloat(c.CgroupItem("memory.memsw.limit_in_bytes")[0], 64)
|
2013-09-09 00:40:18 -04:00
|
|
|
if err != nil {
|
2014-01-26 23:56:04 -05:00
|
|
|
return -1, fmt.Errorf(errMemorySwapLimit)
|
2013-04-03 12:38:41 -04:00
|
|
|
}
|
2014-01-26 23:56:04 -05:00
|
|
|
return ByteSize(memorySwapLimit), nil
|
2013-04-03 12:38:41 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 23:56:04 -05:00
|
|
|
// SetMemorySwapLimit sets memory+swap limit of the container in bytes.
|
|
|
|
|
func (c *Container) SetMemorySwapLimit(limit ByteSize) error {
|
2014-01-26 14:52:11 -05:00
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-09-28 21:10:16 -04:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if err := c.SetCgroupItem("memory.memsw.limit_in_bytes", fmt.Sprintf("%.f", limit)); err != nil {
|
2014-01-26 23:56:04 -05:00
|
|
|
return fmt.Errorf(errSettingMemorySwapLimitFailed, c.name)
|
2013-09-28 21:10:16 -04:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 23:56:04 -05:00
|
|
|
// BlkioUsage returns number of bytes transferred to/from the disk by the container.
|
|
|
|
|
func (c *Container) BlkioUsage() (ByteSize, error) {
|
|
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
|
|
|
|
return -1, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
|
|
|
|
|
|
|
|
|
for _, v := range c.CgroupItem("blkio.throttle.io_service_bytes") {
|
|
|
|
|
b := strings.Split(v, " ")
|
|
|
|
|
if b[0] == "Total" {
|
|
|
|
|
blkioUsed, err := strconv.ParseFloat(b[1], 64)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return -1, err
|
|
|
|
|
}
|
|
|
|
|
return ByteSize(blkioUsed), nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1, fmt.Errorf(errBlkioUsage, c.name)
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// CPUTime returns the total CPU time (in nanoseconds) consumed by all tasks
|
|
|
|
|
// in this cgroup (including tasks lower in the hierarchy).
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) CPUTime() (time.Duration, error) {
|
|
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-09-27 16:34:05 -04:00
|
|
|
return -1, err
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
cpuUsage, err := strconv.ParseInt(c.CgroupItem("cpuacct.usage")[0], 10, 64)
|
2013-09-09 00:40:18 -04:00
|
|
|
if err != nil {
|
|
|
|
|
return -1, err
|
2013-03-30 14:22:08 -04:00
|
|
|
}
|
2014-01-26 23:56:04 -05:00
|
|
|
return time.Duration(cpuUsage), nil
|
2013-03-30 12:14:57 -04:00
|
|
|
}
|
2013-04-03 12:38:41 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
// CPUTimePerCPU returns the CPU time (in nanoseconds) consumed on each CPU by
|
|
|
|
|
// all tasks in this cgroup (including tasks lower in the hierarchy).
|
2014-01-26 23:56:04 -05:00
|
|
|
func (c *Container) CPUTimePerCPU() (map[int]time.Duration, error) {
|
2014-01-26 14:52:11 -05:00
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-09-27 16:34:05 -04:00
|
|
|
return nil, err
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 23:56:04 -05:00
|
|
|
cpuTimes := make(map[int]time.Duration)
|
|
|
|
|
for i, v := range strings.Split(c.CgroupItem("cpuacct.usage_percpu")[0], " ") {
|
2013-09-09 00:40:18 -04:00
|
|
|
cpuUsage, err := strconv.ParseInt(v, 10, 64)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
2013-04-03 12:38:41 -04:00
|
|
|
}
|
2014-01-26 23:56:04 -05:00
|
|
|
cpuTimes[i] = time.Duration(cpuUsage)
|
2013-04-03 12:38:41 -04:00
|
|
|
}
|
2013-09-09 00:40:18 -04:00
|
|
|
return cpuTimes, nil
|
2013-04-03 12:38:41 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
// CPUStats returns the number of CPU cycles (in the units defined by USER_HZ on the system)
|
|
|
|
|
// consumed by tasks in this cgroup and its children in both user mode and system (kernel) mode.
|
2014-01-26 22:47:39 -05:00
|
|
|
func (c *Container) CPUStats() (map[string]int64, error) {
|
2014-01-26 14:52:11 -05:00
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-09-27 16:34:05 -04:00
|
|
|
return nil, err
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2013-09-09 00:40:18 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
cpuStat := c.CgroupItem("cpuacct.stat")
|
2014-01-26 22:47:39 -05:00
|
|
|
user, err := strconv.ParseInt(strings.Split(cpuStat[0], "user ")[1], 10, 64)
|
2013-09-09 00:40:18 -04:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2014-01-26 22:47:39 -05:00
|
|
|
system, err := strconv.ParseInt(strings.Split(cpuStat[1], "system ")[1], 10, 64)
|
2013-09-09 00:40:18 -04:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
2013-04-03 12:38:41 -04:00
|
|
|
}
|
2014-01-26 22:47:39 -05:00
|
|
|
|
|
|
|
|
return map[string]int64{"user": user, "system": system}, nil
|
2013-04-03 12:38:41 -04:00
|
|
|
}
|
2013-09-05 01:15:05 -04:00
|
|
|
|
2013-09-09 00:40:18 -04:00
|
|
|
// ConsoleGetFD allocates a console tty from container
|
2013-11-10 22:30:29 -05:00
|
|
|
// ttynum: tty number to attempt to allocate or -1 to allocate the first available tty
|
|
|
|
|
//
|
|
|
|
|
// Returns "ttyfd" on success, -1 on failure. The returned "ttyfd" is
|
|
|
|
|
// used to keep the tty allocated. The caller should close "ttyfd" to
|
|
|
|
|
// indicate that it is done with the allocated console so that it can
|
|
|
|
|
// be allocated by another caller.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) ConsoleGetFD(ttynum int) (int, error) {
|
2013-11-10 22:30:29 -05:00
|
|
|
// FIXME: Make idiomatic
|
2014-01-26 14:52:11 -05:00
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-09-27 16:34:05 -04:00
|
|
|
return -1, err
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-09-05 01:15:05 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
ret := int(C.lxc_console_getfd(c.container, C.int(ttynum)))
|
2013-09-09 00:40:18 -04:00
|
|
|
if ret < 0 {
|
2014-01-26 14:52:11 -05:00
|
|
|
return -1, fmt.Errorf(errAttachFailed, c.name)
|
2013-09-05 01:15:05 -04:00
|
|
|
}
|
2013-09-09 00:40:18 -04:00
|
|
|
return ret, nil
|
2013-09-05 01:15:05 -04:00
|
|
|
}
|
|
|
|
|
|
2013-09-09 00:40:18 -04:00
|
|
|
// Console allocates and runs a console tty from container
|
2013-11-10 22:30:29 -05:00
|
|
|
// ttynum: tty number to attempt to allocate, -1 to allocate the first available tty, or 0 to allocate the console
|
|
|
|
|
// stdinfd: fd to read input from
|
|
|
|
|
// stdoutfd: fd to write output to
|
|
|
|
|
// stderrfd: fd to write error output to
|
|
|
|
|
// escape: he escape character (1 == 'a', 2 == 'b', ...)
|
|
|
|
|
//
|
|
|
|
|
// This function will not return until the console has been exited by the user.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) Console(ttynum, stdinfd, stdoutfd, stderrfd, escape int) error {
|
2013-11-10 22:30:29 -05:00
|
|
|
// FIXME: Make idiomatic
|
2014-01-26 14:52:11 -05:00
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-09-27 16:34:05 -04:00
|
|
|
return err
|
2013-09-09 00:40:18 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-09-05 01:15:05 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_console(c.container, C.int(ttynum), C.int(stdinfd),
|
|
|
|
|
C.int(stdoutfd), C.int(stderrfd), C.int(escape))) {
|
|
|
|
|
return fmt.Errorf(errAttachFailed, c.name)
|
2013-09-05 01:15:05 -04:00
|
|
|
}
|
2013-09-09 00:40:18 -04:00
|
|
|
return nil
|
2013-09-05 01:15:05 -04:00
|
|
|
}
|
2013-09-17 21:29:29 -04:00
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// AttachShell attaches a shell to the container.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) AttachShell() error {
|
|
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-09-27 16:34:05 -04:00
|
|
|
return err
|
2013-09-17 23:47:47 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-09-17 23:47:47 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if int(C.lxc_attach(c.container, false)) < 0 {
|
|
|
|
|
return fmt.Errorf(errAttachFailed, c.name)
|
2013-09-17 23:47:47 -04:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// AttachShellWithClearEnvironment attaches a shell to the container.
|
|
|
|
|
// It clears all environment variables before attaching.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) AttachShellWithClearEnvironment() error {
|
|
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-12-18 21:15:42 -05:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-12-18 21:15:42 -05:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if int(C.lxc_attach(c.container, true)) < 0 {
|
|
|
|
|
return fmt.Errorf(errAttachFailed, c.name)
|
2013-12-18 21:15:42 -05:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// RunCommand runs the user specified command inside the container and waits for it to exit.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) RunCommand(args ...string) error {
|
2013-09-17 23:47:47 -04:00
|
|
|
if args == nil {
|
2013-09-27 16:34:05 -04:00
|
|
|
return fmt.Errorf(errInsufficientNumberOfArguments)
|
2013-09-17 23:47:47 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-09-27 16:34:05 -04:00
|
|
|
return err
|
2013-09-17 23:47:47 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-09-17 23:47:47 -04:00
|
|
|
|
2013-10-30 17:21:42 -04:00
|
|
|
cargs := makeNullTerminatedArgs(args)
|
|
|
|
|
defer freeNullTerminatedArgs(cargs, len(args))
|
2013-09-17 23:47:47 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if int(C.lxc_attach_run_wait(c.container, false, cargs)) < 0 {
|
|
|
|
|
return fmt.Errorf(errAttachFailed, c.name)
|
2013-12-18 21:15:42 -05:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
// RunCommandWithClearEnvironment runs the user specified command inside the container
|
2014-01-26 18:21:51 -05:00
|
|
|
// and waits for it to exit. It clears all environment variables before runnign.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) RunCommandWithClearEnvironment(args ...string) error {
|
2013-12-18 21:15:42 -05:00
|
|
|
if args == nil {
|
|
|
|
|
return fmt.Errorf(errInsufficientNumberOfArguments)
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-12-18 21:15:42 -05:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-12-18 21:15:42 -05:00
|
|
|
|
|
|
|
|
cargs := makeNullTerminatedArgs(args)
|
|
|
|
|
defer freeNullTerminatedArgs(cargs, len(args))
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if int(C.lxc_attach_run_wait(c.container, true, cargs)) < 0 {
|
|
|
|
|
return fmt.Errorf(errAttachFailed, c.name)
|
2013-09-17 23:47:47 -04:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// Interfaces returns the names of the network interfaces.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) Interfaces() ([]string, error) {
|
|
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-09-27 16:34:05 -04:00
|
|
|
return nil, err
|
2013-09-17 21:29:29 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2013-09-17 21:29:29 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
result := C.lxc_get_interfaces(c.container)
|
2013-09-17 21:29:29 -04:00
|
|
|
if result == nil {
|
2014-01-26 14:52:11 -05:00
|
|
|
return nil, fmt.Errorf(errInterfaces, c.name)
|
2013-09-17 21:29:29 -04:00
|
|
|
}
|
2013-09-27 16:34:05 -04:00
|
|
|
return convertArgs(result), nil
|
2013-09-17 21:29:29 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// IPAddress returns the IP address of the given network interface.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) IPAddress(interfaceName string) ([]string, error) {
|
|
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-09-27 16:34:05 -04:00
|
|
|
return nil, err
|
2013-09-17 21:29:29 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2013-09-17 21:29:29 -04:00
|
|
|
|
|
|
|
|
cinterface := C.CString(interfaceName)
|
|
|
|
|
defer C.free(unsafe.Pointer(cinterface))
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
result := C.lxc_get_ips(c.container, cinterface, nil, 0)
|
2013-09-17 21:29:29 -04:00
|
|
|
if result == nil {
|
2014-01-26 14:52:11 -05:00
|
|
|
return nil, fmt.Errorf(errIPAddress, interfaceName, c.name)
|
2013-09-17 21:29:29 -04:00
|
|
|
}
|
2013-09-27 16:34:05 -04:00
|
|
|
return convertArgs(result), nil
|
2013-09-17 21:29:29 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// IPAddresses returns all IP addresses.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) IPAddresses() ([]string, error) {
|
|
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-09-27 16:34:05 -04:00
|
|
|
return nil, err
|
2013-09-17 21:29:29 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2013-09-17 21:29:29 -04:00
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
result := C.lxc_get_ips(c.container, nil, nil, 0)
|
2013-09-17 21:29:29 -04:00
|
|
|
if result == nil {
|
2014-01-26 14:52:11 -05:00
|
|
|
return nil, fmt.Errorf(errIPAddresses, c.name)
|
2013-09-17 21:29:29 -04:00
|
|
|
}
|
2013-09-27 16:34:05 -04:00
|
|
|
return convertArgs(result), nil
|
2013-09-17 21:29:29 -04:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// IPv4Addresses returns all IPv4 addresses.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) IPv4Addresses() ([]string, error) {
|
|
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-09-27 16:34:05 -04:00
|
|
|
return nil, err
|
2013-09-17 21:29:29 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2013-09-17 21:29:29 -04:00
|
|
|
|
|
|
|
|
cfamily := C.CString("inet")
|
|
|
|
|
defer C.free(unsafe.Pointer(cfamily))
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
result := C.lxc_get_ips(c.container, nil, cfamily, 0)
|
2013-09-17 21:29:29 -04:00
|
|
|
if result == nil {
|
2014-01-26 14:52:11 -05:00
|
|
|
return nil, fmt.Errorf(errIPv4Addresses, c.name)
|
2013-09-17 21:29:29 -04:00
|
|
|
}
|
2013-09-27 16:34:05 -04:00
|
|
|
return convertArgs(result), nil
|
2013-09-17 21:29:29 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// IPv6Addresses returns all IPv6 addresses.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) IPv6Addresses() ([]string, error) {
|
|
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-09-27 16:34:05 -04:00
|
|
|
return nil, err
|
2013-09-17 21:29:29 -04:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.RLock()
|
|
|
|
|
defer c.mu.RUnlock()
|
2013-09-17 21:29:29 -04:00
|
|
|
|
|
|
|
|
cfamily := C.CString("inet6")
|
|
|
|
|
defer C.free(unsafe.Pointer(cfamily))
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
result := C.lxc_get_ips(c.container, nil, cfamily, 0)
|
2013-09-17 21:29:29 -04:00
|
|
|
if result == nil {
|
2014-01-26 14:52:11 -05:00
|
|
|
return nil, fmt.Errorf(errIPv6Addresses, c.name)
|
2013-09-17 21:29:29 -04:00
|
|
|
}
|
2013-09-27 16:34:05 -04:00
|
|
|
return convertArgs(result), nil
|
2013-09-17 21:29:29 -04:00
|
|
|
}
|
2013-11-04 14:31:59 -05:00
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// LogFile returns the name of the logfile.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) LogFile() string {
|
|
|
|
|
return c.ConfigItem("lxc.logfile")[0]
|
2013-11-04 14:31:59 -05:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// SetLogFile sets the name of the logfile.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) SetLogFile(filename string) error {
|
|
|
|
|
if err := c.SetConfigItem("lxc.logfile", filename); err != nil {
|
2013-11-04 14:31:59 -05:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// LogLevel returns the level of the logfile.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) LogLevel() LogLevel {
|
|
|
|
|
return logLevelMap[c.ConfigItem("lxc.loglevel")[0]]
|
2013-11-04 14:31:59 -05:00
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// SetLogLevel sets the level of the logfile.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) SetLogLevel(level LogLevel) error {
|
|
|
|
|
if err := c.SetConfigItem("lxc.loglevel", level.String()); err != nil {
|
2013-11-04 14:31:59 -05:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2013-11-13 16:10:05 -05:00
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// AddDeviceNode adds specified device to the container.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) AddDeviceNode(source string, destination ...string) error {
|
|
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-11-13 16:10:05 -05:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-11-13 16:10:05 -05:00
|
|
|
|
|
|
|
|
csource := C.CString(source)
|
|
|
|
|
defer C.free(unsafe.Pointer(csource))
|
|
|
|
|
|
|
|
|
|
if destination != nil && len(destination) == 1 {
|
|
|
|
|
cdestination := C.CString(destination[0])
|
|
|
|
|
defer C.free(unsafe.Pointer(cdestination))
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_add_device_node(c.container, csource, cdestination)) {
|
|
|
|
|
return fmt.Errorf(errAddDeviceNodeFailed, source, c.name)
|
2013-11-13 16:10:05 -05:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_add_device_node(c.container, csource, nil)) {
|
|
|
|
|
return fmt.Errorf(errAddDeviceNodeFailed, source, c.name)
|
2013-11-13 16:10:05 -05:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 18:21:51 -05:00
|
|
|
// RemoveDeviceNode removes the specified device from the container.
|
2014-01-26 14:52:11 -05:00
|
|
|
func (c *Container) RemoveDeviceNode(source string, destination ...string) error {
|
|
|
|
|
if err := c.makeSure(isDefined | isRunning); err != nil {
|
2013-11-13 16:10:05 -05:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
c.mu.Lock()
|
|
|
|
|
defer c.mu.Unlock()
|
2013-11-13 16:10:05 -05:00
|
|
|
|
|
|
|
|
csource := C.CString(source)
|
|
|
|
|
defer C.free(unsafe.Pointer(csource))
|
|
|
|
|
|
|
|
|
|
if destination != nil && len(destination) == 1 {
|
|
|
|
|
cdestination := C.CString(destination[0])
|
|
|
|
|
defer C.free(unsafe.Pointer(cdestination))
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_remove_device_node(c.container, csource, cdestination)) {
|
|
|
|
|
return fmt.Errorf(errRemoveDeviceNodeFailed, source, c.name)
|
2013-11-13 16:10:05 -05:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-26 14:52:11 -05:00
|
|
|
if !bool(C.lxc_remove_device_node(c.container, csource, nil)) {
|
|
|
|
|
return fmt.Errorf(errRemoveDeviceNodeFailed, source, c.name)
|
2013-11-13 16:10:05 -05:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|