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  }