/
kvm-broker.go
176 lines (156 loc) · 5.14 KB
/
kvm-broker.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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package provisioner
import (
"github.com/juju/errors"
"github.com/juju/loggo"
"github.com/juju/juju/agent"
"github.com/juju/juju/cloudconfig/instancecfg"
"github.com/juju/juju/container"
"github.com/juju/juju/container/kvm"
"github.com/juju/juju/environs"
"github.com/juju/juju/instance"
)
var kvmLogger = loggo.GetLogger("juju.provisioner.kvm")
var _ environs.InstanceBroker = (*kvmBroker)(nil)
func NewKvmBroker(
api APICalls,
agentConfig agent.Config,
managerConfig container.ManagerConfig,
enableNAT bool,
) (environs.InstanceBroker, error) {
manager, err := kvm.NewContainerManager(managerConfig)
if err != nil {
return nil, err
}
return &kvmBroker{
manager: manager,
api: api,
agentConfig: agentConfig,
enableNAT: enableNAT,
}, nil
}
type kvmBroker struct {
manager container.Manager
api APICalls
agentConfig agent.Config
enableNAT bool
}
// StartInstance is specified in the Broker interface.
func (broker *kvmBroker) StartInstance(args environs.StartInstanceParams) (*environs.StartInstanceResult, error) {
if args.InstanceConfig.HasNetworks() {
return nil, errors.New("starting kvm containers with networks is not supported yet")
}
// TODO: refactor common code out of the container brokers.
machineId := args.InstanceConfig.MachineId
kvmLogger.Infof("starting kvm container for machineId: %s", machineId)
// TODO: Default to using the host network until we can configure. Yes,
// this is using the LxcBridge value, we should put it in the api call for
// container config.
bridgeDevice := broker.agentConfig.Value(agent.LxcBridge)
if bridgeDevice == "" {
bridgeDevice = kvm.DefaultKvmBridge
}
if !environs.AddressAllocationEnabled() {
logger.Debugf(
"address allocation feature flag not enabled; using DHCP for container %q",
machineId,
)
} else {
logger.Debugf("trying to allocate static IP for container %q", machineId)
allocatedInfo, err := configureContainerNetwork(
machineId,
bridgeDevice,
broker.api,
args.NetworkInfo,
true, // allocate a new address.
broker.enableNAT,
)
if err != nil {
// It's fine, just ignore it. The effect will be that the
// container won't have a static address configured.
logger.Infof("not allocating static IP for container %q: %v", machineId, err)
} else {
args.NetworkInfo = allocatedInfo
}
}
// Unlike with LXC, we don't override the default MTU to use.
network := container.BridgeNetworkConfig(bridgeDevice, 0, args.NetworkInfo)
series := args.Tools.OneSeries()
args.InstanceConfig.MachineContainerType = instance.KVM
args.InstanceConfig.Tools = args.Tools[0]
config, err := broker.api.ContainerConfig()
if err != nil {
kvmLogger.Errorf("failed to get container config: %v", err)
return nil, err
}
if err := instancecfg.PopulateInstanceConfig(
args.InstanceConfig,
config.ProviderType,
config.AuthorizedKeys,
config.SSLHostnameVerification,
config.Proxy,
config.AptProxy,
config.AptMirror,
config.PreferIPv6,
config.EnableOSRefreshUpdate,
config.EnableOSUpgrade,
); err != nil {
kvmLogger.Errorf("failed to populate machine config: %v", err)
return nil, err
}
storageConfig := &container.StorageConfig{
AllowMount: true,
}
inst, hardware, err := broker.manager.CreateContainer(args.InstanceConfig, series, network, storageConfig)
if err != nil {
kvmLogger.Errorf("failed to start container: %v", err)
return nil, err
}
kvmLogger.Infof("started kvm container for machineId: %s, %s, %s", machineId, inst.Id(), hardware.String())
return &environs.StartInstanceResult{
Instance: inst,
Hardware: hardware,
}, nil
}
// StopInstances shuts down the given instances.
func (broker *kvmBroker) StopInstances(ids ...instance.Id) error {
// TODO: potentially parallelise.
for _, id := range ids {
kvmLogger.Infof("stopping kvm container for instance: %s", id)
if err := broker.manager.DestroyContainer(id); err != nil {
kvmLogger.Errorf("container did not stop: %v", err)
return err
}
}
return nil
}
// AllInstances only returns running containers.
func (broker *kvmBroker) AllInstances() (result []instance.Instance, err error) {
return broker.manager.ListContainers()
}
// MaintainInstance checks that the container's host has the required iptables and routing
// rules to make the container visible to both the host and other machines on the same subnet.
func (broker *kvmBroker) MaintainInstance(args environs.StartInstanceParams) error {
machineId := args.InstanceConfig.MachineId
if !environs.AddressAllocationEnabled() {
kvmLogger.Debugf("address allocation disabled: Not running maintenance for kvm with machineId: %s",
machineId)
return nil
}
kvmLogger.Debugf("running maintenance for kvm with machineId: %s", machineId)
// Default to using the host network until we can configure.
bridgeDevice := broker.agentConfig.Value(agent.LxcBridge)
if bridgeDevice == "" {
bridgeDevice = kvm.DefaultKvmBridge
}
_, err := configureContainerNetwork(
machineId,
bridgeDevice,
broker.api,
args.NetworkInfo,
false, // don't allocate a new address.
broker.enableNAT,
)
return err
}