1
0
mirror of https://github.com/lxc/go-lxc.git synced 2026-02-05 15:47:17 +01:00
Files
go-lxc/container.go

1103 lines
29 KiB
Go
Raw Normal View History

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-10-28 23:32:39 -04:00
// +build linux
package lxc
// #include <lxc/lxccontainer.h>
// #include "lxc.h"
import "C"
import (
"fmt"
"os/exec"
"strconv"
"strings"
2013-04-03 12:38:41 -04:00
"sync"
"time"
"unsafe"
)
// Container struct
type Container struct {
container *C.struct_lxc_container
verbosity Verbosity
2013-12-07 17:18:36 -05:00
mu sync.RWMutex
}
2013-10-11 17:16:41 -04:00
// Snapshot struct
type Snapshot struct {
Name string
CommentPath string
Timestamp string
Path string
}
func (lxc *Container) ensureDefinedAndRunning() error {
if !lxc.Defined() {
return fmt.Errorf(errNotDefined, C.GoString(lxc.container.name))
}
if !lxc.Running() {
return fmt.Errorf(errNotRunning, C.GoString(lxc.container.name))
}
return nil
}
func (lxc *Container) ensureDefinedButNotRunning() error {
if !lxc.Defined() {
return fmt.Errorf(errNotDefined, C.GoString(lxc.container.name))
}
if lxc.Running() {
return fmt.Errorf(errAlreadyRunning, C.GoString(lxc.container.name))
}
return nil
}
2013-12-02 21:22:24 -05:00
// Name returns the name of the container
2013-04-23 00:04:51 -04:00
func (lxc *Container) Name() string {
2013-12-07 17:18:36 -05:00
lxc.mu.RLock()
defer lxc.mu.RUnlock()
return C.GoString(lxc.container.name)
}
2013-12-02 21:22:24 -05:00
// Defined returns true if the container is already defined
func (lxc *Container) Defined() bool {
2013-12-07 17:18:36 -05:00
lxc.mu.RLock()
defer lxc.mu.RUnlock()
return bool(C.lxc_container_defined(lxc.container))
}
2013-12-02 21:22:24 -05:00
// Running returns true if the container is already running
func (lxc *Container) Running() bool {
2013-12-07 17:18:36 -05:00
lxc.mu.RLock()
defer lxc.mu.RUnlock()
return bool(C.lxc_container_running(lxc.container))
}
2013-12-02 21:22:24 -05:00
// MayControl returns true if the caller may control the container
2013-09-30 16:41:17 -04:00
func (lxc *Container) MayControl() bool {
2013-12-07 17:18:36 -05:00
lxc.mu.RLock()
defer lxc.mu.RUnlock()
2013-09-30 16:41:17 -04:00
return bool(C.lxc_container_may_control(lxc.container))
}
// CreateSnapshot creates a new snapshot
func (lxc *Container) CreateSnapshot() (*Snapshot, error) {
2013-10-11 17:16:41 -04:00
if err := lxc.ensureDefinedButNotRunning(); err != nil {
return nil, err
2013-10-11 17:16:41 -04:00
}
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
2013-10-11 17:16:41 -04:00
ret := int(C.lxc_container_snapshot(lxc.container))
if ret < 0 {
return nil, fmt.Errorf(errCreateSnapshotFailed, C.GoString(lxc.container.name))
2013-10-11 17:16:41 -04:00
}
return &Snapshot{Name: fmt.Sprintf("snap%d", ret)}, nil
2013-10-11 17:16:41 -04:00
}
2013-12-02 21:22:24 -05:00
// RestoreSnapshot creates a new container based on a snapshot
func (lxc *Container) RestoreSnapshot(snapshot Snapshot, name string) error {
2013-10-11 17:16:41 -04:00
if !lxc.Defined() {
return fmt.Errorf(errNotDefined, C.GoString(lxc.container.name))
}
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.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))
if !bool(C.lxc_container_snapshot_restore(lxc.container, csnapname, cname)) {
return fmt.Errorf(errRestoreSnapshotFailed, C.GoString(lxc.container.name))
2013-10-11 17:16:41 -04:00
}
return nil
}
2013-12-02 21:22:24 -05:00
// DestroySnapshot destroys the specified snapshot
func (lxc *Container) DestroySnapshot(snapshot Snapshot) error {
if !lxc.Defined() {
return fmt.Errorf(errNotDefined, C.GoString(lxc.container.name))
}
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
csnapname := C.CString(snapshot.Name)
defer C.free(unsafe.Pointer(csnapname))
if !bool(C.lxc_container_snapshot_destroy(lxc.container, csnapname)) {
return fmt.Errorf(errDestroySnapshotFailed, C.GoString(lxc.container.name))
}
return nil
}
2013-12-02 21:22:24 -05:00
// Snapshots returns the list of container snapshots
func (lxc *Container) Snapshots() ([]Snapshot, error) {
2013-10-11 17:16:41 -04:00
if !lxc.Defined() {
return nil, fmt.Errorf(errNotDefined, C.GoString(lxc.container.name))
}
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
var csnapshots *C.struct_lxc_snapshot
2013-10-11 17:16:41 -04:00
var snapshots []Snapshot
size := int(C.lxc_container_snapshot_list(lxc.container, &csnapshots))
defer freeSnapshots(csnapshots, size)
2013-10-11 17:16:41 -04:00
if size < 1 {
return nil, fmt.Errorf("%s has no snapshots", C.GoString(lxc.container.name))
}
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))
s := &Snapshot{Name: C.GoString(z.name), Timestamp: C.GoString(z.timestamp), CommentPath: C.GoString(z.comment_pathname), Path: C.GoString(z.lxcpath)}
snapshots = append(snapshots, *s)
p += unsafe.Sizeof(*csnapshots)
2013-10-11 17:16:41 -04:00
}
return snapshots, nil
}
2013-12-02 21:22:24 -05:00
// State returns the state of the container
2013-04-23 00:04:51 -04:00
func (lxc *Container) State() State {
2013-12-07 17:18:36 -05:00
lxc.mu.RLock()
defer lxc.mu.RUnlock()
return stateMap[C.GoString(C.lxc_container_state(lxc.container))]
}
2013-12-02 21:22:24 -05:00
// InitPID returns the process ID of the container's init process seen from outside the container
2013-04-23 00:04:51 -04:00
func (lxc *Container) InitPID() int {
2013-12-07 17:18:36 -05:00
lxc.mu.RLock()
defer lxc.mu.RUnlock()
return int(C.lxc_container_init_pid(lxc.container))
}
2013-12-02 21:22:24 -05:00
// Daemonize returns whether the container wished to be daemonized
2013-04-23 00:04:51 -04:00
func (lxc *Container) Daemonize() bool {
2013-12-07 17:18:36 -05:00
lxc.mu.RLock()
defer lxc.mu.RUnlock()
return bool(lxc.container.daemonize)
}
2013-12-02 21:22:24 -05:00
// WantDaemonize sets the daemonize flag for the container
func (lxc *Container) WantDaemonize(state bool) error {
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
if !bool(C.lxc_container_want_daemonize(lxc.container, C.bool(state))) {
return fmt.Errorf(errDaemonizeFailed, C.GoString(lxc.container.name))
2013-09-24 15:47:23 -04:00
}
return nil
}
// WantCloseAllFds sets the close_all_fds flag for the container
func (lxc *Container) WantCloseAllFds(state bool) error {
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
if !bool(C.lxc_container_want_close_all_fds(lxc.container, C.bool(state))) {
return fmt.Errorf(errCloseAllFdsFailed, C.GoString(lxc.container.name))
}
return nil
}
// SetVerbosity sets the verbosity level of some API calls
func (lxc *Container) SetVerbosity(verbosity Verbosity) {
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
lxc.verbosity = verbosity
}
// Freeze freezes the running container
func (lxc *Container) Freeze() error {
if err := lxc.ensureDefinedAndRunning(); err != nil {
return err
}
if lxc.State() == FROZEN {
return fmt.Errorf(errAlreadyFrozen, C.GoString(lxc.container.name))
}
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
if !bool(C.lxc_container_freeze(lxc.container)) {
return fmt.Errorf(errFreezeFailed, C.GoString(lxc.container.name))
}
return nil
}
2013-12-02 21:22:24 -05:00
// Unfreeze thaws the frozen container
func (lxc *Container) Unfreeze() error {
if err := lxc.ensureDefinedAndRunning(); err != nil {
return err
}
if lxc.State() != FROZEN {
return fmt.Errorf(errNotFrozen, C.GoString(lxc.container.name))
}
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
if !bool(C.lxc_container_unfreeze(lxc.container)) {
return fmt.Errorf(errUnfreezeFailed, C.GoString(lxc.container.name))
}
return nil
}
// Create creates the container using given template and arguments
func (lxc *Container) Create(template string, args ...string) error {
// FIXME: Support bdevtype and bdev_specs
// bdevtypes:
// "btrfs", "zfs", "lvm", "dir"
//
// best tries to find the best backing store type
//
// bdev_specs:
// zfs requires zfsroot
// lvm requires lvname/vgname/thinpool as well as fstype and fssize
// btrfs requires nothing
// dir requires nothing
if lxc.Defined() {
return fmt.Errorf(errAlreadyDefined, C.GoString(lxc.container.name))
}
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
2013-04-03 12:38:41 -04:00
ctemplate := C.CString(template)
defer C.free(unsafe.Pointer(ctemplate))
cbdevtype := C.CString("dir")
defer C.free(unsafe.Pointer(cbdevtype))
ret := false
if args != nil {
cargs := makeNullTerminatedArgs(args)
defer freeNullTerminatedArgs(cargs, len(args))
ret = bool(C.lxc_container_create(lxc.container, ctemplate, cbdevtype, C.int(lxc.verbosity), cargs))
} else {
ret = bool(C.lxc_container_create(lxc.container, ctemplate, cbdevtype, C.int(lxc.verbosity), nil))
}
if !ret {
return fmt.Errorf(errCreateFailed, C.GoString(lxc.container.name))
}
return nil
}
// Start starts the container
func (lxc *Container) Start() error {
if err := lxc.ensureDefinedButNotRunning(); err != nil {
return err
}
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
2013-04-03 12:38:41 -04:00
2013-11-18 15:49:26 -05:00
if !bool(C.lxc_container_start(lxc.container, 0, nil)) {
return fmt.Errorf(errStartFailed, C.GoString(lxc.container.name))
}
return nil
}
2013-12-02 21:22:24 -05:00
// Execute executes the given command in a temporary container
func (lxc *Container) Execute(args ...string) ([]byte, error) {
if lxc.Defined() {
return nil, fmt.Errorf(errAlreadyDefined, C.GoString(lxc.container.name))
}
cargs := []string{"lxc-execute", "-n", lxc.Name(), "-P", lxc.ConfigPath(), "--"}
cargs = append(cargs, args...)
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
/*
* FIXME: Go runtime and src/lxc/start.c signal_handler are not playing nice together so use lxc-execute for now
*/
output, err := exec.Command(cargs[0], cargs[1:]...).CombinedOutput()
if err != nil {
return nil, fmt.Errorf(errExecuteFailed, C.GoString(lxc.container.name))
}
return output, nil
/*
2013-11-18 15:49:26 -05:00
cargs := makeNullTerminatedArgs(args)
defer freeNullTerminatedArgs(cargs, len(args))
2013-11-18 15:49:26 -05:00
if !bool(C.lxc_container_start(lxc.container, 1, cargs)) {
return fmt.Errorf(errExecuteFailed, C.GoString(lxc.container.name))
}
return nil
*/
}
// Stop stops the container
func (lxc *Container) Stop() error {
if err := lxc.ensureDefinedAndRunning(); err != nil {
return err
}
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
if !bool(C.lxc_container_stop(lxc.container)) {
return fmt.Errorf(errStopFailed, C.GoString(lxc.container.name))
}
return nil
}
// Reboot reboots the container
func (lxc *Container) Reboot() error {
if err := lxc.ensureDefinedAndRunning(); err != nil {
return err
}
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
if !bool(C.lxc_container_reboot(lxc.container)) {
return fmt.Errorf(errRebootFailed, C.GoString(lxc.container.name))
}
return nil
}
// Shutdown shutdowns the container
func (lxc *Container) Shutdown(timeout int) error {
if err := lxc.ensureDefinedAndRunning(); err != nil {
return err
}
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
if !bool(C.lxc_container_shutdown(lxc.container, C.int(timeout))) {
return fmt.Errorf(errShutdownFailed, C.GoString(lxc.container.name))
}
return nil
}
// Destroy destroys the container
func (lxc *Container) Destroy() error {
if err := lxc.ensureDefinedButNotRunning(); err != nil {
return err
}
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
if !bool(C.lxc_container_destroy(lxc.container)) {
return fmt.Errorf(errDestroyFailed, C.GoString(lxc.container.name))
}
return nil
}
// Clone clones the container
func (lxc *Container) Clone(name string, flags int, backend BackendStore) error {
// FIXME: support lxcpath, bdevtype, bdevdata, newsize and hookargs
//
// 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
//
// flags: LXC_CLONE_SNAPSHOT || LXC_CLONE_KEEPNAME || LXC_CLONE_KEEPMACADDR || LXC_CLONE_COPYHOOKS
//
// newsize: for blockdev-backed backingstores
//
// hookargs: additional arguments to pass to the clone hook script
if err := lxc.ensureDefinedButNotRunning(); err != nil {
return err
}
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
2013-04-24 19:32:25 -04:00
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
if !bool(C.lxc_container_clone(lxc.container, cname, C.int(flags), C.CString(backend.String()))) {
return fmt.Errorf(errCloneFailed, C.GoString(lxc.container.name))
2013-04-24 19:32:25 -04:00
}
return nil
}
// CloneToDirectory clones the container using Directory backendstore
func (lxc *Container) CloneToDirectory(name string) error {
return lxc.Clone(name, 0, Directory)
2013-04-24 19:32:25 -04:00
}
2014-01-14 11:02:20 -05:00
// CloneToBtrfs clones the container using Btrfs backendstore
func (lxc *Container) CloneToBtrfs(name string) error {
return lxc.Clone(name, 0, Btrfs)
}
2014-01-14 11:02:20 -05:00
// CloneToOverlayfs clones the container using Overlayfs backendstore
func (lxc *Container) CloneToOverlayfs(name string) error {
return lxc.Clone(name, CloneSnapshot, Overlayfs)
2013-04-25 14:05:11 -04:00
}
// Rename renames the container
func (lxc *Container) Rename(name string) error {
if err := lxc.ensureDefinedButNotRunning(); err != nil {
return err
}
lxc.mu.Lock()
defer lxc.mu.Unlock()
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
if !bool(C.lxc_container_rename(lxc.container, cname)) {
return fmt.Errorf(errRenameFailed, C.GoString(lxc.container.name))
}
return nil
}
2013-12-02 21:22:24 -05:00
// Wait waits for container to reach a given state or timeouts
func (lxc *Container) Wait(state State, timeout int) bool {
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
cstate := C.CString(state.String())
defer C.free(unsafe.Pointer(cstate))
return bool(C.lxc_container_wait(lxc.container, cstate, C.int(timeout)))
}
// ConfigFileName returns the container's configuration file's name
2013-04-23 00:04:51 -04:00
func (lxc *Container) ConfigFileName() string {
2013-12-07 17:18:36 -05:00
lxc.mu.RLock()
defer lxc.mu.RUnlock()
// allocated in lxc.c
configFileName := C.lxc_container_config_file_name(lxc.container)
defer C.free(unsafe.Pointer(configFileName))
return C.GoString(configFileName)
}
2013-12-02 21:22:24 -05:00
// ConfigItem returns the value of the given config item
2013-04-23 00:04:51 -04:00
func (lxc *Container) ConfigItem(key string) []string {
2013-12-07 17:18:36 -05:00
lxc.mu.RLock()
defer lxc.mu.RUnlock()
ckey := C.CString(key)
defer C.free(unsafe.Pointer(ckey))
// allocated in lxc.c
configItem := C.lxc_container_get_config_item(lxc.container, ckey)
defer C.free(unsafe.Pointer(configItem))
ret := strings.TrimSpace(C.GoString(configItem))
return strings.Split(ret, "\n")
}
2013-12-02 21:22:24 -05:00
// SetConfigItem sets the value of the given config item
func (lxc *Container) SetConfigItem(key string, value string) error {
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
ckey := C.CString(key)
defer C.free(unsafe.Pointer(ckey))
cvalue := C.CString(value)
defer C.free(unsafe.Pointer(cvalue))
if !bool(C.lxc_container_set_config_item(lxc.container, ckey, cvalue)) {
return fmt.Errorf(errSettingConfigItemFailed, C.GoString(lxc.container.name), key, value)
}
return nil
}
2013-12-02 21:22:24 -05:00
// CgroupItem returns the value of the given cgroup subsystem value
2013-04-23 00:04:51 -04:00
func (lxc *Container) CgroupItem(key string) []string {
2013-12-07 17:18:36 -05:00
lxc.mu.RLock()
defer lxc.mu.RUnlock()
ckey := C.CString(key)
defer C.free(unsafe.Pointer(ckey))
// allocated in lxc.c
cgroupItem := C.lxc_container_get_cgroup_item(lxc.container, ckey)
defer C.free(unsafe.Pointer(cgroupItem))
ret := strings.TrimSpace(C.GoString(cgroupItem))
return strings.Split(ret, "\n")
}
2013-12-02 21:22:24 -05:00
// SetCgroupItem sets the value of given cgroup subsystem value
func (lxc *Container) SetCgroupItem(key string, value string) error {
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
ckey := C.CString(key)
defer C.free(unsafe.Pointer(ckey))
cvalue := C.CString(value)
defer C.free(unsafe.Pointer(cvalue))
if !bool(C.lxc_container_set_cgroup_item(lxc.container, ckey, cvalue)) {
return fmt.Errorf(errSettingCgroupItemFailed, C.GoString(lxc.container.name), key, value)
}
return nil
}
2013-12-02 21:22:24 -05:00
// ClearConfigItem clears the value of given config item
func (lxc *Container) ClearConfigItem(key string) error {
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
ckey := C.CString(key)
defer C.free(unsafe.Pointer(ckey))
if !bool(C.lxc_container_clear_config_item(lxc.container, ckey)) {
return fmt.Errorf(errClearingCgroupItemFailed, C.GoString(lxc.container.name), key)
}
return nil
}
2013-12-02 21:22:24 -05:00
// ConfigKeys returns the names of the config items
func (lxc *Container) ConfigKeys(key ...string) []string {
2013-12-07 17:18:36 -05:00
lxc.mu.RLock()
defer lxc.mu.RUnlock()
var keys *_Ctype_char
if key != nil && len(key) == 1 {
ckey := C.CString(key[0])
defer C.free(unsafe.Pointer(ckey))
// allocated in lxc.c
keys = C.lxc_container_get_keys(lxc.container, ckey)
defer C.free(unsafe.Pointer(keys))
} else {
// allocated in lxc.c
keys = C.lxc_container_get_keys(lxc.container, nil)
defer C.free(unsafe.Pointer(keys))
}
ret := strings.TrimSpace(C.GoString(keys))
return strings.Split(ret, "\n")
}
// LoadConfigFile loads the configuration file from given path
func (lxc *Container) LoadConfigFile(path string) error {
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
cpath := C.CString(path)
defer C.free(unsafe.Pointer(cpath))
if !bool(C.lxc_container_load_config(lxc.container, cpath)) {
return fmt.Errorf(errLoadConfigFailed, C.GoString(lxc.container.name), path)
}
return nil
}
// SaveConfigFile saves the configuration file to given path
func (lxc *Container) SaveConfigFile(path string) error {
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
cpath := C.CString(path)
defer C.free(unsafe.Pointer(cpath))
if !bool(C.lxc_container_save_config(lxc.container, cpath)) {
return fmt.Errorf(errSaveConfigFailed, C.GoString(lxc.container.name), path)
}
return nil
}
// ConfigPath returns the configuration file's path
2013-04-23 00:04:51 -04:00
func (lxc *Container) ConfigPath() string {
2013-12-07 17:18:36 -05:00
lxc.mu.RLock()
defer lxc.mu.RUnlock()
return C.GoString(C.lxc_container_get_config_path(lxc.container))
}
// SetConfigPath sets the configuration file's path
func (lxc *Container) SetConfigPath(path string) error {
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
cpath := C.CString(path)
defer C.free(unsafe.Pointer(cpath))
if !bool(C.lxc_container_set_config_path(lxc.container, cpath)) {
return fmt.Errorf(errSettingConfigPathFailed, C.GoString(lxc.container.name), path)
}
return nil
}
2013-12-02 21:22:24 -05:00
// MemoryUsage returns memory usage of the container in bytes
func (lxc *Container) MemoryUsage() (ByteSize, error) {
if err := lxc.ensureDefinedAndRunning(); err != nil {
return -1, err
}
2013-12-07 17:18:36 -05:00
lxc.mu.RLock()
defer lxc.mu.RUnlock()
memUsed, err := strconv.ParseFloat(lxc.CgroupItem("memory.usage_in_bytes")[0], 64)
if err != nil {
return -1, fmt.Errorf(errMemLimit)
}
return ByteSize(memUsed), err
}
2013-12-02 21:22:24 -05:00
// SwapUsage returns swap usage of the container in bytes
func (lxc *Container) SwapUsage() (ByteSize, error) {
if err := lxc.ensureDefinedAndRunning(); err != nil {
return -1, err
}
2013-12-07 17:18:36 -05:00
lxc.mu.RLock()
defer lxc.mu.RUnlock()
swapUsed, err := strconv.ParseFloat(lxc.CgroupItem("memory.memsw.usage_in_bytes")[0], 64)
if err != nil {
return -1, fmt.Errorf(errSwapLimit)
}
return ByteSize(swapUsed), err
}
2013-12-02 21:22:24 -05:00
// MemoryLimit returns memory limit of the container in bytes
func (lxc *Container) MemoryLimit() (ByteSize, error) {
if err := lxc.ensureDefinedAndRunning(); err != nil {
return -1, err
}
2013-12-07 17:18:36 -05:00
lxc.mu.RLock()
defer lxc.mu.RUnlock()
memLimit, err := strconv.ParseFloat(lxc.CgroupItem("memory.limit_in_bytes")[0], 64)
if err != nil {
return -1, fmt.Errorf(errMemLimit)
}
return ByteSize(memLimit), err
}
2013-12-02 21:22:24 -05:00
// SetMemoryLimit sets memory limit of the container in bytes
func (lxc *Container) SetMemoryLimit(limit ByteSize) error {
if err := lxc.ensureDefinedAndRunning(); err != nil {
return err
}
if err := lxc.SetCgroupItem("memory.limit_in_bytes", fmt.Sprintf("%.f", limit)); err != nil {
return fmt.Errorf(errSettingMemoryLimitFailed, C.GoString(lxc.container.name))
}
return nil
}
2013-12-02 21:22:24 -05:00
// SwapLimit returns the swap limit of the container in bytes
func (lxc *Container) SwapLimit() (ByteSize, error) {
if err := lxc.ensureDefinedAndRunning(); err != nil {
return -1, err
}
2013-12-07 17:18:36 -05:00
lxc.mu.RLock()
defer lxc.mu.RUnlock()
swapLimit, err := strconv.ParseFloat(lxc.CgroupItem("memory.memsw.limit_in_bytes")[0], 64)
if err != nil {
return -1, fmt.Errorf(errSwapLimit)
2013-04-03 12:38:41 -04:00
}
return ByteSize(swapLimit), err
2013-04-03 12:38:41 -04:00
}
2013-12-02 21:22:24 -05:00
// SetSwapLimit sets memory limit of the container in bytes
func (lxc *Container) SetSwapLimit(limit ByteSize) error {
if err := lxc.ensureDefinedAndRunning(); err != nil {
return err
}
if err := lxc.SetCgroupItem("memory.memsw.limit_in_bytes", fmt.Sprintf("%.f", limit)); err != nil {
return fmt.Errorf(errSettingSwapLimitFailed, C.GoString(lxc.container.name))
}
return nil
}
// CPUTime returns the total CPU time (in nanoseconds) consumed by all tasks in this cgroup (including tasks lower in the hierarchy).
2013-04-23 00:04:51 -04:00
func (lxc *Container) CPUTime() (time.Duration, error) {
if err := lxc.ensureDefinedAndRunning(); err != nil {
return -1, err
}
2013-12-07 17:18:36 -05:00
lxc.mu.RLock()
defer lxc.mu.RUnlock()
cpuUsage, err := strconv.ParseInt(lxc.CgroupItem("cpuacct.usage")[0], 10, 64)
if err != nil {
return -1, err
}
return time.Duration(cpuUsage), err
}
2013-04-03 12:38:41 -04:00
// CPUTimePerCPU returns the CPU time (in nanoseconds) consumed on each CPU by all tasks in this cgroup (including tasks lower in the hierarchy).
2013-04-23 00:04:51 -04:00
func (lxc *Container) CPUTimePerCPU() ([]time.Duration, error) {
if err := lxc.ensureDefinedAndRunning(); err != nil {
return nil, err
}
2013-12-07 17:18:36 -05:00
lxc.mu.RLock()
defer lxc.mu.RUnlock()
2013-04-03 12:38:41 -04:00
var cpuTimes []time.Duration
for _, v := range strings.Split(lxc.CgroupItem("cpuacct.usage_percpu")[0], " ") {
cpuUsage, err := strconv.ParseInt(v, 10, 64)
if err != nil {
return nil, err
2013-04-03 12:38:41 -04:00
}
cpuTimes = append(cpuTimes, time.Duration(cpuUsage))
2013-04-03 12:38:41 -04:00
}
return cpuTimes, nil
2013-04-03 12:38:41 -04: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.
2013-04-23 00:04:51 -04:00
func (lxc *Container) CPUStats() ([]int64, error) {
if err := lxc.ensureDefinedAndRunning(); err != nil {
return nil, err
}
2013-12-07 17:18:36 -05:00
lxc.mu.RLock()
defer lxc.mu.RUnlock()
cpuStat := lxc.CgroupItem("cpuacct.stat")
user, err := strconv.ParseInt(strings.Split(cpuStat[0], " ")[1], 10, 64)
if err != nil {
return nil, err
}
system, err := strconv.ParseInt(strings.Split(cpuStat[1], " ")[1], 10, 64)
if err != nil {
return nil, err
2013-04-03 12:38:41 -04:00
}
return []int64{user, system}, nil
2013-04-03 12:38:41 -04:00
}
// ConsoleGetFD allocates a console tty from container
// 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.
func (lxc *Container) ConsoleGetFD(ttynum int) (int, error) {
// FIXME: Make idiomatic
if err := lxc.ensureDefinedAndRunning(); err != nil {
return -1, err
}
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
ret := int(C.lxc_container_console_getfd(lxc.container, C.int(ttynum)))
if ret < 0 {
return -1, fmt.Errorf(errAttachFailed, C.GoString(lxc.container.name))
}
return ret, nil
}
// Console allocates and runs a console tty from container
// 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.
func (lxc *Container) Console(ttynum, stdinfd, stdoutfd, stderrfd, escape int) error {
// FIXME: Make idiomatic
if err := lxc.ensureDefinedAndRunning(); err != nil {
return err
}
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
if !bool(C.lxc_container_console(lxc.container, C.int(ttynum), C.int(stdinfd), C.int(stdoutfd), C.int(stderrfd), C.int(escape))) {
return fmt.Errorf(errAttachFailed, C.GoString(lxc.container.name))
}
return nil
}
// AttachShell runs a shell inside the container
func (lxc *Container) AttachShell() error {
if err := lxc.ensureDefinedAndRunning(); err != nil {
return err
}
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
if int(C.lxc_container_attach(lxc.container, false)) < 0 {
return fmt.Errorf(errAttachFailed, C.GoString(lxc.container.name))
}
return nil
}
// AttachShellWithClearEnvironment runs a shell inside the container and clears all environment variables before attaching
func (lxc *Container) AttachShellWithClearEnvironment() error {
if err := lxc.ensureDefinedAndRunning(); err != nil {
return err
}
lxc.mu.Lock()
defer lxc.mu.Unlock()
if int(C.lxc_container_attach(lxc.container, true)) < 0 {
return fmt.Errorf(errAttachFailed, C.GoString(lxc.container.name))
}
return nil
}
// RunCommand runs the user specified command inside the container and waits it to exit
func (lxc *Container) RunCommand(args ...string) error {
if args == nil {
return fmt.Errorf(errInsufficientNumberOfArguments)
}
if err := lxc.ensureDefinedAndRunning(); err != nil {
return err
}
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
cargs := makeNullTerminatedArgs(args)
defer freeNullTerminatedArgs(cargs, len(args))
if int(C.lxc_container_attach_run_wait(lxc.container, false, cargs)) < 0 {
return fmt.Errorf(errAttachFailed, C.GoString(lxc.container.name))
}
return nil
}
// RunCommandWithClearEnvironment runs the user specified command inside the container and waits it to exit. It also clears all environment variables before attaching.
func (lxc *Container) RunCommandWithClearEnvironment(args ...string) error {
if args == nil {
return fmt.Errorf(errInsufficientNumberOfArguments)
}
if err := lxc.ensureDefinedAndRunning(); err != nil {
return err
}
lxc.mu.Lock()
defer lxc.mu.Unlock()
cargs := makeNullTerminatedArgs(args)
defer freeNullTerminatedArgs(cargs, len(args))
if int(C.lxc_container_attach_run_wait(lxc.container, true, cargs)) < 0 {
return fmt.Errorf(errAttachFailed, C.GoString(lxc.container.name))
}
return nil
}
2013-12-02 21:22:24 -05:00
// Interfaces returns the name of the network interfaces from the container
func (lxc *Container) Interfaces() ([]string, error) {
if err := lxc.ensureDefinedAndRunning(); err != nil {
return nil, err
}
2013-12-07 17:18:36 -05:00
lxc.mu.RLock()
defer lxc.mu.RUnlock()
result := C.lxc_container_get_interfaces(lxc.container)
if result == nil {
return nil, fmt.Errorf(errInterfaces, C.GoString(lxc.container.name))
}
return convertArgs(result), nil
}
2013-12-02 21:22:24 -05:00
// IPAddress returns the IP address of the given network interface from the container
func (lxc *Container) IPAddress(interfaceName string) ([]string, error) {
if err := lxc.ensureDefinedAndRunning(); err != nil {
return nil, err
}
2013-12-07 17:18:36 -05:00
lxc.mu.RLock()
defer lxc.mu.RUnlock()
cinterface := C.CString(interfaceName)
defer C.free(unsafe.Pointer(cinterface))
result := C.lxc_container_get_ips(lxc.container, cinterface, nil, 0)
if result == nil {
return nil, fmt.Errorf(errIPAddress, interfaceName, C.GoString(lxc.container.name))
}
return convertArgs(result), nil
}
// IPAddresses returns all IP addresses from the container
func (lxc *Container) IPAddresses() ([]string, error) {
if err := lxc.ensureDefinedAndRunning(); err != nil {
return nil, err
}
2013-12-07 17:18:36 -05:00
lxc.mu.RLock()
defer lxc.mu.RUnlock()
result := C.lxc_container_get_ips(lxc.container, nil, nil, 0)
if result == nil {
return nil, fmt.Errorf(errIPAddresses, C.GoString(lxc.container.name))
}
return convertArgs(result), nil
}
// IPv4Addresses returns all IPv4 addresses from the container
func (lxc *Container) IPv4Addresses() ([]string, error) {
if err := lxc.ensureDefinedAndRunning(); err != nil {
return nil, err
}
2013-12-07 17:18:36 -05:00
lxc.mu.RLock()
defer lxc.mu.RUnlock()
cfamily := C.CString("inet")
defer C.free(unsafe.Pointer(cfamily))
result := C.lxc_container_get_ips(lxc.container, nil, cfamily, 0)
if result == nil {
return nil, fmt.Errorf(errIPv4Addresses, C.GoString(lxc.container.name))
}
return convertArgs(result), nil
}
// IPv6Addresses returns all IPv6 addresses from the container
func (lxc *Container) IPv6Addresses() ([]string, error) {
if err := lxc.ensureDefinedAndRunning(); err != nil {
return nil, err
}
2013-12-07 17:18:36 -05:00
lxc.mu.RLock()
defer lxc.mu.RUnlock()
cfamily := C.CString("inet6")
defer C.free(unsafe.Pointer(cfamily))
result := C.lxc_container_get_ips(lxc.container, nil, cfamily, 0)
if result == nil {
return nil, fmt.Errorf(errIPv6Addresses, C.GoString(lxc.container.name))
}
return convertArgs(result), nil
}
2013-11-04 14:31:59 -05:00
// LogFile returns the name of the logfile
func (lxc *Container) LogFile() string {
return lxc.ConfigItem("lxc.logfile")[0]
}
// SetLogFile sets the logfile to given filename
func (lxc *Container) SetLogFile(filename string) error {
if err := lxc.SetConfigItem("lxc.logfile", filename); err != nil {
return err
}
return nil
}
// LogLevel returns the name of the logfile
func (lxc *Container) LogLevel() LogLevel {
return logLevelMap[lxc.ConfigItem("lxc.loglevel")[0]]
}
// SetLogLevel sets the logfile to given filename
func (lxc *Container) SetLogLevel(level LogLevel) error {
if err := lxc.SetConfigItem("lxc.loglevel", level.String()); err != nil {
return err
}
return nil
}
2013-12-02 21:22:24 -05:00
// AddDeviceNode adds specified device to the container
func (lxc *Container) AddDeviceNode(source string, destination ...string) error {
if err := lxc.ensureDefinedAndRunning(); err != nil {
return err
}
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
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))
if !bool(C.lxc_container_add_device_node(lxc.container, csource, cdestination)) {
return fmt.Errorf("adding device %s to container %q failed", source, C.GoString(lxc.container.name))
}
return nil
}
if !bool(C.lxc_container_add_device_node(lxc.container, csource, nil)) {
return fmt.Errorf("adding device %s to container %q failed", source, C.GoString(lxc.container.name))
}
return nil
}
2013-12-02 21:22:24 -05:00
// RemoveDeviceNode removes the specified device from the container
func (lxc *Container) RemoveDeviceNode(source string, destination ...string) error {
if err := lxc.ensureDefinedAndRunning(); err != nil {
return err
}
2013-12-07 17:18:36 -05:00
lxc.mu.Lock()
defer lxc.mu.Unlock()
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))
if !bool(C.lxc_container_remove_device_node(lxc.container, csource, cdestination)) {
return fmt.Errorf("adding device %s to container %q failed", source, C.GoString(lxc.container.name))
}
return nil
}
if !bool(C.lxc_container_remove_device_node(lxc.container, csource, nil)) {
return fmt.Errorf("adding device %s to container %q failed", source, C.GoString(lxc.container.name))
}
return nil
}