~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/provider/cloudsigma/client.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2015 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package cloudsigma
 
5
 
 
6
import (
 
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"
 
12
 
 
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"
 
17
)
 
18
 
 
19
type environClient struct {
 
20
        conn *gosigma.Client
 
21
        uuid string
 
22
}
 
23
 
 
24
type tracer struct{}
 
25
 
 
26
func (tracer) Logf(format string, args ...interface{}) {
 
27
        logger.Tracef(format, args...)
 
28
}
 
29
 
 
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)
 
33
 
 
34
        credAttrs := cloud.Credential.Attributes()
 
35
        username := credAttrs[credAttrUsername]
 
36
        password := credAttrs[credAttrPassword]
 
37
 
 
38
        // create connection to CloudSigma
 
39
        conn, err := gosigma.NewClient(cloud.Endpoint, username, password, nil)
 
40
        if err != nil {
 
41
                return nil, err
 
42
        }
 
43
 
 
44
        // configure trace logger
 
45
        if logger.LogLevel() <= loggo.TRACE {
 
46
                conn.Logger(&tracer{})
 
47
        }
 
48
 
 
49
        client = &environClient{
 
50
                conn: conn,
 
51
                uuid: uuid,
 
52
        }
 
53
 
 
54
        return client, nil
 
55
}
 
56
 
 
57
const (
 
58
        jujuMetaInstance           = "juju-instance"
 
59
        jujuMetaInstanceController = "controller"
 
60
        jujuMetaInstanceServer     = "server"
 
61
 
 
62
        jujuMetaEnvironment = "juju-model"
 
63
        jujuMetaCoudInit    = "cloudinit-user-data"
 
64
        jujuMetaBase64      = "base64_fields"
 
65
)
 
66
 
 
67
func (c *environClient) isMyEnvironment(s gosigma.Server) bool {
 
68
        if v, ok := s.Get(jujuMetaEnvironment); ok && c.uuid == v {
 
69
                return true
 
70
        }
 
71
        return false
 
72
}
 
73
 
 
74
func (c *environClient) isMyServer(s gosigma.Server) bool {
 
75
        if _, ok := s.Get(jujuMetaInstance); ok {
 
76
                return c.isMyEnvironment(s)
 
77
        }
 
78
        return false
 
79
}
 
80
 
 
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)
 
85
        }
 
86
        return false
 
87
}
 
88
 
 
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)
 
92
}
 
93
 
 
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)
 
97
        if err != nil {
 
98
                return nil, errors.Trace(err)
 
99
        }
 
100
 
 
101
        m := make(map[string]gosigma.Server, len(servers))
 
102
        for _, s := range servers {
 
103
                m[s.UUID()] = s
 
104
        }
 
105
 
 
106
        return m, nil
 
107
}
 
108
 
 
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...")
 
112
 
 
113
        servers, err := c.conn.ServersFiltered(gosigma.RequestDetail, c.isMyController)
 
114
        if err != nil {
 
115
                return []instance.Id{}, errors.Trace(err)
 
116
        }
 
117
 
 
118
        if len(servers) == 0 {
 
119
                return []instance.Id{}, environs.ErrNotBootstrapped
 
120
        }
 
121
 
 
122
        ids = make([]instance.Id, len(servers))
 
123
 
 
124
        for i, server := range servers {
 
125
                logger.Tracef("controller id: %s", server.UUID())
 
126
                ids[i] = instance.Id(server.UUID())
 
127
        }
 
128
 
 
129
        return ids, nil
 
130
}
 
131
 
 
132
//stopInstance stops the CloudSigma server corresponding to the given instance ID.
 
133
func (c *environClient) stopInstance(id instance.Id) error {
 
134
        uuid := string(id)
 
135
        if uuid == "" {
 
136
                return errors.New("invalid instance id")
 
137
        }
 
138
 
 
139
        s, err := c.conn.Server(uuid)
 
140
        if err != nil {
 
141
                return errors.Trace(err)
 
142
        }
 
143
 
 
144
        err = s.StopWait()
 
145
        logger.Tracef("environClient.StopInstance - stop server, %q = %v", uuid, err)
 
146
 
 
147
        err = s.Remove(gosigma.RecurseAllDrives)
 
148
        logger.Tracef("environClient.StopInstance - remove server, %q = %v", uuid, err)
 
149
 
 
150
        return nil
 
151
}
 
152
 
 
153
//newInstance creates and starts new instance.
 
154
func (c *environClient) newInstance(
 
155
        args environs.StartInstanceParams,
 
156
        img *imagemetadata.ImageMetadata,
 
157
        userData []byte,
 
158
        authorizedKeys string,
 
159
) (srv gosigma.Server, drv gosigma.Drive, ar string, err error) {
 
160
 
 
161
        defer func() {
 
162
                if err == nil {
 
163
                        return
 
164
                }
 
165
                if srv != nil {
 
166
                        srv.Remove(gosigma.RecurseAllDrives)
 
167
                } else if drv != nil {
 
168
                        drv.Remove()
 
169
                }
 
170
                srv = nil
 
171
                drv = nil
 
172
        }()
 
173
 
 
174
        if args.InstanceConfig == nil {
 
175
                err = errors.New("invalid configuration for new instance: InstanceConfig is nil")
 
176
                return nil, nil, "", err
 
177
        }
 
178
 
 
179
        logger.Debugf("Tools: %v", args.Tools.URLs())
 
180
        logger.Debugf("Juju Constraints:" + args.Constraints.String())
 
181
        logger.Debugf("InstanceConfig: %#v", args.InstanceConfig)
 
182
 
 
183
        constraints := newConstraints(args.InstanceConfig.Bootstrap != nil, args.Constraints, img)
 
184
        logger.Debugf("CloudSigma Constraints: %v", constraints)
 
185
 
 
186
        originalDrive, err := c.conn.Drive(constraints.driveTemplate, gosigma.LibraryMedia)
 
187
        if err != nil {
 
188
                err = errors.Annotatef(err, "Failed to query drive template")
 
189
                return nil, nil, "", err
 
190
        }
 
191
 
 
192
        baseName := "juju-" + c.uuid + "-" + args.InstanceConfig.MachineId
 
193
 
 
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
 
198
        }
 
199
 
 
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
 
204
                }
 
205
        }
 
206
 
 
207
        cc, err := c.generateSigmaComponents(
 
208
                baseName, constraints, args, drv, userData, authorizedKeys,
 
209
        )
 
210
        if err != nil {
 
211
                return nil, nil, "", errors.Trace(err)
 
212
        }
 
213
 
 
214
        if srv, err = c.conn.CreateServer(cc); err != nil {
 
215
                err = errors.Annotatef(err, "error creating new instance")
 
216
                return nil, nil, "", err
 
217
        }
 
218
 
 
219
        if err = srv.Start(); err != nil {
 
220
                err = errors.Annotatef(err, "error booting new instance")
 
221
                return nil, nil, "", err
 
222
        }
 
223
 
 
224
        // populate root drive hardware characteristics
 
225
        switch originalDrive.Arch() {
 
226
        case "64":
 
227
                ar = arch.AMD64
 
228
        case "32":
 
229
                ar = arch.I386
 
230
        default:
 
231
                err = errors.Errorf("unknown arch: %v", ar)
 
232
                return nil, nil, "", err
 
233
        }
 
234
 
 
235
        return srv, drv, ar, nil
 
236
}
 
237
 
 
238
func (c *environClient) generateSigmaComponents(
 
239
        baseName string,
 
240
        constraints *sigmaConstraints,
 
241
        args environs.StartInstanceParams,
 
242
        drv gosigma.Drive,
 
243
        userData []byte,
 
244
        authorizedKeys string,
 
245
) (cc gosigma.Components, err error) {
 
246
        cc.SetName(baseName)
 
247
        cc.SetDescription(baseName)
 
248
        cc.SetSMP(constraints.cores)
 
249
        cc.SetCPU(constraints.power)
 
250
        cc.SetMem(constraints.mem)
 
251
 
 
252
        vncpass, err := utils.RandomPassword()
 
253
        if err != nil {
 
254
                err = errors.Errorf("error generating password: %v", err)
 
255
                return
 
256
        }
 
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)
 
262
 
 
263
        if multiwatcher.AnyJobNeedsState(args.InstanceConfig.Jobs...) {
 
264
                cc.SetMeta(jujuMetaInstance, jujuMetaInstanceController)
 
265
        } else {
 
266
                cc.SetMeta(jujuMetaInstance, jujuMetaInstanceServer)
 
267
        }
 
268
 
 
269
        cc.SetMeta(jujuMetaEnvironment, c.uuid)
 
270
        cc.SetMeta(jujuMetaCoudInit, string(userData))
 
271
        cc.SetMeta(jujuMetaBase64, jujuMetaCoudInit)
 
272
 
 
273
        return cc, nil
 
274
}