github.com/vmware/govmomi@v0.51.0/simulator/alarm_manager.go (about) 1 // © Broadcom. All Rights Reserved. 2 // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. 3 // SPDX-License-Identifier: Apache-2.0 4 5 package simulator 6 7 import ( 8 "strings" 9 "time" 10 11 "github.com/vmware/govmomi/simulator/vpx" 12 "github.com/vmware/govmomi/vim25/methods" 13 "github.com/vmware/govmomi/vim25/mo" 14 "github.com/vmware/govmomi/vim25/soap" 15 "github.com/vmware/govmomi/vim25/types" 16 ) 17 18 type AlarmManager struct { 19 mo.AlarmManager 20 21 types.GetAlarmResponse 22 } 23 24 func (m *AlarmManager) init(r *Registry) { 25 if m.GetAlarmResponse.Returnval != nil { 26 return 27 } 28 29 m.GetAlarmResponse.Returnval = make([]types.ManagedObjectReference, len(vpx.Alarm)) 30 for i, alarm := range vpx.Alarm { 31 m.GetAlarmResponse.Returnval[i] = alarm.Self 32 r.Put(&Alarm{Alarm: alarm}) 33 } 34 } 35 36 func (*AlarmManager) trimPrefix(s string) string { 37 return strings.TrimPrefix(s, "vim.") 38 } 39 40 func (*AlarmManager) key(refs ...types.ManagedObjectReference) string { 41 keys := make([]string, len(refs)) 42 for i := range refs { 43 s := strings.Split(refs[i].Value, "-") 44 keys[i] = s[len(s)-1] 45 } 46 return strings.Join(keys, ".") 47 } 48 49 // only handling the common use case of EventEx for now 50 func (m *AlarmManager) matchAlarm(alarm *Alarm, event *types.EventEx) (*mo.Alarm, types.ManagedEntityStatus) { 51 id := event.EventTypeId 52 kind := m.trimPrefix(event.ObjectType) 53 54 switch op := alarm.Info.Expression.(type) { 55 case *types.OrAlarmExpression: 56 for i := range op.Expression { 57 switch x := op.Expression[i].(type) { 58 case *types.EventAlarmExpression: 59 if x.EventTypeId == id && kind == m.trimPrefix(x.ObjectType) { 60 return &alarm.Alarm, x.Status 61 } 62 } 63 } 64 } 65 return nil, "" 66 } 67 68 // update (e.g. triggeredAlarmState) and propagate up the inventory hierarchy 69 func (*AlarmManager) update(ctx *Context, me mo.Entity, update func(mo.Entity) *types.ManagedObjectReference) { 70 for { 71 if me == nil { 72 break 73 } 74 ctx.WithLock(me, func() { 75 parent := update(me) 76 if parent == nil { 77 me = nil 78 } else { 79 me = ctx.Map.Get(*parent).(mo.Entity) 80 } 81 }) 82 } 83 } 84 85 // postEvent triggers Alarms based on Events 86 func (m *AlarmManager) postEvent(ctx *Context, base types.BaseEvent) { 87 event, ok := base.(*types.EventEx) 88 if !ok { 89 return 90 } 91 92 entity := types.ManagedObjectReference{Type: event.ObjectType, Value: event.ObjectId} 93 me := ctx.Map.Get(entity).(mo.Entity) 94 95 for _, ref := range m.GetAlarmResponse.Returnval { 96 alarm := ctx.Map.Get(ref).(*Alarm) 97 match, status := m.matchAlarm(alarm, event) 98 if match == nil { 99 continue 100 } 101 102 now := time.Now() 103 key := m.key(match.Self, entity) 104 105 update := func(me mo.Entity) *types.ManagedObjectReference { 106 obj := me.Entity() 107 108 for i, state := range obj.TriggeredAlarmState { 109 if state.Key != key { 110 continue 111 } 112 113 switch status { 114 case state.OverallStatus: 115 // no change 116 return nil 117 case types.ManagedEntityStatusGreen: 118 // remove 119 obj.TriggeredAlarmState = 120 append(obj.TriggeredAlarmState[:i], 121 obj.TriggeredAlarmState[i+1:]...) 122 return obj.Parent 123 default: 124 // status change (e.g. yellow -> red) 125 obj.TriggeredAlarmState[i].OverallStatus = status 126 return obj.Parent 127 } 128 } 129 130 if status == types.ManagedEntityStatusGreen { 131 return nil // green only clears a triggered alarm 132 } 133 134 // add 135 state := types.AlarmState{ 136 Key: key, 137 Entity: entity, 138 Alarm: match.Self, 139 OverallStatus: status, 140 Time: now, 141 EventKey: event.Key, 142 Acknowledged: types.NewBool(false), 143 } 144 145 obj.TriggeredAlarmState = append(obj.TriggeredAlarmState, state) 146 147 return obj.Parent 148 } 149 150 m.update(ctx, me, update) 151 } 152 } 153 154 func (m *AlarmManager) GetAlarm(ctx *Context, req *types.GetAlarm) soap.HasFault { 155 body := &methods.GetAlarmBody{ 156 Res: new(types.GetAlarmResponse), 157 } 158 159 if req.Entity == nil || *req.Entity == ctx.Map.content().RootFolder { 160 body.Res.Returnval = m.GetAlarmResponse.Returnval 161 } // else TODO 162 163 return body 164 } 165 166 func (m *AlarmManager) CreateAlarm(ctx *Context, req *types.CreateAlarm) soap.HasFault { 167 body := new(methods.CreateAlarmBody) 168 169 name := req.Spec.GetAlarmSpec().Name 170 171 for _, alarm := range ctx.Map.AllReference("Alarm") { 172 if alarm.(*Alarm).Info.Name == name { 173 body.Fault_ = Fault("", &types.DuplicateName{Name: name}) 174 return body 175 } 176 } 177 178 alarm := Alarm{ 179 Alarm: mo.Alarm{ 180 Info: types.AlarmInfo{ 181 AlarmSpec: *req.Spec.GetAlarmSpec(), 182 Entity: req.Entity, 183 LastModifiedTime: time.Now(), 184 LastModifiedUser: ctx.Session.UserName, 185 }, 186 }, 187 } 188 189 ref := ctx.Map.Put(&alarm).Reference() 190 alarm.Info.Alarm = ref 191 m.GetAlarmResponse.Returnval = append(m.GetAlarmResponse.Returnval, ref) 192 193 body.Res = &types.CreateAlarmResponse{ 194 Returnval: ref, 195 } 196 197 return body 198 } 199 200 func (m *AlarmManager) AcknowledgeAlarm(ctx *Context, req *types.AcknowledgeAlarm) soap.HasFault { 201 body := new(methods.AcknowledgeAlarmBody) 202 203 now := types.NewTime(time.Now()) 204 key := m.key(req.Alarm, req.Entity) 205 me := ctx.Map.Get(req.Entity).(mo.Entity) 206 207 update := func(me mo.Entity) *types.ManagedObjectReference { 208 obj := me.Entity() 209 210 for i, state := range obj.TriggeredAlarmState { 211 if state.Key == key { 212 if *obj.TriggeredAlarmState[i].Acknowledged { 213 return nil // already ack-ed 214 } 215 obj.TriggeredAlarmState[i].Acknowledged = types.NewBool(true) 216 obj.TriggeredAlarmState[i].AcknowledgedTime = now 217 obj.TriggeredAlarmState[i].AcknowledgedByUser = ctx.Session.UserName 218 return obj.Parent 219 } 220 } 221 222 return nil 223 } 224 225 m.update(ctx, me, update) 226 227 body.Res = new(types.AcknowledgeAlarmResponse) 228 229 return body 230 } 231 232 type Alarm struct { 233 mo.Alarm 234 } 235 236 func (a *Alarm) ReconfigureAlarm(ctx *Context, req *types.ReconfigureAlarm) soap.HasFault { 237 body := new(methods.ReconfigureAlarmBody) 238 239 // TODO: spec validation 240 241 a.Info.AlarmSpec = *req.Spec.GetAlarmSpec() 242 243 body.Res = new(types.ReconfigureAlarmResponse) 244 245 return body 246 } 247 248 func (a *Alarm) RemoveAlarm(ctx *Context, req *types.RemoveAlarm) soap.HasFault { 249 m := ctx.Map.AlarmManager() 250 251 RemoveReference(&m.GetAlarmResponse.Returnval, req.This) 252 253 ctx.Map.Remove(ctx, req.This) 254 255 return &methods.RemoveAlarmBody{ 256 Res: new(types.RemoveAlarmResponse), 257 } 258 }