Skip to content

Commit

Permalink
Merge pull request #3516 from dougm/alarms
Browse files Browse the repository at this point in the history
Add vCenter Alarm support
  • Loading branch information
dougm authored Aug 9, 2024
2 parents fc7056a + 9c28210 commit a22290f
Show file tree
Hide file tree
Showing 18 changed files with 1,592 additions and 6 deletions.
220 changes: 220 additions & 0 deletions alarm/manager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
/*
Copyright (c) 2024-2024 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package alarm

import (
"context"

"github.com/vmware/govmomi/event"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/property"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)

type Manager struct {
object.Common

pc *property.Collector
}

var Severity = map[types.ManagedEntityStatus]string{
types.ManagedEntityStatusGray: "Unknown",
types.ManagedEntityStatusGreen: "Normal",
types.ManagedEntityStatusYellow: "Warning",
types.ManagedEntityStatusRed: "Alert",
}

// GetManager wraps NewManager, returning ErrNotSupported
// when the client is not connected to a vCenter instance.
func GetManager(c *vim25.Client) (*Manager, error) {
if c.ServiceContent.AlarmManager == nil {
return nil, object.ErrNotSupported
}
return NewManager(c), nil
}

func NewManager(c *vim25.Client) *Manager {
m := Manager{
Common: object.NewCommon(c, *c.ServiceContent.AlarmManager),
pc: property.DefaultCollector(c),
}

return &m
}

func (m Manager) CreateAlarm(ctx context.Context, entity object.Reference, spec types.BaseAlarmSpec) (*types.ManagedObjectReference, error) {
req := types.CreateAlarm{
This: m.Reference(),
Entity: entity.Reference(),
Spec: spec,
}

res, err := methods.CreateAlarm(ctx, m.Client(), &req)
if err != nil {
return nil, err
}
return &res.Returnval, err
}

func (m Manager) AcknowledgeAlarm(ctx context.Context, alarm types.ManagedObjectReference, entity object.Reference) error {
req := types.AcknowledgeAlarm{
This: m.Reference(),
Alarm: alarm,
Entity: entity.Reference(),
}

_, err := methods.AcknowledgeAlarm(ctx, m.Client(), &req)

return err
}

// GetAlarm returns available alarms defined on the entity.
func (m Manager) GetAlarm(ctx context.Context, entity object.Reference) ([]mo.Alarm, error) {
req := types.GetAlarm{
This: m.Reference(),
}

if entity != nil {
req.Entity = types.NewReference(entity.Reference())
}

res, err := methods.GetAlarm(ctx, m.Client(), &req)
if err != nil {
return nil, err
}

if len(res.Returnval) == 0 {
return nil, nil
}

alarms := make([]mo.Alarm, 0, len(res.Returnval))

err = m.pc.Retrieve(ctx, res.Returnval, []string{"info"}, &alarms)
if err != nil {
return nil, err
}

return alarms, nil
}

// StateInfo combines AlarmState with Alarm.Info
type StateInfo struct {
types.AlarmState
Info *types.AlarmInfo `json:"name,omitempty"`
Path string `json:"path,omitempty"`
Event types.BaseEvent `json:"event,omitempty"`
}

// StateInfoOptions for the GetStateInfo method
type StateInfoOptions struct {
Declared bool
InventoryPath bool
Event bool
}

// GetStateInfo combines AlarmState with Alarm.Info
func (m Manager) GetStateInfo(ctx context.Context, entity object.Reference, opts StateInfoOptions) ([]StateInfo, error) {
prop := "triggeredAlarmState"
if opts.Declared {
prop = "declaredAlarmState"
opts.Event = false
}

var e mo.ManagedEntity

err := m.pc.RetrieveOne(ctx, entity.Reference(), []string{prop}, &e)
if err != nil {
return nil, err
}

var objs []types.ManagedObjectReference
alarms := append(e.DeclaredAlarmState, e.TriggeredAlarmState...)
if len(alarms) == 0 {
return nil, nil
}
for i := range alarms {
objs = append(objs, alarms[i].Alarm)
}

var info []mo.Alarm
err = m.pc.Retrieve(ctx, objs, []string{"info"}, &info)
if err != nil {
return nil, err
}

state := make([]StateInfo, len(alarms))
paths := make(map[types.ManagedObjectReference]string)

em := event.NewManager(m.Client())

for i, a := range alarms {
path := paths[a.Entity]
if opts.InventoryPath {
if path == "" {
path, err = find.InventoryPath(ctx, m.Client(), a.Entity)
if err != nil {
return nil, err
}
paths[a.Entity] = path
}
} else {
path = a.Entity.String()
}

state[i] = StateInfo{
AlarmState: a,
Path: path,
}

for j := range info {
if info[j].Self == a.Alarm {
state[i].Info = &info[j].Info
break
}
}

if !opts.Event || a.EventKey == 0 {
continue
}

spec := types.EventFilterSpec{
EventChainId: a.EventKey,
Entity: &types.EventFilterSpecByEntity{
Entity: a.Entity,
Recursion: types.EventFilterSpecRecursionOptionSelf,
},
}

events, err := em.QueryEvents(ctx, spec)
if err != nil {
return nil, err
}

for j := range events {
if events[j].GetEvent().Key == a.EventKey {
state[i].Event = events[j]
break
}
}
}

return state, nil
}
56 changes: 56 additions & 0 deletions govc/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ but appear via `govc $cmd -h`:

- [about](#about)
- [about.cert](#aboutcert)
- [alarm.info](#alarminfo)
- [alarms](#alarms)
- [cluster.add](#clusteradd)
- [cluster.change](#clusterchange)
- [cluster.create](#clustercreate)
Expand Down Expand Up @@ -455,6 +457,60 @@ Options:
-thumbprint=false Output host hash and thumbprint only
```

## alarm.info

```
Usage: govc alarm.info [OPTIONS] PATH
Alarm definition info.
Examples:
govc alarm.info
govc alarm.info /dc1/host/cluster1
govc alarm.info -n alarm.WCPRegisterVMFailedAlarm
Options:
-n=[] Alarm name
```

## alarms

```
Usage: govc alarms [OPTIONS] [PATH]
Show triggered or declared alarms.
Triggered alarms: alarms triggered by this entity or by its descendants.
Triggered alarms are propagated up the inventory hierarchy so that a user
can readily tell when a descendant has triggered an alarm.
Declared alarms: alarms that apply to this managed entity.
Includes alarms defined on this entity and alarms inherited from the parent
entity, or from any ancestors in the inventory hierarchy.
PATH defaults to the root folder '/'.
When PATH is provided it should be an absolute inventory path or relative
to GOVC_DATACENTER. See also:
govc find -h
govc tree -h
Examples:
govc alarms
govc alarms vm/folder/vm-name
govc alarms /dc1/host/cluster1
govc alarms /dc1/host/cluster1 -d
govc alarms -n alarm.WCPRegisterVMFailedAlarm
govc alarms -ack /dc1/host/cluster1
govc alarms -ack -n alarm.WCPRegisterVMFailedAlarm vm/vm-name
Options:
-ack=false Acknowledge alarms
-d=false Show declared alarms
-l=false Long listing output
-n= Filter by alarm name
```

## cluster.add

```
Expand Down
Loading

0 comments on commit a22290f

Please sign in to comment.