mirror of
https://github.com/OpenNebula/one.git
synced 2025-01-21 18:03:38 +03:00
Docker integration and GO API (#1889)
* added docker integration files * changed dependencies for docker-integration files * README.md deleted * Deleted LICENSE
This commit is contained in:
parent
4f880598e9
commit
c3b113c5f2
@ -167,6 +167,9 @@ main_env.Append(rubygems=ARGUMENTS.get('rubygems', 'no'))
|
||||
# Sunstone minified files generation
|
||||
main_env.Append(sunstone=ARGUMENTS.get('sunstone', 'no'))
|
||||
|
||||
# Docker-machine addon generation
|
||||
main_env.Append(docker_machine=ARGUMENTS.get('docker_machine', 'no'))
|
||||
|
||||
if not main_env.GetOption('clean'):
|
||||
try:
|
||||
if mysql=='yes':
|
||||
@ -263,7 +266,8 @@ build_scripts=[
|
||||
'src/sunstone/public/SConstruct',
|
||||
'share/rubygems/SConstruct',
|
||||
'src/im_mad/collectd/SConstruct',
|
||||
'src/client/SConstruct'
|
||||
'src/client/SConstruct',
|
||||
'src/docker_machine/SConstruct'
|
||||
]
|
||||
|
||||
for script in build_scripts:
|
||||
|
@ -591,6 +591,7 @@ BIN_FILES="src/nebula/oned \
|
||||
src/cli/onevcenter \
|
||||
src/onedb/onedb \
|
||||
src/mad/utils/tty_expect \
|
||||
src/docker_machine/src/docker_machine/bin/docker-machine-driver-opennebula \
|
||||
share/scripts/one"
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
29
src/docker_machine/SConstruct
Normal file
29
src/docker_machine/SConstruct
Normal file
@ -0,0 +1,29 @@
|
||||
# SConstruct for share/scripts/rubygems
|
||||
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2002-2018, OpenNebula Project, OpenNebula Systems #
|
||||
# #
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may #
|
||||
# not use this file except in compliance with the License. You may obtain #
|
||||
# a copy of the License at #
|
||||
# #
|
||||
# http://www.apache.org/licenses/LICENSE-2.0 #
|
||||
# #
|
||||
# Unless required by applicable law or agreed to in writing, software #
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, #
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
|
||||
# See the License for the specific language governing permissions and #
|
||||
# limitations under the License. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
import os
|
||||
|
||||
Import('env')
|
||||
|
||||
if env['docker_machine']=='yes':
|
||||
print "Generating docker-machine-driver-opennebula\n"
|
||||
exit_code=os.system("./generate.sh")
|
||||
|
||||
if exit_code != 0:
|
||||
print "Error generating docker-machine-driver-opennebula\n"
|
||||
exit(-1)
|
8
src/docker_machine/generate.sh
Executable file
8
src/docker_machine/generate.sh
Executable file
@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
GOCA_PATH=$PWD/../oca/go
|
||||
ADDON_PATH=$PWD
|
||||
|
||||
export GOPATH=$GOPATH:$GOCA_PATH:$ADDON_PATH
|
||||
|
||||
cd src/docker_machine/
|
||||
make build
|
95
src/docker_machine/src/docker_machine/Gopkg.lock
generated
Normal file
95
src/docker_machine/src/docker_machine/Gopkg.lock
generated
Normal file
@ -0,0 +1,95 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/Azure/go-ansiterm"
|
||||
packages = [
|
||||
".",
|
||||
"winterm"
|
||||
]
|
||||
revision = "d6e3b3328b783f23731bc4d058875b0371ff8109"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/docker/docker"
|
||||
packages = [
|
||||
"pkg/term",
|
||||
"pkg/term/windows"
|
||||
]
|
||||
revision = "0c1006f1abc1af7aa6b9847754370d054dfa6c68"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/docker/machine"
|
||||
packages = [
|
||||
"libmachine/drivers",
|
||||
"libmachine/drivers/plugin",
|
||||
"libmachine/drivers/plugin/localbinary",
|
||||
"libmachine/drivers/rpc",
|
||||
"libmachine/log",
|
||||
"libmachine/mcnflag",
|
||||
"libmachine/mcnutils",
|
||||
"libmachine/ssh",
|
||||
"libmachine/state",
|
||||
"libmachine/version",
|
||||
"version"
|
||||
]
|
||||
revision = "89b833253d9412716a0291cbdccc94454c33d1b5"
|
||||
version = "v0.14.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/kolo/xmlrpc"
|
||||
packages = ["."]
|
||||
revision = "0826b98aaa29c0766956cb40d45cf7482a597671"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/sirupsen/logrus"
|
||||
packages = ["."]
|
||||
revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc"
|
||||
version = "v1.0.5"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = [
|
||||
"curve25519",
|
||||
"ed25519",
|
||||
"ed25519/internal/edwards25519",
|
||||
"internal/chacha20",
|
||||
"poly1305",
|
||||
"ssh",
|
||||
"ssh/terminal"
|
||||
]
|
||||
revision = "c3a3ad6d03f7a915c0f7e194b7152974bb73d287"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"html",
|
||||
"html/atom"
|
||||
]
|
||||
revision = "6078986fec03a1dcc236c34816c71b0e05018fda"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = [
|
||||
"unix",
|
||||
"windows"
|
||||
]
|
||||
revision = "7ceb54c8418b8f9cdf0177b511d5cbb06e9fae39"
|
||||
|
||||
[[projects]]
|
||||
branch = "v2"
|
||||
name = "gopkg.in/xmlpath.v2"
|
||||
packages = ["."]
|
||||
revision = "860cbeca3ebcc600db0b213c0e83ad6ce91f5739"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "8e5052c0521dfead3dd5b8672b0d2f066c70177a5e5a3ba4abd1449d087b98aa"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
43
src/docker_machine/src/docker_machine/Gopkg.toml
Normal file
43
src/docker_machine/src/docker_machine/Gopkg.toml
Normal file
@ -0,0 +1,43 @@
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
#
|
||||
# [prune]
|
||||
# non-go = false
|
||||
# go-tests = true
|
||||
# unused-packages = true
|
||||
|
||||
required = ["github.com/kolo/xmlrpc", "gopkg.in/xmlpath.v2"]
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/docker/machine"
|
||||
version = "0.14.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/kolo/xmlrpc"
|
||||
|
||||
[[constraint]]
|
||||
branch = "v2"
|
||||
name = "gopkg.in/xmlpath.v2"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
17
src/docker_machine/src/docker_machine/Makefile
Normal file
17
src/docker_machine/src/docker_machine/Makefile
Normal file
@ -0,0 +1,17 @@
|
||||
GODEP_BIN := $(word 1, $(subst :, ,$(GOPATH)))/bin/dep
|
||||
|
||||
default: build
|
||||
|
||||
bin/docker-machine-driver-opennebula:
|
||||
$(GODEP_BIN) ensure
|
||||
mv vendor/* ..
|
||||
go build -o ./bin/docker-machine-driver-opennebula ./bin
|
||||
build: clean bin/docker-machine-driver-opennebula
|
||||
|
||||
clean:
|
||||
$(RM) bin/docker-machine-driver-opennebula
|
||||
|
||||
install: bin/docker-machine-driver-opennebula
|
||||
cp -f ./bin/docker-machine-driver-opennebula $(GOPATH)/bin/
|
||||
|
||||
.PHONY: clean build install
|
10
src/docker_machine/src/docker_machine/bin/main.go
Normal file
10
src/docker_machine/src/docker_machine/bin/main.go
Normal file
@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"docker_machine"
|
||||
"github.com/docker/machine/libmachine/drivers/plugin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
plugin.RegisterDriver(opennebula.NewDriver("", ""))
|
||||
}
|
731
src/docker_machine/src/docker_machine/opennebula.go
Normal file
731
src/docker_machine/src/docker_machine/opennebula.go
Normal file
@ -0,0 +1,731 @@
|
||||
package opennebula
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"time"
|
||||
"goca"
|
||||
|
||||
"github.com/docker/machine/libmachine/drivers"
|
||||
"github.com/docker/machine/libmachine/log"
|
||||
"github.com/docker/machine/libmachine/mcnflag"
|
||||
"github.com/docker/machine/libmachine/ssh"
|
||||
"github.com/docker/machine/libmachine/state"
|
||||
)
|
||||
|
||||
type Driver struct {
|
||||
*drivers.BaseDriver
|
||||
TemplateName string
|
||||
TemplateID string
|
||||
NetworkName string
|
||||
NetworkOwner string
|
||||
NetworkID string
|
||||
ImageName string
|
||||
ImageOwner string
|
||||
ImageID string
|
||||
CPU string
|
||||
VCPU string
|
||||
Memory string
|
||||
DiskSize string
|
||||
ImageDevPrefix string
|
||||
B2DSize string
|
||||
User string
|
||||
Password string
|
||||
Xmlrpcurl string
|
||||
Config goca.OneConfig
|
||||
DisableVNC bool
|
||||
}
|
||||
|
||||
const (
|
||||
defaultTimeout = 1 * time.Second
|
||||
defaultSSHUser = "docker"
|
||||
defaultCPU = "1"
|
||||
defaultVCPU = "1"
|
||||
defaultMemory = "1024"
|
||||
|
||||
// This is the contextualization script that will be executed by OpenNebula
|
||||
contextScript = `#!/bin/sh
|
||||
|
||||
if [ -f /etc/boot2docker ]; then
|
||||
USERNAME=docker
|
||||
USER_HOME=/home/docker
|
||||
else
|
||||
USERNAME=$DOCKER_SSH_USER
|
||||
GROUPNAME=$DOCKER_SSH_USER
|
||||
|
||||
if ! getent group $GROUPNAME; then
|
||||
groupadd $GROUPNAME
|
||||
fi
|
||||
|
||||
if ! getent passwd $USERNAME; then
|
||||
USER_HOME=/var/lib/$DOCKER_SSH_USER
|
||||
useradd -m -d $USER_HOME -g $USERNAME $GROUPNAME
|
||||
else
|
||||
USER_HOME=$(getent passwd $USERNAME | cut -d: -f 6)
|
||||
fi
|
||||
|
||||
# Write sudoers
|
||||
if [ ! -f /etc/sudoers.d/$USERNAME ]; then
|
||||
echo -n "Defaults:$USERNAME " >> /etc/sudoers.d/$USERNAME
|
||||
echo '!requiretty' >> /etc/sudoers.d/$USERNAME
|
||||
echo "$USERNAME ALL=(ALL:ALL) NOPASSWD: ALL" >> /etc/sudoers.d/$USERNAME
|
||||
fi
|
||||
fi
|
||||
|
||||
# Add DOCKER_SSH_PUBLIC_KEY
|
||||
|
||||
AUTH_DIR="${USER_HOME}/.ssh"
|
||||
AUTH_FILE="${AUTH_DIR}/authorized_keys"
|
||||
|
||||
mkdir -m0700 -p $AUTH_DIR
|
||||
|
||||
echo "$DOCKER_SSH_PUBLIC_KEY" >> $AUTH_FILE
|
||||
|
||||
chown "${USERNAME}": ${AUTH_DIR} ${AUTH_FILE}
|
||||
chmod 600 $AUTH_FILE`
|
||||
)
|
||||
|
||||
func NewDriver(hostName, storePath string) *Driver {
|
||||
return &Driver{
|
||||
BaseDriver: &drivers.BaseDriver{
|
||||
SSHUser: defaultSSHUser,
|
||||
MachineName: hostName,
|
||||
StorePath: storePath,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Driver) buildConfig() {
|
||||
d.Config = goca.NewConfig(d.User, d.Password, d.Xmlrpcurl)
|
||||
}
|
||||
|
||||
func (d *Driver) setClient() error {
|
||||
d.buildConfig()
|
||||
return goca.SetClient(d.Config)
|
||||
}
|
||||
|
||||
// GetCreateFlags registers the flags this driver adds to
|
||||
// "docker hosts create"
|
||||
func (d *Driver) GetCreateFlags() []mcnflag.Flag {
|
||||
return []mcnflag.Flag{
|
||||
mcnflag.StringFlag{
|
||||
Name: "opennebula-cpu",
|
||||
Usage: fmt.Sprintf("CPU value for the VM. Default: %d", defaultCPU),
|
||||
EnvVar: "ONE_CPU",
|
||||
Value: "",
|
||||
},
|
||||
mcnflag.StringFlag{
|
||||
Name: "opennebula-vcpu",
|
||||
Usage: fmt.Sprintf("VCPUs for the VM. Default: %d", defaultVCPU),
|
||||
EnvVar: "ONE_VCPU",
|
||||
Value: "",
|
||||
},
|
||||
mcnflag.StringFlag{
|
||||
Name: "opennebula-memory",
|
||||
Usage: fmt.Sprintf("Size of memory for VM in MB. Default: %d", defaultMemory),
|
||||
EnvVar: "ONE_MEMORY",
|
||||
Value: "",
|
||||
},
|
||||
mcnflag.StringFlag{
|
||||
Name: "opennebula-template-name",
|
||||
Usage: "Template to use",
|
||||
EnvVar: "ONE_TEMPLATE_NAME",
|
||||
Value: "",
|
||||
},
|
||||
mcnflag.StringFlag{
|
||||
Name: "opennebula-template-id",
|
||||
Usage: "Template ID to use",
|
||||
EnvVar: "ONE_TEMPLATE_ID",
|
||||
Value: "",
|
||||
},
|
||||
mcnflag.StringFlag{
|
||||
Name: "opennebula-network-name",
|
||||
Usage: "Network to connect the machine to",
|
||||
EnvVar: "ONE_NETWORK_NAME",
|
||||
Value: "",
|
||||
},
|
||||
mcnflag.StringFlag{
|
||||
Name: "opennebula-network-id",
|
||||
Usage: "Network ID to connect the machine to",
|
||||
EnvVar: "ONE_NETWORK_ID",
|
||||
Value: "",
|
||||
},
|
||||
mcnflag.StringFlag{
|
||||
Name: "opennebula-network-owner",
|
||||
Usage: "User ID of the Network to connect the machine to",
|
||||
EnvVar: "ONE_NETWORK_OWNER",
|
||||
Value: "",
|
||||
},
|
||||
mcnflag.StringFlag{
|
||||
Name: "opennebula-image-name",
|
||||
Usage: "Image to use as the OS",
|
||||
EnvVar: "ONE_IMAGE_NAME",
|
||||
Value: "",
|
||||
},
|
||||
mcnflag.StringFlag{
|
||||
Name: "opennebula-image-id",
|
||||
Usage: "Image ID to use as the OS",
|
||||
EnvVar: "ONE_IMAGE_ID",
|
||||
Value: "",
|
||||
},
|
||||
mcnflag.StringFlag{
|
||||
Name: "opennebula-image-owner",
|
||||
Usage: "Owner of the image to use as the OS",
|
||||
EnvVar: "ONE_IMAGE_OWNER",
|
||||
Value: "",
|
||||
},
|
||||
mcnflag.StringFlag{
|
||||
Name: "opennebula-dev-prefix",
|
||||
Usage: "Dev prefix to use for the images: 'vd', 'sd', 'hd', etc..",
|
||||
EnvVar: "ONE_IMAGE_DEV_PREFIX",
|
||||
Value: "",
|
||||
},
|
||||
mcnflag.StringFlag{
|
||||
Name: "opennebula-disk-resize",
|
||||
Usage: "Size of disk for VM in MB",
|
||||
EnvVar: "ONE_DISK_SIZE",
|
||||
Value: "",
|
||||
},
|
||||
mcnflag.StringFlag{
|
||||
Name: "opennebula-b2d-size",
|
||||
Usage: "Size of the Volatile disk in MB (only for b2d)",
|
||||
EnvVar: "ONE_B2D_DATA_SIZE",
|
||||
Value: "",
|
||||
},
|
||||
mcnflag.StringFlag{
|
||||
Name: "opennebula-ssh-user",
|
||||
Usage: "Set the name of the SSH user",
|
||||
EnvVar: "ONE_SSH_USER",
|
||||
Value: defaultSSHUser,
|
||||
},
|
||||
mcnflag.BoolFlag{
|
||||
Name: "opennebula-disable-vnc",
|
||||
Usage: "VNC is enabled by default. Disable it with this flag",
|
||||
EnvVar: "ONE_DISABLE_VNC",
|
||||
},
|
||||
mcnflag.StringFlag{
|
||||
Name: "opennebula-user",
|
||||
Usage: "Set the user for authentication",
|
||||
EnvVar: "ONE_USER",
|
||||
},
|
||||
mcnflag.StringFlag{
|
||||
Name: "opennebula-password",
|
||||
Usage: "Set the password for authentication",
|
||||
EnvVar: "ONE_PASSWORD",
|
||||
},
|
||||
mcnflag.StringFlag{
|
||||
Name: "opennebula-xmlrpcurl",
|
||||
Usage: "Set the url for one xmlrpc server",
|
||||
EnvVar: "ONE_XMLRPC",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
|
||||
d.SetSwarmConfigFromFlags(flags)
|
||||
|
||||
// Authentication
|
||||
d.User = flags.String("opennebula-user")
|
||||
d.Password = flags.String("opennebula-password")
|
||||
d.Xmlrpcurl = flags.String("opennebula-xmlrpcurl")
|
||||
|
||||
// Capacity
|
||||
d.CPU = flags.String("opennebula-cpu")
|
||||
d.VCPU = flags.String("opennebula-vcpu")
|
||||
d.Memory = flags.String("opennebula-memory")
|
||||
|
||||
// Template
|
||||
d.TemplateName = flags.String("opennebula-template-name")
|
||||
d.TemplateID = flags.String("opennebula-template-id")
|
||||
|
||||
// Network
|
||||
d.NetworkName = flags.String("opennebula-network-name")
|
||||
d.NetworkID = flags.String("opennebula-network-id")
|
||||
d.NetworkOwner = flags.String("opennebula-network-owner")
|
||||
|
||||
// Storage
|
||||
d.ImageID = flags.String("opennebula-image-id")
|
||||
d.ImageName = flags.String("opennebula-image-name")
|
||||
d.ImageOwner = flags.String("opennebula-image-owner")
|
||||
|
||||
d.ImageDevPrefix = flags.String("opennebula-dev-prefix")
|
||||
d.DiskSize = flags.String("opennebula-disk-resize")
|
||||
d.B2DSize = flags.String("opennebula-b2d-size")
|
||||
|
||||
// Provision
|
||||
d.SSHUser = flags.String("opennebula-ssh-user")
|
||||
|
||||
// VNC
|
||||
d.DisableVNC = flags.Bool("opennebula-disable-vnc")
|
||||
|
||||
// Either TemplateName or TemplateID
|
||||
if d.TemplateName != "" && d.TemplateID != "" {
|
||||
return errors.New("specify only one of: --opennebula-template-name or --opennebula-template-id, not both")
|
||||
}
|
||||
|
||||
// Either NetworkName or NetworkID
|
||||
if d.NetworkName != "" && d.NetworkID != "" {
|
||||
return errors.New("specify only one of: --opennebula-network-name or --opennebula-network-id, not both")
|
||||
}
|
||||
|
||||
// Either ImageName or ImageID
|
||||
if d.ImageName != "" && d.ImageID != "" {
|
||||
return errors.New("specify only one of: --opennebula-image-name or --opennebula-image-id, not both")
|
||||
}
|
||||
|
||||
// Required and incompatible options for Template
|
||||
if d.TemplateName != "" || d.TemplateID != "" {
|
||||
// Template has been specified:
|
||||
|
||||
// ImageName and ImageID are incompatible
|
||||
if d.ImageName != "" || d.ImageID != "" {
|
||||
return errors.New("options --opennebula-image-* are incompatible with --opennebula-template-*")
|
||||
}
|
||||
|
||||
// ImageDevPrefix is incompatible
|
||||
if d.ImageDevPrefix != "" {
|
||||
return errors.New("option: --opennebula-dev-prefix is incompatible with --opennebula-template-*")
|
||||
}
|
||||
// DiskSize is incompatible
|
||||
if d.DiskSize != "" {
|
||||
return errors.New("option: --opennebula-disk-resize is incompatible with --opennebula-template-*")
|
||||
}
|
||||
// B2DSize is incompatible
|
||||
if d.B2DSize != "" {
|
||||
return errors.New("option: --opennebula-disk-resize is incompatible with --opennebula-template-*")
|
||||
}
|
||||
// DisableVNC is incompatible
|
||||
if d.DisableVNC {
|
||||
return errors.New("option: --opennebula-disable-vnc is incompatible with --opennebula-template-*")
|
||||
}
|
||||
} else {
|
||||
//Template has NOT been specified:
|
||||
|
||||
// ImageName or ImageID is required
|
||||
if d.ImageName == "" && d.ImageID == "" {
|
||||
return errors.New("specify a image to use as the OS with --opennebula-image-name or --opennebula-image-id")
|
||||
}
|
||||
|
||||
// NetworkName or NetworkID is required
|
||||
if d.NetworkName == "" && d.NetworkID == "" {
|
||||
return errors.New("specify a network to connect to with --opennebula-network-name or --opennebula-network-id")
|
||||
}
|
||||
|
||||
// Assign default capacity values
|
||||
if d.CPU == "" {
|
||||
d.CPU = defaultCPU
|
||||
}
|
||||
|
||||
if d.VCPU == "" {
|
||||
d.VCPU = defaultVCPU
|
||||
}
|
||||
|
||||
if d.Memory == "" {
|
||||
d.Memory = defaultMemory
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) DriverName() string {
|
||||
return "opennebula"
|
||||
}
|
||||
|
||||
func (d *Driver) GetSSHHostname() (string, error) {
|
||||
return d.GetIP()
|
||||
}
|
||||
|
||||
func (d *Driver) GetSSHUsername() string {
|
||||
return d.SSHUser
|
||||
}
|
||||
|
||||
func (d *Driver) PreCreateCheck() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) Create() error {
|
||||
var (
|
||||
vector *goca.TemplateBuilderVector
|
||||
vmtemplate *goca.Template
|
||||
|
||||
err error
|
||||
)
|
||||
|
||||
// build config and set the xmlrpc client
|
||||
d.setClient()
|
||||
|
||||
log.Infof("Creating SSH key..")
|
||||
if err := ssh.GenerateSSHKey(d.GetSSHKeyPath()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pubKey, err := ioutil.ReadFile(d.publicSSHKeyPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create template
|
||||
template := goca.NewTemplateBuilder()
|
||||
|
||||
if d.TemplateName != "" || d.TemplateID != "" {
|
||||
// Template has been specified
|
||||
} else {
|
||||
// Template has NOT been specified
|
||||
template.AddValue("NAME", d.MachineName)
|
||||
|
||||
// OS Boot
|
||||
vector = template.NewVector("OS")
|
||||
vector.AddValue("BOOT", "disk0")
|
||||
|
||||
// OS Disk
|
||||
vector = template.NewVector("DISK")
|
||||
|
||||
if d.ImageID != "" {
|
||||
vector.AddValue("IMAGE_ID", d.ImageID)
|
||||
} else {
|
||||
vector.AddValue("IMAGE", d.ImageName)
|
||||
if d.ImageOwner != "" {
|
||||
vector.AddValue("IMAGE_UNAME", d.ImageOwner)
|
||||
}
|
||||
}
|
||||
|
||||
if d.DiskSize != "" {
|
||||
vector.AddValue("SIZE", d.DiskSize)
|
||||
}
|
||||
|
||||
if d.ImageDevPrefix != "" {
|
||||
vector.AddValue("DEV_PREFIX", d.ImageDevPrefix)
|
||||
}
|
||||
|
||||
// Add a volatile disk for b2d
|
||||
if d.B2DSize != "" {
|
||||
vector = template.NewVector("DISK")
|
||||
vector.AddValue("SIZE", d.B2DSize)
|
||||
vector.AddValue("TYPE", "fs")
|
||||
vector.AddValue("FORMAT", "raw")
|
||||
}
|
||||
|
||||
// VNC
|
||||
if !d.DisableVNC {
|
||||
vector = template.NewVector("GRAPHICS")
|
||||
vector.AddValue("LISTEN", "0.0.0.0")
|
||||
vector.AddValue("TYPE", "vnc")
|
||||
}
|
||||
}
|
||||
|
||||
// Capacity
|
||||
if d.CPU != "" {
|
||||
template.AddValue("CPU", d.CPU)
|
||||
}
|
||||
|
||||
if d.Memory != "" {
|
||||
template.AddValue("MEMORY", d.Memory)
|
||||
}
|
||||
|
||||
if d.VCPU != "" {
|
||||
template.AddValue("VCPU", d.VCPU)
|
||||
}
|
||||
|
||||
// Network
|
||||
if d.NetworkName != "" || d.NetworkID != "" {
|
||||
vector = template.NewVector("NIC")
|
||||
|
||||
if d.NetworkName != "" {
|
||||
vector.AddValue("NETWORK", d.NetworkName)
|
||||
if d.NetworkOwner != "" {
|
||||
vector.AddValue("NETWORK_UNAME", d.NetworkOwner)
|
||||
}
|
||||
}
|
||||
|
||||
if d.NetworkID != "" {
|
||||
vector.AddValue("NETWORK_ID", d.NetworkID)
|
||||
}
|
||||
}
|
||||
|
||||
// Context
|
||||
vector = template.NewVector("CONTEXT")
|
||||
vector.AddValue("NETWORK", "YES")
|
||||
vector.AddValue("SSH_PUBLIC_KEY", "$USER[SSH_PUBLIC_KEY]")
|
||||
vector.AddValue("DOCKER_SSH_USER", d.SSHUser)
|
||||
vector.AddValue("DOCKER_SSH_PUBLIC_KEY", string(pubKey))
|
||||
contextScript64 := base64.StdEncoding.EncodeToString([]byte(contextScript))
|
||||
vector.AddValue("START_SCRIPT_BASE64", contextScript64)
|
||||
|
||||
// Instantiate
|
||||
log.Infof("Starting VM..")
|
||||
|
||||
if d.TemplateName != "" || d.TemplateID != "" {
|
||||
|
||||
if d.TemplateName != "" {
|
||||
vmtemplate, err = goca.NewTemplateFromName(d.TemplateName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
templateID, err := strconv.Atoi(d.TemplateID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
vmtemplate = goca.NewTemplate(uint(templateID))
|
||||
}
|
||||
|
||||
_, err = vmtemplate.Instantiate(d.MachineName, false, template.String())
|
||||
|
||||
} else {
|
||||
_, err = goca.CreateVM(template.String(), false)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if d.IPAddress, err = d.GetIP(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return d.Start()
|
||||
}
|
||||
|
||||
func (d *Driver) GetURL() (string, error) {
|
||||
ip, err := d.GetIP()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("tcp://%s:2376", ip), nil
|
||||
}
|
||||
|
||||
func (d *Driver) GetIP() (string, error) {
|
||||
d.setClient()
|
||||
vm, err := goca.NewVMFromName(d.MachineName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = vm.Info()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if ip, ok := vm.XPath("/VM/TEMPLATE/NIC/IP"); ok {
|
||||
d.IPAddress = ip
|
||||
}
|
||||
|
||||
if d.IPAddress == "" {
|
||||
return "", fmt.Errorf("IP address is not set")
|
||||
}
|
||||
|
||||
return d.IPAddress, nil
|
||||
}
|
||||
|
||||
func (d *Driver) GetState() (state.State, error) {
|
||||
d.setClient()
|
||||
vm, err := goca.NewVMFromName(d.MachineName)
|
||||
if err != nil {
|
||||
return state.None, err
|
||||
}
|
||||
|
||||
err = vm.Info()
|
||||
if err != nil {
|
||||
return state.None, err
|
||||
}
|
||||
|
||||
vmState, lcmState, err := vm.StateString()
|
||||
if err != nil {
|
||||
return state.None, err
|
||||
}
|
||||
|
||||
switch vmState {
|
||||
case "INIT", "PENDING", "HOLD", "CLONING":
|
||||
return state.Starting, nil
|
||||
case "ACTIVE":
|
||||
switch lcmState {
|
||||
case "RUNNING",
|
||||
|
||||
// migration is considered running
|
||||
"MIGRATE",
|
||||
"SAVE_MIGRATE",
|
||||
"PROLOG_MIGRATE",
|
||||
"BOOT_MIGRATE",
|
||||
|
||||
// recover --recreate is also considered running
|
||||
"CLEANUP_RESUBMIT",
|
||||
|
||||
// operation on the VMs
|
||||
"HOTPLUG",
|
||||
"HOTPLUG_SNAPSHOT",
|
||||
"HOTPLUG_NIC",
|
||||
"HOTPLUG_SAVEAS",
|
||||
"DISK_SNAPSHOT",
|
||||
"DISK_SNAPSHOT_DELETE":
|
||||
|
||||
return state.Running, nil
|
||||
|
||||
case "LCM_INIT",
|
||||
"PROLOG",
|
||||
"BOOT",
|
||||
"PROLOG_RESUME",
|
||||
"BOOT_UNKNOWN",
|
||||
"BOOT_POWEROFF",
|
||||
"BOOT_SUSPENDED",
|
||||
"BOOT_STOPPED",
|
||||
"PROLOG_UNDEPLOY",
|
||||
"BOOT_UNDEPLOY",
|
||||
"PROLOG_MIGRATE_UNKNOWN":
|
||||
|
||||
return state.Starting, nil
|
||||
|
||||
case "HOTPLUG_SAVEAS_POWEROFF",
|
||||
"HOTPLUG_SAVEAS_SUSPENDED",
|
||||
"HOTPLUG_PROLOG_POWEROFF",
|
||||
"HOTPLUG_EPILOG_POWEROFF",
|
||||
"PROLOG_MIGRATE_POWEROFF",
|
||||
"PROLOG_MIGRATE_SUSPEND",
|
||||
"DISK_SNAPSHOT_POWEROFF",
|
||||
"DISK_SNAPSHOT_REVERT_POWEROFF",
|
||||
"DISK_SNAPSHOT_DELETE_POWEROFF",
|
||||
"DISK_SNAPSHOT_SUSPENDED",
|
||||
"DISK_SNAPSHOT_REVERT_SUSPENDED",
|
||||
"DISK_SNAPSHOT_DELETE_SUSPENDED":
|
||||
|
||||
return state.Stopped, nil
|
||||
|
||||
case "SAVE_SUSPEND",
|
||||
"SAVE_STOP",
|
||||
"EPILOG_STOP",
|
||||
"EPILOG",
|
||||
"SHUTDOWN",
|
||||
"SHUTDOWN_POWEROFF",
|
||||
"SHUTDOWN_UNDEPLOY",
|
||||
"EPILOG_UNDEPLOY":
|
||||
return state.Stopping, nil
|
||||
|
||||
case "UNKNOWN",
|
||||
"CLEANUP_DELETE",
|
||||
"BOOT_FAILURE",
|
||||
"BOOT_MIGRATE_FAILURE",
|
||||
"PROLOG_MIGRATE_FAILURE",
|
||||
"PROLOG_FAILURE",
|
||||
"EPILOG_FAILURE",
|
||||
"EPILOG_STOP_FAILURE",
|
||||
"EPILOG_UNDEPLOY_FAILURE",
|
||||
"PROLOG_MIGRATE_POWEROFF_FAILURE",
|
||||
"PROLOG_MIGRATE_SUSPEND_FAILURE",
|
||||
"BOOT_UNDEPLOY_FAILURE",
|
||||
"BOOT_STOPPED_FAILURE",
|
||||
"PROLOG_RESUME_FAILURE",
|
||||
"PROLOG_UNDEPLOY_FAILURE",
|
||||
"PROLOG_MIGRATE_UNKNOWN_FAILURE":
|
||||
|
||||
return state.Error, nil
|
||||
|
||||
}
|
||||
|
||||
case "POWEROFF", "UNDEPLOYED":
|
||||
return state.Stopped, nil
|
||||
case "STOPPED", "SUSPENDED":
|
||||
return state.Saved, nil
|
||||
case "DONE", "FAILED", "CLONING_FAILURE":
|
||||
return state.Error, nil
|
||||
}
|
||||
|
||||
return state.Error, nil
|
||||
}
|
||||
|
||||
func (d *Driver) Start() error {
|
||||
d.setClient()
|
||||
vm, err := goca.NewVMFromName(d.MachineName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vm.Resume()
|
||||
|
||||
s := state.None
|
||||
for retry := 0; retry < 50 && s != state.Running; retry++ {
|
||||
s, err = d.GetState()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch s {
|
||||
case state.Error:
|
||||
return errors.New("vM in error state")
|
||||
default:
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
if d.IPAddress == "" {
|
||||
if d.IPAddress, err = d.GetIP(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("Waiting for SSH..")
|
||||
|
||||
// Wait for SSH over NAT to be available before returning to user
|
||||
return drivers.WaitForSSH(d)
|
||||
}
|
||||
|
||||
func (d *Driver) Stop() error {
|
||||
d.setClient()
|
||||
vm, err := goca.NewVMFromName(d.MachineName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = vm.Poweroff()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) Remove() error {
|
||||
d.setClient()
|
||||
vm, err := goca.NewVMFromName(d.MachineName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = vm.TerminateHard()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) Restart() error {
|
||||
d.setClient()
|
||||
vm, err := goca.NewVMFromName(d.MachineName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = vm.Reboot()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Driver) Kill() error {
|
||||
d.setClient()
|
||||
vm, err := goca.NewVMFromName(d.MachineName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return vm.PoweroffHard()
|
||||
}
|
||||
|
||||
func (d *Driver) publicSSHKeyPath() string {
|
||||
return d.GetSSHKeyPath() + ".pub"
|
||||
}
|
1
src/docker_machine/src/docker_machine/opennebula_test.go
Normal file
1
src/docker_machine/src/docker_machine/opennebula_test.go
Normal file
@ -0,0 +1 @@
|
||||
package opennebula
|
18
src/oca/go/src/goca/Makefile
Normal file
18
src/oca/go/src/goca/Makefile
Normal file
@ -0,0 +1,18 @@
|
||||
.PHONY: build test help default
|
||||
|
||||
default: test
|
||||
|
||||
help:
|
||||
@echo 'Management commands for goca:'
|
||||
@echo
|
||||
@echo 'Usage:'
|
||||
@echo ' make test Run the tests.'
|
||||
@echo ' make get-deps runs glide install, mostly used for ci.'
|
||||
@echo
|
||||
|
||||
test:
|
||||
go test $(glide nv)
|
||||
golint $(glide nv)
|
||||
|
||||
get-deps:
|
||||
glide install
|
38
src/oca/go/src/goca/acl.go
Normal file
38
src/oca/go/src/goca/acl.go
Normal file
@ -0,0 +1,38 @@
|
||||
package goca
|
||||
|
||||
// ACLPool represents an OpenNebula ACL list pool
|
||||
type ACLPool struct {
|
||||
XMLResource
|
||||
}
|
||||
|
||||
// NewACLPool returns an acl pool. A connection to OpenNebula is
|
||||
// performed.
|
||||
func NewACLPool() (*ACLPool, error) {
|
||||
response, err := client.Call("one.acl.info")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
aclpool := &ACLPool{XMLResource{body: response.Body()}}
|
||||
|
||||
return aclpool, err
|
||||
}
|
||||
|
||||
// CreateACLRule adds a new ACL rule.
|
||||
// * user: User component of the new rule. A string containing a hex number.
|
||||
// * resource: Resource component of the new rule. A string containing a hex number.
|
||||
// * rights: Rights component of the new rule. A string containing a hex number.
|
||||
func CreateACLRule(user, resource, rights string) (uint, error) {
|
||||
response, err := client.Call("one.acl.addrule", user, resource, rights)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint(response.BodyInt()), nil
|
||||
}
|
||||
|
||||
// DeleteACLRule deletes an ACL rule.
|
||||
func DeleteACLRule(aclID uint) error {
|
||||
_, err := client.Call("one.acl.delrule", int(aclID))
|
||||
return err
|
||||
}
|
130
src/oca/go/src/goca/cluster.go
Normal file
130
src/oca/go/src/goca/cluster.go
Normal file
@ -0,0 +1,130 @@
|
||||
package goca
|
||||
|
||||
// Cluster represents an OpenNebula Cluster
|
||||
type Cluster struct {
|
||||
XMLResource
|
||||
ID uint
|
||||
Name string
|
||||
}
|
||||
|
||||
// ClusterPool represents an OpenNebula ClusterPool
|
||||
type ClusterPool struct {
|
||||
XMLResource
|
||||
}
|
||||
|
||||
// NewClusterPool returns a cluster pool. A connection to OpenNebula is
|
||||
// performed.
|
||||
func NewClusterPool() (*ClusterPool, error) {
|
||||
response, err := client.Call("one.clusterpool.info")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusterpool := &ClusterPool{XMLResource{body: response.Body()}}
|
||||
|
||||
return clusterpool, err
|
||||
|
||||
}
|
||||
|
||||
// NewCluster finds a cluster object by ID. No connection to OpenNebula.
|
||||
func NewCluster(id uint) *Cluster {
|
||||
return &Cluster{ID: id}
|
||||
}
|
||||
|
||||
// NewClusterFromName finds a cluster object by name. It connects to
|
||||
// OpenNebula to retrieve the pool, but doesn't perform the Info() call to
|
||||
// retrieve the attributes of the cluster.
|
||||
func NewClusterFromName(name string) (*Cluster, error) {
|
||||
clusterPool, err := NewClusterPool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, err := clusterPool.GetIDFromName(name, "/CLUSTER_POOL/CLUSTER")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewCluster(id), nil
|
||||
}
|
||||
|
||||
// CreateCluster allocates a new cluster. It returns the new cluster ID.
|
||||
func CreateCluster(name string) (uint, error) {
|
||||
response, err := client.Call("one.cluster.allocate", name)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint(response.BodyInt()), nil
|
||||
}
|
||||
|
||||
// Delete deletes the given cluster from the pool.
|
||||
func (cluster *Cluster) Delete() error {
|
||||
_, err := client.Call("one.cluster.delete", cluster.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// Update replaces the cluster cluster contents.
|
||||
// * tpl: The new cluster contents. Syntax can be the usual attribute=value or
|
||||
// XML.
|
||||
// * appendCluster: Update type: 0: Replace the whole cluster. 1: Merge new
|
||||
// cluster with the existing one.
|
||||
func (cluster *Cluster) Update(tpl string, appendCluster int) error {
|
||||
_, err := client.Call("one.cluster.update", cluster.ID, tpl, appendCluster)
|
||||
return err
|
||||
}
|
||||
|
||||
// AddHost adds a host to the given cluster.
|
||||
// * hostID: The host ID.
|
||||
func (cluster *Cluster) AddHost(hostID uint) error {
|
||||
_, err := client.Call("one.cluster.addhost", cluster.ID, int(hostID))
|
||||
return err
|
||||
}
|
||||
|
||||
// DelHost removes a host from the given cluster.
|
||||
// * hostID: The host ID.
|
||||
func (cluster *Cluster) DelHost(hostID uint) error {
|
||||
_, err := client.Call("one.cluster.delhost", cluster.ID, int(hostID))
|
||||
return err
|
||||
}
|
||||
|
||||
// AddDatastore adds a datastore to the given cluster.
|
||||
// * dsID: The datastore ID.
|
||||
func (cluster *Cluster) AddDatastore(dsID uint) error {
|
||||
_, err := client.Call("one.cluster.adddatastore", cluster.ID, int(dsID))
|
||||
return err
|
||||
}
|
||||
|
||||
// DelDatastore removes a datastore from the given cluster.
|
||||
// * dsID: The datastore ID.
|
||||
func (cluster *Cluster) DelDatastore(dsID uint) error {
|
||||
_, err := client.Call("one.cluster.deldatastore", cluster.ID, int(dsID))
|
||||
return err
|
||||
}
|
||||
|
||||
// AddVnet adds a vnet to the given cluster.
|
||||
// * vnetID: The vnet ID.
|
||||
func (cluster *Cluster) AddVnet(vnetID uint) error {
|
||||
_, err := client.Call("one.cluster.addvnet", cluster.ID, int(vnetID))
|
||||
return err
|
||||
}
|
||||
|
||||
// DelVnet removes a vnet from the given cluster.
|
||||
// * vnetID: The vnet ID.
|
||||
func (cluster *Cluster) DelVnet(vnetID uint) error {
|
||||
_, err := client.Call("one.cluster.delvnet", cluster.ID, int(vnetID))
|
||||
return err
|
||||
}
|
||||
|
||||
// Rename renames a cluster.
|
||||
// * newName: The new name.
|
||||
func (cluster *Cluster) Rename(newName string) error {
|
||||
_, err := client.Call("one.cluster.rename", cluster.ID, newName)
|
||||
return err
|
||||
}
|
||||
|
||||
// Info retrieves information for the cluster.
|
||||
func (cluster *Cluster) Info() error {
|
||||
_, err := client.Call("one.cluster.info", cluster.ID)
|
||||
return err
|
||||
}
|
117
src/oca/go/src/goca/datastore.go
Normal file
117
src/oca/go/src/goca/datastore.go
Normal file
@ -0,0 +1,117 @@
|
||||
package goca
|
||||
|
||||
// Datastore represents an OpenNebula Datastore
|
||||
type Datastore struct {
|
||||
XMLResource
|
||||
ID uint
|
||||
Name string
|
||||
}
|
||||
|
||||
// DatastorePool represents an OpenNebula DatastorePool
|
||||
type DatastorePool struct {
|
||||
XMLResource
|
||||
}
|
||||
|
||||
// NewDatastorePool returns a datastore pool. A connection to OpenNebula is
|
||||
// performed.
|
||||
func NewDatastorePool() (*DatastorePool, error) {
|
||||
response, err := client.Call("one.datastorepool.info")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
datastorepool := &DatastorePool{XMLResource{body: response.Body()}}
|
||||
|
||||
return datastorepool, err
|
||||
}
|
||||
|
||||
// NewDatastore finds a datastore object by ID. No connection to OpenNebula.
|
||||
func NewDatastore(id uint) *Datastore {
|
||||
return &Datastore{ID: id}
|
||||
}
|
||||
|
||||
// NewDatastoreFromName finds a datastore object by name. It connects to
|
||||
// OpenNebula to retrieve the pool, but doesn't perform the Info() call to
|
||||
// retrieve the attributes of the datastore.
|
||||
func NewDatastoreFromName(name string) (*Datastore, error) {
|
||||
datastorePool, err := NewDatastorePool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, err := datastorePool.GetIDFromName(name, "/DATASTORE_POOL/DATASTORE")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewDatastore(id), nil
|
||||
}
|
||||
|
||||
// CreateDatastore allocates a new datastore. It returns the new datastore ID.
|
||||
// * tpl: template of the datastore
|
||||
// * clusterID: The cluster ID. If it is -1, the default one will be used.
|
||||
func CreateDatastore(tpl string, clusterID int) (uint, error) {
|
||||
response, err := client.Call("one.datastore.allocate", tpl, clusterID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint(response.BodyInt()), nil
|
||||
}
|
||||
|
||||
// Delete deletes the given datastore from the pool.
|
||||
func (datastore *Datastore) Delete() error {
|
||||
_, err := client.Call("one.datastore.delete", datastore.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// Update replaces the datastore template contents.
|
||||
// * tpl: The new template contents. Syntax can be the usual attribute=value or XML.
|
||||
// * appendTemplate: Update type: 0: Replace the whole template. 1: Merge new template with the existing one.
|
||||
func (datastore *Datastore) Update(tpl string, appendTemplate int) error {
|
||||
_, err := client.Call("one.datastore.update", datastore.ID, tpl, appendTemplate)
|
||||
return err
|
||||
}
|
||||
|
||||
// Chmod changes the permission bits of a datastore.
|
||||
// * uu: USER USE bit. If set to -1, it will not change.
|
||||
// * um: USER MANAGE bit. If set to -1, it will not change.
|
||||
// * ua: USER ADMIN bit. If set to -1, it will not change.
|
||||
// * gu: GROUP USE bit. If set to -1, it will not change.
|
||||
// * gm: GROUP MANAGE bit. If set to -1, it will not change.
|
||||
// * ga: GROUP ADMIN bit. If set to -1, it will not change.
|
||||
// * ou: OTHER USE bit. If set to -1, it will not change.
|
||||
// * om: OTHER MANAGE bit. If set to -1, it will not change.
|
||||
// * oa: OTHER ADMIN bit. If set to -1, it will not change.
|
||||
func (datastore *Datastore) Chmod(uu, um, ua, gu, gm, ga, ou, om, oa int) error {
|
||||
_, err := client.Call("one.datastore.chmod", datastore.ID, uu, um, ua, gu, gm, ga, ou, om, oa)
|
||||
return err
|
||||
}
|
||||
|
||||
// Chown changes the ownership of a datastore.
|
||||
// * userID: The User ID of the new owner. If set to -1, it will not change.
|
||||
// * groupID: The Group ID of the new group. If set to -1, it will not change.
|
||||
func (datastore *Datastore) Chown(userID, groupID uint) error {
|
||||
_, err := client.Call("one.datastore.chown", datastore.ID, int(userID), int(groupID))
|
||||
return err
|
||||
}
|
||||
|
||||
// Rename renames a datastore.
|
||||
// * newName: The new name.
|
||||
func (datastore *Datastore) Rename(newName string) error {
|
||||
_, err := client.Call("one.datastore.rename", datastore.ID, newName)
|
||||
return err
|
||||
}
|
||||
|
||||
// Enable enables or disables a datastore.
|
||||
// * enable: True for enabling
|
||||
func (datastore *Datastore) Enable(enable bool) error {
|
||||
_, err := client.Call("one.datastore.enable", datastore.ID, enable)
|
||||
return err
|
||||
}
|
||||
|
||||
// Info retrieves information for the datastore.
|
||||
func (datastore *Datastore) Info() error {
|
||||
_, err := client.Call("one.datastore.info", datastore.ID)
|
||||
return err
|
||||
}
|
144
src/oca/go/src/goca/document.go
Normal file
144
src/oca/go/src/goca/document.go
Normal file
@ -0,0 +1,144 @@
|
||||
package goca
|
||||
|
||||
import "errors"
|
||||
|
||||
// Document represents an OpenNebula Document
|
||||
type Document struct {
|
||||
XMLResource
|
||||
ID uint
|
||||
Name string
|
||||
}
|
||||
|
||||
// DocumentPool represents an OpenNebula DocumentPool
|
||||
type DocumentPool struct {
|
||||
XMLResource
|
||||
}
|
||||
|
||||
// NewDocumentPool returns a document pool. A connection to OpenNebula is
|
||||
// performed.
|
||||
func NewDocumentPool(documentType int, args ...int) (*DocumentPool, error) {
|
||||
var who, start, end int
|
||||
|
||||
switch len(args) {
|
||||
case 0:
|
||||
who = PoolWhoMine
|
||||
start = -1
|
||||
end = -1
|
||||
case 1:
|
||||
who = args[0]
|
||||
start = -1
|
||||
end = -1
|
||||
case 3:
|
||||
who = args[0]
|
||||
start = args[1]
|
||||
end = args[2]
|
||||
default:
|
||||
return nil, errors.New("Wrong number of arguments")
|
||||
}
|
||||
|
||||
response, err := client.Call("one.documentpool.info", who, start, end, documentType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
documentpool := &DocumentPool{XMLResource{body: response.Body()}}
|
||||
|
||||
return documentpool, err
|
||||
}
|
||||
|
||||
// NewDocument finds a document object by ID. No connection to OpenNebula.
|
||||
func NewDocument(id uint) *Document {
|
||||
return &Document{ID: id}
|
||||
}
|
||||
|
||||
// NewDocumentFromName finds a document object by name. It connects to
|
||||
// OpenNebula to retrieve the pool, but doesn't perform the Info() call to
|
||||
// retrieve the attributes of the document.
|
||||
func NewDocumentFromName(name string, documentType int) (*Document, error) {
|
||||
documentPool, err := NewDocumentPool(documentType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, err := documentPool.GetIDFromName(name, "/DOCUMENT_POOL/DOCUMENT")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewDocument(id), nil
|
||||
}
|
||||
|
||||
// CreateDocument allocates a new document. It returns the new document ID.
|
||||
func CreateDocument(tpl string, documentType int) (uint, error) {
|
||||
response, err := client.Call("one.document.allocate", tpl, documentType)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint(response.BodyInt()), nil
|
||||
}
|
||||
|
||||
// Clone clones an existing document.
|
||||
// * newName: Name for the new document.
|
||||
func (document *Document) Clone(newName string) error {
|
||||
_, err := client.Call("one.document.clone", document.ID, newName)
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete deletes the given document from the pool.
|
||||
func (document *Document) Delete() error {
|
||||
_, err := client.Call("one.document.delete", document.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// Update replaces the document template contents.
|
||||
// * tpl: The new document template contents. Syntax can be the usual attribute=value or XML.
|
||||
// * appendTemplate: Update type: 0: Replace the whole template. 1: Merge new template with the existing one.
|
||||
func (document *Document) Update(tpl string, appendTemplate int) error {
|
||||
_, err := client.Call("one.document.update", document.ID, tpl, appendTemplate)
|
||||
return err
|
||||
}
|
||||
|
||||
// Chmod changes the permission bits of a document.
|
||||
// * uu: USER USE bit. If set to -1, it will not change.
|
||||
// * um: USER MANAGE bit. If set to -1, it will not change.
|
||||
// * ua: USER ADMIN bit. If set to -1, it will not change.
|
||||
// * gu: GROUP USE bit. If set to -1, it will not change.
|
||||
// * gm: GROUP MANAGE bit. If set to -1, it will not change.
|
||||
// * ga: GROUP ADMIN bit. If set to -1, it will not change.
|
||||
// * ou: OTHER USE bit. If set to -1, it will not change.
|
||||
// * om: OTHER MANAGE bit. If set to -1, it will not change.
|
||||
// * oa: OTHER ADMIN bit. If set to -1, it will not change.
|
||||
func (document *Document) Chmod(uu, um, ua, gu, gm, ga, ou, om, oa int) error {
|
||||
_, err := client.Call("one.document.chmod", document.ID, uu, um, ua, gu, gm, ga, ou, om, oa)
|
||||
return err
|
||||
}
|
||||
|
||||
// Chown changes the ownership of a document.
|
||||
// * userID: The User ID of the new owner. If set to -1, it will not change.
|
||||
// * groupID: The Group ID of the new group. If set to -1, it will not change.
|
||||
func (document *Document) Chown(userID, groupID uint) error {
|
||||
_, err := client.Call("one.document.chown", document.ID, int(userID), int(groupID))
|
||||
return err
|
||||
}
|
||||
|
||||
// Rename renames a document.
|
||||
// * newName: The new name.
|
||||
func (document *Document) Rename(newName string) error {
|
||||
_, err := client.Call("one.document.rename", document.ID, newName)
|
||||
return err
|
||||
}
|
||||
|
||||
// Lock locks the document at the api level. The lock automatically expires after 2 minutes.
|
||||
// * applicationName: String to identify the application requesting the lock.
|
||||
func (document *Document) Lock(applicationName string) error {
|
||||
_, err := client.Call("one.document.lock", document.ID, applicationName)
|
||||
return err
|
||||
}
|
||||
|
||||
// Unlock unlocks the document at the api level.
|
||||
// * applicationName: String to identify the application requesting the lock.
|
||||
func (document *Document) Unlock(applicationName string) error {
|
||||
_, err := client.Call("one.document.unlock", document.ID, applicationName)
|
||||
return err
|
||||
}
|
13
src/oca/go/src/goca/glide.lock
generated
Normal file
13
src/oca/go/src/goca/glide.lock
generated
Normal file
@ -0,0 +1,13 @@
|
||||
hash: ac9bb59552226f818b59a1f54831140c20901e060de067f144f54d4e100d0e12
|
||||
updated: 2017-11-28T12:03:57.539724958+01:00
|
||||
imports:
|
||||
- name: github.com/kolo/xmlrpc
|
||||
version: 0826b98aaa29c0766956cb40d45cf7482a597671
|
||||
- name: golang.org/x/net
|
||||
version: fc492d2e106922eb3aa4bca863d55e882c087ae3
|
||||
subpackages:
|
||||
- html
|
||||
- html/atom
|
||||
- name: gopkg.in/xmlpath.v2
|
||||
version: 860cbeca3ebcc600db0b213c0e83ad6ce91f5739
|
||||
testImports: []
|
4
src/oca/go/src/goca/glide.yaml
Normal file
4
src/oca/go/src/goca/glide.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
package: github.com/OpenNebula/goca
|
||||
import:
|
||||
- package: github.com/kolo/xmlrpc
|
||||
- package: gopkg.in/xmlpath.v2
|
178
src/oca/go/src/goca/goca.go
Normal file
178
src/oca/go/src/goca/goca.go
Normal file
@ -0,0 +1,178 @@
|
||||
package goca
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/kolo/xmlrpc"
|
||||
)
|
||||
|
||||
var (
|
||||
client *oneClient
|
||||
)
|
||||
|
||||
// OneConfig contains the information to communicate with OpenNebula
|
||||
type OneConfig struct {
|
||||
// Token is the authentication string. In the format of <user>:<password>
|
||||
Token string
|
||||
|
||||
// XmlrpcURL contains OpenNebula's XML-RPC API endpoint. Defaults to
|
||||
// http://localhost:2633/RPC2
|
||||
XmlrpcURL string
|
||||
}
|
||||
|
||||
type oneClient struct {
|
||||
token string
|
||||
xmlrpcClient *xmlrpc.Client
|
||||
xmlrpcClientError error
|
||||
}
|
||||
|
||||
type response struct {
|
||||
status bool
|
||||
body string
|
||||
bodyInt int
|
||||
}
|
||||
|
||||
// Resource implements an OpenNebula Resource methods. *XMLResource implements
|
||||
// all these methods
|
||||
type Resource interface {
|
||||
Body() string
|
||||
XPath(string) (string, bool)
|
||||
XPathIter(string) *XMLIter
|
||||
GetIDFromName(string, string) (uint, error)
|
||||
}
|
||||
|
||||
// Initializes the client variable, used as a singleton
|
||||
func init() {
|
||||
err := SetClient(NewConfig("", "", ""))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// NewConfig returns a new OneConfig object with the specified user, password,
|
||||
// and xmlrpcURL
|
||||
func NewConfig(user string, password string, xmlrpcURL string) OneConfig {
|
||||
var authToken string
|
||||
var oneAuthPath string
|
||||
|
||||
oneXmlrpc := xmlrpcURL
|
||||
|
||||
if user == "" && password == "" {
|
||||
oneAuthPath = os.Getenv("ONE_AUTH")
|
||||
if oneAuthPath == "" {
|
||||
oneAuthPath = os.Getenv("HOME") + "/.one/one_auth"
|
||||
}
|
||||
|
||||
token, err := ioutil.ReadFile(oneAuthPath)
|
||||
if err == nil {
|
||||
authToken = strings.TrimSpace(string(token))
|
||||
} else {
|
||||
authToken = ""
|
||||
}
|
||||
} else {
|
||||
authToken = user + ":" + password
|
||||
}
|
||||
|
||||
if oneXmlrpc == "" {
|
||||
oneXmlrpc = os.Getenv("ONE_XMLRPC")
|
||||
if oneXmlrpc == "" {
|
||||
oneXmlrpc = "http://localhost:2633/RPC2"
|
||||
}
|
||||
}
|
||||
|
||||
config := OneConfig{
|
||||
Token: authToken,
|
||||
XmlrpcURL: oneXmlrpc,
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// SetClient assigns a value to the client variable
|
||||
func SetClient(conf OneConfig) error {
|
||||
|
||||
xmlrpcClient, xmlrpcClientError := xmlrpc.NewClient(conf.XmlrpcURL, nil)
|
||||
|
||||
client = &oneClient{
|
||||
token: conf.Token,
|
||||
xmlrpcClient: xmlrpcClient,
|
||||
xmlrpcClientError: xmlrpcClientError,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SystemVersion returns the current OpenNebula Version
|
||||
func SystemVersion() (string, error) {
|
||||
response, err := client.Call("one.system.version")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return response.Body(), nil
|
||||
}
|
||||
|
||||
// Call is an XML-RPC wrapper. It returns a pointer to response and an error.
|
||||
func (c *oneClient) Call(method string, args ...interface{}) (*response, error) {
|
||||
var (
|
||||
ok bool
|
||||
|
||||
status bool
|
||||
body string
|
||||
bodyInt int64
|
||||
)
|
||||
|
||||
if c.xmlrpcClientError != nil {
|
||||
return nil, fmt.Errorf("Unitialized client. Token: '%s', xmlrpcClient: '%s'", c.token, c.xmlrpcClientError)
|
||||
}
|
||||
|
||||
result := []interface{}{}
|
||||
|
||||
xmlArgs := make([]interface{}, len(args)+1)
|
||||
|
||||
xmlArgs[0] = c.token
|
||||
copy(xmlArgs[1:], args[:])
|
||||
|
||||
err := c.xmlrpcClient.Call(method, xmlArgs, &result)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
status, ok = result[0].(bool)
|
||||
if ok == false {
|
||||
log.Fatal("Unexpected XML-RPC response. Expected: Index 0 Boolean")
|
||||
}
|
||||
|
||||
body, ok = result[1].(string)
|
||||
if ok == false {
|
||||
bodyInt, ok = result[1].(int64)
|
||||
if ok == false {
|
||||
log.Fatal("Unexpected XML-RPC response. Expected: Index 0 Int or String")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: errCode? result[2]
|
||||
|
||||
r := &response{status, body, int(bodyInt)}
|
||||
|
||||
if status == false {
|
||||
err = errors.New(body)
|
||||
}
|
||||
|
||||
return r, err
|
||||
}
|
||||
|
||||
// Body accesses the body of the response
|
||||
func (r *response) Body() string {
|
||||
return r.body
|
||||
}
|
||||
|
||||
// BodyInt accesses the body of the response, if it's an int.
|
||||
func (r *response) BodyInt() int {
|
||||
return r.bodyInt
|
||||
}
|
99
src/oca/go/src/goca/group.go
Normal file
99
src/oca/go/src/goca/group.go
Normal file
@ -0,0 +1,99 @@
|
||||
package goca
|
||||
|
||||
// Group represents an OpenNebula Group
|
||||
type Group struct {
|
||||
XMLResource
|
||||
ID uint
|
||||
Name string
|
||||
}
|
||||
|
||||
// GroupPool represents an OpenNebula GroupPool
|
||||
type GroupPool struct {
|
||||
XMLResource
|
||||
}
|
||||
|
||||
// NewGroupPool returns a group pool. A connection to OpenNebula is
|
||||
// performed.
|
||||
func NewGroupPool() (*GroupPool, error) {
|
||||
response, err := client.Call("one.grouppool.info")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
grouppool := &GroupPool{XMLResource{body: response.Body()}}
|
||||
|
||||
return grouppool, err
|
||||
}
|
||||
|
||||
// NewGroup finds a group object by ID. No connection to OpenNebula.
|
||||
func NewGroup(id uint) *Group {
|
||||
return &Group{ID: id}
|
||||
}
|
||||
|
||||
// NewGroupFromName finds a group object by name. It connects to
|
||||
// OpenNebula to retrieve the pool, but doesn't perform the Info() call to
|
||||
// retrieve the attributes of the group.
|
||||
func NewGroupFromName(name string) (*Group, error) {
|
||||
groupPool, err := NewGroupPool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, err := groupPool.GetIDFromName(name, "/GROUP_POOL/GROUP")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewGroup(id), nil
|
||||
}
|
||||
|
||||
// CreateGroup allocates a new group. It returns the new group ID.
|
||||
func CreateGroup(name string) (uint, error) {
|
||||
response, err := client.Call("one.group.allocate", name)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint(response.BodyInt()), nil
|
||||
}
|
||||
|
||||
// Delete deletes the given group from the pool.
|
||||
func (group *Group) Delete() error {
|
||||
_, err := client.Call("one.group.delete", group.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// Info retrieves information for the group.
|
||||
func (group *Group) Info() error {
|
||||
_, err := client.Call("one.group.info", group.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// Update replaces the group template contents.
|
||||
// * tpl: The new template contents. Syntax can be the usual attribute=value or XML.
|
||||
// * appendTemplate: Update type: 0: Replace the whole template. 1: Merge new template with the existing one.
|
||||
func (group *Group) Update(tpl string, appendTemplate int) error {
|
||||
_, err := client.Call("one.group.update", group.ID, tpl, appendTemplate)
|
||||
return err
|
||||
}
|
||||
|
||||
// AddAdmin adds a User to the Group administrators set
|
||||
// * userID: The user ID.
|
||||
func (group *Group) AddAdmin(userID uint) error {
|
||||
_, err := client.Call("one.group.addadmin", group.ID, int(userID))
|
||||
return err
|
||||
}
|
||||
|
||||
// DelAdmin removes a User from the Group administrators set
|
||||
// * userID: The user ID.
|
||||
func (group *Group) DelAdmin(userID uint) error {
|
||||
_, err := client.Call("one.group.deladmin", group.ID, int(userID))
|
||||
return err
|
||||
}
|
||||
|
||||
// Quota sets the group quota limits.
|
||||
// * tpl: The new quota template contents. Syntax can be the usual attribute=value or XML.
|
||||
func (group *Group) Quota(tpl string) error {
|
||||
_, err := client.Call("one.group.quota", group.ID, tpl)
|
||||
return err
|
||||
}
|
45
src/oca/go/src/goca/helper_test.go
Normal file
45
src/oca/go/src/goca/helper_test.go
Normal file
@ -0,0 +1,45 @@
|
||||
package goca
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Extracts the ID of a resource
|
||||
func GetID(t *testing.T, r Resource, s string) (uint, error) {
|
||||
path := fmt.Sprintf("/%s/ID", s)
|
||||
|
||||
sIDFromXML, ok := r.XPath(path)
|
||||
if !ok {
|
||||
t.Error("Could not find ID")
|
||||
}
|
||||
|
||||
idFromXML, err := strconv.ParseUint(sIDFromXML, 10, strconv.IntSize)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
return uint(idFromXML), nil
|
||||
}
|
||||
|
||||
// Appends a random string to a name
|
||||
func GenName(name string) string {
|
||||
t := strconv.FormatInt(time.Now().UnixNano(), 10)
|
||||
|
||||
d := []byte(t)
|
||||
h := fmt.Sprintf("%x", md5.Sum(d))[:6]
|
||||
return name + "-" + h
|
||||
}
|
||||
|
||||
func WaitResource(f func() bool) bool {
|
||||
for i := 0; i < 20; i++ {
|
||||
if f() {
|
||||
return true
|
||||
}
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
return false
|
||||
}
|
102
src/oca/go/src/goca/host.go
Normal file
102
src/oca/go/src/goca/host.go
Normal file
@ -0,0 +1,102 @@
|
||||
package goca
|
||||
|
||||
// Host represents an OpenNebula Host
|
||||
type Host struct {
|
||||
XMLResource
|
||||
ID uint
|
||||
Name string
|
||||
}
|
||||
|
||||
// HostPool represents an OpenNebula HostPool
|
||||
type HostPool struct {
|
||||
XMLResource
|
||||
}
|
||||
|
||||
// NewHostPool returns a host pool. A connection to OpenNebula is
|
||||
// performed.
|
||||
func NewHostPool() (*HostPool, error) {
|
||||
response, err := client.Call("one.hostpool.info")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hostpool := &HostPool{XMLResource{body: response.Body()}}
|
||||
|
||||
return hostpool, err
|
||||
}
|
||||
|
||||
// NewHost finds a host object by ID. No connection to OpenNebula.
|
||||
func NewHost(id uint) *Host {
|
||||
return &Host{ID: id}
|
||||
}
|
||||
|
||||
// NewHostFromName finds a host object by name. It connects to
|
||||
// OpenNebula to retrieve the pool, but doesn't perform the Info() call to
|
||||
// retrieve the attributes of the host.
|
||||
func NewHostFromName(name string) (*Host, error) {
|
||||
hostPool, err := NewHostPool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, err := hostPool.GetIDFromName(name, "/HOST_POOL/HOST")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewHost(id), nil
|
||||
}
|
||||
|
||||
// CreateHost allocates a new host. It returns the new host ID.
|
||||
// * name: name of the host
|
||||
// * im: information driver for the host
|
||||
// * vm: virtualization driver for the host
|
||||
// * clusterID: The cluster ID. If it is -1, the default one will be used.
|
||||
func CreateHost(name, im, vm string, clusterID int) (uint, error) {
|
||||
response, err := client.Call("one.host.allocate", name, im, vm, clusterID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint(response.BodyInt()), nil
|
||||
}
|
||||
|
||||
// Delete deletes the given host from the pool
|
||||
func (host *Host) Delete() error {
|
||||
_, err := client.Call("one.host.delete", host.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// Status sets the status of the host
|
||||
// * status: 0: ENABLED, 1: DISABLED, 2: OFFLINE
|
||||
func (host *Host) Status(status int) error {
|
||||
_, err := client.Call("one.host.status", host.ID, status)
|
||||
return err
|
||||
}
|
||||
|
||||
// Update replaces the host’s template contents.
|
||||
// * tpl: The new template contents. Syntax can be the usual attribute=value or XML.
|
||||
// * appendTemplate: Update type: 0: Replace the whole template. 1: Merge new template with the existing one.
|
||||
func (host *Host) Update(tpl string, appendTemplate int) error {
|
||||
_, err := client.Call("one.host.update", host.ID, tpl, appendTemplate)
|
||||
return err
|
||||
}
|
||||
|
||||
// Rename renames a host.
|
||||
// * newName: The new name.
|
||||
func (host *Host) Rename(newName string) error {
|
||||
_, err := client.Call("one.host.rename", host.ID, newName)
|
||||
return err
|
||||
}
|
||||
|
||||
// Info retrieves information for the host.
|
||||
func (host *Host) Info() error {
|
||||
_, err := client.Call("one.host.info", host.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// Monitoring returns the host monitoring records.
|
||||
func (host *Host) Monitoring() error {
|
||||
_, err := client.Call("one.host.monitoring", host.ID)
|
||||
return err
|
||||
}
|
168
src/oca/go/src/goca/image.go
Normal file
168
src/oca/go/src/goca/image.go
Normal file
@ -0,0 +1,168 @@
|
||||
package goca
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Image represents an OpenNebula Image
|
||||
type Image struct {
|
||||
XMLResource
|
||||
ID uint
|
||||
Name string
|
||||
}
|
||||
|
||||
// ImagePool represents an OpenNebula Image pool
|
||||
type ImagePool struct {
|
||||
XMLResource
|
||||
}
|
||||
|
||||
// ImageState is the state of the Image
|
||||
type ImageState int
|
||||
|
||||
const (
|
||||
// ImageInit image is being initialized
|
||||
ImageInit ImageState = iota
|
||||
|
||||
// ImageReady image is ready to be used
|
||||
ImageReady
|
||||
|
||||
// ImageUsed image is in use
|
||||
ImageUsed
|
||||
|
||||
// ImageDisabled image is in disabled
|
||||
ImageDisabled
|
||||
|
||||
// ImageLocked image is locked
|
||||
ImageLocked
|
||||
|
||||
// ImageError image is in error state
|
||||
ImageError
|
||||
|
||||
// ImageClone image is in clone state
|
||||
ImageClone
|
||||
|
||||
// ImageDelete image is in delete state
|
||||
ImageDelete
|
||||
|
||||
// ImageLockUsed image is in locked state (non-persistent)
|
||||
ImageLockUsed
|
||||
|
||||
// ImageLockUsedPers image is in locked state (persistent)
|
||||
ImageLockUsedPers
|
||||
)
|
||||
|
||||
// String returns the string version of the ImageState
|
||||
func (s ImageState) String() string {
|
||||
return [...]string{
|
||||
"INIT",
|
||||
"READY",
|
||||
"USED",
|
||||
"DISABLED",
|
||||
"LOCKED",
|
||||
"ERROR",
|
||||
"CLONE",
|
||||
"DELETE",
|
||||
"USED_PERS",
|
||||
"LOCKED_USED",
|
||||
"LOCKED_USED_PERS",
|
||||
}[s]
|
||||
}
|
||||
|
||||
// CreateImage allocates a new image based on the template string provided. It
|
||||
// returns the image ID.
|
||||
func CreateImage(template string, dsid uint) (uint, error) {
|
||||
response, err := client.Call("one.image.allocate", template, dsid)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint(response.BodyInt()), nil
|
||||
}
|
||||
|
||||
// NewImagePool returns a new image pool. It accepts the scope of the query. It
|
||||
// performs an OpenNebula connectio to fetch the information.
|
||||
func NewImagePool(args ...int) (*ImagePool, error) {
|
||||
var who, start, end int
|
||||
|
||||
switch len(args) {
|
||||
case 0:
|
||||
who = PoolWhoMine
|
||||
start = -1
|
||||
end = -1
|
||||
case 3:
|
||||
who = args[0]
|
||||
start = args[1]
|
||||
end = args[2]
|
||||
default:
|
||||
return nil, errors.New("Wrong number of arguments")
|
||||
}
|
||||
|
||||
response, err := client.Call("one.imagepool.info", who, start, end)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
imagepool := &ImagePool{XMLResource{body: response.Body()}}
|
||||
|
||||
return imagepool, err
|
||||
|
||||
}
|
||||
|
||||
// NewImage finds an image by ID returns a new Image object. At this stage no
|
||||
// connection to OpenNebula is performed.
|
||||
func NewImage(id uint) *Image {
|
||||
return &Image{ID: id}
|
||||
}
|
||||
|
||||
// NewImageFromName finds an image by name and returns Image object. It connects
|
||||
// to OpenNebula to retrieve the pool, but doesn't perform the Info() call to
|
||||
// retrieve the attributes of the image.
|
||||
func NewImageFromName(name string) (*Image, error) {
|
||||
imagePool, err := NewImagePool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, err := imagePool.GetIDFromName(name, "/IMAGE_POOL/IMAGE")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewImage(id), nil
|
||||
}
|
||||
|
||||
// Info connects to OpenNebula and fetches the information of the Image
|
||||
func (image *Image) Info() error {
|
||||
response, err := client.Call("one.image.info", image.ID)
|
||||
image.body = response.Body()
|
||||
return err
|
||||
}
|
||||
|
||||
// State looks up the state of the image and returns the ImageState
|
||||
func (image *Image) State() (ImageState, error) {
|
||||
stateString, ok := image.XPath("/IMAGE/STATE")
|
||||
if ok != true {
|
||||
return -1, errors.New("Unable to parse Image State")
|
||||
}
|
||||
|
||||
state, _ := strconv.Atoi(stateString)
|
||||
|
||||
return ImageState(state), nil
|
||||
}
|
||||
|
||||
// StateString returns the state in string format
|
||||
func (image *Image) StateString() (string, error) {
|
||||
state, err := image.State()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return ImageState(state).String(), nil
|
||||
}
|
||||
|
||||
// Delete will remove the image from OpenNebula, which will remove it from the
|
||||
// backend.
|
||||
func (image *Image) Delete() error {
|
||||
_, err := client.Call("one.image.delete", image.ID)
|
||||
return err
|
||||
}
|
136
src/oca/go/src/goca/template.go
Normal file
136
src/oca/go/src/goca/template.go
Normal file
@ -0,0 +1,136 @@
|
||||
package goca
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Template represents an OpenNebula Template
|
||||
type Template struct {
|
||||
XMLResource
|
||||
ID uint
|
||||
Name string
|
||||
}
|
||||
|
||||
// TemplatePool represents an OpenNebula TemplatePool
|
||||
type TemplatePool struct {
|
||||
XMLResource
|
||||
}
|
||||
|
||||
// NewTemplatePool returns a template pool. A connection to OpenNebula is
|
||||
// performed.
|
||||
func NewTemplatePool(args ...int) (*TemplatePool, error) {
|
||||
var who, start, end int
|
||||
|
||||
switch len(args) {
|
||||
case 0:
|
||||
who = PoolWhoMine
|
||||
start = -1
|
||||
end = -1
|
||||
case 3:
|
||||
who = args[0]
|
||||
start = args[1]
|
||||
end = args[2]
|
||||
default:
|
||||
return nil, errors.New("Wrong number of arguments")
|
||||
}
|
||||
|
||||
response, err := client.Call("one.templatepool.info", who, start, end)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
templatepool := &TemplatePool{XMLResource{body: response.Body()}}
|
||||
|
||||
return templatepool, err
|
||||
|
||||
}
|
||||
|
||||
// NewTemplate finds a template object by ID. No connection to OpenNebula.
|
||||
func NewTemplate(id uint) *Template {
|
||||
return &Template{ID: id}
|
||||
}
|
||||
|
||||
// NewTemplateFromName finds a template object by name. It connects to
|
||||
// OpenNebula to retrieve the pool, but doesn't perform the Info() call to
|
||||
// retrieve the attributes of the template.
|
||||
func NewTemplateFromName(name string) (*Template, error) {
|
||||
templatePool, err := NewTemplatePool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, err := templatePool.GetIDFromName(name, "/VMTEMPLATE_POOL/VMTEMPLATE")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewTemplate(id), nil
|
||||
}
|
||||
|
||||
// CreateTemplate allocates a new template. It returns the new template ID.
|
||||
func CreateTemplate(template string) (uint, error) {
|
||||
response, err := client.Call("one.template.allocate", template)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint(response.BodyInt()), nil
|
||||
}
|
||||
|
||||
// Info connects to OpenNebula and fetches the information of the Template
|
||||
func (template *Template) Info() error {
|
||||
response, err := client.Call("one.template.info", template.ID)
|
||||
template.body = response.Body()
|
||||
return err
|
||||
}
|
||||
|
||||
// Update will modify the template. If appendTemplate is 0, it will
|
||||
// replace the whole template. If its 1, it will merge.
|
||||
func (template *Template) Update(tpl string, appendTemplate int) error {
|
||||
_, err := client.Call("one.template.update", template.ID, tpl, appendTemplate)
|
||||
return err
|
||||
}
|
||||
|
||||
// Chown changes the owner/group of a template. If uid or gid is -1 it will not
|
||||
// change
|
||||
func (template *Template) Chown(uid, gid int) error {
|
||||
_, err := client.Call("one.template.chown", template.ID, uid, gid)
|
||||
return err
|
||||
}
|
||||
|
||||
// Chmod changes the permissions of a template. If any perm is -1 it will not
|
||||
// change
|
||||
func (template *Template) Chmod(uu, um, ua, gu, gm, ga, ou, om, oa int) error {
|
||||
_, err := client.Call("one.template.chmod", template.ID, uu, um, ua, gu, gm, ga, ou, om, oa)
|
||||
return err
|
||||
}
|
||||
|
||||
// Rename changes the name of template
|
||||
func (template *Template) Rename(newName string) error {
|
||||
_, err := client.Call("one.template.rename", template.ID, newName)
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete will remove the template from OpenNebula.
|
||||
func (template *Template) Delete() error {
|
||||
_, err := client.Call("one.template.delete", template.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// Instantiate will instantiate the template
|
||||
func (template *Template) Instantiate(name string, pending bool, extra string) (uint, error) {
|
||||
response, err := client.Call("one.template.instantiate", template.ID, name, pending, extra)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint(response.BodyInt()), nil
|
||||
}
|
||||
|
||||
// Clone an existing template. If recursive is true it will clone the template
|
||||
// plus any image defined in DISK. The new IMAGE_ID is set into each DISK.
|
||||
func (template *Template) Clone(name string, recursive bool) error {
|
||||
_, err := client.Call("one.template.clone", template.ID, name, recursive)
|
||||
return err
|
||||
}
|
117
src/oca/go/src/goca/template_builder.go
Normal file
117
src/oca/go/src/goca/template_builder.go
Normal file
@ -0,0 +1,117 @@
|
||||
package goca
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TemplateBuilder represents an OpenNebula syntax template
|
||||
type TemplateBuilder struct {
|
||||
elements []TemplateBuilderElement
|
||||
}
|
||||
|
||||
// TemplateBuilderElement is an interface that must implement the String
|
||||
// function
|
||||
type TemplateBuilderElement interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
// TemplateBuilderPair is a key / value pair
|
||||
type TemplateBuilderPair struct {
|
||||
key string
|
||||
value string
|
||||
}
|
||||
|
||||
// TemplateBuilderVector contains an array of keyvalue pairs
|
||||
type TemplateBuilderVector struct {
|
||||
key string
|
||||
pairs []TemplateBuilderPair
|
||||
}
|
||||
|
||||
// NewTemplateBuilder returns a new TemplateBuilder object
|
||||
func NewTemplateBuilder() *TemplateBuilder {
|
||||
return &TemplateBuilder{}
|
||||
}
|
||||
|
||||
// NewVector creates a new vector in the template
|
||||
func (t *TemplateBuilder) NewVector(key string) *TemplateBuilderVector {
|
||||
vector := &TemplateBuilderVector{key: key}
|
||||
t.elements = append(t.elements, vector)
|
||||
return vector
|
||||
}
|
||||
|
||||
// String prints the TemplateBuilder in OpenNebula syntax
|
||||
func (t *TemplateBuilder) String() string {
|
||||
s := ""
|
||||
endToken := "\n"
|
||||
|
||||
for i, element := range t.elements {
|
||||
if i == len(t.elements)-1 {
|
||||
endToken = ""
|
||||
}
|
||||
s += element.String() + endToken
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// String prints a TemplateBuilderPair in OpenNebula syntax
|
||||
func (t *TemplateBuilderPair) String() string {
|
||||
return fmt.Sprintf("%s=\"%s\"", t.key, t.value)
|
||||
}
|
||||
|
||||
func (t *TemplateBuilderVector) String() string {
|
||||
s := fmt.Sprintf("%s=[\n", strings.ToUpper(t.key))
|
||||
|
||||
endToken := ",\n"
|
||||
for i, pair := range t.pairs {
|
||||
if i == len(t.pairs)-1 {
|
||||
endToken = ""
|
||||
}
|
||||
|
||||
s += fmt.Sprintf(" %s%s", pair.String(), endToken)
|
||||
|
||||
}
|
||||
s += " ]"
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// AddValue adds a new pair to a TemplateBuilder objects
|
||||
func (t *TemplateBuilder) AddValue(key string, v interface{}) error {
|
||||
var val string
|
||||
|
||||
switch v := v.(type) {
|
||||
default:
|
||||
return errors.New("Unexpected type")
|
||||
case int, uint:
|
||||
val = fmt.Sprintf("%d", v)
|
||||
case string:
|
||||
val = v
|
||||
}
|
||||
|
||||
pair := &TemplateBuilderPair{strings.ToUpper(key), val}
|
||||
t.elements = append(t.elements, pair)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddValue adds a new pair to a TemplateBuilderVector
|
||||
func (t *TemplateBuilderVector) AddValue(key string, v interface{}) error {
|
||||
var val string
|
||||
|
||||
switch v := v.(type) {
|
||||
default:
|
||||
return errors.New("Unexpected type")
|
||||
case int, uint:
|
||||
val = fmt.Sprintf("%d", v)
|
||||
case string:
|
||||
val = v
|
||||
}
|
||||
|
||||
pair := TemplateBuilderPair{strings.ToUpper(key), val}
|
||||
t.pairs = append(t.pairs, pair)
|
||||
|
||||
return nil
|
||||
}
|
36
src/oca/go/src/goca/template_builder_test.go
Normal file
36
src/oca/go/src/goca/template_builder_test.go
Normal file
@ -0,0 +1,36 @@
|
||||
package goca
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func Example() {
|
||||
template := NewTemplateBuilder()
|
||||
|
||||
// Main
|
||||
template.AddValue("cpu", 1)
|
||||
template.AddValue("memory", "64")
|
||||
template.AddValue("vcpu", "2")
|
||||
|
||||
// Disk
|
||||
vector := template.NewVector("disk")
|
||||
vector.AddValue("image_id", "119")
|
||||
vector.AddValue("dev_prefix", "vd")
|
||||
|
||||
// NIC
|
||||
vector = template.NewVector("nic")
|
||||
vector.AddValue("network_id", "3")
|
||||
vector.AddValue("model", "virtio")
|
||||
|
||||
fmt.Println(template)
|
||||
// Output:
|
||||
// CPU="1"
|
||||
// MEMORY="64"
|
||||
// VCPU="2"
|
||||
// DISK=[
|
||||
// IMAGE_ID="119",
|
||||
// DEV_PREFIX="vd" ]
|
||||
// NIC=[
|
||||
// NETWORK_ID="3",
|
||||
// MODEL="virtio" ]
|
||||
}
|
132
src/oca/go/src/goca/template_test.go
Normal file
132
src/oca/go/src/goca/template_test.go
Normal file
@ -0,0 +1,132 @@
|
||||
package goca
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Helper to create a template
|
||||
func createTemplate(t *testing.T) *Template {
|
||||
templateName := GenName("template")
|
||||
|
||||
// Create template
|
||||
tpl := NewTemplateBuilder()
|
||||
|
||||
tpl.AddValue("name", templateName)
|
||||
tpl.AddValue("cpu", 1)
|
||||
tpl.AddValue("memory", "64")
|
||||
|
||||
id, err := CreateTemplate(tpl.String())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// Get template by ID
|
||||
template := NewTemplate(id)
|
||||
|
||||
err = template.Info()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
return template
|
||||
}
|
||||
|
||||
func TestTemplateCreateAndDelete(t *testing.T) {
|
||||
template := createTemplate(t)
|
||||
|
||||
idParse, err := GetID(t, template, "VMTEMPLATE")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if idParse != template.ID {
|
||||
t.Errorf("Template ID does not match")
|
||||
}
|
||||
|
||||
// Get template by Name
|
||||
templateName, ok := template.XPath("/VMTEMPLATE/NAME")
|
||||
if !ok {
|
||||
t.Errorf("Could not get name")
|
||||
}
|
||||
|
||||
template, err = NewTemplateFromName(templateName)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
err = template.Info()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
idParse, err = GetID(t, template, "VMTEMPLATE")
|
||||
|
||||
if idParse != template.ID {
|
||||
t.Errorf("Template ID does not match")
|
||||
}
|
||||
|
||||
// Delete template
|
||||
err = template.Delete()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplateInstantiate(t *testing.T) {
|
||||
templateName := GenName("template")
|
||||
|
||||
// Create template
|
||||
tpl := NewTemplateBuilder()
|
||||
|
||||
tpl.AddValue("name", templateName)
|
||||
tpl.AddValue("cpu", 1)
|
||||
tpl.AddValue("memory", "64")
|
||||
|
||||
id, err := CreateTemplate(tpl.String())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// Get template by ID
|
||||
template := NewTemplate(id)
|
||||
|
||||
// Instantiate(name string, pending bool, extra string) (uint, error)
|
||||
vmid, err := template.Instantiate("", false, "")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
vm := NewVM(vmid)
|
||||
vm.Terminate()
|
||||
|
||||
// Delete template
|
||||
err = template.Delete()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplateUpdate(t *testing.T) {
|
||||
template := createTemplate(t)
|
||||
|
||||
tpl := NewTemplateBuilder()
|
||||
tpl.AddValue("A", "B")
|
||||
|
||||
// Update
|
||||
template.Update(tpl.String(), 1)
|
||||
|
||||
err := template.Info()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if val, ok := template.XPath("/VMTEMPLATE/TEMPLATE/A"); !ok || val != "B" {
|
||||
t.Errorf("Expecting A=B")
|
||||
}
|
||||
|
||||
// Delete template
|
||||
err = template.Delete()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
134
src/oca/go/src/goca/user.go
Normal file
134
src/oca/go/src/goca/user.go
Normal file
@ -0,0 +1,134 @@
|
||||
package goca
|
||||
|
||||
// User represents an OpenNebula User
|
||||
type User struct {
|
||||
XMLResource
|
||||
ID uint
|
||||
Name string
|
||||
}
|
||||
|
||||
// UserPool represents an OpenNebula UserPool
|
||||
type UserPool struct {
|
||||
XMLResource
|
||||
}
|
||||
|
||||
// NewUserPool returns a user pool. A connection to OpenNebula is
|
||||
// performed.
|
||||
func NewUserPool() (*UserPool, error) {
|
||||
response, err := client.Call("one.userpool.info")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userpool := &UserPool{XMLResource{body: response.Body()}}
|
||||
|
||||
return userpool, err
|
||||
}
|
||||
|
||||
// NewUser finds a user object by ID. No connection to OpenNebula.
|
||||
func NewUser(id uint) *User {
|
||||
return &User{ID: id}
|
||||
}
|
||||
|
||||
// NewUserFromName finds a user object by name. It connects to
|
||||
// OpenNebula to retrieve the pool, but doesn't perform the Info() call to
|
||||
// retrieve the attributes of the user.
|
||||
func NewUserFromName(name string) (*User, error) {
|
||||
userPool, err := NewUserPool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, err := userPool.GetIDFromName(name, "/USER_POOL/USER")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewUser(id), nil
|
||||
}
|
||||
|
||||
// CreateUser allocates a new user. It returns the new user ID.
|
||||
// * name: name of the user
|
||||
// * password: password of the user
|
||||
// * authDriver: auth driver
|
||||
// * groupIDs: array of groupIDs to add to the user
|
||||
func CreateUser(name, password, authDriver string, groupIDs []uint) (uint, error) {
|
||||
response, err := client.Call("one.user.allocate", name, password, authDriver, groupIDs)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint(response.BodyInt()), nil
|
||||
}
|
||||
|
||||
// Delete deletes the given user from the pool.
|
||||
func (user *User) Delete() error {
|
||||
_, err := client.Call("one.user.delete", user.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// Passwd changes the password for the given user.
|
||||
// * password: The new password
|
||||
func (user *User) Passwd(password string) error {
|
||||
_, err := client.Call("one.user.passwd", user.ID, password)
|
||||
return err
|
||||
}
|
||||
|
||||
// Login generates or sets a login token.
|
||||
// * token: The token
|
||||
// * timeSeconds: Valid period in seconds; 0 reset the token and -1 for a non-expiring token.
|
||||
// * effectiveGID: Effective GID to use with this token. To use the current GID and user groups set it to -1
|
||||
func (user *User) Login(token string, timeSeconds int, effectiveGID uint) error {
|
||||
_, err := client.Call("one.user.login", user.ID, token, timeSeconds, int(effectiveGID))
|
||||
return err
|
||||
}
|
||||
|
||||
// Update replaces the user template contents.
|
||||
// * tpl: The new template contents. Syntax can be the usual attribute=value or XML.
|
||||
// * appendTemplate: Update type: 0: Replace the whole template. 1: Merge new template with the existing one.
|
||||
func (user *User) Update(tpl string, appendTemplate int) error {
|
||||
_, err := client.Call("one.user.update", user.ID, tpl, appendTemplate)
|
||||
return err
|
||||
}
|
||||
|
||||
// Chauth changes the authentication driver and the password for the given user.
|
||||
// * authDriver: The new authentication driver.
|
||||
// * password: The new password. If it is an empty string
|
||||
func (user *User) Chauth(authDriver, password string) error {
|
||||
_, err := client.Call("one.user.chauth", user.ID, authDriver, password)
|
||||
return err
|
||||
}
|
||||
|
||||
// Quota sets the user quota limits.
|
||||
// * tpl: The new quota template contents. Syntax can be the usual attribute=value or XML.
|
||||
func (user *User) Quota(tpl string) error {
|
||||
_, err := client.Call("one.user.quota", user.ID, tpl)
|
||||
return err
|
||||
}
|
||||
|
||||
// Chgrp changes the group of the given user.
|
||||
// * groupID: The Group ID of the new group.
|
||||
func (user *User) Chgrp(groupID uint) error {
|
||||
_, err := client.Call("one.user.chgrp", user.ID, int(groupID))
|
||||
return err
|
||||
}
|
||||
|
||||
// AddGroup adds the User to a secondary group.
|
||||
// * groupID: The Group ID of the new group.
|
||||
func (user *User) AddGroup(groupID uint) error {
|
||||
_, err := client.Call("one.user.addgroup", user.ID, int(groupID))
|
||||
return err
|
||||
}
|
||||
|
||||
// DelGroup removes the User from a secondary group
|
||||
// * groupID: The Group ID.
|
||||
func (user *User) DelGroup(groupID uint) error {
|
||||
_, err := client.Call("one.user.delgroup", user.ID, int(groupID))
|
||||
return err
|
||||
}
|
||||
|
||||
// Info retrieves information for the user.
|
||||
func (user *User) Info() error {
|
||||
_, err := client.Call("one.user.info", user.ID)
|
||||
return err
|
||||
}
|
167
src/oca/go/src/goca/vdc.go
Normal file
167
src/oca/go/src/goca/vdc.go
Normal file
@ -0,0 +1,167 @@
|
||||
package goca
|
||||
|
||||
// Vdc represents an OpenNebula Vdc
|
||||
type Vdc struct {
|
||||
XMLResource
|
||||
ID uint
|
||||
Name string
|
||||
}
|
||||
|
||||
// VdcPool represents an OpenNebula VdcPool
|
||||
type VdcPool struct {
|
||||
XMLResource
|
||||
}
|
||||
|
||||
// NewVdcPool returns a vdc pool. A connection to OpenNebula is
|
||||
// performed.
|
||||
func NewVdcPool() (*VdcPool, error) {
|
||||
response, err := client.Call("one.vdcpool.info")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vdcpool := &VdcPool{XMLResource{body: response.Body()}}
|
||||
|
||||
return vdcpool, err
|
||||
}
|
||||
|
||||
// NewVdc finds a vdc object by ID. No connection to OpenNebula.
|
||||
func NewVdc(id uint) *Vdc {
|
||||
return &Vdc{ID: id}
|
||||
}
|
||||
|
||||
// NewVdcFromName finds a vdc object by name. It connects to
|
||||
// OpenNebula to retrieve the pool, but doesn't perform the Info() call to
|
||||
// retrieve the attributes of the vdc.
|
||||
func NewVdcFromName(name string) (*Vdc, error) {
|
||||
vdcPool, err := NewVdcPool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, err := vdcPool.GetIDFromName(name, "/VDC_POOL/VDC")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewVdc(id), nil
|
||||
}
|
||||
|
||||
// CreateVdc allocates a new vdc. It returns the new vdc ID.
|
||||
// * tpl: A string containing the template of the VDC. Syntax can be the usual
|
||||
// attribute=value or XML.
|
||||
// * clusterID: The cluster ID. If it is -1, this virtual network won’t be added
|
||||
// to any cluster
|
||||
func CreateVdc(tpl string, clusterID int) (uint, error) {
|
||||
response, err := client.Call("one.vdc.allocate", tpl, clusterID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint(response.BodyInt()), nil
|
||||
}
|
||||
|
||||
// Delete deletes the given VDC from the pool.
|
||||
func (vdc *Vdc) Delete() error {
|
||||
_, err := client.Call("one.vdc.delete", vdc.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// Update replaces the VDC template contents.
|
||||
// * tpl: The new template contents. Syntax can be the usual attribute=value or XML.
|
||||
// * appendTemplate: Update type: 0: Replace the whole template. 1: Merge new template with the existing one.
|
||||
func (vdc *Vdc) Update(tpl string, appendTemplate int) error {
|
||||
_, err := client.Call("one.vdc.update", vdc.ID, tpl, appendTemplate)
|
||||
return err
|
||||
}
|
||||
|
||||
// Rename renames a VDC.
|
||||
// * newName: The new name.
|
||||
func (vdc *Vdc) Rename(newName string) error {
|
||||
_, err := client.Call("one.vdc.rename", vdc.ID, newName)
|
||||
return err
|
||||
}
|
||||
|
||||
// Info retrieves information for the VDC.
|
||||
func (vdc *Vdc) Info() error {
|
||||
_, err := client.Call("one.vdc.info", vdc.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// AddGroup adds a group to the VDC
|
||||
// * groupID: The group ID.
|
||||
func (vdc *Vdc) AddGroup(groupID uint) error {
|
||||
_, err := client.Call("one.vdc.addgroup", vdc.ID, int(groupID))
|
||||
return err
|
||||
}
|
||||
|
||||
// DelGroup deletes a group from the VDC
|
||||
// * groupID: The group ID.
|
||||
func (vdc *Vdc) DelGroup(groupID uint) error {
|
||||
_, err := client.Call("one.vdc.delgroup", vdc.ID, int(groupID))
|
||||
return err
|
||||
}
|
||||
|
||||
// AddCluster adds a cluster to the VDC
|
||||
// * zoneID: The Zone ID.
|
||||
// * clusterID: The Cluster ID.
|
||||
func (vdc *Vdc) AddCluster(zoneID, clusterID uint) error {
|
||||
_, err := client.Call("one.vdc.addcluster", vdc.ID, int(zoneID), int(clusterID))
|
||||
return err
|
||||
}
|
||||
|
||||
// DelCluster deletes a cluster from the VDC
|
||||
// * zoneID: The Zone ID.
|
||||
// * clusterID: The Cluster ID.
|
||||
func (vdc *Vdc) DelCluster(zoneID, clusterID uint) error {
|
||||
_, err := client.Call("one.vdc.delcluster", vdc.ID, int(zoneID), int(clusterID))
|
||||
return err
|
||||
}
|
||||
|
||||
// AddHost adds a host to the VDC
|
||||
// * zoneID: The Zone ID.
|
||||
// * hostID: The Host ID.
|
||||
func (vdc *Vdc) AddHost(zoneID, hostID uint) error {
|
||||
_, err := client.Call("one.vdc.addhost", vdc.ID, int(zoneID), int(hostID))
|
||||
return err
|
||||
}
|
||||
|
||||
// DelHost deletes a host from the VDC
|
||||
// * zoneID: The Zone ID.
|
||||
// * hostID: The Host ID.
|
||||
func (vdc *Vdc) DelHost(zoneID, hostID uint) error {
|
||||
_, err := client.Call("one.vdc.delhost", vdc.ID, int(zoneID), int(hostID))
|
||||
return err
|
||||
}
|
||||
|
||||
// AddDatastore adds a datastore to the VDC
|
||||
// * zoneID: The Zone ID.
|
||||
// * dsID: The Datastore ID.
|
||||
func (vdc *Vdc) AddDatastore(zoneID, dsID uint) error {
|
||||
_, err := client.Call("one.vdc.adddatastore", vdc.ID, int(zoneID), int(dsID))
|
||||
return err
|
||||
}
|
||||
|
||||
// DelDatastore deletes a datastore from the VDC
|
||||
// * zoneID: The Zone ID.
|
||||
// * dsID: The Datastore ID.
|
||||
func (vdc *Vdc) DelDatastore(zoneID, dsID uint) error {
|
||||
_, err := client.Call("one.vdc.deldatastore", vdc.ID, int(zoneID), int(dsID))
|
||||
return err
|
||||
}
|
||||
|
||||
// AddVnet adds a vnet to the VDC
|
||||
// * zoneID: The Zone ID.
|
||||
// * vnetID: The Vnet ID.
|
||||
func (vdc *Vdc) AddVnet(zoneID, vnetID uint) error {
|
||||
_, err := client.Call("one.vdc.addvnet", vdc.ID, int(zoneID), int(vnetID))
|
||||
return err
|
||||
}
|
||||
|
||||
// DelVnet deletes a vnet from the VDC
|
||||
// * zoneID: The Zone ID.
|
||||
// * vnetID: The Vnet ID.
|
||||
func (vdc *Vdc) DelVnet(zoneID, vnetID uint) error {
|
||||
_, err := client.Call("one.vdc.delvnet", vdc.ID, int(zoneID), int(vnetID))
|
||||
return err
|
||||
}
|
13
src/oca/go/src/goca/version.go
Normal file
13
src/oca/go/src/goca/version.go
Normal file
@ -0,0 +1,13 @@
|
||||
package goca
|
||||
|
||||
// GitCommit is the git commit that was compiled. This will be filled in by the
|
||||
// compiler.
|
||||
var GitCommit string
|
||||
|
||||
// Version is the main version number that is being run at the moment.
|
||||
const Version = "0.1.0"
|
||||
|
||||
// VersionPrerelease is the pre-release marker for the version. If this is ""
|
||||
// (empty string) then it means that it is a final release. Otherwise, this is a
|
||||
// pre-release such as "dev" (in development)
|
||||
var VersionPrerelease = ""
|
180
src/oca/go/src/goca/virtualnetwork.go
Normal file
180
src/oca/go/src/goca/virtualnetwork.go
Normal file
@ -0,0 +1,180 @@
|
||||
package goca
|
||||
|
||||
import "errors"
|
||||
|
||||
// VirtualNetwork represents an OpenNebula VirtualNetwork
|
||||
type VirtualNetwork struct {
|
||||
XMLResource
|
||||
ID uint
|
||||
Name string
|
||||
}
|
||||
|
||||
// VirtualNetworkPool represents an OpenNebula VirtualNetworkPool
|
||||
type VirtualNetworkPool struct {
|
||||
XMLResource
|
||||
}
|
||||
|
||||
// NewVirtualNetworkPool returns a virtualnetwork pool. A connection to OpenNebula is
|
||||
// performed.
|
||||
func NewVirtualNetworkPool(args ...int) (*VirtualNetworkPool, error) {
|
||||
var who, start, end int
|
||||
|
||||
switch len(args) {
|
||||
case 0:
|
||||
who = PoolWhoMine
|
||||
start = -1
|
||||
end = -1
|
||||
case 1:
|
||||
who = args[0]
|
||||
start = -1
|
||||
end = -1
|
||||
case 3:
|
||||
who = args[0]
|
||||
start = args[1]
|
||||
end = args[2]
|
||||
default:
|
||||
return nil, errors.New("Wrong number of arguments")
|
||||
}
|
||||
|
||||
response, err := client.Call("one.vnpool.info", who, start, end)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vnpool := &VirtualNetworkPool{XMLResource{body: response.Body()}}
|
||||
|
||||
return vnpool, err
|
||||
}
|
||||
|
||||
// NewVirtualNetwork finds a virtualnetwork object by ID. No connection to OpenNebula.
|
||||
func NewVirtualNetwork(id uint) *VirtualNetwork {
|
||||
return &VirtualNetwork{ID: id}
|
||||
}
|
||||
|
||||
// NewVirtualNetworkFromName finds a virtualnetwork object by name. It connects to
|
||||
// OpenNebula to retrieve the pool, but doesn't perform the Info() call to
|
||||
// retrieve the attributes of the virtualnetwork.
|
||||
func NewVirtualNetworkFromName(name string) (*VirtualNetwork, error) {
|
||||
virtualnetworkPool, err := NewVirtualNetworkPool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, err := virtualnetworkPool.GetIDFromName(name, "/VNET_POOL/VNET")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewVirtualNetwork(id), nil
|
||||
}
|
||||
|
||||
// CreateVirtualnetwork allocates a new virtualnetwork. It returns the new virtualnetwork ID.
|
||||
// * tpl: template of the virtualnetwork
|
||||
// * clusterID: The cluster ID. If it is -1, the default one will be used.
|
||||
func CreateVirtualnetwork(tpl string, clusterID int) (uint, error) {
|
||||
response, err := client.Call("one.vn.allocate", tpl, clusterID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint(response.BodyInt()), nil
|
||||
}
|
||||
|
||||
// Delete deletes the given virtual network from the pool.
|
||||
func (vn *VirtualNetwork) Delete() error {
|
||||
_, err := client.Call("one.vn.delete", vn.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// AddAr adds address ranges to a virtual network.
|
||||
// * tpl: template of the address ranges to add. Syntax can be the usual attribute=value or XML
|
||||
func (vn *VirtualNetwork) AddAr(tpl string) error {
|
||||
_, err := client.Call("one.vn.add_ar", vn.ID, tpl)
|
||||
return err
|
||||
}
|
||||
|
||||
// RmAr removes an address range from a virtual network.
|
||||
// * arID: ID of the address range to remove.
|
||||
func (vn *VirtualNetwork) RmAr(arID int) error {
|
||||
_, err := client.Call("one.vn.rm_ar", vn.ID, arID)
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateAr updates the attributes of an address range.
|
||||
// * tpl: template of the address ranges to update. Syntax can be the usual attribute=value or XML
|
||||
func (vn *VirtualNetwork) UpdateAr(tpl string) error {
|
||||
_, err := client.Call("one.vn.update_ar", vn.ID, tpl)
|
||||
return err
|
||||
}
|
||||
|
||||
// Reserve reserve network addresses.
|
||||
// * tpl: Template
|
||||
func (vn *VirtualNetwork) Reserve(tpl string) error {
|
||||
_, err := client.Call("one.vn.reserve", vn.ID, tpl)
|
||||
return err
|
||||
}
|
||||
|
||||
// FreeAr frees a reserved address range from a virtual network.
|
||||
// * arID: ID of the address range to free.
|
||||
func (vn *VirtualNetwork) FreeAr(arID int) error {
|
||||
_, err := client.Call("one.vn.free_ar", vn.ID, arID)
|
||||
return err
|
||||
}
|
||||
|
||||
// Hold holds a virtual network Lease as used.
|
||||
// * tpl: template of the lease to hold
|
||||
func (vn *VirtualNetwork) Hold(tpl string) error {
|
||||
_, err := client.Call("one.vn.hold", vn.ID, tpl)
|
||||
return err
|
||||
}
|
||||
|
||||
// Release releases a virtual network Lease on hold.
|
||||
// * tpl: template of the lease to release
|
||||
func (vn *VirtualNetwork) Release(tpl string) error {
|
||||
_, err := client.Call("one.vn.release", vn.ID, tpl)
|
||||
return err
|
||||
}
|
||||
|
||||
// Update replaces the virtual network template contents.
|
||||
// * tpl: The new template contents. Syntax can be the usual attribute=value or XML.
|
||||
// * appendTemplate: Update type: 0: Replace the whole template. 1: Merge new template with the existing one.
|
||||
func (vn *VirtualNetwork) Update(tpl string, appendTemplate int) error {
|
||||
_, err := client.Call("one.vn.update", vn.ID, tpl, appendTemplate)
|
||||
return err
|
||||
}
|
||||
|
||||
// Chmod changes the permission bits of a virtual network.
|
||||
// * uu: USER USE bit. If set to -1, it will not change.
|
||||
// * um: USER MANAGE bit. If set to -1, it will not change.
|
||||
// * ua: USER ADMIN bit. If set to -1, it will not change.
|
||||
// * gu: GROUP USE bit. If set to -1, it will not change.
|
||||
// * gm: GROUP MANAGE bit. If set to -1, it will not change.
|
||||
// * ga: GROUP ADMIN bit. If set to -1, it will not change.
|
||||
// * ou: OTHER USE bit. If set to -1, it will not change.
|
||||
// * om: OTHER MANAGE bit. If set to -1, it will not change.
|
||||
// * oa: OTHER ADMIN bit. If set to -1, it will not change.
|
||||
func (vn *VirtualNetwork) Chmod(uu, um, ua, gu, gm, ga, ou, om, oa int) error {
|
||||
_, err := client.Call("one.vn.chmod", vn.ID, uu, um, ua, gu, gm, ga, ou, om, oa)
|
||||
return err
|
||||
}
|
||||
|
||||
// Chown changes the ownership of a virtual network.
|
||||
// * userID: The User ID of the new owner. If set to -1, it will not change.
|
||||
// * groupID: The Group ID of the new group. If set to -1, it will not change.
|
||||
func (vn *VirtualNetwork) Chown(userID, groupID uint) error {
|
||||
_, err := client.Call("one.vn.chown", vn.ID, int(userID), int(groupID))
|
||||
return err
|
||||
}
|
||||
|
||||
// Rename renames a virtual network.
|
||||
// * newName: The new name.
|
||||
func (vn *VirtualNetwork) Rename(newName string) error {
|
||||
_, err := client.Call("one.vn.rename", vn.ID, newName)
|
||||
return err
|
||||
}
|
||||
|
||||
// Info retrieves information for the virtual network.
|
||||
func (vn *VirtualNetwork) Info() error {
|
||||
_, err := client.Call("one.vn.info", vn.ID)
|
||||
return err
|
||||
}
|
840
src/oca/go/src/goca/vm.go
Normal file
840
src/oca/go/src/goca/vm.go
Normal file
@ -0,0 +1,840 @@
|
||||
package goca
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// VM represents an OpenNebula Virtual Machine
|
||||
type VM struct {
|
||||
XMLResource
|
||||
ID uint
|
||||
Name string
|
||||
}
|
||||
|
||||
// VMPool represents an OpenNebula Virtual Machine pool
|
||||
type VMPool struct {
|
||||
XMLResource
|
||||
}
|
||||
|
||||
// VMState is the state of the Virtual Machine
|
||||
type VMState int
|
||||
|
||||
const (
|
||||
// Init state
|
||||
Init VMState = 0
|
||||
|
||||
// Pending state
|
||||
Pending VMState = 1
|
||||
|
||||
// Hold state
|
||||
Hold VMState = 2
|
||||
|
||||
// Active state
|
||||
Active VMState = 3
|
||||
|
||||
// Stopped state
|
||||
Stopped VMState = 4
|
||||
|
||||
// Suspended state
|
||||
Suspended VMState = 5
|
||||
|
||||
// Done state
|
||||
Done VMState = 6
|
||||
|
||||
// Deprecated
|
||||
// Failed VMState = 7
|
||||
|
||||
// Poweroff state
|
||||
Poweroff VMState = 8
|
||||
|
||||
// Undeployed state
|
||||
Undeployed VMState = 9
|
||||
|
||||
// Cloning state
|
||||
Cloning VMState = 10
|
||||
|
||||
// CloningFailure state
|
||||
CloningFailure VMState = 11
|
||||
)
|
||||
|
||||
func (s VMState) String() string {
|
||||
switch s {
|
||||
case Init:
|
||||
return "INIT"
|
||||
case Pending:
|
||||
return "PENDING"
|
||||
case Hold:
|
||||
return "HOLD"
|
||||
case Active:
|
||||
return "ACTIVE"
|
||||
case Stopped:
|
||||
return "STOPPED"
|
||||
case Suspended:
|
||||
return "SUSPENDED"
|
||||
case Done:
|
||||
return "DONE"
|
||||
case Poweroff:
|
||||
return "POWEROFF"
|
||||
case Undeployed:
|
||||
return "UNDEPLOYED"
|
||||
case Cloning:
|
||||
return "CLONING"
|
||||
case CloningFailure:
|
||||
return "CLONINGFAILURE"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// LCMState is the life-cycle manager state of the virtual machine. It is used
|
||||
// only when the VM's state is active, otherwise it's LcmInit
|
||||
type LCMState int
|
||||
|
||||
const (
|
||||
// LcmInit lcm state
|
||||
LcmInit LCMState = 0
|
||||
|
||||
// Prolog lcm state
|
||||
Prolog LCMState = 1
|
||||
|
||||
// Boot lcm state
|
||||
Boot LCMState = 2
|
||||
|
||||
// Running lcm state
|
||||
Running LCMState = 3
|
||||
|
||||
// Migrate lcm state
|
||||
Migrate LCMState = 4
|
||||
|
||||
// SaveStop lcm state
|
||||
SaveStop LCMState = 5
|
||||
|
||||
// SaveSuspend lcm state
|
||||
SaveSuspend LCMState = 6
|
||||
|
||||
// SaveMigrate lcm state
|
||||
SaveMigrate LCMState = 7
|
||||
|
||||
// PrologMigrate lcm state
|
||||
PrologMigrate LCMState = 8
|
||||
|
||||
// PrologResume lcm state
|
||||
PrologResume LCMState = 9
|
||||
|
||||
// EpilogStop lcm state
|
||||
EpilogStop LCMState = 10
|
||||
|
||||
// Epilog lcm state
|
||||
Epilog LCMState = 11
|
||||
|
||||
// Shutdown lcm state
|
||||
Shutdown LCMState = 12
|
||||
|
||||
// Deprecated
|
||||
// Cancel LCMState = 13
|
||||
// Failure LCMState = 14
|
||||
|
||||
// CleanupResubmit lcm state
|
||||
CleanupResubmit LCMState = 15
|
||||
|
||||
// Unknown lcm state
|
||||
Unknown LCMState = 16
|
||||
|
||||
// Hotplug lcm state
|
||||
Hotplug LCMState = 17
|
||||
|
||||
// ShutdownPoweroff lcm state
|
||||
ShutdownPoweroff LCMState = 18
|
||||
|
||||
// BootUnknown lcm state
|
||||
BootUnknown LCMState = 19
|
||||
|
||||
// BootPoweroff lcm state
|
||||
BootPoweroff LCMState = 20
|
||||
|
||||
// BootSuspended lcm state
|
||||
BootSuspended LCMState = 21
|
||||
|
||||
// BootStopped lcm state
|
||||
BootStopped LCMState = 22
|
||||
|
||||
// CleanupDelete lcm state
|
||||
CleanupDelete LCMState = 23
|
||||
|
||||
// HotplugSnapshot lcm state
|
||||
HotplugSnapshot LCMState = 24
|
||||
|
||||
// HotplugNic lcm state
|
||||
HotplugNic LCMState = 25
|
||||
|
||||
// HotplugSaveas lcm state
|
||||
HotplugSaveas LCMState = 26
|
||||
|
||||
// HotplugSaveasPoweroff lcm state
|
||||
HotplugSaveasPoweroff LCMState = 27
|
||||
|
||||
// HotplugSaveasSuspended lcm state
|
||||
HotplugSaveasSuspended LCMState = 28
|
||||
|
||||
// ShutdownUndeploy lcm state
|
||||
ShutdownUndeploy LCMState = 29
|
||||
|
||||
// EpilogUndeploy lcm state
|
||||
EpilogUndeploy LCMState = 30
|
||||
|
||||
// PrologUndeploy lcm state
|
||||
PrologUndeploy LCMState = 31
|
||||
|
||||
// BootUndeploy lcm state
|
||||
BootUndeploy LCMState = 32
|
||||
|
||||
// HotplugPrologPoweroff lcm state
|
||||
HotplugPrologPoweroff LCMState = 33
|
||||
|
||||
// HotplugEpilogPoweroff lcm state
|
||||
HotplugEpilogPoweroff LCMState = 34
|
||||
|
||||
// BootMigrate lcm state
|
||||
BootMigrate LCMState = 35
|
||||
|
||||
// BootFailure lcm state
|
||||
BootFailure LCMState = 36
|
||||
|
||||
// BootMigrateFailure lcm state
|
||||
BootMigrateFailure LCMState = 37
|
||||
|
||||
// PrologMigrateFailure lcm state
|
||||
PrologMigrateFailure LCMState = 38
|
||||
|
||||
// PrologFailure lcm state
|
||||
PrologFailure LCMState = 39
|
||||
|
||||
// EpilogFailure lcm state
|
||||
EpilogFailure LCMState = 40
|
||||
|
||||
// EpilogStopFailure lcm state
|
||||
EpilogStopFailure LCMState = 41
|
||||
|
||||
// EpilogUndeployFailure lcm state
|
||||
EpilogUndeployFailure LCMState = 42
|
||||
|
||||
// PrologMigratePoweroff lcm state
|
||||
PrologMigratePoweroff LCMState = 43
|
||||
|
||||
// PrologMigratePoweroffFailure lcm state
|
||||
PrologMigratePoweroffFailure LCMState = 44
|
||||
|
||||
// PrologMigrateSuspend lcm state
|
||||
PrologMigrateSuspend LCMState = 45
|
||||
|
||||
// PrologMigrateSuspendFailure lcm state
|
||||
PrologMigrateSuspendFailure LCMState = 46
|
||||
|
||||
// BootUndeployFailure lcm state
|
||||
BootUndeployFailure LCMState = 47
|
||||
|
||||
// BootStoppedFailure lcm state
|
||||
BootStoppedFailure LCMState = 48
|
||||
|
||||
// PrologResumeFailure lcm state
|
||||
PrologResumeFailure LCMState = 49
|
||||
|
||||
// PrologUndeployFailure lcm state
|
||||
PrologUndeployFailure LCMState = 50
|
||||
|
||||
// DiskSnapshotPoweroff lcm state
|
||||
DiskSnapshotPoweroff LCMState = 51
|
||||
|
||||
// DiskSnapshotRevertPoweroff lcm state
|
||||
DiskSnapshotRevertPoweroff LCMState = 52
|
||||
|
||||
// DiskSnapshotDeletePoweroff lcm state
|
||||
DiskSnapshotDeletePoweroff LCMState = 53
|
||||
|
||||
// DiskSnapshotSuspended lcm state
|
||||
DiskSnapshotSuspended LCMState = 54
|
||||
|
||||
// DiskSnapshotRevertSuspended lcm state
|
||||
DiskSnapshotRevertSuspended LCMState = 55
|
||||
|
||||
// DiskSnapshotDeleteSuspended lcm state
|
||||
DiskSnapshotDeleteSuspended LCMState = 56
|
||||
|
||||
// DiskSnapshot lcm state
|
||||
DiskSnapshot LCMState = 57
|
||||
|
||||
// Deprecated
|
||||
// DiskSnapshotRevert LCMState = 58
|
||||
|
||||
// DiskSnapshotDelete lcm state
|
||||
DiskSnapshotDelete LCMState = 59
|
||||
|
||||
// PrologMigrateUnknown lcm state
|
||||
PrologMigrateUnknown LCMState = 60
|
||||
|
||||
// PrologMigrateUnknownFailure lcm state
|
||||
PrologMigrateUnknownFailure LCMState = 61
|
||||
|
||||
// DiskResize lcm state
|
||||
DiskResize LCMState = 62
|
||||
|
||||
// DiskResizePoweroff lcm state
|
||||
DiskResizePoweroff LCMState = 63
|
||||
|
||||
// DiskResizeUndeployed lcm state
|
||||
DiskResizeUndeployed LCMState = 64
|
||||
)
|
||||
|
||||
func (l LCMState) String() string {
|
||||
switch l {
|
||||
case LcmInit:
|
||||
return "LCM_INIT"
|
||||
case Prolog:
|
||||
return "PROLOG"
|
||||
case Boot:
|
||||
return "BOOT"
|
||||
case Running:
|
||||
return "RUNNING"
|
||||
case Migrate:
|
||||
return "MIGRATE"
|
||||
case SaveStop:
|
||||
return "SAVE_STOP"
|
||||
case SaveSuspend:
|
||||
return "SAVESuspend"
|
||||
case SaveMigrate:
|
||||
return "SAVE_MIGRATE"
|
||||
case PrologMigrate:
|
||||
return "PROLOG_MIGRATE"
|
||||
case PrologResume:
|
||||
return "PROLOG_RESUME"
|
||||
case EpilogStop:
|
||||
return "EPILOG_STOP"
|
||||
case Epilog:
|
||||
return "EPILOG"
|
||||
case Shutdown:
|
||||
return "SHUTDOWN"
|
||||
case CleanupResubmit:
|
||||
return "CLEANUP_RESUBMIT"
|
||||
case Unknown:
|
||||
return "UNKNOWN"
|
||||
case Hotplug:
|
||||
return "HOTPLUG"
|
||||
case ShutdownPoweroff:
|
||||
return "SHUTDOWN_POWEROFF"
|
||||
case BootUnknown:
|
||||
return "BOOT_UNKNOWN"
|
||||
case BootPoweroff:
|
||||
return "BOOT_POWEROFF"
|
||||
case BootSuspended:
|
||||
return "BOOTSuspendED"
|
||||
case BootStopped:
|
||||
return "BOOT_STOPPED"
|
||||
case CleanupDelete:
|
||||
return "CLEANUP_DELETE"
|
||||
case HotplugSnapshot:
|
||||
return "HOTPLUG_SNAPSHOT"
|
||||
case HotplugNic:
|
||||
return "HOTPLUG_NIC"
|
||||
case HotplugSaveas:
|
||||
return "HOTPLUG_SAVEAS"
|
||||
case HotplugSaveasPoweroff:
|
||||
return "HOTPLUG_SAVEAS_POWEROFF"
|
||||
case HotplugSaveasSuspended:
|
||||
return "HOTPLUG_SAVEASSuspendED"
|
||||
case ShutdownUndeploy:
|
||||
return "SHUTDOWN_UNDEPLOY"
|
||||
case EpilogUndeploy:
|
||||
return "EPILOG_UNDEPLOY"
|
||||
case PrologUndeploy:
|
||||
return "PROLOG_UNDEPLOY"
|
||||
case BootUndeploy:
|
||||
return "BOOT_UNDEPLOY"
|
||||
case HotplugPrologPoweroff:
|
||||
return "HOTPLUG_PROLOG_POWEROFF"
|
||||
case HotplugEpilogPoweroff:
|
||||
return "HOTPLUG_EPILOG_POWEROFF"
|
||||
case BootMigrate:
|
||||
return "BOOT_MIGRATE"
|
||||
case BootFailure:
|
||||
return "BOOT_FAILURE"
|
||||
case BootMigrateFailure:
|
||||
return "BOOT_MIGRATE_FAILURE"
|
||||
case PrologMigrateFailure:
|
||||
return "PROLOG_MIGRATE_FAILURE"
|
||||
case PrologFailure:
|
||||
return "PROLOG_FAILURE"
|
||||
case EpilogFailure:
|
||||
return "EPILOG_FAILURE"
|
||||
case EpilogStopFailure:
|
||||
return "EPILOG_STOP_FAILURE"
|
||||
case EpilogUndeployFailure:
|
||||
return "EPILOG_UNDEPLOY_FAILURE"
|
||||
case PrologMigratePoweroff:
|
||||
return "PROLOG_MIGRATE_POWEROFF"
|
||||
case PrologMigratePoweroffFailure:
|
||||
return "PROLOG_MIGRATE_POWEROFF_FAILURE"
|
||||
case PrologMigrateSuspend:
|
||||
return "PROLOG_MIGRATESuspend"
|
||||
case PrologMigrateSuspendFailure:
|
||||
return "PROLOG_MIGRATESuspend_FAILURE"
|
||||
case BootUndeployFailure:
|
||||
return "BOOT_UNDEPLOY_FAILURE"
|
||||
case BootStoppedFailure:
|
||||
return "BOOT_STOPPED_FAILURE"
|
||||
case PrologResumeFailure:
|
||||
return "PROLOG_RESUME_FAILURE"
|
||||
case PrologUndeployFailure:
|
||||
return "PROLOG_UNDEPLOY_FAILURE"
|
||||
case DiskSnapshotPoweroff:
|
||||
return "DISK_SNAPSHOT_POWEROFF"
|
||||
case DiskSnapshotRevertPoweroff:
|
||||
return "DISK_SNAPSHOT_REVERT_POWEROFF"
|
||||
case DiskSnapshotDeletePoweroff:
|
||||
return "DISK_SNAPSHOT_DELETE_POWEROFF"
|
||||
case DiskSnapshotSuspended:
|
||||
return "DISK_SNAPSHOTSuspendED"
|
||||
case DiskSnapshotRevertSuspended:
|
||||
return "DISK_SNAPSHOT_REVERTSuspendED"
|
||||
case DiskSnapshotDeleteSuspended:
|
||||
return "DISK_SNAPSHOT_DELETESuspendED"
|
||||
case DiskSnapshot:
|
||||
return "DISK_SNAPSHOT"
|
||||
case DiskSnapshotDelete:
|
||||
return "DISK_SNAPSHOT_DELETE"
|
||||
case PrologMigrateUnknown:
|
||||
return "PROLOG_MIGRATE_UNKNOWN"
|
||||
case PrologMigrateUnknownFailure:
|
||||
return "PROLOG_MIGRATE_UNKNOWN_FAILURE"
|
||||
case DiskResize:
|
||||
return "DISK_RESIZE"
|
||||
case DiskResizePoweroff:
|
||||
return "DISK_RESIZE_POWEROFF"
|
||||
case DiskResizeUndeployed:
|
||||
return "DISK_RESIZE_UNDEPLOYED"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// NewVMPool returns a new image pool. It accepts the scope of the query.
|
||||
func NewVMPool(args ...int) (*VMPool, error) {
|
||||
var who, start, end, state int
|
||||
|
||||
switch len(args) {
|
||||
case 0:
|
||||
who = PoolWhoMine
|
||||
start = -1
|
||||
end = -1
|
||||
state = -1
|
||||
case 1:
|
||||
who = args[0]
|
||||
start = -1
|
||||
end = -1
|
||||
state = -1
|
||||
case 3:
|
||||
who = args[0]
|
||||
start = args[1]
|
||||
end = args[2]
|
||||
state = -1
|
||||
case 4:
|
||||
who = args[0]
|
||||
start = args[1]
|
||||
end = args[2]
|
||||
state = args[3]
|
||||
default:
|
||||
return nil, errors.New("Wrong number of arguments")
|
||||
}
|
||||
|
||||
response, err := client.Call("one.vmpool.info", who, start, end, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vmpool := &VMPool{XMLResource{body: response.Body()}}
|
||||
|
||||
return vmpool, err
|
||||
|
||||
}
|
||||
|
||||
// Monitoring returns all the virtual machine monitorin records
|
||||
// filter flag:
|
||||
// -4: Resources belonging to the user's primary group
|
||||
// -3: Resources belonging to the user
|
||||
// -2: All resources
|
||||
// -1: Resources belonging to the user and any of his groups
|
||||
// >= 0: UID User's Resources
|
||||
func (vmpool *VMPool) Monitoring(filter int) error {
|
||||
_, err := client.Call("one.vmpool.monitoring", filter)
|
||||
return err
|
||||
}
|
||||
|
||||
// Accounting returns the virtual machine history records
|
||||
// filter flag:
|
||||
// -4: Resources belonging to the user's primary group
|
||||
// -3: Resources belonging to the user
|
||||
// -2: All resources
|
||||
// -1: Resources belonging to the user and any of his groups
|
||||
// >= 0: UID User's Resources
|
||||
// if startTime and/or endTime are -1 it means no limit
|
||||
func (vmpool *VMPool) Accounting(filter, startTime, endTime int) error {
|
||||
_, err := client.Call("one.vmpool.accounting", filter)
|
||||
return err
|
||||
}
|
||||
|
||||
// Showback returns the virtual machine showback records
|
||||
// filter flag
|
||||
// <= -3: Connected user's resources
|
||||
// -2: All resources
|
||||
// -1: Connected user's and his group's resources
|
||||
// >= 0: UID User's Resources
|
||||
// firstMonth: January is 1. Can be -1, in which case the time interval won't have
|
||||
// a left boundary.
|
||||
// firstYear: Can be -1, in which case the time interval won't have a left
|
||||
// boundary.
|
||||
// lastMonth: January is 1. Can be -1, in which case the time interval won't have
|
||||
// a right boundary.
|
||||
// lastYear: Can be -1, in which case the time interval won't have a right
|
||||
// boundary.
|
||||
func (vmpool *VMPool) Showback(filter, firstMonth, firstYear, lastMonth, lastYear int) error {
|
||||
_, err := client.Call("one.vmpool.showback", filter, firstMonth, firstYear, lastMonth, lastYear)
|
||||
return err
|
||||
}
|
||||
|
||||
// CalculateShowback processes all the history records, and stores the monthly cost for each VM
|
||||
// firstMonth: January is 1. Can be -1, in which case the time interval won't have
|
||||
// a left boundary.
|
||||
// firstYear: Can be -1, in which case the time interval won't have a left
|
||||
// boundary.
|
||||
// lastMonth: January is 1. Can be -1, in which case the time interval won't have
|
||||
// a right boundary.
|
||||
// lastYear: Can be -1, in which case the time interval won't have a right
|
||||
// boundary.
|
||||
func (vmpool *VMPool) CalculateShowback(firstMonth, firstYear, lastMonth, lastYear int) error {
|
||||
_, err := client.Call("one.vmpool.calculateshowback", firstMonth, firstYear, lastMonth, lastYear)
|
||||
return err
|
||||
}
|
||||
|
||||
// CreateVM allocates a new VM based on the template string provided. It
|
||||
// returns the image ID
|
||||
func CreateVM(template string, pending bool) (uint, error) {
|
||||
response, err := client.Call("one.vm.allocate", template, pending)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint(response.BodyInt()), nil
|
||||
}
|
||||
|
||||
// NewVM finds an VM by ID returns a new VM object. At this stage no
|
||||
// connection to OpenNebula is performed.
|
||||
func NewVM(id uint) *VM {
|
||||
return &VM{ID: id}
|
||||
}
|
||||
|
||||
// NewVMFromName finds the VM by name and returns a VM object. It connects to
|
||||
// OpenNebula to retrieve the pool, but doesn't perform the Info() call to
|
||||
// retrieve the attributes of the VM.
|
||||
func NewVMFromName(name string) (*VM, error) {
|
||||
vmpool, err := NewVMPool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, err := vmpool.GetIDFromName(name, "/VM_POOL/VM")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewVM(id), nil
|
||||
}
|
||||
|
||||
// State returns the VMState and LCMState
|
||||
func (vm *VM) State() (VMState, LCMState, error) {
|
||||
vmStateString, ok := vm.XPath("/VM/STATE")
|
||||
if ok != true {
|
||||
return -1, -1, errors.New("Unable to parse VM State")
|
||||
}
|
||||
|
||||
lcmStateString, ok := vm.XPath("/VM/LCM_STATE")
|
||||
if ok != true {
|
||||
return -1, -1, errors.New("Unable to parse LCM State")
|
||||
}
|
||||
|
||||
vmState, _ := strconv.Atoi(vmStateString)
|
||||
lcmState, _ := strconv.Atoi(lcmStateString)
|
||||
|
||||
return VMState(vmState), LCMState(lcmState), nil
|
||||
}
|
||||
|
||||
// StateString returns the VMState and LCMState as strings
|
||||
func (vm *VM) StateString() (string, string, error) {
|
||||
vmState, lcmState, err := vm.State()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return VMState(vmState).String(), LCMState(lcmState).String(), nil
|
||||
}
|
||||
|
||||
// Action is the generic method to run any action on the VM
|
||||
func (vm *VM) Action(action string) error {
|
||||
_, err := client.Call("one.vm.action", action, vm.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// Info connects to OpenNebula and fetches the information of the VM
|
||||
func (vm *VM) Info() error {
|
||||
response, err := client.Call("one.vm.info", vm.ID)
|
||||
vm.body = response.Body()
|
||||
return err
|
||||
}
|
||||
|
||||
// Update will modify the VM's template. If appendTemplate is 0, it will
|
||||
// replace the whole template. If its 1, it will merge.
|
||||
func (vm *VM) Update(tpl string, appendTemplate int) error {
|
||||
_, err := client.Call("one.vm.update", vm.ID, tpl, appendTemplate)
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateConf updates (appends) a set of supported configuration attributes in
|
||||
// the VM template
|
||||
func (vm *VM) UpdateConf(tpl string) error {
|
||||
_, err := client.Call("one.vm.updateconf", vm.ID, tpl)
|
||||
return err
|
||||
}
|
||||
|
||||
// Monitoring Returns the virtual machine monitoring records
|
||||
func (vm *VM) Monitoring() error {
|
||||
_, err := client.Call("one.vm.monitoring", vm.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// Chown changes the owner/group of a VM. If uid or gid is -1 it will not
|
||||
// change
|
||||
func (vm *VM) Chown(uid, gid int) error {
|
||||
_, err := client.Call("one.vm.chown", vm.ID, uid, gid)
|
||||
return err
|
||||
}
|
||||
|
||||
// Chmod changes the permissions of a VM. If any perm is -1 it will not
|
||||
// change
|
||||
func (vm *VM) Chmod(uu, um, ua, gu, gm, ga, ou, om, oa int) error {
|
||||
_, err := client.Call("one.vm.chmod", vm.ID, uu, um, ua, gu, gm, ga, ou, om, oa)
|
||||
return err
|
||||
}
|
||||
|
||||
// Rename changes the name of a VM
|
||||
func (vm *VM) Rename(newName string) error {
|
||||
_, err := client.Call("one.vm.rename", vm.ID, newName)
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete will remove the VM from OpenNebula
|
||||
func (vm *VM) Delete() error {
|
||||
_, err := client.Call("one.vm.delete", vm.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// Deploy in the selected hostID and/or dsID. Enforce to return error in case of
|
||||
// overcommitment. Enforce is automatically enabled for non-oneadmin users.
|
||||
func (vm *VM) Deploy(hostID uint, enforce bool, dsID uint) error {
|
||||
_, err := client.Call("one.vm.deploy", vm.ID, int(hostID), enforce, int(dsID))
|
||||
return err
|
||||
}
|
||||
|
||||
// Resize changes the capacity of the virtual machine
|
||||
func (vm *VM) Resize(template string, enforce bool) error {
|
||||
_, err := client.Call("one.vm.resize", vm.ID, template, enforce)
|
||||
return err
|
||||
}
|
||||
|
||||
// DiskSaveas exports a disk to an image. If imageType is empty the default one
|
||||
// will be used. If snapID is -1 the current image state will be exported
|
||||
func (vm *VM) DiskSaveas(diskID int, imageName, imageType string, snapID int) error {
|
||||
_, err := client.Call("one.vm.disksaveas", vm.ID, diskID, imageName, imageType, snapID)
|
||||
return err
|
||||
}
|
||||
|
||||
// DiskSnapshotCreate will create a snapshot of the disk image
|
||||
func (vm *VM) DiskSnapshotCreate(diskID int, description string) error {
|
||||
_, err := client.Call("one.vm.disksnapshotcreate", vm.ID, diskID, description)
|
||||
return err
|
||||
}
|
||||
|
||||
// DiskSnapshotDelete will delete a snapshot
|
||||
func (vm *VM) DiskSnapshotDelete(diskID, snapID int) error {
|
||||
_, err := client.Call("one.vm.disksnapshotdelete", vm.ID, diskID, snapID)
|
||||
return err
|
||||
}
|
||||
|
||||
// DiskSnapshotRevert will revert disk state to a previously taken snapshot
|
||||
func (vm *VM) DiskSnapshotRevert(diskID, snapID int) error {
|
||||
_, err := client.Call("one.vm.disksnapshotrevert", vm.ID, diskID, snapID)
|
||||
return err
|
||||
}
|
||||
|
||||
// SnapshotCreate creates a new virtual machine snapshot. name can be empty
|
||||
func (vm *VM) SnapshotCreate(name string) error {
|
||||
_, err := client.Call("one.vm.snapshotcreate", vm.ID, name)
|
||||
return err
|
||||
}
|
||||
|
||||
// SnapshotDelete deletes a virtual machine snapshot
|
||||
func (vm *VM) SnapshotDelete(snapID int) error {
|
||||
_, err := client.Call("one.vm.snapshotdelete", vm.ID, snapID)
|
||||
return err
|
||||
}
|
||||
|
||||
// SnapshotRevert reverts a virtual machine to a snapshot
|
||||
func (vm *VM) SnapshotRevert(snapID int) error {
|
||||
_, err := client.Call("one.vm.snapshotrevert", vm.ID, snapID)
|
||||
return err
|
||||
}
|
||||
|
||||
// Attach a new disk to the virtual machine. diskTemplate is a string containing
|
||||
// a single DISK vector attribute. Syntax can be the usual attribute=value or
|
||||
// XML
|
||||
func (vm *VM) Attach(diskTemplate string) error {
|
||||
_, err := client.Call("one.vm.attach", vm.ID, diskTemplate)
|
||||
return err
|
||||
}
|
||||
|
||||
// Detach a disk from a virtual machine
|
||||
func (vm *VM) Detach(diskID int) error {
|
||||
_, err := client.Call("one.vm.detach", vm.ID, diskID)
|
||||
return err
|
||||
}
|
||||
|
||||
// DiskResize a disk of a virtual machine
|
||||
func (vm *VM) DiskResize(diskID int, size string) error {
|
||||
_, err := client.Call("one.vm.diskresize", vm.ID, diskID, size)
|
||||
return err
|
||||
}
|
||||
|
||||
// Migrate a VM to a target host and/or to another ds
|
||||
func (vm *VM) Migrate(hostID uint, live, enforce bool, dsID uint) error {
|
||||
_, err := client.Call("one.vm.migrate", int(hostID), live, enforce, int(dsID))
|
||||
return err
|
||||
}
|
||||
|
||||
// AttachNic attaches new network interface to the virtual machine
|
||||
func (vm *VM) AttachNic(tpl string) error {
|
||||
_, err := client.Call("one.vm.attachnic", vm.ID, tpl)
|
||||
return err
|
||||
}
|
||||
|
||||
// DetachNic detaches a network interface from the virtual machine
|
||||
func (vm *VM) DetachNic(nicID string) error {
|
||||
_, err := client.Call("one.vm.detachnic", vm.ID, nicID)
|
||||
return err
|
||||
}
|
||||
|
||||
// VM Actions
|
||||
|
||||
// TerminateHard action on the VM
|
||||
func (vm *VM) TerminateHard() error {
|
||||
return vm.Action("terminate-hard")
|
||||
}
|
||||
|
||||
// Terminate action on the VM
|
||||
func (vm *VM) Terminate() error {
|
||||
return vm.Action("terminate")
|
||||
}
|
||||
|
||||
// UndeployHard action on the VM
|
||||
func (vm *VM) UndeployHard() error {
|
||||
return vm.Action("undeploy-hard")
|
||||
}
|
||||
|
||||
// Undeploy action on the VM
|
||||
func (vm *VM) Undeploy() error {
|
||||
return vm.Action("undeploy")
|
||||
}
|
||||
|
||||
// PoweroffHard action on the VM
|
||||
func (vm *VM) PoweroffHard() error {
|
||||
return vm.Action("poweroff-hard")
|
||||
}
|
||||
|
||||
// Poweroff action on the VM
|
||||
func (vm *VM) Poweroff() error {
|
||||
return vm.Action("poweroff")
|
||||
}
|
||||
|
||||
// RebootHard action on the VM
|
||||
func (vm *VM) RebootHard() error {
|
||||
return vm.Action("reboot-hard")
|
||||
}
|
||||
|
||||
// Reboot action on the VM
|
||||
func (vm *VM) Reboot() error {
|
||||
return vm.Action("reboot")
|
||||
}
|
||||
|
||||
// Hold action on the VM
|
||||
func (vm *VM) Hold() error {
|
||||
return vm.Action("hold")
|
||||
}
|
||||
|
||||
// Release action on the VM
|
||||
func (vm *VM) Release() error {
|
||||
return vm.Action("release")
|
||||
}
|
||||
|
||||
// Stop action on the VM
|
||||
func (vm *VM) Stop() error {
|
||||
return vm.Action("stop")
|
||||
}
|
||||
|
||||
// Suspend action on the VM
|
||||
func (vm *VM) Suspend() error {
|
||||
return vm.Action("suspend")
|
||||
}
|
||||
|
||||
// Resume action on the VM
|
||||
func (vm *VM) Resume() error {
|
||||
return vm.Action("resume")
|
||||
}
|
||||
|
||||
// Resched action on the VM
|
||||
func (vm *VM) Resched() error {
|
||||
return vm.Action("resched")
|
||||
}
|
||||
|
||||
// Unresched action on the VM
|
||||
func (vm *VM) Unresched() error {
|
||||
return vm.Action("unresched")
|
||||
}
|
||||
|
||||
// End actions
|
||||
|
||||
// Recover recovers a stuck VM that is waiting for a driver operation
|
||||
func (vm *VM) Recover(op int) error {
|
||||
_, err := client.Call("one.vm.recover", vm.ID, op)
|
||||
return err
|
||||
}
|
||||
|
||||
// RecoverSuccess forces a success
|
||||
func (vm *VM) RecoverSuccess() error {
|
||||
return vm.Recover(1)
|
||||
}
|
||||
|
||||
// RecoverFailure forces a success
|
||||
func (vm *VM) RecoverFailure() error {
|
||||
return vm.Recover(0)
|
||||
}
|
||||
|
||||
// RecoverRetry forces a success
|
||||
func (vm *VM) RecoverRetry() error {
|
||||
return vm.Recover(2)
|
||||
}
|
||||
|
||||
// RecoverDelete forces a delete
|
||||
func (vm *VM) RecoverDelete() error {
|
||||
return vm.Recover(3)
|
||||
}
|
||||
|
||||
// RecoverDeleteRecreate forces a delete
|
||||
func (vm *VM) RecoverDeleteRecreate() error {
|
||||
return vm.Recover(4)
|
||||
}
|
204
src/oca/go/src/goca/vm_test.go
Normal file
204
src/oca/go/src/goca/vm_test.go
Normal file
@ -0,0 +1,204 @@
|
||||
package goca
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
// Hook up gocheck into the "go test" runner.
|
||||
func Test(t *testing.T) { TestingT(t) }
|
||||
|
||||
type VMSuite struct {
|
||||
templateID uint
|
||||
vmID uint
|
||||
}
|
||||
|
||||
var _ = Suite(&VMSuite{})
|
||||
|
||||
func (s *VMSuite) SetUpSuite(c *C) {
|
||||
// Create template
|
||||
tpl := NewTemplateBuilder()
|
||||
|
||||
tpl.AddValue("NAME", GenName("VMSuite-template"))
|
||||
tpl.AddValue("CPU", 1)
|
||||
tpl.AddValue("MEMORY", "64")
|
||||
|
||||
templateID, err := CreateTemplate(tpl.String())
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
s.templateID = templateID
|
||||
}
|
||||
|
||||
func (s *VMSuite) SetUpTest(c *C) {
|
||||
template := NewTemplate(s.templateID)
|
||||
|
||||
vmID, err := template.Instantiate("", true, "")
|
||||
c.Assert(err, IsNil)
|
||||
s.vmID = vmID
|
||||
}
|
||||
|
||||
func (s *VMSuite) TearDownTest(c *C) {
|
||||
vm := NewVM(s.vmID)
|
||||
|
||||
err := vm.TerminateHard()
|
||||
if err != nil {
|
||||
err = vm.RecoverDelete()
|
||||
}
|
||||
c.Assert(err, IsNil)
|
||||
}
|
||||
|
||||
func (s *VMSuite) TearDownSuite(c *C) {
|
||||
template := NewTemplate(s.templateID)
|
||||
template.Delete()
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func VMExpectState(c *C, vm *VM, state, lcmState string) func() bool {
|
||||
return func() bool {
|
||||
vm.Info()
|
||||
|
||||
s, l, err := vm.StateString()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if lcmState != "" && l == lcmState {
|
||||
return true
|
||||
}
|
||||
|
||||
if state != "" && s == state {
|
||||
return true
|
||||
}
|
||||
|
||||
c.Logf("VM: %d. Expecting: %s/%s, Got: %s/%s", vm.ID, state, lcmState, s, l)
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func (s *VMSuite) TestVMDeploy(c *C) {
|
||||
vm := NewVM(s.vmID)
|
||||
err := vm.Release()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(WaitResource(VMExpectState(c, vm, "ACTIVE", "RUNNING")), Equals, true)
|
||||
}
|
||||
|
||||
func (s *VMSuite) TestVMHoldRelease(c *C) {
|
||||
vm := NewVM(s.vmID)
|
||||
c.Assert(WaitResource(VMExpectState(c, vm, "HOLD", "")), Equals, true)
|
||||
|
||||
err := vm.Release()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(WaitResource(VMExpectState(c, vm, "PENDING", "")), Equals, true)
|
||||
}
|
||||
|
||||
func (s *VMSuite) TestVMUpdate(c *C) {
|
||||
vm := NewVM(s.vmID)
|
||||
err := vm.Update("A=B", 1)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
err = vm.Info()
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
val, ok := vm.XPath("/VM/USER_TEMPLATE/A")
|
||||
c.Assert(ok, Equals, true)
|
||||
c.Assert(val, Equals, "B")
|
||||
}
|
||||
|
||||
// TODO: Hosts
|
||||
// func (s *VMSuite) TestVMMigrate(c *C) {
|
||||
// }
|
||||
// func (s *VMSuite) TestVMLiveMigrate(c *C) {
|
||||
// }
|
||||
|
||||
func (s *VMSuite) TestVMTerminate(c *C) {
|
||||
vm := NewVM(s.vmID)
|
||||
err := vm.Release()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(WaitResource(VMExpectState(c, vm, "ACTIVE", "RUNNING")), Equals, true)
|
||||
|
||||
err = vm.Terminate()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(WaitResource(VMExpectState(c, vm, "DONE", "")), Equals, true)
|
||||
}
|
||||
|
||||
func (s *VMSuite) TestVMTerminateHard(c *C) {
|
||||
vm := NewVM(s.vmID)
|
||||
err := vm.Release()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(WaitResource(VMExpectState(c, vm, "ACTIVE", "RUNNING")), Equals, true)
|
||||
|
||||
vm.TerminateHard()
|
||||
c.Assert(WaitResource(VMExpectState(c, vm, "DONE", "")), Equals, true)
|
||||
}
|
||||
|
||||
func (s *VMSuite) TestVMStop(c *C) {
|
||||
vm := NewVM(s.vmID)
|
||||
err := vm.Release()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(WaitResource(VMExpectState(c, vm, "ACTIVE", "RUNNING")), Equals, true)
|
||||
|
||||
err = vm.Stop()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(WaitResource(VMExpectState(c, vm, "STOPPED", "")), Equals, true)
|
||||
}
|
||||
|
||||
func (s *VMSuite) TestVMSuspend(c *C) {
|
||||
vm := NewVM(s.vmID)
|
||||
err := vm.Release()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(WaitResource(VMExpectState(c, vm, "ACTIVE", "RUNNING")), Equals, true)
|
||||
|
||||
err = vm.Suspend()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(WaitResource(VMExpectState(c, vm, "SUSPENDED", "")), Equals, true)
|
||||
}
|
||||
|
||||
func (s *VMSuite) TestVMResume(c *C) {
|
||||
vm := NewVM(s.vmID)
|
||||
err := vm.Release()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(WaitResource(VMExpectState(c, vm, "ACTIVE", "RUNNING")), Equals, true)
|
||||
|
||||
err = vm.Suspend()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(WaitResource(VMExpectState(c, vm, "SUSPENDED", "")), Equals, true)
|
||||
|
||||
err = vm.Resume()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(WaitResource(VMExpectState(c, vm, "ACTIVE", "RUNNING")), Equals, true)
|
||||
}
|
||||
|
||||
func (s *VMSuite) TestVMResize(c *C) {
|
||||
vm := NewVM(s.vmID)
|
||||
err := vm.Release()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(WaitResource(VMExpectState(c, vm, "ACTIVE", "RUNNING")), Equals, true)
|
||||
|
||||
err = vm.Poweroff()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(WaitResource(VMExpectState(c, vm, "POWEROFF", "")), Equals, true)
|
||||
|
||||
err = vm.Resize("CPU=2.5\nMEMORY=512", false)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(WaitResource(VMExpectState(c, vm, "POWEROFF", "")), Equals, true)
|
||||
|
||||
err = vm.Resume()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(WaitResource(VMExpectState(c, vm, "ACTIVE", "RUNNING")), Equals, true)
|
||||
|
||||
err = vm.Info()
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
cpu, ok := vm.XPath("/VM/TEMPLATE/CPU")
|
||||
c.Assert(ok, Equals, true)
|
||||
c.Assert(cpu, Equals, "2.5")
|
||||
|
||||
memory, ok := vm.XPath("/VM/TEMPLATE/MEMORY")
|
||||
c.Assert(ok, Equals, true)
|
||||
c.Assert(memory, Equals, "512")
|
||||
}
|
112
src/oca/go/src/goca/xmlresource.go
Normal file
112
src/oca/go/src/goca/xmlresource.go
Normal file
@ -0,0 +1,112 @@
|
||||
package goca
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"gopkg.in/xmlpath.v2"
|
||||
)
|
||||
|
||||
const (
|
||||
// PoolWhoPrimaryGroup resources belonging to the user’s primary group.
|
||||
PoolWhoPrimaryGroup = -4
|
||||
|
||||
// PoolWhoMine to list resources that belong to the user that performs the
|
||||
// query.
|
||||
PoolWhoMine = -3
|
||||
|
||||
// PoolWhoAll to list all the resources seen by the user that performs the
|
||||
// query.
|
||||
PoolWhoAll = -2
|
||||
|
||||
// PoolWhoGroup to list all the resources that belong to the group that performs
|
||||
// the query.
|
||||
PoolWhoGroup = -1
|
||||
)
|
||||
|
||||
// XMLResource contains an XML body field. All the resources in OpenNebula are
|
||||
// of this kind.
|
||||
type XMLResource struct {
|
||||
body string
|
||||
}
|
||||
|
||||
// XMLIter is used to iterate over XML xpaths in an object.
|
||||
type XMLIter struct {
|
||||
iter *xmlpath.Iter
|
||||
}
|
||||
|
||||
// XMLNode represent an XML node.
|
||||
type XMLNode struct {
|
||||
node *xmlpath.Node
|
||||
}
|
||||
|
||||
// Body accesses the body of an XMLResource
|
||||
func (r *XMLResource) Body() string {
|
||||
return r.body
|
||||
}
|
||||
|
||||
// XPath returns the string pointed at by xpath, for an XMLResource
|
||||
func (r *XMLResource) XPath(xpath string) (string, bool) {
|
||||
path := xmlpath.MustCompile(xpath)
|
||||
b := bytes.NewBufferString(r.Body())
|
||||
|
||||
root, _ := xmlpath.Parse(b)
|
||||
|
||||
return path.String(root)
|
||||
}
|
||||
|
||||
// XPathIter returns an XMLIter object pointed at by the xpath
|
||||
func (r *XMLResource) XPathIter(xpath string) *XMLIter {
|
||||
path := xmlpath.MustCompile(xpath)
|
||||
b := bytes.NewBufferString(string(r.Body()))
|
||||
|
||||
root, _ := xmlpath.Parse(b)
|
||||
|
||||
return &XMLIter{iter: path.Iter(root)}
|
||||
}
|
||||
|
||||
// GetIDFromName finds the a resource by ID by looking at an xpath contained
|
||||
// in that resource
|
||||
func (r *XMLResource) GetIDFromName(name string, xpath string) (uint, error) {
|
||||
var id int
|
||||
var match = false
|
||||
|
||||
iter := r.XPathIter(xpath)
|
||||
for iter.Next() {
|
||||
node := iter.Node()
|
||||
|
||||
n, _ := node.XPath("NAME")
|
||||
if n == name {
|
||||
if match {
|
||||
return 0, errors.New("multiple resources with that name")
|
||||
}
|
||||
|
||||
idString, _ := node.XPath("ID")
|
||||
id, _ = strconv.Atoi(idString)
|
||||
match = true
|
||||
}
|
||||
}
|
||||
|
||||
if !match {
|
||||
return 0, errors.New("resource not found")
|
||||
}
|
||||
|
||||
return uint(id), nil
|
||||
}
|
||||
|
||||
// Next moves on to the next resource
|
||||
func (i *XMLIter) Next() bool {
|
||||
return i.iter.Next()
|
||||
}
|
||||
|
||||
// Node returns the XMLNode
|
||||
func (i *XMLIter) Node() *XMLNode {
|
||||
return &XMLNode{node: i.iter.Node()}
|
||||
}
|
||||
|
||||
// XPath returns an XMLNode pointed at by xpath
|
||||
func (n *XMLNode) XPath(xpath string) (string, bool) {
|
||||
path := xmlpath.MustCompile(xpath)
|
||||
return path.String(n.node)
|
||||
}
|
88
src/oca/go/src/goca/zone.go
Normal file
88
src/oca/go/src/goca/zone.go
Normal file
@ -0,0 +1,88 @@
|
||||
package goca
|
||||
|
||||
// Zone represents an OpenNebula Zone
|
||||
type Zone struct {
|
||||
XMLResource
|
||||
ID uint
|
||||
Name string
|
||||
}
|
||||
|
||||
// ZonePool represents an OpenNebula ZonePool
|
||||
type ZonePool struct {
|
||||
XMLResource
|
||||
}
|
||||
|
||||
// NewZonePool returns a zone pool. A connection to OpenNebula is
|
||||
// performed.
|
||||
func NewZonePool() (*ZonePool, error) {
|
||||
response, err := client.Call("one.zonepool.info")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
zonepool := &ZonePool{XMLResource{body: response.Body()}}
|
||||
|
||||
return zonepool, err
|
||||
}
|
||||
|
||||
// NewZone finds a zone object by ID. No connection to OpenNebula.
|
||||
func NewZone(id uint) *Zone {
|
||||
return &Zone{ID: id}
|
||||
}
|
||||
|
||||
// NewZoneFromName finds a zone object by name. It connects to
|
||||
// OpenNebula to retrieve the pool, but doesn't perform the Info() call to
|
||||
// retrieve the attributes of the zone.
|
||||
func NewZoneFromName(name string) (*Zone, error) {
|
||||
zonePool, err := NewZonePool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, err := zonePool.GetIDFromName(name, "/ZONE_POOL/ZONE")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewZone(id), nil
|
||||
}
|
||||
|
||||
// CreateZone allocates a new zone. It returns the new zone ID.
|
||||
// * tpl: A string containing the template of the ZONE. Syntax can be the usual
|
||||
// attribute=value or XML.
|
||||
// * clusterID: The id of the cluster. If -1, the default one will be used
|
||||
func CreateZone(tpl string, clusterID int) (uint, error) {
|
||||
response, err := client.Call("one.zone.allocate", tpl, clusterID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint(response.BodyInt()), nil
|
||||
}
|
||||
|
||||
// Delete deletes the given zone from the pool.
|
||||
func (zone *Zone) Delete() error {
|
||||
_, err := client.Call("one.zone.delete", zone.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// Update replaces the zone template contents.
|
||||
// * tpl: The new template contents. Syntax can be the usual attribute=value or XML.
|
||||
// * appendTemplate: Update type: 0: Replace the whole template. 1: Merge new template with the existing one.
|
||||
func (zone *Zone) Update(tpl string, appendTemplate int) error {
|
||||
_, err := client.Call("one.zone.update", zone.ID, tpl, appendTemplate)
|
||||
return err
|
||||
}
|
||||
|
||||
// Rename renames a zone.
|
||||
// * newName: The new name.
|
||||
func (zone *Zone) Rename(newName string) error {
|
||||
_, err := client.Call("one.zone.rename", zone.ID, newName)
|
||||
return err
|
||||
}
|
||||
|
||||
// Info retrieves information for the zone.
|
||||
func (zone *Zone) Info() error {
|
||||
_, err := client.Call("one.zone.info", zone.ID)
|
||||
return err
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user