github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/state/meterstatus.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "github.com/juju/errors" 8 jujutxn "github.com/juju/txn" 9 "gopkg.in/mgo.v2/bson" 10 "gopkg.in/mgo.v2/txn" 11 ) 12 13 // MeterStatus represents the metering status of a unit. 14 type MeterStatus struct { 15 Code MeterStatusCode 16 Info string 17 } 18 19 // Severity returns relative severity of the meter status. 20 func (m *MeterStatus) Severity() int { 21 return m.Code.Severity() 22 } 23 24 // MeterStatusCode represents the meter status code of a unit. 25 // The int value represents its relative severity when compared to 26 // other MeterStatusCodes. 27 type MeterStatusCode int 28 29 // Severity returns the relative severity. 30 func (m MeterStatusCode) Severity() int { 31 return int(m) 32 } 33 34 // String returns a human readable string representation of the meter status. 35 func (m MeterStatusCode) String() string { 36 s, ok := meterString[m] 37 if !ok { 38 return MeterNotAvailable.String() 39 } 40 return s 41 } 42 43 // MeterStatusFromString returns a valid MeterStatusCode given a string representation. 44 func MeterStatusFromString(str string) MeterStatusCode { 45 for m, s := range meterString { 46 if s == str { 47 return m 48 } 49 } 50 return MeterNotAvailable 51 } 52 53 // This const block defines the relative severities of the valid MeterStatusCodes in ascending order. 54 const ( 55 MeterNotAvailable MeterStatusCode = iota 56 MeterRed 57 MeterAmber 58 MeterNotSet 59 MeterGreen 60 ) 61 62 var ( 63 meterString = map[MeterStatusCode]string{ 64 MeterGreen: "GREEN", 65 MeterNotSet: "NOT SET", 66 MeterAmber: "AMBER", 67 MeterNotAvailable: "NOT AVAILABLE", 68 MeterRed: "RED", 69 } 70 ) 71 72 type meterStatusDoc struct { 73 DocID string `bson:"_id"` 74 ModelUUID string `bson:"model-uuid"` 75 Code string `bson:"code"` 76 Info string `bson:"info"` 77 } 78 79 // SetMeterStatus sets the meter status for the unit. 80 func (u *Unit) SetMeterStatus(codeStr, info string) error { 81 code := MeterStatusFromString(codeStr) 82 switch code { 83 case MeterGreen, MeterAmber, MeterRed: 84 default: 85 return errors.Errorf("invalid meter status %q", code) 86 } 87 meterDoc, err := u.getMeterStatusDoc() 88 if err != nil { 89 return errors.Annotatef(err, "cannot update meter status for unit %s", u.Name()) 90 } 91 if meterDoc.Code == code.String() && meterDoc.Info == info { 92 return nil 93 } 94 95 buildTxn := func(attempt int) ([]txn.Op, error) { 96 if attempt > 0 { 97 err := u.Refresh() 98 if err != nil { 99 return nil, errors.Trace(err) 100 } 101 meterDoc, err = u.getMeterStatusDoc() 102 if err != nil { 103 return nil, errors.Annotatef(err, "cannot update meter status for unit %s", u.Name()) 104 } 105 if meterDoc.Code == code.String() && meterDoc.Info == info { 106 return nil, jujutxn.ErrNoOperations 107 } 108 } 109 return []txn.Op{ 110 { 111 C: unitsC, 112 Id: u.doc.DocID, 113 Assert: isAliveDoc, 114 }, { 115 C: meterStatusC, 116 Id: u.st.docID(u.globalMeterStatusKey()), 117 Assert: txn.DocExists, 118 Update: bson.D{{"$set", bson.D{{"code", code.String()}, {"info", info}}}}, 119 }}, nil 120 } 121 return errors.Annotatef(u.st.run(buildTxn), "cannot set meter state for unit %s", u.Name()) 122 } 123 124 // createMeterStatusOp returns the operation needed to create the meter status 125 // document associated with the given globalKey. 126 func createMeterStatusOp(st *State, globalKey string, doc *meterStatusDoc) txn.Op { 127 doc.ModelUUID = st.ModelUUID() 128 return txn.Op{ 129 C: meterStatusC, 130 Id: st.docID(globalKey), 131 Assert: txn.DocMissing, 132 Insert: doc, 133 } 134 } 135 136 // removeMeterStatusOp returns the operation needed to remove the meter status 137 // document associated with the given globalKey. 138 func removeMeterStatusOp(st *State, globalKey string) txn.Op { 139 return txn.Op{ 140 C: meterStatusC, 141 Id: st.docID(globalKey), 142 Remove: true, 143 } 144 } 145 146 // GetMeterStatus returns the meter status for the unit. 147 func (u *Unit) GetMeterStatus() (MeterStatus, error) { 148 mm, err := u.st.MetricsManager() 149 if err != nil { 150 return MeterStatus{MeterNotAvailable, ""}, errors.Annotatef(err, "cannot retrieve meter status for metrics manager") 151 } 152 153 mmStatus := mm.MeterStatus() 154 if mmStatus.Code == MeterRed { 155 return mmStatus, nil 156 } 157 158 status, err := u.getMeterStatusDoc() 159 if err != nil { 160 return MeterStatus{MeterNotAvailable, ""}, errors.Annotatef(err, "cannot retrieve meter status for unit %s", u.Name()) 161 } 162 163 code := MeterStatusFromString(status.Code) 164 165 unitMeterStatus := MeterStatus{code, status.Info} 166 return combineMeterStatus(mmStatus, unitMeterStatus), nil 167 } 168 169 func combineMeterStatus(a, b MeterStatus) MeterStatus { 170 if a.Severity() < b.Severity() { 171 return a 172 } 173 return b 174 } 175 176 func (u *Unit) getMeterStatusDoc() (*meterStatusDoc, error) { 177 meterStatuses, closer := u.st.getCollection(meterStatusC) 178 defer closer() 179 var status meterStatusDoc 180 err := meterStatuses.FindId(u.globalMeterStatusKey()).One(&status) 181 if err != nil { 182 return nil, errors.Trace(err) 183 } 184 return &status, nil 185 }