forked from juju/juju
/
environ_broker.go
174 lines (154 loc) · 5.09 KB
/
environ_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
// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
// +build !gccgo
package vsphere
import (
"github.com/juju/errors"
"github.com/juju/govmomi/vim25/mo"
"github.com/juju/juju/cloudconfig/cloudinit"
"github.com/juju/juju/cloudconfig/instancecfg"
"github.com/juju/juju/cloudconfig/providerinit"
"github.com/juju/juju/environs"
"github.com/juju/juju/instance"
"github.com/juju/juju/provider/common"
"github.com/juju/juju/tools"
)
const (
DefaultCpuCores = uint64(2)
DefaultCpuPower = uint64(2000)
DefaultMemMb = uint64(2000)
)
// MaintainInstance is specified in the InstanceBroker interface.
func (*environ) MaintainInstance(args environs.StartInstanceParams) error {
return nil
}
// StartInstance implements environs.InstanceBroker.
func (env *environ) StartInstance(args environs.StartInstanceParams) (*environs.StartInstanceResult, error) {
img, err := findImageMetadata(env, args)
if err != nil {
return nil, errors.Trace(err)
}
if err := env.finishMachineConfig(args, img); err != nil {
return nil, errors.Trace(err)
}
raw, hwc, err := env.newRawInstance(args, img)
if err != nil {
return nil, errors.Trace(err)
}
logger.Infof("started instance %q", raw.Name)
inst := newInstance(raw, env)
result := environs.StartInstanceResult{
Instance: inst,
Hardware: hwc,
}
return &result, nil
}
//this variable is exported, because it has to be rewritten in external unit tests
var FinishInstanceConfig = instancecfg.FinishInstanceConfig
// finishMachineConfig updates args.MachineConfig in place. Setting up
// the API, StateServing, and SSHkeys information.
func (env *environ) finishMachineConfig(args environs.StartInstanceParams, img *OvaFileMetadata) error {
envTools, err := args.Tools.Match(tools.Filter{Arch: img.Arch})
if err != nil {
return err
}
if err := args.InstanceConfig.SetTools(envTools); err != nil {
return errors.Trace(err)
}
return FinishInstanceConfig(args.InstanceConfig, env.Config())
}
// newRawInstance is where the new physical instance is actually
// provisioned, relative to the provided args and spec. Info for that
// low-level instance is returned.
func (env *environ) newRawInstance(args environs.StartInstanceParams, img *OvaFileMetadata) (*mo.VirtualMachine, *instance.HardwareCharacteristics, error) {
machineID, err := env.namespace.Hostname(args.InstanceConfig.MachineId)
if err != nil {
return nil, nil, errors.Trace(err)
}
cloudcfg, err := cloudinit.New(args.Tools.OneSeries())
if err != nil {
return nil, nil, errors.Trace(err)
}
cloudcfg.AddPackage("open-vm-tools")
cloudcfg.AddPackage("iptables-persistent")
userData, err := providerinit.ComposeUserData(args.InstanceConfig, cloudcfg, VsphereRenderer{})
if err != nil {
return nil, nil, errors.Annotate(err, "cannot make user data")
}
logger.Debugf("Vmware user data; %d bytes", len(userData))
rootDisk := common.MinRootDiskSizeGiB(args.InstanceConfig.Series) * 1024
if args.Constraints.RootDisk != nil && *args.Constraints.RootDisk > rootDisk {
rootDisk = *args.Constraints.RootDisk
}
cpuCores := DefaultCpuCores
if args.Constraints.CpuCores != nil {
cpuCores = *args.Constraints.CpuCores
}
cpuPower := DefaultCpuPower
if args.Constraints.CpuPower != nil {
cpuPower = *args.Constraints.CpuPower
}
mem := DefaultMemMb
if args.Constraints.Mem != nil {
mem = *args.Constraints.Mem
}
hwc := &instance.HardwareCharacteristics{
Arch: &img.Arch,
Mem: &mem,
CpuCores: &cpuCores,
CpuPower: &cpuPower,
RootDisk: &rootDisk,
}
zones, err := env.parseAvailabilityZones(args)
if err != nil {
return nil, nil, errors.Trace(err)
}
var inst *mo.VirtualMachine
for _, zone := range zones {
var availZone *vmwareAvailZone
availZone, err = env.availZone(zone)
if err != nil {
logger.Warningf("Error while getting availability zone %s: %s", zone, err)
continue
}
apiPort := 0
if args.InstanceConfig.Controller != nil {
apiPort = args.InstanceConfig.Controller.Config.APIPort()
}
spec := &instanceSpec{
machineID: machineID,
zone: availZone,
hwc: hwc,
img: img,
userData: userData,
sshKey: args.InstanceConfig.AuthorizedKeys,
isController: args.InstanceConfig.Controller != nil,
controllerUUID: args.ControllerUUID,
apiPort: apiPort,
}
inst, err = env.client.CreateInstance(env.ecfg, spec)
if err != nil {
logger.Warningf("Error while trying to create instance in %s availability zone: %s", zone, err)
continue
}
break
}
if err != nil {
return nil, nil, errors.Annotate(err, "Can't create instance in any of availability zones, last error")
}
return inst, hwc, err
}
// AllInstances implements environs.InstanceBroker.
func (env *environ) AllInstances() ([]instance.Instance, error) {
instances, err := env.instances()
return instances, errors.Trace(err)
}
// StopInstances implements environs.InstanceBroker.
func (env *environ) StopInstances(instances ...instance.Id) error {
var ids []string
for _, id := range instances {
ids = append(ids, string(id))
}
err := env.client.RemoveInstances(ids...)
return errors.Trace(err)
}