~ubuntu-branches/ubuntu/trusty/juju-core/trusty-proposed

« back to all changes in this revision

Viewing changes to src/launchpad.net/juju-core/state/addmachine.go

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-02-03 09:22:46 UTC
  • mfrom: (1.1.17)
  • Revision ID: package-import@ubuntu.com-20140203092246-e03vg402vztzo4qa
Tags: 1.17.2-0ubuntu1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
        "launchpad.net/juju-core/constraints"
13
13
        "launchpad.net/juju-core/errors"
14
14
        "launchpad.net/juju-core/instance"
 
15
        "launchpad.net/juju-core/replicaset"
15
16
        "launchpad.net/juju-core/state/api/params"
16
17
        "launchpad.net/juju-core/utils"
17
18
)
28
29
 
29
30
        // Jobs holds the jobs to run on the machine's instance.
30
31
        // A machine must have at least one job to do.
 
32
        // JobManageEnviron can only be part of the jobs
 
33
        // when the first (bootstrap) machine is added.
31
34
        Jobs []MachineJob
32
35
 
 
36
        // NoVote holds whether a machine running
 
37
        // a state server should abstain from peer voting.
 
38
        // It is ignored if Jobs does not contain JobManageEnviron.
 
39
        NoVote bool
 
40
 
33
41
        // Addresses holds the addresses to be associated with the
34
42
        // new machine.
35
43
        Addresses []instance.Address
56
64
        // principals holds the principal units that will
57
65
        // associated with the machine.
58
66
        principals []string
59
 
 
60
 
        // instanceId holds the instance id to be associated with the
61
 
        // new machine. It should only be set by InjectMachine.
62
 
        instanceId instance.Id
63
 
 
64
 
        // nonce holds the nonce for the new machine.
65
 
        // It should only be set by InjectMachine.
66
 
        nonce string
67
67
}
68
68
 
69
69
// AddMachineInsideNewMachine creates a new machine within a container
122
122
                return nil, fmt.Errorf("environment is no longer alive")
123
123
        }
124
124
        var ops []txn.Op
 
125
        var mdocs []*machineDoc
125
126
        for _, template := range templates {
126
127
                mdoc, addOps, err := st.addMachineOps(template)
127
128
                if err != nil {
128
129
                        return nil, err
129
130
                }
 
131
                mdocs = append(mdocs, mdoc)
130
132
                ms = append(ms, newMachine(st, mdoc))
131
133
                ops = append(ops, addOps...)
132
134
        }
 
135
        ssOps, err := st.maintainStateServersOps(mdocs, nil)
 
136
        if err != nil {
 
137
                return nil, err
 
138
        }
 
139
        ops = append(ops, ssOps...)
133
140
        ops = append(ops, env.assertAliveOp())
134
141
        if err := st.runTransaction(ops); err != nil {
135
142
                return nil, onAbort(err, fmt.Errorf("environment is no longer alive"))
161
168
// valid and combines it with values from the state
162
169
// to produce a resulting template that more accurately
163
170
// represents the data that will be inserted into the state.
164
 
func (st *State) effectiveMachineTemplate(p MachineTemplate) (MachineTemplate, error) {
 
171
func (st *State) effectiveMachineTemplate(p MachineTemplate, allowStateServer bool) (MachineTemplate, error) {
165
172
        if p.Series == "" {
166
173
                return MachineTemplate{}, fmt.Errorf("no series specified")
167
174
        }
187
194
                }
188
195
                jset[j] = true
189
196
        }
 
197
        if jset[JobManageEnviron] {
 
198
                if !allowStateServer {
 
199
                        return MachineTemplate{}, errStateServerNotAllowed
 
200
                }
 
201
        }
190
202
 
191
203
        if p.InstanceId != "" {
192
204
                if p.Nonce == "" {
202
214
// based on the given template. It also returns the machine document
203
215
// that will be inserted.
204
216
func (st *State) addMachineOps(template MachineTemplate) (*machineDoc, []txn.Op, error) {
205
 
        template, err := st.effectiveMachineTemplate(template)
 
217
        template, err := st.effectiveMachineTemplate(template, true)
206
218
        if err != nil {
207
219
                return nil, nil, err
208
220
        }
221
233
                        Assert: txn.DocMissing,
222
234
                        Insert: &instanceData{
223
235
                                Id:         mdoc.Id,
224
 
                                InstanceId: template.instanceId,
 
236
                                InstanceId: template.InstanceId,
225
237
                                Arch:       template.HardwareCharacteristics.Arch,
226
238
                                Mem:        template.HardwareCharacteristics.Mem,
227
239
                                RootDisk:   template.HardwareCharacteristics.RootDisk,
259
271
        if template.InstanceId != "" {
260
272
                return nil, nil, fmt.Errorf("cannot specify instance id for a new container")
261
273
        }
262
 
        template, err := st.effectiveMachineTemplate(template)
 
274
        template, err := st.effectiveMachineTemplate(template, false)
263
275
        if err != nil {
264
276
                return nil, nil, err
265
277
        }
315
327
        if err != nil {
316
328
                return nil, nil, err
317
329
        }
318
 
        parentTemplate, err = st.effectiveMachineTemplate(parentTemplate)
 
330
        parentTemplate, err = st.effectiveMachineTemplate(parentTemplate, false)
319
331
        if err != nil {
320
332
                return nil, nil, err
321
333
        }
324
336
        if err != nil {
325
337
                return nil, nil, err
326
338
        }
327
 
        template, err = st.effectiveMachineTemplate(template)
 
339
        template, err = st.effectiveMachineTemplate(template, false)
328
340
        if err != nil {
329
341
                return nil, nil, err
330
342
        }
353
365
                InstanceId: template.InstanceId,
354
366
                Nonce:      template.Nonce,
355
367
                Addresses:  instanceAddressesToAddresses(template.Addresses),
 
368
                NoVote:     template.NoVote,
356
369
        }
357
370
}
358
371
 
372
385
                }),
373
386
        }
374
387
}
 
388
 
 
389
func hasJob(jobs []MachineJob, job MachineJob) bool {
 
390
        for _, j := range jobs {
 
391
                if j == job {
 
392
                        return true
 
393
                }
 
394
        }
 
395
        return false
 
396
}
 
397
 
 
398
var errStateServerNotAllowed = fmt.Errorf("state server jobs specified without calling EnsureAvailability")
 
399
 
 
400
// maintainStateServersOps returns a set of operations that will maintain
 
401
// the state server information when the given machine documents
 
402
// are added to the machines collection. If currentInfo is nil,
 
403
// there can be only one machine document and it must have
 
404
// id 0 (this is a special case to allow adding the bootstrap machine)
 
405
func (st *State) maintainStateServersOps(mdocs []*machineDoc, currentInfo *StateServerInfo) ([]txn.Op, error) {
 
406
        var newIds, newVotingIds []string
 
407
        for _, doc := range mdocs {
 
408
                if !hasJob(doc.Jobs, JobManageEnviron) {
 
409
                        continue
 
410
                }
 
411
                newIds = append(newIds, doc.Id)
 
412
                if !doc.NoVote {
 
413
                        newVotingIds = append(newVotingIds, doc.Id)
 
414
                }
 
415
        }
 
416
        if len(newIds) == 0 {
 
417
                return nil, nil
 
418
        }
 
419
        if currentInfo == nil {
 
420
                // Allow bootstrap machine only.
 
421
                if len(mdocs) != 1 || mdocs[0].Id != "0" {
 
422
                        return nil, errStateServerNotAllowed
 
423
                }
 
424
                var err error
 
425
                currentInfo, err = st.StateServerInfo()
 
426
                if err != nil {
 
427
                        return nil, fmt.Errorf("cannot get state server info: %v", err)
 
428
                }
 
429
                if len(currentInfo.MachineIds) > 0 || len(currentInfo.VotingMachineIds) > 0 {
 
430
                        return nil, fmt.Errorf("state servers already exist")
 
431
                }
 
432
        }
 
433
        ops := []txn.Op{{
 
434
                C:  st.stateServers.Name,
 
435
                Id: environGlobalKey,
 
436
                Assert: D{{
 
437
                        "$and", []D{
 
438
                                {{"machineids", D{{"$size", len(currentInfo.MachineIds)}}}},
 
439
                                {{"votingmachineids", D{{"$size", len(currentInfo.VotingMachineIds)}}}},
 
440
                        },
 
441
                }},
 
442
                Update: D{
 
443
                        {"$addToSet", D{{"machineids", D{{"$each", newIds}}}}},
 
444
                        {"$addToSet", D{{"votingmachineids", D{{"$each", newVotingIds}}}}},
 
445
                },
 
446
        }}
 
447
        return ops, nil
 
448
}
 
449
 
 
450
// EnsureAvailability adds state server machines as necessary to make
 
451
// the number of live state servers equal to numStateServers. The given
 
452
// constraints and series will be attached to any new machines.
 
453
//
 
454
// TODO(rog):
 
455
// If any current state servers are down, they will be
 
456
// removed from the current set of voting replica set
 
457
// peers (although the machines themselves will remain
 
458
// and they will still remain part of the replica set).
 
459
// Once a machine's voting status has been removed,
 
460
// the machine itself may be removed.
 
461
func (st *State) EnsureAvailability(numStateServers int, cons constraints.Value, series string) error {
 
462
        if numStateServers%2 != 1 || numStateServers <= 0 {
 
463
                return fmt.Errorf("number of state servers must be odd and greater than zero")
 
464
        }
 
465
        if numStateServers > replicaset.MaxPeers {
 
466
                return fmt.Errorf("state server count is too large (allowed %d)", replicaset.MaxPeers)
 
467
        }
 
468
        info, err := st.StateServerInfo()
 
469
        if err != nil {
 
470
                return err
 
471
        }
 
472
        if len(info.VotingMachineIds) == numStateServers {
 
473
                // TODO(rog) #1271504 2014-01-22
 
474
                // Find machines which are down, set
 
475
                // their NoVote flag and add new machines to
 
476
                // replace them.
 
477
                return nil
 
478
        }
 
479
        if len(info.VotingMachineIds) > numStateServers {
 
480
                return fmt.Errorf("cannot reduce state server count")
 
481
        }
 
482
        mdocs := make([]*machineDoc, 0, numStateServers-len(info.MachineIds))
 
483
        var ops []txn.Op
 
484
        for i := len(info.MachineIds); i < numStateServers; i++ {
 
485
                template := MachineTemplate{
 
486
                        Series: series,
 
487
                        Jobs: []MachineJob{
 
488
                                JobHostUnits,
 
489
                                JobManageEnviron,
 
490
                        },
 
491
                        Constraints: cons,
 
492
                }
 
493
                mdoc, addOps, err := st.addMachineOps(template)
 
494
                if err != nil {
 
495
                        return err
 
496
                }
 
497
                mdocs = append(mdocs, mdoc)
 
498
                ops = append(ops, addOps...)
 
499
        }
 
500
        ssOps, err := st.maintainStateServersOps(mdocs, info)
 
501
        if err != nil {
 
502
                return fmt.Errorf("cannot prepare machine add operations: %v", err)
 
503
        }
 
504
        ops = append(ops, ssOps...)
 
505
        err = st.runTransaction(ops)
 
506
        if err != nil {
 
507
                return fmt.Errorf("failed to create new state server machines: %v", err)
 
508
        }
 
509
        return nil
 
510
}