forked from juju/juju
/
libvirt.go
155 lines (136 loc) · 4.41 KB
/
libvirt.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package kvm
// This file contains wrappers around the following executables:
// uvt-simplestreams-libvirt
// uvt-kvm
// virsh
// Those executables are found in the following packages:
// uvtool-libvirt
// libvirt-bin
//
// These executables provide Juju's interface to dealing with kvm containers.
// The define how we start, stop and list running containers on the host
import (
"fmt"
"path/filepath"
"regexp"
"strings"
"github.com/juju/errors"
"github.com/juju/utils"
"github.com/juju/juju/network"
)
var (
// The regular expression for breaking up the results of 'virsh list'
// (?m) - specify that this is a multiline regex
// first part is the opaque identifier we don't care about
// then the hostname, and lastly the status.
machineListPattern = regexp.MustCompile(`(?m)^\s+\d+\s+(?P<hostname>[-\w]+)\s+(?P<status>.+)\s*$`)
)
// run the command and return the combined output.
func run(command string, args ...string) (output string, err error) {
logger.Tracef("%s %v", command, args)
output, err = utils.RunCommand(command, args...)
logger.Tracef("output: %v", output)
return output, err
}
// SyncImages updates the local cached images by reading the simplestreams
// data and downloading the cloud images to the uvtool pool (used by libvirt).
func SyncImages(series, arch, source string) error {
args := []string{
"sync",
fmt.Sprintf("arch=%s", arch),
fmt.Sprintf("release=%s", series),
}
if source != "" {
args = append(args, fmt.Sprintf("--source=%s", source))
}
_, err := run("uvt-simplestreams-libvirt", args...)
return err
}
type CreateMachineParams struct {
Hostname string
Series string
Arch string
UserDataFile string
NetworkBridge string
Memory uint64
CpuCores uint64
RootDisk uint64
Interfaces []network.InterfaceInfo
}
// CreateMachine creates a virtual machine and starts it.
func CreateMachine(params CreateMachineParams) error {
if params.Hostname == "" {
return fmt.Errorf("Hostname is required")
}
args := []string{
"create",
"--log-console-output", // do wonder where this goes...
}
if params.UserDataFile != "" {
args = append(args, "--user-data", params.UserDataFile)
}
if params.Memory != 0 {
args = append(args, "--memory", fmt.Sprint(params.Memory))
}
if params.CpuCores != 0 {
args = append(args, "--cpu", fmt.Sprint(params.CpuCores))
}
if params.RootDisk != 0 {
args = append(args, "--disk", fmt.Sprint(params.RootDisk))
}
if params.NetworkBridge != "" {
if len(params.Interfaces) != 0 {
templateDir := filepath.Dir(params.UserDataFile)
templatePath := filepath.Join(templateDir, "kvm-template.xml")
err := WriteTemplate(templatePath, params)
if err != nil {
return errors.Trace(err)
}
args = append(args, "--template", templatePath)
} else {
args = append(args, "--bridge", params.NetworkBridge)
}
}
args = append(args, params.Hostname)
if params.Series != "" {
args = append(args, fmt.Sprintf("release=%s", params.Series))
}
if params.Arch != "" {
args = append(args, fmt.Sprintf("arch=%s", params.Arch))
}
output, err := run("uvt-kvm", args...)
logger.Debugf("is this the logged output?:\n%s", output)
return err
}
// DestroyMachine destroys the virtual machine identified by hostname.
func DestroyMachine(hostname string) error {
_, err := run("uvt-kvm", "destroy", hostname)
return err
}
// AutostartMachine indicates that the virtual machines should automatically
// restart when the host restarts.
func AutostartMachine(hostname string) error {
_, err := run("virsh", "autostart", hostname)
return err
}
// ListMachines returns a map of machine name to state, where state is one of:
// running, idle, paused, shutdown, shut off, crashed, dying, pmsuspended.
func ListMachines() (map[string]string, error) {
output, err := run("virsh", "-q", "list", "--all")
if err != nil {
return nil, err
}
// Split the output into lines.
// Regex matching is the easiest way to match the lines.
// id hostname status
// separated by whitespace, with whitespace at the start too.
result := make(map[string]string)
for _, s := range machineListPattern.FindAllStringSubmatchIndex(output, -1) {
hostnameAndStatus := machineListPattern.ExpandString(nil, "$hostname $status", output, s)
parts := strings.SplitN(string(hostnameAndStatus), " ", 2)
result[parts[0]] = parts[1]
}
return result, nil
}