1
0
mirror of https://github.com/lxc/go-lxc.git synced 2026-02-05 06:46:38 +01:00

[major] make API more idiomatic, make go vet and golint happy and modify examples accordingly

This commit is contained in:
S.Çağlar Onur
2013-09-09 00:40:18 -04:00
parent 1a78e912ec
commit 16396d448f
28 changed files with 904 additions and 603 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
examples/clone
examples/concurrent_create
examples/concurrent_destroy
examples/concurrent_shutdown

View File

@@ -1,6 +1,11 @@
all: format vet lint
format:
@gofmt -s -w *.go
test:
@sudo `which go` test -v
docs:
@`which godoc` github.com/caglar10ur/lxc
doc:
@`which godoc` github.com/caglar10ur/lxc | less
vet:
`which vet` .
lint:
`which golint` .

View File

@@ -1,54 +0,0 @@
/*
* backendstore.go: Go bindings for lxc
*
* Copyright © 2013, S.Çağlar Onur
*
* Authors:
* S.Çağlar Onur <caglar@10ur.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package lxc
type BackendStore int
const (
BTRFS BackendStore = iota
DIRECTORY
LVM
ZFS
OVERLAYFS
LOOPBACK
)
// BackendStore as string
func (t BackendStore) String() string {
switch t {
case DIRECTORY:
return "Directory"
case ZFS:
return "ZFS"
case BTRFS:
return "BtrFS"
case LVM:
return "LVM"
case OVERLAYFS:
return "OverlayFS"
case LOOPBACK:
return "Loopback"
}
return "<INVALID>"
}

View File

@@ -1,48 +0,0 @@
/*
* bytesize.go: Go bindings for lxc
*
* Taken from http://golang.org/doc/effective_go.html#constants
*
*/
package lxc
import (
"fmt"
)
type ByteSize float64
const (
_ = iota
KB ByteSize = 1 << (10 * iota)
MB
GB
TB
PB
EB
ZB
YB
)
func (b ByteSize) String() string {
switch {
case b >= YB:
return fmt.Sprintf("%.2fYB", b/YB)
case b >= ZB:
return fmt.Sprintf("%.2fZB", b/ZB)
case b >= EB:
return fmt.Sprintf("%.2fEB", b/EB)
case b >= PB:
return fmt.Sprintf("%.2fPB", b/PB)
case b >= TB:
return fmt.Sprintf("%.2fTB", b/TB)
case b >= GB:
return fmt.Sprintf("%.2fGB", b/GB)
case b >= MB:
return fmt.Sprintf("%.2fMB", b/MB)
case b >= KB:
return fmt.Sprintf("%.2fKB", b/KB)
}
return fmt.Sprintf("%.2fB", b)
}

View File

@@ -29,6 +29,7 @@ package lxc
import "C"
import (
"fmt"
"strconv"
"strings"
"sync"
@@ -36,395 +37,645 @@ import (
"unsafe"
)
const (
network_key = "lxc.network"
)
// Container struct
type Container struct {
container *C.struct_lxc_container
verbosity Verbosity
sync.RWMutex
}
// Returns container's name
// Name returns container's name
func (lxc *Container) Name() string {
lxc.RLock()
defer lxc.RUnlock()
return C.GoString(lxc.container.name)
}
// Returns whether the container is already defined or not
// Defined returns whether the container is already defined or not
func (lxc *Container) Defined() bool {
lxc.RLock()
defer lxc.RUnlock()
return bool(C.lxc_container_defined(lxc.container))
}
// Returns whether the container is already running or not
// Running returns whether the container is already running or not
func (lxc *Container) Running() bool {
lxc.RLock()
defer lxc.RUnlock()
return bool(C.lxc_container_running(lxc.container))
}
// Returns the container's state
// State returns the container's state
func (lxc *Container) State() State {
lxc.RLock()
defer lxc.RUnlock()
return stateMap[C.GoString(C.lxc_container_state(lxc.container))]
}
// Returns the container's PID
// InitPID returns the container's PID
func (lxc *Container) InitPID() int {
lxc.RLock()
defer lxc.RUnlock()
return int(C.lxc_container_init_pid(lxc.container))
}
// Returns whether the daemonize flag is set
// Daemonize returns whether the daemonize flag is set
func (lxc *Container) Daemonize() bool {
lxc.RLock()
defer lxc.RUnlock()
return bool(lxc.container.daemonize != 0)
}
// Sets the daemonize flag
// SetDaemonize sets the daemonize flag
func (lxc *Container) SetDaemonize() {
lxc.Lock()
defer lxc.Unlock()
C.lxc_container_want_daemonize(lxc.container)
}
// Freezes the running container
func (lxc *Container) Freeze() bool {
// SetVerbosity sets the verbosity level of some API calls
func (lxc *Container) SetVerbosity(verbosity Verbosity) {
lxc.Lock()
defer lxc.Unlock()
return bool(C.lxc_container_freeze(lxc.container))
lxc.verbosity = verbosity
}
// Unfreezes the frozen container
func (lxc *Container) Unfreeze() bool {
// Freeze freezes the running container
func (lxc *Container) Freeze() error {
if !lxc.Defined() {
return fmt.Errorf("there is no container named %q", lxc.Name())
}
if !lxc.Running() {
return fmt.Errorf("container %q is not running", lxc.Name())
}
if lxc.State() == FROZEN {
return fmt.Errorf("container %q is already frozen", lxc.Name())
}
lxc.Lock()
defer lxc.Unlock()
return bool(C.lxc_container_unfreeze(lxc.container))
if !bool(C.lxc_container_freeze(lxc.container)) {
return fmt.Errorf("freezing the container %q failed", lxc.Name())
}
return nil
}
// Creates the container using given template and arguments
func (lxc *Container) Create(template string, args ...string) bool {
// Unfreeze unfreezes the frozen container
func (lxc *Container) Unfreeze() error {
if !lxc.Defined() {
return fmt.Errorf("there is no container named %q", lxc.Name())
}
if !lxc.Running() {
return fmt.Errorf("container %q is not running", lxc.Name())
}
if lxc.State() != FROZEN {
return fmt.Errorf("container %q is not frozen", lxc.Name())
}
lxc.Lock()
defer lxc.Unlock()
if !bool(C.lxc_container_unfreeze(lxc.container)) {
return fmt.Errorf("unfreezing the container %q failed", lxc.Name())
}
return nil
}
// Create creates the container using given template and arguments
func (lxc *Container) Create(template string, args ...string) error {
if lxc.Defined() {
return fmt.Errorf("container %q is already defined", lxc.Name())
}
lxc.Lock()
defer lxc.Unlock()
ctemplate := C.CString(template)
defer C.free(unsafe.Pointer(ctemplate))
ret := false
if args != nil {
cargs := makeArgs(args)
defer freeArgs(cargs)
return bool(C.lxc_container_create(lxc.container, ctemplate, &cargs[0]))
ret = bool(C.lxc_container_create(lxc.container, ctemplate, C.int(lxc.verbosity), &cargs[0]))
} else {
ret = bool(C.lxc_container_create(lxc.container, ctemplate, C.int(lxc.verbosity), nil))
}
return bool(C.lxc_container_create(lxc.container, ctemplate, nil))
if !ret {
return fmt.Errorf("creating the container %q failed", lxc.Name())
}
return nil
}
// Starts the container
func (lxc *Container) Start(useinit bool, args ...string) bool {
// Start starts the container
func (lxc *Container) Start(useinit bool, args ...string) error {
if !lxc.Defined() {
return fmt.Errorf("there is no container named %q", lxc.Name())
}
if lxc.Running() {
return fmt.Errorf("container %q is already running", lxc.Name())
}
lxc.Lock()
defer lxc.Unlock()
ret := false
cuseinit := 0
if useinit {
cuseinit = 1
}
if args != nil {
cargs := makeArgs(args)
defer freeArgs(cargs)
return bool(C.lxc_container_start(lxc.container, C.int(cuseinit), &cargs[0]))
ret = bool(C.lxc_container_start(lxc.container, C.int(cuseinit), &cargs[0]))
} else {
ret = bool(C.lxc_container_start(lxc.container, C.int(cuseinit), nil))
}
return bool(C.lxc_container_start(lxc.container, C.int(cuseinit), nil))
if !ret {
return fmt.Errorf("starting the container %q failed", lxc.Name())
}
return nil
}
// Stops the container
func (lxc *Container) Stop() bool {
// Stop stops the container
func (lxc *Container) Stop() error {
if !lxc.Defined() {
return fmt.Errorf("there is no container named %q", lxc.Name())
}
if !lxc.Running() {
return fmt.Errorf("container %q is already stoped", lxc.Name())
}
lxc.Lock()
defer lxc.Unlock()
return bool(C.lxc_container_stop(lxc.container))
if !bool(C.lxc_container_stop(lxc.container)) {
return fmt.Errorf("stoping the container %q failed", lxc.Name())
}
return nil
}
// Reboots the container
func (lxc *Container) Reboot() bool {
// Reboot reboots the container
func (lxc *Container) Reboot() error {
if !lxc.Defined() {
return fmt.Errorf("there is no container named %q", lxc.Name())
}
if !lxc.Running() {
return fmt.Errorf("container %q is not running", lxc.Name())
}
lxc.Lock()
defer lxc.Unlock()
return bool(C.lxc_container_reboot(lxc.container))
if !bool(C.lxc_container_reboot(lxc.container)) {
return fmt.Errorf("rebooting the container %q failed", lxc.Name())
}
return nil
}
// Shutdowns the container
func (lxc *Container) Shutdown(timeout int) bool {
// Shutdown shutdowns the container
func (lxc *Container) Shutdown(timeout int) error {
if !lxc.Defined() {
return fmt.Errorf("there is no container named %q", lxc.Name())
}
if !lxc.Running() {
return fmt.Errorf("container %q is not running", lxc.Name())
}
lxc.Lock()
defer lxc.Unlock()
return bool(C.lxc_container_shutdown(lxc.container, C.int(timeout)))
if !bool(C.lxc_container_shutdown(lxc.container, C.int(timeout))) {
return fmt.Errorf("shutting down the container %q failed", lxc.Name())
}
return nil
}
// Destroys the container
func (lxc *Container) Destroy() bool {
// Destroy destroys the container
func (lxc *Container) Destroy() error {
if !lxc.Defined() {
return fmt.Errorf("there is no container named %q", lxc.Name())
}
if lxc.Running() {
return fmt.Errorf("container %q is running", lxc.Name())
}
lxc.Lock()
defer lxc.Unlock()
return bool(C.lxc_container_destroy(lxc.container))
if !bool(C.lxc_container_destroy(lxc.container)) {
return fmt.Errorf("destroying the container %q failed", lxc.Name())
}
return nil
}
// FIXME: consider other parameters
// FIXME: Clone or CloneToOverlayFS, CloneToLVM, SnapshotToLVM etc?
func (lxc *Container) Clone(name string, backend BackendStore) bool {
// Clone clones the container
func (lxc *Container) Clone(name string, flags int, backend BackendStore) error {
if !lxc.Defined() {
return fmt.Errorf("there is no container named %q", lxc.Name())
}
if lxc.Running() {
return fmt.Errorf("container %q is running", lxc.Name())
}
lxc.Lock()
defer lxc.Unlock()
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
if backend == OVERLAYFS {
return bool(C.lxc_container_clone(lxc.container, cname, C.int(CLONE_SNAPSHOT), C.CString("overlayfs")))
} else {
return bool(C.lxc_container_clone(lxc.container, cname, 0, nil))
if !bool(C.lxc_container_clone(lxc.container, cname, C.int(flags), C.CString(backend.String()))) {
return fmt.Errorf("cloning the container %q failed", lxc.Name())
}
return nil
}
func (lxc *Container) CloneToOverlayFS(name string) bool {
return lxc.Clone(name, OVERLAYFS)
// CloneToDirectory clones the container using Directory backendstore
func (lxc *Container) CloneToDirectory(name string) error {
return lxc.Clone(name, 0, Directory)
}
// Waits till the container changes its state or timeouts
// CloneToOverlayFS clones the container using OverlayFS backendstore
func (lxc *Container) CloneToOverlayFS(name string) error {
return lxc.Clone(name, CloneSnapshot, OverlayFS)
}
// Wait waits till the container changes its state or timeouts
func (lxc *Container) Wait(state State, timeout int) bool {
lxc.Lock()
defer lxc.Unlock()
cstate := C.CString(state.String())
defer C.free(unsafe.Pointer(cstate))
return bool(C.lxc_container_wait(lxc.container, cstate, C.int(timeout)))
}
// Returns the container's configuration file's name
// ConfigFileName returns the container's configuration file's name
func (lxc *Container) ConfigFileName() string {
lxc.RLock()
defer lxc.RUnlock()
return C.GoString(C.lxc_container_config_file_name(lxc.container))
}
// Returns the value of the given key
// ConfigItem returns the value of the given key
func (lxc *Container) ConfigItem(key string) []string {
lxc.RLock()
defer lxc.RUnlock()
ckey := C.CString(key)
defer C.free(unsafe.Pointer(ckey))
ret := strings.TrimSpace(C.GoString(C.lxc_container_get_config_item(lxc.container, ckey)))
return strings.Split(ret, "\n")
}
// Sets the value of given key
func (lxc *Container) SetConfigItem(key string, value string) bool {
// SetConfigItem sets the value of given key
func (lxc *Container) SetConfigItem(key string, value string) error {
lxc.Lock()
defer lxc.Unlock()
ckey := C.CString(key)
defer C.free(unsafe.Pointer(ckey))
cvalue := C.CString(value)
defer C.free(unsafe.Pointer(cvalue))
return bool(C.lxc_container_set_config_item(lxc.container, ckey, cvalue))
if !bool(C.lxc_container_set_config_item(lxc.container, ckey, cvalue)) {
return fmt.Errorf("setting config item for the container %q failed (key: %s, value: %s)", lxc.Name(), key, value)
}
return nil
}
// Returns the value of the given key
// CgroupItem returns the value of the given key
func (lxc *Container) CgroupItem(key string) []string {
lxc.RLock()
defer lxc.RUnlock()
ckey := C.CString(key)
defer C.free(unsafe.Pointer(ckey))
ret := strings.TrimSpace(C.GoString(C.lxc_container_get_cgroup_item(lxc.container, ckey)))
return strings.Split(ret, "\n")
}
// Sets the value of given key
func (lxc *Container) SetCgroupItem(key string, value string) bool {
// SetCgroupItem sets the value of given key
func (lxc *Container) SetCgroupItem(key string, value string) error {
lxc.Lock()
defer lxc.Unlock()
ckey := C.CString(key)
defer C.free(unsafe.Pointer(ckey))
cvalue := C.CString(value)
defer C.free(unsafe.Pointer(cvalue))
return bool(C.lxc_container_set_cgroup_item(lxc.container, ckey, cvalue))
if !bool(C.lxc_container_set_cgroup_item(lxc.container, ckey, cvalue)) {
return fmt.Errorf("setting cgroup item for the container %q failed (key: %s, value: %s)", lxc.Name(), key, value)
}
return nil
}
// Clears the value of given key
func (lxc *Container) ClearConfigItem(key string) bool {
// ClearConfigItem clears the value of given key
func (lxc *Container) ClearConfigItem(key string) error {
lxc.Lock()
defer lxc.Unlock()
ckey := C.CString(key)
defer C.free(unsafe.Pointer(ckey))
return bool(C.lxc_container_clear_config_item(lxc.container, ckey))
if !bool(C.lxc_container_clear_config_item(lxc.container, ckey)) {
return fmt.Errorf("clearing cgroup item for the container %q failed (key: %s)", lxc.Name(), key)
}
return nil
}
// Returns the keys
// Keys returns the keys
func (lxc *Container) Keys(key string) []string {
lxc.RLock()
defer lxc.RUnlock()
ckey := C.CString(key)
defer C.free(unsafe.Pointer(ckey))
ret := strings.TrimSpace(C.GoString(C.lxc_container_get_keys(lxc.container, ckey)))
return strings.Split(ret, "\n")
}
// Loads the configuration file from given path
func (lxc *Container) LoadConfigFile(path string) bool {
// LoadConfigFile loads the configuration file from given path
func (lxc *Container) LoadConfigFile(path string) error {
lxc.Lock()
defer lxc.Unlock()
cpath := C.CString(path)
defer C.free(unsafe.Pointer(cpath))
return bool(C.lxc_container_load_config(lxc.container, cpath))
if !bool(C.lxc_container_load_config(lxc.container, cpath)) {
return fmt.Errorf("loading config file for the container %q failed (path: %s)", lxc.Name(), path)
}
return nil
}
// Saves the configuration file to given path
func (lxc *Container) SaveConfigFile(path string) bool {
// SaveConfigFile saves the configuration file to given path
func (lxc *Container) SaveConfigFile(path string) error {
lxc.Lock()
defer lxc.Unlock()
cpath := C.CString(path)
defer C.free(unsafe.Pointer(cpath))
return bool(C.lxc_container_save_config(lxc.container, cpath))
if !bool(C.lxc_container_save_config(lxc.container, cpath)) {
return fmt.Errorf("saving config file for the container %q failed (path: %s)", lxc.Name(), path)
}
return nil
}
// Returns the configuration file's path
// ConfigPath returns the configuration file's path
func (lxc *Container) ConfigPath() string {
lxc.RLock()
defer lxc.RUnlock()
return C.GoString(C.lxc_container_get_config_path(lxc.container))
}
// Sets the configuration file's path
func (lxc *Container) SetConfigPath(path string) bool {
// SetConfigPath sets the configuration file's path
func (lxc *Container) SetConfigPath(path string) error {
lxc.Lock()
defer lxc.Unlock()
cpath := C.CString(path)
defer C.free(unsafe.Pointer(cpath))
return bool(C.lxc_container_set_config_path(lxc.container, cpath))
if !bool(C.lxc_container_set_config_path(lxc.container, cpath)) {
return fmt.Errorf("setting config file for the container %q failed (path: %s)", lxc.Name(), path)
}
return nil
}
// NumberOfNetworkInterfaces returns the number of network interfaces
func (lxc *Container) NumberOfNetworkInterfaces() int {
lxc.RLock()
defer lxc.RUnlock()
if lxc.Running() {
return len(lxc.ConfigItem(network_key))
} else {
return -1
}
// Unreachable code but required for compatibility with Go 1.0.3 see -- https://groups.google.com/d/msg/golang-nuts/OXcMGoGYSLg/nfH_zvhnBHsJ
panic("unreachable")
}
func (lxc *Container) MemoryUsageInBytes() (ByteSize, error) {
lxc.RLock()
defer lxc.RUnlock()
if lxc.Running() {
memUsed, err := strconv.ParseFloat(lxc.CgroupItem("memory.usage_in_bytes")[0], 64)
if err != nil {
return -1, err
}
return ByteSize(memUsed), err
}
return -1, nil
}
func (lxc *Container) SwapUsageInBytes() (ByteSize, error) {
lxc.RLock()
defer lxc.RUnlock()
if lxc.Running() {
swapUsed, err := strconv.ParseFloat(lxc.CgroupItem("memory.memsw.usage_in_bytes")[0], 64)
if err != nil {
return -1, err
}
return ByteSize(swapUsed), err
}
return -1, nil
}
func (lxc *Container) MemoryLimitInBytes() (ByteSize, error) {
lxc.RLock()
defer lxc.RUnlock()
if lxc.Running() {
memLimit, err := strconv.ParseFloat(lxc.CgroupItem("memory.limit_in_bytes")[0], 64)
if err != nil {
return -1, err
}
return ByteSize(memLimit), err
}
return -1, nil
}
func (lxc *Container) SwapLimitInBytes() (ByteSize, error) {
lxc.RLock()
defer lxc.RUnlock()
if lxc.Running() {
swapLimit, err := strconv.ParseFloat(lxc.CgroupItem("memory.memsw.limit_in_bytes")[0], 64)
if err != nil {
return -1, err
}
return ByteSize(swapLimit), err
}
return -1, nil
}
// Returns the total CPU time (in nanoseconds) consumed by all tasks in this cgroup (including tasks lower in the hierarchy).
func (lxc *Container) CPUTime() (time.Duration, error) {
lxc.RLock()
defer lxc.RUnlock()
if lxc.Running() {
cpuUsage, err := strconv.ParseInt(lxc.CgroupItem("cpuacct.usage")[0], 10, 64)
if err != nil {
return -1, err
}
return time.Duration(cpuUsage), err
}
return -1, nil
}
// Returns the CPU time (in nanoseconds) consumed on each CPU by all tasks in this cgroup (including tasks lower in the hierarchy).
func (lxc *Container) CPUTimePerCPU() ([]time.Duration, error) {
lxc.RLock()
defer lxc.RUnlock()
var cpuTimes []time.Duration
if lxc.Running() {
for _, v := range strings.Split(lxc.CgroupItem("cpuacct.usage_percpu")[0], " ") {
cpuUsage, err := strconv.ParseInt(v, 10, 64)
if err != nil {
return nil, err
}
cpuTimes = append(cpuTimes, time.Duration(cpuUsage))
}
return cpuTimes, nil
}
return nil, nil
}
// 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.
func (lxc *Container) CPUStats() ([]int64, error) {
lxc.RLock()
defer lxc.RUnlock()
if lxc.Running() {
cpuStat := lxc.CgroupItem("cpuacct.stat")
user, _ := strconv.ParseInt(strings.Split(cpuStat[0], " ")[1], 10, 64)
system, _ := strconv.ParseInt(strings.Split(cpuStat[1], " ")[1], 10, 64)
return []int64{user, system}, nil
}
return nil, nil
}
func (lxc *Container) ConsoleGetFD(ttynum int) int {
lxc.RLock()
defer lxc.RUnlock()
if lxc.Running() {
return int(C.lxc_container_console_getfd(lxc.container, C.int(ttynum)))
return len(lxc.ConfigItem("lxc.network"))
}
return -1
}
func (lxc *Container) Console(ttynum, stdinfd, stdoutfd, stderrfd, escape int) bool {
// MemoryUsageInBytes returns memory usage in bytes
func (lxc *Container) MemoryUsageInBytes() (ByteSize, error) {
if !lxc.Defined() {
return -1, fmt.Errorf("there is no container named %q", lxc.Name())
}
if !lxc.Running() {
return -1, fmt.Errorf("container %q is not running", lxc.Name())
}
lxc.RLock()
defer lxc.RUnlock()
if lxc.Running() {
return bool(C.lxc_container_console(lxc.container, C.int(ttynum), C.int(stdinfd), C.int(stdoutfd), C.int(stderrfd), C.int(escape)))
memUsed, err := strconv.ParseFloat(lxc.CgroupItem("memory.usage_in_bytes")[0], 64)
if err != nil {
return -1, err
}
return false
return ByteSize(memUsed), err
}
// SwapUsageInBytes returns swap usage in bytes
func (lxc *Container) SwapUsageInBytes() (ByteSize, error) {
if !lxc.Defined() {
return -1, fmt.Errorf("there is no container named %q", lxc.Name())
}
if !lxc.Running() {
return -1, fmt.Errorf("container %q is not running", lxc.Name())
}
lxc.RLock()
defer lxc.RUnlock()
swapUsed, err := strconv.ParseFloat(lxc.CgroupItem("memory.memsw.usage_in_bytes")[0], 64)
if err != nil {
return -1, err
}
return ByteSize(swapUsed), err
}
// MemoryLimitInBytes returns memory limit in bytes
func (lxc *Container) MemoryLimitInBytes() (ByteSize, error) {
if !lxc.Defined() {
return -1, fmt.Errorf("there is no container named %q", lxc.Name())
}
if !lxc.Running() {
return -1, fmt.Errorf("container %q is not running", lxc.Name())
}
lxc.RLock()
defer lxc.RUnlock()
memLimit, err := strconv.ParseFloat(lxc.CgroupItem("memory.limit_in_bytes")[0], 64)
if err != nil {
return -1, err
}
return ByteSize(memLimit), err
}
// SwapLimitInBytes returns the swap limit in bytes
func (lxc *Container) SwapLimitInBytes() (ByteSize, error) {
if !lxc.Defined() {
return -1, fmt.Errorf("there is no container named %q", lxc.Name())
}
if !lxc.Running() {
return -1, fmt.Errorf("container %q is not running", lxc.Name())
}
lxc.RLock()
defer lxc.RUnlock()
swapLimit, err := strconv.ParseFloat(lxc.CgroupItem("memory.memsw.limit_in_bytes")[0], 64)
if err != nil {
return -1, err
}
return ByteSize(swapLimit), err
}
// CPUTime returns the total CPU time (in nanoseconds) consumed by all tasks in this cgroup (including tasks lower in the hierarchy).
func (lxc *Container) CPUTime() (time.Duration, error) {
if !lxc.Defined() {
return -1, fmt.Errorf("there is no container named %q", lxc.Name())
}
if !lxc.Running() {
return -1, fmt.Errorf("container %q is not running", lxc.Name())
}
lxc.RLock()
defer lxc.RUnlock()
cpuUsage, err := strconv.ParseInt(lxc.CgroupItem("cpuacct.usage")[0], 10, 64)
if err != nil {
return -1, err
}
return time.Duration(cpuUsage), err
}
// CPUTimePerCPU returns the CPU time (in nanoseconds) consumed on each CPU by all tasks in this cgroup (including tasks lower in the hierarchy).
func (lxc *Container) CPUTimePerCPU() ([]time.Duration, error) {
if !lxc.Defined() {
return nil, fmt.Errorf("there is no container named %q", lxc.Name())
}
if !lxc.Running() {
return nil, fmt.Errorf("container %q is not running", lxc.Name())
}
lxc.RLock()
defer lxc.RUnlock()
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
}
cpuTimes = append(cpuTimes, time.Duration(cpuUsage))
}
return cpuTimes, nil
}
// 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.
func (lxc *Container) CPUStats() ([]int64, error) {
if !lxc.Defined() {
return nil, fmt.Errorf("there is no container named %q", lxc.Name())
}
if !lxc.Running() {
return nil, fmt.Errorf("container %q is not running", lxc.Name())
}
lxc.RLock()
defer lxc.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
}
return []int64{user, system}, nil
}
// ConsoleGetFD allocates a console tty from container
func (lxc *Container) ConsoleGetFD(ttynum int) (int, error) {
if !lxc.Defined() {
return -1, fmt.Errorf("there is no container named %q", lxc.Name())
}
if !lxc.Running() {
return -1, fmt.Errorf("container %q is not running", lxc.Name())
}
lxc.RLock()
defer lxc.RUnlock()
ret := int(C.lxc_container_console_getfd(lxc.container, C.int(ttynum)))
if ret < 0 {
return -1, fmt.Errorf("allocating a console tty container %q failed", lxc.Name())
}
return ret, nil
}
// Console allocates and runs a console tty from container
func (lxc *Container) Console(ttynum, stdinfd, stdoutfd, stderrfd, escape int) error {
if !lxc.Defined() {
return fmt.Errorf("there is no container named %q", lxc.Name())
}
if !lxc.Running() {
return fmt.Errorf("container %q is not running", lxc.Name())
}
lxc.RLock()
defer lxc.RUnlock()
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("allocating a console for the container %q failed", lxc.Name())
}
return nil
}

56
examples/clone.go Normal file
View File

@@ -0,0 +1,56 @@
/*
* clone.go
*
* Copyright © 2013, S.Çağlar Onur
*
* Authors:
* S.Çağlar Onur <caglar@10ur.org>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package main
import (
"flag"
"fmt"
"github.com/caglar10ur/lxc"
)
var (
name string
)
func init() {
flag.StringVar(&name, "name", "rubik", "Name of the original container")
flag.Parse()
}
func main() {
c := lxc.NewContainer(name)
defer lxc.PutContainer(c)
directoryClone := name + "Directory"
overlayClone := name + "OverlayFS"
fmt.Printf("Cloning the container using Directory backend...\n")
if err := c.CloneToDirectory(directoryClone); err != nil {
fmt.Printf("ERROR: %s\n", err.Error())
}
fmt.Printf("Cloning the container using OverlayFS backend...\n")
if err := c.CloneToOverlayFS(overlayClone); err != nil {
fmt.Printf("ERROR: %s\n", err.Error())
}
}

View File

@@ -40,12 +40,12 @@ func main() {
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
z := lxc.NewContainer(strconv.Itoa(i))
defer lxc.PutContainer(z)
c := lxc.NewContainer(strconv.Itoa(i))
defer lxc.PutContainer(c)
fmt.Printf("Creating the container (%d)...\n", i)
if !z.Create("ubuntu", "amd64", "quantal") {
fmt.Printf("Creating the container (%d) failed...\n", i)
if err := c.Create("ubuntu", "amd64", "quantal"); err != nil {
fmt.Printf("ERROR: %s\n", err.Error())
}
wg.Done()
}(i)

View File

@@ -44,8 +44,8 @@ func main() {
defer lxc.PutContainer(z)
fmt.Printf("Destroying the container (%d)...\n", i)
if !z.Destroy() {
fmt.Printf("Destroying the container (%d) failed...\n", i)
if err := z.Destroy(); err != nil {
fmt.Printf("ERROR: %s\n", err.Error())
}
wg.Done()
}(i)

View File

@@ -43,11 +43,9 @@ func main() {
z := lxc.NewContainer(strconv.Itoa(i))
defer lxc.PutContainer(z)
if z.Defined() && z.Running() {
fmt.Printf("Shutting down the container (%d)...\n", i)
if !z.Shutdown(30) {
fmt.Printf("Shutting down the container (%d) failed...\n", i)
}
fmt.Printf("Shutting down the container (%d)...\n", i)
if err := z.Shutdown(30); err != nil {
fmt.Printf("ERROR: %s\n", err.Error())
}
wg.Done()
}(i)

View File

@@ -43,12 +43,10 @@ func main() {
z := lxc.NewContainer(strconv.Itoa(i))
defer lxc.PutContainer(z)
if z.Defined() && !z.Running() {
z.SetDaemonize()
fmt.Printf("Starting the container (%d)...\n", i)
if !z.Start(false) {
fmt.Printf("Starting the container (%d) failed...\n", i)
}
z.SetDaemonize()
fmt.Printf("Starting the container (%d)...\n", i)
if err := z.Start(false); err != nil {
fmt.Printf("ERROR: %s\n", err.Error())
}
wg.Done()
}(i)

View File

@@ -43,11 +43,10 @@ func main() {
z := lxc.NewContainer(strconv.Itoa(i))
defer lxc.PutContainer(z)
if z.Defined() && z.Running() {
fmt.Printf("Stopping the container (%d)...\n", i)
if !z.Stop() {
fmt.Printf("Stopping the container (%d) failed...\n", i)
}
z.SetDaemonize()
fmt.Printf("Stoping the container (%d)...\n", i)
if err := z.Stop(); err != nil {
fmt.Printf("ERROR: %s\n", err.Error())
}
wg.Done()
}(i)

View File

@@ -44,28 +44,28 @@ func main() {
go func(i int) {
name := strconv.Itoa(rand.Intn(10))
z := lxc.NewContainer(name)
defer lxc.PutContainer(z)
c := lxc.NewContainer(name)
defer lxc.PutContainer(c)
// sleep for a while to simulate some dummy work
time.Sleep(time.Millisecond * time.Duration(rand.Intn(500)))
if z.Defined() {
if !z.Running() {
z.SetDaemonize()
// fmt.Printf("Starting the container (%s)...\n", name)
if !z.Start(false) {
fmt.Printf("Starting the container (%s) failed...\n", name)
if c.Defined() {
if !c.Running() {
c.SetDaemonize()
fmt.Printf("Starting the container (%s)...\n", name)
if err := c.Start(false); err != nil {
fmt.Printf("ERROR: %s\n", err.Error())
}
} else {
// fmt.Printf("Stopping the container (%s)...\n", name)
if !z.Stop() {
fmt.Printf("Stopping the container (%s) failed...\n", name)
fmt.Printf("Stopping the container (%s)...\n", name)
if err := c.Stop(); err != nil {
fmt.Printf("ERROR: %s\n", err.Error())
}
}
} else {
if !z.Create("ubuntu", "amd64", "quantal") {
fmt.Printf("Creating the container (%s) failed...\n", name)
if err := c.Create("ubuntu", "amd64", "quantal"); err != nil {
fmt.Printf("ERROR: %s\n", err.Error())
}
}
wg.Done()

View File

@@ -41,14 +41,8 @@ func main() {
c := lxc.NewContainer(name)
defer lxc.PutContainer(c)
if c.Defined() {
if c.Running() {
fmt.Printf("Attaching to container's console...\n")
c.Console(-1, 0, 1, 2, 1)
} else {
fmt.Printf("Container is not running...\n")
}
} else {
fmt.Printf("No such container...\n")
fmt.Printf("Attaching to container's console...\n")
if err := c.Console(-1, 0, 1, 2, 1); err != nil {
fmt.Printf("ERROR: %s\n", err.Error())
}
}

View File

@@ -41,10 +41,9 @@ func main() {
c := lxc.NewContainer(name)
defer lxc.PutContainer(c)
if !c.Defined() {
fmt.Printf("Creating container...\n")
c.Create("ubuntu", "amd64", "quantal")
} else {
fmt.Printf("Container is already created...\n")
fmt.Printf("Creating container...\n")
c.SetVerbosity(lxc.Verbose)
if err := c.Create("ubuntu", "amd64", "quantal"); err != nil {
fmt.Printf("ERROR: %s\n", err.Error())
}
}

View File

@@ -41,10 +41,8 @@ func main() {
c := lxc.NewContainer(name)
defer lxc.PutContainer(c)
if c.Defined() {
fmt.Printf("Destroying container...\n")
c.Destroy()
} else {
fmt.Printf("No such container...\n")
fmt.Printf("Destroying container...\n")
if err := c.Destroy(); err != nil {
fmt.Printf("ERROR: %s\n", err.Error())
}
}

View File

@@ -41,15 +41,9 @@ func main() {
c := lxc.NewContainer(name)
defer lxc.PutContainer(c)
if c.Defined() {
if c.Running() {
fmt.Printf("Freezing the container...\n")
c.Freeze()
c.Wait(lxc.FROZEN, 10)
} else {
fmt.Printf("Container is not running...\n")
}
} else {
fmt.Printf("No such container...\n")
fmt.Printf("Freezing the container...\n")
if err := c.Freeze(); err != nil {
fmt.Printf("ERROR: %s\n", err.Error())
}
c.Wait(lxc.FROZEN, 10)
}

View File

@@ -41,14 +41,8 @@ func main() {
c := lxc.NewContainer(name)
defer lxc.PutContainer(c)
if c.Defined() {
if c.Running() {
fmt.Printf("Shutting down the container...\n")
c.Shutdown(30)
} else {
fmt.Printf("Container is already stopped...\n")
}
} else {
fmt.Printf("No such container...\n")
fmt.Printf("Shutting down the container...\n")
if err := c.Shutdown(30); err != nil {
fmt.Printf("ERROR: %s\n", err.Error())
}
}

View File

@@ -41,15 +41,9 @@ func main() {
c := lxc.NewContainer(name)
defer lxc.PutContainer(c)
if c.Defined() {
if !c.Running() {
fmt.Printf("Starting the container...\n")
c.SetDaemonize()
c.Start(false)
} else {
fmt.Printf("Container is already running...\n")
}
} else {
fmt.Printf("No such container...\n")
fmt.Printf("Starting the container...\n")
c.SetDaemonize()
if err := c.Start(false); err != nil {
fmt.Printf("ERROR: %s\n", err.Error())
}
}

View File

@@ -43,18 +43,18 @@ func main() {
if c.Running() {
// mem
mem_used, _ := c.MemoryUsageInBytes()
fmt.Printf("mem_used: %s\n", mem_used)
memUsed, _ := c.MemoryUsageInBytes()
fmt.Printf("mem_used: %s\n", memUsed)
mem_limit, _ := c.MemoryLimitInBytes()
fmt.Printf("mem_limit: %s\n", mem_limit)
memLimit, _ := c.MemoryLimitInBytes()
fmt.Printf("mem_limit: %s\n", memLimit)
// swap
swap_used, _ := c.SwapUsageInBytes()
fmt.Printf("memsw_used: %s\n", swap_used)
swapUsed, _ := c.SwapUsageInBytes()
fmt.Printf("memsw_used: %s\n", swapUsed)
swap_limit, _ := c.SwapLimitInBytes()
fmt.Printf("memsw_used: %s\n", swap_limit)
swapLimit, _ := c.SwapLimitInBytes()
fmt.Printf("memsw_used: %s\n", swapLimit)
} else {
fmt.Printf("Container is not running...\n")
}

View File

@@ -41,14 +41,8 @@ func main() {
c := lxc.NewContainer(name)
defer lxc.PutContainer(c)
if c.Defined() {
if c.Running() {
fmt.Printf("Stopping the container...\n")
c.Stop()
} else {
fmt.Printf("Container is already stopped...\n")
}
} else {
fmt.Printf("No such container...\n")
fmt.Printf("Stopping the container...\n")
if err := c.Stop(); err != nil {
fmt.Printf("ERROR: %s\n", err.Error())
}
}

View File

@@ -41,15 +41,9 @@ func main() {
c := lxc.NewContainer(name)
defer lxc.PutContainer(c)
if c.Defined() {
if c.State() == lxc.FROZEN {
fmt.Printf("Unfreezing the container...\n")
c.Unfreeze()
c.Wait(lxc.RUNNING, 10)
} else {
fmt.Printf("Container is not running...\n")
}
} else {
fmt.Printf("No such container...\n")
fmt.Printf("Unfreezing the container...\n")
if err := c.Unfreeze(); err != nil {
fmt.Printf("ERROR: %s\n", err.Error())
}
c.Wait(lxc.RUNNING, 10)
}

4
lxc.c
View File

@@ -55,8 +55,8 @@ void lxc_container_want_daemonize(struct lxc_container *c) {
c->want_daemonize(c);
}
bool lxc_container_create(struct lxc_container *c, char *t, char **argv) {
return c->create(c, t, NULL, NULL, LXC_CREATE_QUIET, argv);
bool lxc_container_create(struct lxc_container *c, char *t, int flags, char **argv) {
return c->create(c, t, NULL, NULL, !!(flags & LXC_CREATE_QUIET), argv);
}
bool lxc_container_start(struct lxc_container *c, int useinit, char ** argv) {

68
lxc.go
View File

@@ -21,9 +21,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
//Go (golang) Bindings for LXC (Linux Containers)
//
//This package implements Go bindings for the LXC C API.
// Package lxc provides Go (golang) Bindings for LXC (Linux Containers) C API.
package lxc
// #cgo linux LDFLAGS: -llxc -lutil
@@ -39,20 +37,21 @@ import (
)
const (
// Timeout
WAIT_FOREVER int = iota - 1
DONT_WAIT
// WaitForever timeout
WaitForever int = iota - 1
// DontWait timeout
DontWait
)
const (
CLONE_KEEPNAME int = 1 << iota
CLONE_COPYHOOKS
CLONE_KEEPMACADDR
CLONE_SNAPSHOT
)
const (
CREATE_QUIET int = 1 << iota
// CloneKeepName means don't edit the rootfs to change the hostname.
CloneKeepName int = 1 << iota
// CloneCopyHooks means copy all hooks into the container directory.
CloneCopyHooks
// CloneKeepMACAddr means don't change the mac address on network interfaces.
CloneKeepMACAddr
// CloneSnapshot means snapshot the original filesystem(s).
CloneSnapshot
)
func init() {
@@ -61,57 +60,62 @@ func init() {
}
}
// NewContainer returns a new container struct
func NewContainer(name string) *Container {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
return &Container{container: C.lxc_container_new(cname, nil)}
return &Container{container: C.lxc_container_new(cname, nil), verbosity: Quiet}
}
// Increments reference counter of the container object
// GetContainer increments reference counter of the container object
func GetContainer(lxc *Container) bool {
return C.lxc_container_get(lxc.container) == 1
}
// Decrements reference counter of the container object
// PutContainer decrements reference counter of the container object
func PutContainer(lxc *Container) bool {
return C.lxc_container_put(lxc.container) == 1
}
// Returns LXC version
// Version returns LXC version
func Version() string {
return C.GoString(C.lxc_get_version())
}
// Returns default config path
// DefaultConfigPath returns default config path
func DefaultConfigPath() string {
return C.GoString(C.lxc_get_default_config_path())
}
// Returns default LVM volume group
// DefaultLvmVg returns default LVM volume group
func DefaultLvmVg() string {
return C.GoString(C.lxc_get_default_lvm_vg())
}
// Returns default ZFS root
// DefaultZfsRoot returns default ZFS root
func DefaultZfsRoot() string {
return C.GoString(C.lxc_get_default_zfs_root())
}
// Returns the names of containers on the system.
func ContainerNames() []string {
// FIXME: Support custom config paths
matches, err := filepath.Glob(filepath.Join(DefaultConfigPath(), "/*/config"))
if err != nil {
return nil
}
// ContainerNames returns the names of containers on the system.
func ContainerNames(paths ...string) []string {
if paths == nil {
matches, err := filepath.Glob(filepath.Join(DefaultConfigPath(), "/*/config"))
if err != nil {
return nil
}
for i, v := range matches {
matches[i] = filepath.Base(filepath.Dir(v))
for i, v := range matches {
matches[i] = filepath.Base(filepath.Dir(v))
}
return matches
}
return matches
// FIXME: Support custom config paths
return nil
}
// Returns the containers on the system.
// Containers returns the containers on the system.
func Containers() []Container {
var containers []Container

11
lxc.h
View File

@@ -22,18 +22,20 @@
*/
extern bool lxc_container_clear_config_item(struct lxc_container *, char *);
extern bool lxc_container_create(struct lxc_container *, char *, char **);
extern bool lxc_container_clone(struct lxc_container *, const char *, int, const char *);
extern bool lxc_container_console(struct lxc_container *, int, int, int, int, int);
extern bool lxc_container_create(struct lxc_container *, char *, int, char **);
extern bool lxc_container_defined(struct lxc_container *);
extern bool lxc_container_destroy(struct lxc_container *);
extern bool lxc_container_freeze(struct lxc_container *);
extern bool lxc_container_load_config(struct lxc_container *, char *);
extern bool lxc_container_reboot(struct lxc_container *);
extern bool lxc_container_running(struct lxc_container *);
extern bool lxc_container_save_config(struct lxc_container *, char *);
extern bool lxc_container_set_cgroup_item(struct lxc_container *, char *key, char *);
extern bool lxc_container_set_config_item(struct lxc_container *, char *, char *);
extern bool lxc_container_set_config_path(struct lxc_container *, char *);
extern bool lxc_container_shutdown(struct lxc_container *, int);
extern bool lxc_container_reboot(struct lxc_container *);
extern bool lxc_container_start(struct lxc_container *, int, char **);
extern bool lxc_container_stop(struct lxc_container *);
extern bool lxc_container_unfreeze(struct lxc_container *);
@@ -44,13 +46,10 @@ extern char* lxc_container_get_config_item(struct lxc_container *, char *);
extern char* lxc_container_get_keys(struct lxc_container *, char *);
extern const char* lxc_container_get_config_path(struct lxc_container *);
extern const char* lxc_container_state(struct lxc_container *);
extern int lxc_container_console_getfd(struct lxc_container *, int);
extern pid_t lxc_container_init_pid(struct lxc_container *);
extern void lxc_container_want_daemonize(struct lxc_container *);
extern bool lxc_container_clone(struct lxc_container *, const char *, int, const char *);
extern int lxc_container_console_getfd(struct lxc_container *, int);
extern bool lxc_container_console(struct lxc_container *, int, int, int, int, int);
//FIXME: Missing API functionality
// char** (*get_ips)(struct lxc_container *c, char* interface, char* family, int scope);
// int (*attach)(struct lxc_container *c, lxc_attach_exec_t exec_function, void *exec_payload, lxc_attach_options_t *options, pid_t *attached_process);

View File

@@ -33,11 +33,11 @@ import (
)
const (
CONTAINER_NAME = "rubik"
CLONE_CONTAINER_NAME = "O"
CLONE_OVERLAY_CONTAINER_NAME = "O_o"
CONFIG_FILE_PATH = "/var/lib/lxc"
CONFIG_FILE_NAME = "/var/lib/lxc/rubik/config"
ContainerName = "rubik"
CloneContainerName = "O"
CloneOverlayContainerName = "O_o"
ConfigFilePath = "/var/lib/lxc"
ConfigFileName = "/var/lib/lxc/rubik/config"
)
func init() {
@@ -49,20 +49,22 @@ func TestVersion(t *testing.T) {
}
func TestDefaultConfigPath(t *testing.T) {
if DefaultConfigPath() != CONFIG_FILE_PATH {
if DefaultConfigPath() != ConfigFilePath {
t.Errorf("DefaultConfigPath failed...")
}
}
func TestSetConfigPath(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
current_path := z.ConfigPath()
z.SetConfigPath("/tmp")
new_path := z.ConfigPath()
currentPath := z.ConfigPath()
if err := z.SetConfigPath("/tmp"); err != nil {
t.Errorf(err.Error())
}
newPath := z.ConfigPath()
if current_path == new_path {
if currentPath == newPath {
t.Errorf("SetConfigPath failed...")
}
}
@@ -89,7 +91,7 @@ func TestConcurrentDefined_Negative(t *testing.T) {
}
func TestDefined_Negative(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
if z.Defined() {
@@ -98,24 +100,24 @@ func TestDefined_Negative(t *testing.T) {
}
func TestCreate(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
if !z.Create("ubuntu", "amd64", "quantal") {
t.Errorf("Creating the container failed...")
if err := z.Create("ubuntu", "amd64", "quantal"); err != nil {
t.Errorf(err.Error())
}
}
func TestClone(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
if !z.Clone(CLONE_CONTAINER_NAME, DIRECTORY) {
t.Errorf("Cloning the DIRECTORY backed container failed...")
if err := z.CloneToDirectory(CloneContainerName); err != nil {
t.Errorf(err.Error())
}
if !z.Clone(CLONE_OVERLAY_CONTAINER_NAME, OVERLAYFS) {
t.Errorf("Cloning the OVERLAYFS backed container failed...")
if err := z.CloneToOverlayFS(CloneOverlayContainerName); err != nil {
t.Errorf(err.Error())
}
}
@@ -131,8 +133,8 @@ func TestConcurrentCreate(t *testing.T) {
// sleep for a while to simulate some dummy work
time.Sleep(time.Millisecond * time.Duration(rand.Intn(250)))
if !z.Create("ubuntu", "amd64", "quantal") {
t.Errorf("Creating the container (%d) failed...", i)
if err := z.Create("ubuntu", "amd64", "quantal"); err != nil {
t.Errorf(err.Error())
}
wg.Done()
}(i)
@@ -160,7 +162,9 @@ func TestConcurrentStart(t *testing.T) {
defer PutContainer(z)
z.SetDaemonize()
z.Start(false)
if err := z.Start(false); err != nil {
t.Errorf(err.Error())
}
z.Wait(RUNNING, 30)
if !z.Running() {
t.Errorf("Starting the container failed...")
@@ -173,15 +177,15 @@ func TestConcurrentStart(t *testing.T) {
}
func TestConfigFileName(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
if z.ConfigFileName() != CONFIG_FILE_NAME {
if z.ConfigFileName() != ConfigFileName {
t.Errorf("ConfigFileName failed...")
}
}
func TestDefined_Positive(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
if !z.Defined() {
@@ -211,7 +215,7 @@ func TestConcurrentDefined_Positive(t *testing.T) {
}
func TestInitPID_Negative(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
if z.InitPID() != -1 {
@@ -220,7 +224,7 @@ func TestInitPID_Negative(t *testing.T) {
}
func TestStart(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
z.SetDaemonize()
@@ -233,7 +237,7 @@ func TestStart(t *testing.T) {
}
func TestSetDaemonize(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
z.SetDaemonize()
@@ -243,7 +247,7 @@ func TestSetDaemonize(t *testing.T) {
}
func TestInitPID_Positive(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
if z.InitPID() == -1 {
@@ -252,19 +256,21 @@ func TestInitPID_Positive(t *testing.T) {
}
func TestName(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
if z.Name() != CONTAINER_NAME {
if z.Name() != ContainerName {
t.Errorf("Name failed...")
}
}
func TestFreeze(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
z.Freeze()
if err := z.Freeze(); err != nil {
t.Errorf(err.Error())
}
z.Wait(FROZEN, 30)
if z.State() != FROZEN {
@@ -273,10 +279,12 @@ func TestFreeze(t *testing.T) {
}
func TestUnfreeze(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
z.Unfreeze()
if err := z.Unfreeze(); err != nil {
t.Errorf(err.Error())
}
z.Wait(RUNNING, 30)
if z.State() != RUNNING {
@@ -285,68 +293,74 @@ func TestUnfreeze(t *testing.T) {
}
func TestLoadConfigFile(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
if !z.LoadConfigFile(CONFIG_FILE_NAME) {
t.Errorf("LoadConfigFile failed...")
if err := z.LoadConfigFile(ConfigFileName); err != nil {
t.Errorf(err.Error())
}
}
func TestSaveConfigFile(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
if !z.SaveConfigFile(CONFIG_FILE_NAME) {
t.Errorf("LoadConfigFile failed...")
if err := z.SaveConfigFile(ConfigFileName); err != nil {
t.Errorf(err.Error())
}
}
func TestConfigItem(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
if z.ConfigItem("lxc.utsname")[0] != CONTAINER_NAME {
if z.ConfigItem("lxc.utsname")[0] != ContainerName {
t.Errorf("ConfigItem failed...")
}
}
func TestSetConfigItem(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
z.SetConfigItem("lxc.utsname", CONTAINER_NAME)
if z.ConfigItem("lxc.utsname")[0] != CONTAINER_NAME {
if err := z.SetConfigItem("lxc.utsname", ContainerName); err != nil {
t.Errorf(err.Error())
}
if z.ConfigItem("lxc.utsname")[0] != ContainerName {
t.Errorf("ConfigItem failed...")
}
}
func TestSetCgroupItem(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
max_mem := z.CgroupItem("memory.max_usage_in_bytes")[0]
current_mem := z.CgroupItem("memory.limit_in_bytes")[0]
z.SetCgroupItem("memory.limit_in_bytes", max_mem)
new_mem := z.CgroupItem("memory.limit_in_bytes")[0]
maxMem := z.CgroupItem("memory.max_usage_in_bytes")[0]
currentMem := z.CgroupItem("memory.limit_in_bytes")[0]
if err := z.SetCgroupItem("memory.limit_in_bytes", maxMem); err != nil {
t.Errorf(err.Error())
}
newMem := z.CgroupItem("memory.limit_in_bytes")[0]
if new_mem == current_mem {
if newMem == currentMem {
t.Errorf("SetCgroupItem failed...")
}
}
func TestClearConfigItem(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
z.ClearConfigItem("lxc.cap.drop")
if err := z.ClearConfigItem("lxc.cap.drop"); err != nil {
t.Errorf(err.Error())
}
if z.ConfigItem("lxc.cap.drop")[0] != "" {
t.Errorf("ClearConfigItem failed...")
}
}
func TestKeys(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
keys := strings.Join(z.Keys("lxc.network.0"), " ")
@@ -356,7 +370,7 @@ func TestKeys(t *testing.T) {
}
func TestNumberOfNetworkInterfaces(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
if z.NumberOfNetworkInterfaces() != 1 {
@@ -365,28 +379,28 @@ func TestNumberOfNetworkInterfaces(t *testing.T) {
}
func TestMemoryUsageInBytes(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
mem_used, _ := z.MemoryUsageInBytes()
swap_used, _ := z.SwapUsageInBytes()
mem_limit, _ := z.MemoryLimitInBytes()
swap_limit, _ := z.SwapLimitInBytes()
memUsed, _ := z.MemoryUsageInBytes()
swapUsed, _ := z.SwapUsageInBytes()
memLimit, _ := z.MemoryLimitInBytes()
swapLimit, _ := z.SwapLimitInBytes()
t.Logf("Mem usage: %0.0f\n", mem_used)
t.Logf("Mem usage: %s\n", mem_used)
t.Logf("Swap usage: %0.0f\n", swap_used)
t.Logf("Swap usage: %s\n", swap_used)
t.Logf("Mem limit: %0.0f\n", mem_limit)
t.Logf("Mem limit: %s\n", mem_limit)
t.Logf("Swap limit: %0.0f\n", swap_limit)
t.Logf("Swap limit: %s\n", swap_limit)
t.Logf("Mem usage: %0.0f\n", memUsed)
t.Logf("Mem usage: %s\n", memUsed)
t.Logf("Swap usage: %0.0f\n", swapUsed)
t.Logf("Swap usage: %s\n", swapUsed)
t.Logf("Mem limit: %0.0f\n", memLimit)
t.Logf("Mem limit: %s\n", memLimit)
t.Logf("Swap limit: %0.0f\n", swapLimit)
t.Logf("Swap limit: %s\n", swapLimit)
}
/*
func TestReboot(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
t.Logf("Rebooting the container...\n")
@@ -407,8 +421,9 @@ func TestConcurrentShutdown(t *testing.T) {
z := NewContainer(strconv.Itoa(i))
defer PutContainer(z)
z.Shutdown(30)
if err := z.Shutdown(30); err != nil {
t.Errorf(err.Error())
}
if z.Running() {
t.Errorf("Shutting down the container failed...")
}
@@ -420,10 +435,12 @@ func TestConcurrentShutdown(t *testing.T) {
}
func TestShutdown(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
z.Shutdown(30)
if err := z.Shutdown(30); err != nil {
t.Errorf(err.Error())
}
if z.Running() {
t.Errorf("Shutting down the container failed...")
@@ -431,10 +448,17 @@ func TestShutdown(t *testing.T) {
}
func TestStop(t *testing.T) {
z := NewContainer(CONTAINER_NAME)
z := NewContainer(ContainerName)
defer PutContainer(z)
z.Stop()
z.SetDaemonize()
if err := z.Start(false); err != nil {
t.Errorf(err.Error())
}
if err := z.Stop(); err != nil {
t.Errorf(err.Error())
}
if z.Running() {
t.Errorf("Stopping the container failed...")
@@ -442,25 +466,25 @@ func TestStop(t *testing.T) {
}
func TestDestroy(t *testing.T) {
z := NewContainer(CLONE_OVERLAY_CONTAINER_NAME)
z := NewContainer(CloneOverlayContainerName)
defer PutContainer(z)
if !z.Destroy() {
t.Errorf("Destroying the container failed...")
if err := z.Destroy(); err != nil {
t.Errorf(err.Error())
}
z = NewContainer(CLONE_CONTAINER_NAME)
z = NewContainer(CloneContainerName)
defer PutContainer(z)
if !z.Destroy() {
t.Errorf("Destroying the container failed...")
if err := z.Destroy(); err != nil {
t.Errorf(err.Error())
}
z = NewContainer(CONTAINER_NAME)
z = NewContainer(ContainerName)
defer PutContainer(z)
if !z.Destroy() {
t.Errorf("Destroying the container failed...")
if err := z.Destroy(); err != nil {
t.Errorf(err.Error())
}
}
@@ -476,8 +500,8 @@ func TestConcurrentDestroy(t *testing.T) {
// sleep for a while to simulate some dummy work
time.Sleep(time.Millisecond * time.Duration(rand.Intn(250)))
if !z.Destroy() {
t.Errorf("Destroying the container failed...")
if err := z.Destroy(); err != nil {
t.Errorf(err.Error())
}
wg.Done()
}(i)

View File

@@ -1,75 +0,0 @@
/*
* state.go: Go bindings for lxc
*
* Copyright © 2013, S.Çağlar Onur
*
* Authors:
* S.Çağlar Onur <caglar@10ur.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package lxc
// #include <lxc/lxc.h>
// #include <lxc/lxccontainer.h>
import "C"
type State int
const (
STOPPED State = C.STOPPED
STARTING State = C.STARTING
RUNNING State = C.RUNNING
STOPPING State = C.STOPPING
ABORTING State = C.ABORTING
FREEZING State = C.FREEZING
FROZEN State = C.FROZEN
THAWED State = C.THAWED
)
var stateMap = map[string]State{
"STOPPED": STOPPED,
"STARTING": STARTING,
"RUNNING": RUNNING,
"STOPPING": STOPPING,
"ABORTING": ABORTING,
"FREEZING": FREEZING,
"FROZEN": FROZEN,
"THAWED": THAWED,
}
// State as string
func (t State) String() string {
switch t {
case STOPPED:
return "STOPPED"
case STARTING:
return "STARTING"
case RUNNING:
return "RUNNING"
case STOPPING:
return "STOPPING"
case ABORTING:
return "ABORTING"
case FREEZING:
return "FREEZING"
case FROZEN:
return "FROZEN"
case THAWED:
return "THAWED"
}
return "<INVALID>"
}

182
type.go Normal file
View File

@@ -0,0 +1,182 @@
/*
* type.go: Go bindings for lxc
*
* Copyright © 2013, S.Çağlar Onur
*
* Authors:
* S.Çağlar Onur <caglar@10ur.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package lxc
// #include <lxc/lxc.h>
// #include <lxc/lxccontainer.h>
import "C"
import (
"fmt"
)
// Verbosity type
type Verbosity int
const (
// Quiet makes some API calls not to write anything to stdout
Quiet Verbosity = 1 << iota
// Verbose makes some API calls write to stdout
Verbose
)
// BackendStore type
type BackendStore int
const (
// BtrFS backendstore type
BtrFS BackendStore = iota
// Directory backendstore type
Directory
// LVM backendstore type
LVM
// ZFS backendstore type
ZFS
// OverlayFS backendstore type
OverlayFS
// Loopback backendstore type
Loopback
)
// BackendStore as string
func (t BackendStore) String() string {
switch t {
case Directory:
return "dir"
case ZFS:
return "zfs"
case BtrFS:
return "btrfs"
case LVM:
return "lvm"
case OverlayFS:
return "overlayfs"
case Loopback:
return "loopback"
}
return "<INVALID>"
}
// State type
type State int
const (
// STOPPED means container is not running
STOPPED State = C.STOPPED
// STARTING means container is starting
STARTING State = C.STARTING
// RUNNING means container is running
RUNNING State = C.RUNNING
// STOPPING means container is stopping
STOPPING State = C.STOPPING
// ABORTING means container is aborting
ABORTING State = C.ABORTING
// FREEZING means container is freezing
FREEZING State = C.FREEZING
// FROZEN means containe is frozen
FROZEN State = C.FROZEN
// THAWED means container is thawed
THAWED State = C.THAWED
)
var stateMap = map[string]State{
"STOPPED": STOPPED,
"STARTING": STARTING,
"RUNNING": RUNNING,
"STOPPING": STOPPING,
"ABORTING": ABORTING,
"FREEZING": FREEZING,
"FROZEN": FROZEN,
"THAWED": THAWED,
}
// State as string
func (t State) String() string {
switch t {
case STOPPED:
return "STOPPED"
case STARTING:
return "STARTING"
case RUNNING:
return "RUNNING"
case STOPPING:
return "STOPPING"
case ABORTING:
return "ABORTING"
case FREEZING:
return "FREEZING"
case FROZEN:
return "FROZEN"
case THAWED:
return "THAWED"
}
return "<INVALID>"
}
// Taken from http://golang.org/doc/effective_go.html#constants
// ByteSize type
type ByteSize float64
const (
_ = iota
// KB - kilobyte
KB ByteSize = 1 << (10 * iota)
// MB - megabyte
MB
// GB - gigabyte
GB
// TB - terabyte
TB
// PB - petabyte
PB
// EB - exabyte
EB
// ZB - zettabyte
ZB
// YB - yottabyte
YB
)
func (b ByteSize) String() string {
switch {
case b >= YB:
return fmt.Sprintf("%.2fYB", b/YB)
case b >= ZB:
return fmt.Sprintf("%.2fZB", b/ZB)
case b >= EB:
return fmt.Sprintf("%.2fEB", b/EB)
case b >= PB:
return fmt.Sprintf("%.2fPB", b/PB)
case b >= TB:
return fmt.Sprintf("%.2fTB", b/TB)
case b >= GB:
return fmt.Sprintf("%.2fGB", b/GB)
case b >= MB:
return fmt.Sprintf("%.2fMB", b/MB)
case b >= KB:
return fmt.Sprintf("%.2fKB", b/KB)
}
return fmt.Sprintf("%.2fB", b)
}

View File

@@ -1,5 +1,5 @@
/*
* utils.go: Go bindings for lxc
* util.go: Go bindings for lxc
*
* Copyright © 2013, S.Çağlar Onur
*