~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/state/reboot.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 2014 Canonical Ltd.
 
2
// Copyright 2014 Cloudbase Solutions SRL
 
3
// Licensed under the AGPLv3, see LICENCE file for details.
 
4
 
 
5
package state
 
6
 
 
7
import (
 
8
        "fmt"
 
9
 
 
10
        "github.com/juju/errors"
 
11
        "gopkg.in/mgo.v2"
 
12
        "gopkg.in/mgo.v2/bson"
 
13
        "gopkg.in/mgo.v2/txn"
 
14
)
 
15
 
 
16
var _ RebootFlagSetter = (*Machine)(nil)
 
17
var _ RebootActionGetter = (*Machine)(nil)
 
18
 
 
19
// RebootAction defines the action a machine should
 
20
// take when a hook needs to reboot
 
21
type RebootAction string
 
22
 
 
23
const (
 
24
        // ShouldDoNothing instructs a machine agent that no action
 
25
        // is required on its part
 
26
        ShouldDoNothing RebootAction = "noop"
 
27
        // ShouldReboot instructs a machine to reboot
 
28
        // this happens when a hook running on a machine, requests
 
29
        // a reboot
 
30
        ShouldReboot RebootAction = "reboot"
 
31
        // ShouldShutdown instructs a machine to shut down. This usually
 
32
        // happens when running inside a container, and a hook on the parent
 
33
        // machine requests a reboot
 
34
        ShouldShutdown RebootAction = "shutdown"
 
35
)
 
36
 
 
37
// rebootDoc will hold the reboot flag for a machine.
 
38
type rebootDoc struct {
 
39
        DocID     string `bson:"_id"`
 
40
        Id        string `bson:"machineid"`
 
41
        ModelUUID string `bson:"model-uuid"`
 
42
}
 
43
 
 
44
func (m *Machine) setFlag() error {
 
45
        if m.Life() == Dead {
 
46
                return mgo.ErrNotFound
 
47
        }
 
48
        ops := []txn.Op{
 
49
                assertModelActiveOp(m.st.ModelUUID()),
 
50
                {
 
51
                        C:      machinesC,
 
52
                        Id:     m.doc.DocID,
 
53
                        Assert: notDeadDoc,
 
54
                }, {
 
55
                        C:      rebootC,
 
56
                        Id:     m.doc.DocID,
 
57
                        Insert: &rebootDoc{Id: m.Id()},
 
58
                },
 
59
        }
 
60
        err := m.st.runTransaction(ops)
 
61
        if err == txn.ErrAborted {
 
62
                if err := checkModelActive(m.st); err != nil {
 
63
                        return errors.Trace(err)
 
64
                }
 
65
                return mgo.ErrNotFound
 
66
        } else if err != nil {
 
67
                return errors.Errorf("failed to set reboot flag: %v", err)
 
68
        }
 
69
        return nil
 
70
}
 
71
 
 
72
func removeRebootDocOp(st *State, machineId string) txn.Op {
 
73
        op := txn.Op{
 
74
                C:      rebootC,
 
75
                Id:     st.docID(machineId),
 
76
                Remove: true,
 
77
        }
 
78
        return op
 
79
}
 
80
 
 
81
func (m *Machine) clearFlag() error {
 
82
        reboot, closer := m.st.getCollection(rebootC)
 
83
        defer closer()
 
84
 
 
85
        docID := m.doc.DocID
 
86
        count, err := reboot.FindId(docID).Count()
 
87
        if count == 0 {
 
88
                return nil
 
89
        }
 
90
        ops := []txn.Op{removeRebootDocOp(m.st, m.Id())}
 
91
        err = m.st.runTransaction(ops)
 
92
        if err != nil {
 
93
                return errors.Errorf("failed to clear reboot flag: %v", err)
 
94
        }
 
95
        return nil
 
96
}
 
97
 
 
98
// SetRebootFlag sets the reboot flag of a machine to a boolean value. It will also
 
99
// do a lazy create of a reboot document if needed; i.e. If a document
 
100
// does not exist yet for this machine, it will create it.
 
101
func (m *Machine) SetRebootFlag(flag bool) error {
 
102
        if flag {
 
103
                return m.setFlag()
 
104
        }
 
105
        return m.clearFlag()
 
106
}
 
107
 
 
108
// GetRebootFlag returns the reboot flag for this machine.
 
109
func (m *Machine) GetRebootFlag() (bool, error) {
 
110
        rebootCol, closer := m.st.getCollection(rebootC)
 
111
        defer closer()
 
112
 
 
113
        count, err := rebootCol.FindId(m.doc.DocID).Count()
 
114
        if err != nil {
 
115
                return false, fmt.Errorf("failed to get reboot flag: %v", err)
 
116
        }
 
117
        if count == 0 {
 
118
                return false, nil
 
119
        }
 
120
        return true, nil
 
121
}
 
122
 
 
123
func (m *Machine) machinesToCareAboutRebootsFor() []string {
 
124
        var possibleIds []string
 
125
        for currentId := m.Id(); currentId != ""; {
 
126
                possibleIds = append(possibleIds, currentId)
 
127
                currentId = ParentId(currentId)
 
128
        }
 
129
        return possibleIds
 
130
}
 
131
 
 
132
// ShouldRebootOrShutdown check if the current node should reboot or shutdown
 
133
// If we are a container, and our parent needs to reboot, this should return:
 
134
// ShouldShutdown
 
135
func (m *Machine) ShouldRebootOrShutdown() (RebootAction, error) {
 
136
        rebootCol, closer := m.st.getCollection(rebootC)
 
137
        defer closer()
 
138
 
 
139
        machines := m.machinesToCareAboutRebootsFor()
 
140
 
 
141
        docs := []rebootDoc{}
 
142
        sel := bson.D{{"machineid", bson.D{{"$in", machines}}}}
 
143
        if err := rebootCol.Find(sel).All(&docs); err != nil {
 
144
                return ShouldDoNothing, errors.Trace(err)
 
145
        }
 
146
 
 
147
        iNeedReboot := false
 
148
        for _, val := range docs {
 
149
                if val.Id != m.doc.Id {
 
150
                        return ShouldShutdown, nil
 
151
                }
 
152
                iNeedReboot = true
 
153
        }
 
154
        if iNeedReboot {
 
155
                return ShouldReboot, nil
 
156
        }
 
157
        return ShouldDoNothing, nil
 
158
}
 
159
 
 
160
type RebootFlagSetter interface {
 
161
        SetRebootFlag(flag bool) error
 
162
}
 
163
 
 
164
type RebootActionGetter interface {
 
165
        ShouldRebootOrShutdown() (RebootAction, error)
 
166
}