1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
7
"github.com/altoros/gosigma"
8
"github.com/juju/errors"
9
"github.com/juju/loggo"
10
"github.com/juju/utils"
11
"github.com/juju/utils/arch"
13
"github.com/juju/juju/environs"
14
"github.com/juju/juju/environs/imagemetadata"
15
"github.com/juju/juju/instance"
16
"github.com/juju/juju/state/multiwatcher"
19
type environClient struct {
26
func (tracer) Logf(format string, args ...interface{}) {
27
logger.Tracef(format, args...)
30
// newClient returns an instance of the CloudSigma client.
31
var newClient = func(cloud environs.CloudSpec, uuid string) (client *environClient, err error) {
32
logger.Debugf("creating CloudSigma client: id=%q", uuid)
34
credAttrs := cloud.Credential.Attributes()
35
username := credAttrs[credAttrUsername]
36
password := credAttrs[credAttrPassword]
38
// create connection to CloudSigma
39
conn, err := gosigma.NewClient(cloud.Endpoint, username, password, nil)
44
// configure trace logger
45
if logger.LogLevel() <= loggo.TRACE {
46
conn.Logger(&tracer{})
49
client = &environClient{
58
jujuMetaInstance = "juju-instance"
59
jujuMetaInstanceController = "controller"
60
jujuMetaInstanceServer = "server"
62
jujuMetaEnvironment = "juju-model"
63
jujuMetaCoudInit = "cloudinit-user-data"
64
jujuMetaBase64 = "base64_fields"
67
func (c *environClient) isMyEnvironment(s gosigma.Server) bool {
68
if v, ok := s.Get(jujuMetaEnvironment); ok && c.uuid == v {
74
func (c *environClient) isMyServer(s gosigma.Server) bool {
75
if _, ok := s.Get(jujuMetaInstance); ok {
76
return c.isMyEnvironment(s)
81
// isMyController is used to filter servers in the CloudSigma account
82
func (c environClient) isMyController(s gosigma.Server) bool {
83
if v, ok := s.Get(jujuMetaInstance); ok && v == jujuMetaInstanceController {
84
return c.isMyEnvironment(s)
89
// instances returns a list of CloudSigma servers for this environment
90
func (c *environClient) instances() ([]gosigma.Server, error) {
91
return c.conn.ServersFiltered(gosigma.RequestDetail, c.isMyServer)
94
// instanceMap of server ids to servers at CloudSigma account
95
func (c *environClient) instanceMap() (map[string]gosigma.Server, error) {
96
servers, err := c.conn.ServersFiltered(gosigma.RequestDetail, c.isMyServer)
98
return nil, errors.Trace(err)
101
m := make(map[string]gosigma.Server, len(servers))
102
for _, s := range servers {
109
//getControllerIds get list of ids for all controller instances
110
func (c *environClient) getControllerIds() (ids []instance.Id, err error) {
111
logger.Tracef("query state...")
113
servers, err := c.conn.ServersFiltered(gosigma.RequestDetail, c.isMyController)
115
return []instance.Id{}, errors.Trace(err)
118
if len(servers) == 0 {
119
return []instance.Id{}, environs.ErrNotBootstrapped
122
ids = make([]instance.Id, len(servers))
124
for i, server := range servers {
125
logger.Tracef("controller id: %s", server.UUID())
126
ids[i] = instance.Id(server.UUID())
132
//stopInstance stops the CloudSigma server corresponding to the given instance ID.
133
func (c *environClient) stopInstance(id instance.Id) error {
136
return errors.New("invalid instance id")
139
s, err := c.conn.Server(uuid)
141
return errors.Trace(err)
145
logger.Tracef("environClient.StopInstance - stop server, %q = %v", uuid, err)
147
err = s.Remove(gosigma.RecurseAllDrives)
148
logger.Tracef("environClient.StopInstance - remove server, %q = %v", uuid, err)
153
//newInstance creates and starts new instance.
154
func (c *environClient) newInstance(
155
args environs.StartInstanceParams,
156
img *imagemetadata.ImageMetadata,
158
authorizedKeys string,
159
) (srv gosigma.Server, drv gosigma.Drive, ar string, err error) {
166
srv.Remove(gosigma.RecurseAllDrives)
167
} else if drv != nil {
174
if args.InstanceConfig == nil {
175
err = errors.New("invalid configuration for new instance: InstanceConfig is nil")
176
return nil, nil, "", err
179
logger.Debugf("Tools: %v", args.Tools.URLs())
180
logger.Debugf("Juju Constraints:" + args.Constraints.String())
181
logger.Debugf("InstanceConfig: %#v", args.InstanceConfig)
183
constraints := newConstraints(args.InstanceConfig.Bootstrap != nil, args.Constraints, img)
184
logger.Debugf("CloudSigma Constraints: %v", constraints)
186
originalDrive, err := c.conn.Drive(constraints.driveTemplate, gosigma.LibraryMedia)
188
err = errors.Annotatef(err, "Failed to query drive template")
189
return nil, nil, "", err
192
baseName := "juju-" + c.uuid + "-" + args.InstanceConfig.MachineId
194
cloneParams := gosigma.CloneParams{Name: baseName}
195
if drv, err = originalDrive.CloneWait(cloneParams, nil); err != nil {
196
err = errors.Errorf("error cloning drive: %v", err)
197
return nil, nil, "", err
200
if drv.Size() < constraints.driveSize {
201
if err = drv.ResizeWait(constraints.driveSize); err != nil {
202
err = errors.Errorf("error resizing drive: %v", err)
203
return nil, nil, "", err
207
cc, err := c.generateSigmaComponents(
208
baseName, constraints, args, drv, userData, authorizedKeys,
211
return nil, nil, "", errors.Trace(err)
214
if srv, err = c.conn.CreateServer(cc); err != nil {
215
err = errors.Annotatef(err, "error creating new instance")
216
return nil, nil, "", err
219
if err = srv.Start(); err != nil {
220
err = errors.Annotatef(err, "error booting new instance")
221
return nil, nil, "", err
224
// populate root drive hardware characteristics
225
switch originalDrive.Arch() {
231
err = errors.Errorf("unknown arch: %v", ar)
232
return nil, nil, "", err
235
return srv, drv, ar, nil
238
func (c *environClient) generateSigmaComponents(
240
constraints *sigmaConstraints,
241
args environs.StartInstanceParams,
244
authorizedKeys string,
245
) (cc gosigma.Components, err error) {
247
cc.SetDescription(baseName)
248
cc.SetSMP(constraints.cores)
249
cc.SetCPU(constraints.power)
250
cc.SetMem(constraints.mem)
252
vncpass, err := utils.RandomPassword()
254
err = errors.Errorf("error generating password: %v", err)
257
cc.SetVNCPassword(vncpass)
258
logger.Debugf("Setting ssh key: %s end", authorizedKeys)
259
cc.SetSSHPublicKey(authorizedKeys)
260
cc.AttachDrive(1, "0:0", "virtio", drv.UUID())
261
cc.NetworkDHCP4(gosigma.ModelVirtio)
263
if multiwatcher.AnyJobNeedsState(args.InstanceConfig.Jobs...) {
264
cc.SetMeta(jujuMetaInstance, jujuMetaInstanceController)
266
cc.SetMeta(jujuMetaInstance, jujuMetaInstanceServer)
269
cc.SetMeta(jujuMetaEnvironment, c.uuid)
270
cc.SetMeta(jujuMetaCoudInit, string(userData))
271
cc.SetMeta(jujuMetaBase64, jujuMetaCoudInit)