bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/cmd/bosun/sched/host.go (about)

     1  package sched // import "bosun.org/cmd/bosun/sched"
     2  import (
     3  	"encoding/json"
     4  	"fmt"
     5  	"strconv"
     6  	"time"
     7  
     8  	"bosun.org/metadata"
     9  	"bosun.org/opentsdb"
    10  	"bosun.org/slog"
    11  )
    12  
    13  func (s *Schedule) Host(filter string) (map[string]*HostData, error) {
    14  	timeFilterAge := time.Hour * 2 * 24
    15  	hosts := make(map[string]*HostData)
    16  	allHosts, err := s.Search.TagValuesByTagKey("host", timeFilterAge)
    17  	if err != nil {
    18  		return nil, err
    19  	}
    20  	for _, h := range allHosts {
    21  		hosts[h] = newHostData()
    22  	}
    23  	states, err := s.GetOpenStates()
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  	silences := s.Silenced()
    28  	// These are all fetched by metric since that is how we store it in redis,
    29  	// so this makes for the fastest response
    30  	tagsByKey := func(metric, hostKey string) (map[string][]opentsdb.TagSet, error) {
    31  		byKey := make(map[string][]opentsdb.TagSet)
    32  		tags, err := s.Search.FilteredTagSets(metric, nil, 0)
    33  		if err != nil {
    34  			return byKey, err
    35  		}
    36  		for _, ts := range tags {
    37  			if host, ok := ts[hostKey]; ok {
    38  				// Make sure the host exists based on our time filter
    39  				if _, ok := hosts[host]; ok {
    40  					byKey[host] = append(byKey[host], ts)
    41  				}
    42  			}
    43  		}
    44  		return byKey, nil
    45  	}
    46  	oldTimestamp := utcNow().Add(-timeFilterAge).Unix()
    47  	oldOrErr := func(ts int64, err error) bool {
    48  		if ts < oldTimestamp || err != nil {
    49  			return true
    50  		}
    51  		return false
    52  	}
    53  	osNetBytesTags, err := tagsByKey("os.net.bytes", "host")
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  	osNetVirtualBytesTags, err := tagsByKey("os.net.virtual.bytes", "host")
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  	osNetBondBytesTags, err := tagsByKey("os.net.bond.bytes", "host")
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  	osNetTunnelBytesTags, err := tagsByKey("os.net.tunnel.bytes", "host")
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  	osNetOtherBytesTags, err := tagsByKey("os.net.other.bytes", "host")
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	osNetIfSpeedTags, err := tagsByKey("os.net.ifspeed", "host")
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  	osNetVirtualIfSpeedTags, err := tagsByKey("os.net.virtual.ifspeed", "host")
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  	osNetBondIfSpeedTags, err := tagsByKey("os.net.bond.ifspeed", "host")
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  	osNetTunnelIfSpeedTags, err := tagsByKey("os.net.tunnel.ifspeed", "host")
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  	osNetOtherIfSpeedTags, err := tagsByKey("os.net.other.ifspeed", "host")
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  	hwChassisTags, err := tagsByKey("hw.chassis", "host")
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	hwPhysicalDiskTags, err := tagsByKey("hw.storage.pdisk", "host")
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  	hwVirtualDiskTags, err := tagsByKey("hw.storage.vdisk", "host")
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  	hwControllersTags, err := tagsByKey("hw.storage.controller", "host")
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  	hwBatteriesTags, err := tagsByKey("hw.storage.battery", "host")
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	hwPowerSuppliesTags, err := tagsByKey("hw.ps", "host")
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  	hwTempsTags, err := tagsByKey("hw.chassis.temps.reading", "host")
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  	hwBoardPowerTags, err := tagsByKey("hw.chassis.power.reading", "host")
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  	diskTags, err := tagsByKey("os.disk.fs.space_total", "host")
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  	serviceTags, err := tagsByKey("os.service.running", "host")
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	// Will assume the same tagsets exist .mem_real, .mem_virtual and possibly .count
   134  	processTags, err := tagsByKey("os.proc.cpu", "host")
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  	// Will make the assumption that the metric bosun.ping.timeout, resolved, and rtt
   139  	// all share the same tagset
   140  	icmpTimeOutTags, err := tagsByKey("bosun.ping.timeout", "dst_host")
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  	for name, host := range hosts {
   145  		host.Name = name
   146  		hostTagSet := opentsdb.TagSet{"host": host.Name}
   147  		hostMetadata, err := s.GetMetadata("", hostTagSet)
   148  		if err != nil {
   149  			slog.Error(err)
   150  		}
   151  		processHostIncidents(host, states, silences)
   152  		for _, ts := range icmpTimeOutTags[host.Name] {
   153  			// The host tag represents the polling source for these set of metrics
   154  			source, ok := ts["host"]
   155  			if !ok {
   156  				slog.Errorf("couldn't find source tag for icmp data for host %s", host.Name)
   157  			}
   158  			// 1 Means it timed out
   159  			timeout, timestamp, err := s.Search.GetLast("bosun.ping.timeout", ts.String(), false)
   160  			if oldOrErr(timestamp, err) {
   161  				continue
   162  			}
   163  			rtt, rttTimestamp, _ := s.Search.GetLast("bosun.ping.rtt", ts.String(), false)
   164  			// 1 means dns resolution was successful
   165  			dnsLookup, dnsTimestamp, dnsErr := s.Search.GetLast("bosun.ping.resolved", ts.String(), false)
   166  			host.ICMPData[source] = &ICMPData{
   167  				TimedOut:               timeout == 1 && err == nil,
   168  				TimedOutLastUpdated:    timestamp,
   169  				DNSResolved:            dnsLookup == 1 && dnsErr == nil,
   170  				DNSResolvedLastUpdated: dnsTimestamp,
   171  				RTTMS:                  rtt,
   172  				RTTLastUpdated:         rttTimestamp,
   173  			}
   174  
   175  		}
   176  		for _, ts := range serviceTags[host.Name] {
   177  			name, ok := ts["name"]
   178  			if !ok {
   179  				slog.Errorf("couldn't find service name tag %s for host %s", name, host.Name)
   180  				continue
   181  			}
   182  			fstatus, timestamp, err := s.Search.GetLast("os.service.running", ts.String(), false)
   183  			running := false
   184  			if fstatus != 0 {
   185  				running = true
   186  			}
   187  			if !oldOrErr(timestamp, err) {
   188  				host.Services[name] = &ServiceStatus{
   189  					Running:            running,
   190  					RunningLastUpdated: timestamp,
   191  				}
   192  			}
   193  		}
   194  		for _, ts := range processTags[host.Name] {
   195  			name, ok := ts["name"]
   196  			if !ok {
   197  				slog.Errorf("couldn't find process name tag %s for host %s", name, host.Name)
   198  				continue
   199  			}
   200  			p := &Process{}
   201  			p.CPUPercentUsed, p.CPUPercentLastUpdated, err = s.Search.GetLast("os.proc.cpu", ts.String(), true)
   202  			if oldOrErr(p.CPUPercentLastUpdated, err) {
   203  				continue
   204  			}
   205  			p.UsedRealBytes, p.UsedRealBytesLastUpdated, _ = s.Search.GetLastInt64("os.proc.mem.real", ts.String(), false)
   206  			p.UsedVirtualBytes, p.UsedVirtualBytesLastUpdated, _ = s.Search.GetLastInt64("os.proc.mem.virtual", ts.String(), false)
   207  			p.Count, p.CountLastUpdated, _ = s.Search.GetLastInt64("os.proc.count", ts.String(), false)
   208  			host.Processes[name] = p
   209  		}
   210  		// Process Hardware Chassis States
   211  		for _, ts := range hwChassisTags[host.Name] {
   212  			component, ok := ts["component"]
   213  			if !ok {
   214  				return nil, fmt.Errorf("couldn't find component tag for host %s", host.Name)
   215  			}
   216  			fstatus, timestamp, err := s.Search.GetLast("hw.chassis", ts.String(), false)
   217  			if !oldOrErr(timestamp, err) {
   218  				host.Hardware.ChassisComponents[component] = &ChassisComponent{
   219  					Status:            statusString(int64(fstatus), 0, "Ok", "Bad"),
   220  					StatusLastUpdated: timestamp,
   221  				}
   222  			}
   223  		}
   224  		for _, ts := range hwTempsTags[host.Name] {
   225  			name, ok := ts["name"]
   226  			if !ok {
   227  				slog.Errorf("couldn't find name tag %s for host %s", name, host.Name)
   228  			}
   229  			t := &Temp{}
   230  			var tStatus float64
   231  			tStatus, t.StatusLastUpdated, err = s.Search.GetLast("hw.chassis.temps", ts.String(), false)
   232  			t.Celsius, t.CelsiusLastUpdated, _ = s.Search.GetLast("hw.chassis.temps.reading", ts.String(), false)
   233  			if oldOrErr(t.StatusLastUpdated, err) {
   234  				continue
   235  			}
   236  			t.Status = statusString(int64(tStatus), 0, "Ok", "Bad")
   237  			host.Hardware.Temps[name] = t
   238  		}
   239  		for _, ts := range hwPowerSuppliesTags[host.Name] {
   240  			id, ok := ts["id"]
   241  			if !ok {
   242  				return nil, fmt.Errorf("couldn't find power supply tag for host %s", host.Name)
   243  			}
   244  			idPlus, err := strconv.Atoi(id)
   245  			if err != nil {
   246  				slog.Errorf("couldn't conver it do integer for power supply id %s", id)
   247  			}
   248  			idPlus++
   249  			ps := &PowerSupply{}
   250  			fstatus, timestamp, err := s.Search.GetLast("hw.ps", ts.String(), false)
   251  			ps.Amps, ps.AmpsLastUpdated, _ = s.Search.GetLast("hw.chassis.current.reading", opentsdb.TagSet{"host": host.Name, "id": fmt.Sprintf("PS%v", idPlus)}.String(), false)
   252  			ps.Volts, ps.VoltsLastUpdated, _ = s.Search.GetLast("hw.chassis.volts.reading", opentsdb.TagSet{"host": host.Name, "name": fmt.Sprintf("PS%v_Voltage_%v", idPlus, idPlus)}.String(), false)
   253  			if oldOrErr(timestamp, err) {
   254  				continue
   255  			}
   256  			ps.Status = statusString(int64(fstatus), 0, "Ok", "Bad")
   257  			host.Hardware.PowerSupplies[id] = ps
   258  			for _, m := range hostMetadata {
   259  				if m.Name != "psMeta" || m.Time.Before(utcNow().Add(-timeFilterAge)) || !m.Tags.Equal(ts) {
   260  					continue
   261  				}
   262  				if val, ok := m.Value.(string); ok {
   263  					err = json.Unmarshal([]byte(val), &ps)
   264  					if err != nil {
   265  						slog.Errorf("error unmarshalling power supply meta for host %s, while generating host api: %s", host.Name, err)
   266  					} else {
   267  						host.Hardware.PowerSupplies[id] = ps
   268  					}
   269  				}
   270  			}
   271  		}
   272  		for _, ts := range hwBatteriesTags[host.Name] {
   273  			id, ok := ts["id"]
   274  			if !ok {
   275  				slog.Errorf("couldn't find battery id tag %s for host %s", id, host.Name)
   276  				continue
   277  			}
   278  			fstatus, timestamp, err := s.Search.GetLast("hw.storage.battery", ts.String(), false)
   279  			if !oldOrErr(timestamp, err) {
   280  				host.Hardware.Storage.Batteries[id] = &Battery{
   281  					Status:            statusString(int64(fstatus), 0, "Ok", "Bad"),
   282  					StatusLastUpdated: timestamp,
   283  				}
   284  			}
   285  		}
   286  		for _, ts := range hwBoardPowerTags[host.Name] {
   287  			fstatus, timestamp, err := s.Search.GetLast("hw.chassis.power.reading", ts.String(), false)
   288  			if !oldOrErr(timestamp, err) {
   289  				host.Hardware.BoardPowerReading = &BoardPowerReading{
   290  					Watts:            int64(fstatus),
   291  					WattsLastUpdated: timestamp,
   292  				}
   293  			}
   294  		}
   295  		for _, ts := range hwPhysicalDiskTags[host.Name] {
   296  			id, ok := ts["id"]
   297  			if !ok {
   298  				return nil, fmt.Errorf("couldn't find physical disk id tag for host %s", host.Name)
   299  			}
   300  			pd := &PhysicalDisk{}
   301  			fstatus, timestamp, err := s.Search.GetLast("hw.storage.pdisk", ts.String(), false)
   302  			if !oldOrErr(timestamp, err) {
   303  				pd.Status = statusString(int64(fstatus), 0, "Ok", "Bad")
   304  				pd.StatusLastUpdated = timestamp
   305  				host.Hardware.Storage.PhysicalDisks[id] = pd
   306  			}
   307  			for _, m := range hostMetadata {
   308  				if m.Name != "physicalDiskMeta" || m.Time.Before(utcNow().Add(-timeFilterAge)) || !m.Tags.Equal(ts) {
   309  					continue
   310  				}
   311  				if val, ok := m.Value.(string); ok {
   312  					err = json.Unmarshal([]byte(val), &pd)
   313  					if err != nil {
   314  						slog.Errorf("error unmarshalling addresses for host %s, interface %s while generating host api: %s", host.Name, m.Tags["iface"], err)
   315  					} else {
   316  						host.Hardware.Storage.PhysicalDisks[id] = pd
   317  					}
   318  				}
   319  			}
   320  		}
   321  		for _, ts := range hwVirtualDiskTags[host.Name] {
   322  			id, ok := ts["id"]
   323  			if !ok {
   324  				return nil, fmt.Errorf("couldn't find virtual disk id tag for host %s", host.Name)
   325  			}
   326  			fstatus, timestamp, err := s.Search.GetLast("hw.storage.vdisk", ts.String(), false)
   327  			if !oldOrErr(timestamp, err) {
   328  				host.Hardware.Storage.VirtualDisks[id] = &VirtualDisk{
   329  					Status:            statusString(int64(fstatus), 0, "Ok", "Bad"),
   330  					StatusLastUpdated: timestamp,
   331  				}
   332  			}
   333  		}
   334  		for _, ts := range hwControllersTags[host.Name] {
   335  			id, ok := ts["id"]
   336  			if !ok {
   337  				return nil, fmt.Errorf("couldn't find controller id tag for host %s", host.Name)
   338  			}
   339  			fstatus, timestamp, err := s.Search.GetLast("hw.storage.controller", ts.String(), false)
   340  			c := &Controller{}
   341  			if !oldOrErr(timestamp, err) {
   342  				c.Status = statusString(int64(fstatus), 0, "Ok", "Bad")
   343  				c.StatusLastUpdated = timestamp
   344  				host.Hardware.Storage.Controllers[id] = c
   345  			}
   346  			for _, m := range hostMetadata {
   347  				if m.Name != "controllerMeta" || m.Time.Before(utcNow().Add(-timeFilterAge)) || !m.Tags.Equal(ts) {
   348  					continue
   349  				}
   350  				if val, ok := m.Value.(string); ok {
   351  					err = json.Unmarshal([]byte(val), &c)
   352  					if err != nil {
   353  						slog.Errorf("error unmarshalling controller meta for host %s: %s", host.Name, err)
   354  					} else {
   355  						host.Hardware.Storage.Controllers[id] = c
   356  					}
   357  				}
   358  			}
   359  		}
   360  		for _, ts := range diskTags[host.Name] {
   361  			disk, ok := ts["disk"]
   362  			if !ok {
   363  				return nil, fmt.Errorf("couldn't find disk tag for host %s", host.Name)
   364  			}
   365  			d := &Disk{}
   366  			d.TotalBytes, d.StatsLastUpdated, err = s.Search.GetLastInt64("os.disk.fs.space_total", ts.String(), false)
   367  			d.UsedBytes, _, _ = s.Search.GetLastInt64("os.disk.fs.space_used", ts.String(), false)
   368  			if oldOrErr(d.StatsLastUpdated, err) {
   369  				continue
   370  			}
   371  			host.Disks[disk] = d
   372  			for _, m := range hostMetadata {
   373  				if m.Name != "label" || m.Time.Before(utcNow().Add(-timeFilterAge)) || !m.Tags.Equal(ts) {
   374  					continue
   375  				}
   376  				if label, ok := m.Value.(string); ok {
   377  					host.Disks[disk].Label = label
   378  					break
   379  				}
   380  			}
   381  		}
   382  		// Get CPU, Memory, Uptime
   383  		var timestamp int64
   384  		var cpu float64
   385  		if cpu, timestamp, err = s.Search.GetLast("os.cpu", hostTagSet.String(), true); err != nil {
   386  			cpu, timestamp, _ = s.Search.GetLast("cisco.cpu", hostTagSet.String(), false)
   387  		}
   388  		host.CPU.PercentUsed = cpu
   389  		host.CPU.StatsLastUpdated = timestamp
   390  		host.Memory.TotalBytes, host.Memory.StatsLastUpdated, _ = s.Search.GetLast("os.mem.total", hostTagSet.String(), false)
   391  		host.Memory.UsedBytes, _, _ = s.Search.GetLast("os.mem.used", hostTagSet.String(), false)
   392  		host.UptimeSeconds, _, _ = s.Search.GetLastInt64("os.system.uptime", hostTagSet.String(), false)
   393  		for _, m := range hostMetadata {
   394  			if m.Time.Before(utcNow().Add(-timeFilterAge)) {
   395  				continue
   396  			}
   397  			var iface *HostInterface
   398  			if name := m.Tags["iface"]; name != "" {
   399  				if host.Interfaces[name] == nil {
   400  					h := new(HostInterface)
   401  					host.Interfaces[name] = h
   402  				}
   403  				iface = host.Interfaces[name]
   404  			}
   405  			if name := m.Tags["iname"]; name != "" && iface != nil {
   406  				iface.Name = name
   407  			}
   408  			switch val := m.Value.(type) {
   409  			case string:
   410  				switch m.Name {
   411  				case "addresses":
   412  					if iface != nil {
   413  						addresses := []string{}
   414  						err = json.Unmarshal([]byte(val), &addresses)
   415  						if err != nil {
   416  							slog.Errorf("error unmarshalling addresses for host %s, interface %s while generating host api: %s", host.Name, m.Tags["iface"], err)
   417  						}
   418  						for _, address := range addresses {
   419  							iface.IPAddresses = append(iface.IPAddresses, address)
   420  						}
   421  					}
   422  				case "cdpCacheEntries":
   423  					if iface != nil {
   424  						var cdpCacheEntries CDPCacheEntries
   425  						err = json.Unmarshal([]byte(val), &cdpCacheEntries)
   426  						if err != nil {
   427  							slog.Errorf("error unmarshalling cdpCacheEntries for host %s, interface %s while generating host api: %s", host.Name, m.Tags["iface"], err)
   428  						} else {
   429  							iface.CDPCacheEntries = cdpCacheEntries
   430  						}
   431  					}
   432  				case "remoteMacs":
   433  					if iface != nil {
   434  						remoteMacs := []string{}
   435  						err = json.Unmarshal([]byte(val), &remoteMacs)
   436  						if err != nil {
   437  							slog.Errorf("error unmarshalling remoteMacs for host %s, interface %s while generating host api: %s", host.Name, m.Tags["iface"], err)
   438  						} else {
   439  							iface.RemoteMacs = remoteMacs
   440  						}
   441  					}
   442  				case "description", "alias":
   443  					if iface != nil {
   444  						iface.Description = val
   445  					}
   446  				case "dataStores":
   447  					dataStores := []string{}
   448  					err = json.Unmarshal([]byte(val), &dataStores)
   449  					if err != nil {
   450  						slog.Errorf("error unmarshalling datastores for host %s while generating host api: %s", host.Name, err)
   451  					}
   452  					for _, dataStore := range dataStores {
   453  						tags := opentsdb.TagSet{"disk": dataStore}.String()
   454  						total, totalTs, totalErr := s.Search.GetLastInt64("vsphere.disk.space_total", tags, false)
   455  						used, usedTs, usedErr := s.Search.GetLastInt64("vsphere.disk.space_used", tags, false)
   456  						if totalErr != nil || usedErr != nil || totalTs < 1 || usedTs < 1 {
   457  							continue
   458  						}
   459  						host.Disks[dataStore] = &Disk{
   460  							TotalBytes: total,
   461  							UsedBytes:  used,
   462  						}
   463  					}
   464  				case "mac":
   465  					if iface != nil {
   466  						iface.MAC = val
   467  					}
   468  				case "manufacturer":
   469  					host.Manufacturer = val
   470  				case "master":
   471  					if iface != nil {
   472  						iface.Master = val
   473  					}
   474  				case "memory":
   475  					if name := m.Tags["name"]; name != "" {
   476  						fstatus, timestamp, err := s.Search.GetLast("hw.chassis.memory", opentsdb.TagSet{"host": host.Name, "name": name}.String(), false)
   477  						// Status code uses the severity function in collectors/dell_hw.go. That is a binary
   478  						// state that is 0 for non-critical or Ok. Todo would be to update this with more
   479  						// complete status codes when HW collector is refactored and we have something to
   480  						// clean out addr entries from the tagset metadata db
   481  						host.Hardware.Memory[name] = &MemoryModule{
   482  							StatusLastUpdated: timestamp,
   483  							Size:              val,
   484  						}
   485  						// Only set if we have a value
   486  						if err == nil && timestamp > 0 {
   487  							host.Hardware.Memory[name].Status = statusString(int64(fstatus), 0, "Ok", "Bad")
   488  						}
   489  					}
   490  				case "hypervisor":
   491  					host.VM = &VM{}
   492  					host.VM.Host = val
   493  					powerstate, timestamp, err := s.Search.GetLast("vsphere.guest.powered_state", opentsdb.TagSet{"guest": host.Name}.String(), false)
   494  					if timestamp > 0 && err != nil {
   495  						switch int64(powerstate) {
   496  						case 0:
   497  							host.VM.PowerState = "poweredOn"
   498  						case 1:
   499  							host.VM.PowerState = "poweredOff"
   500  						case 2:
   501  							host.VM.PowerState = "suspended"
   502  						}
   503  						host.VM.PowerStateLastUpdated = timestamp
   504  					}
   505  					if hostsHost, ok := hosts[val]; ok {
   506  						hostsHost.Guests = append(hostsHost.Guests, host.Name)
   507  					}
   508  				case "model":
   509  					host.Model = val
   510  				case "name":
   511  					if iface != nil {
   512  						iface.Name = val
   513  					}
   514  				case "processor":
   515  					if name := m.Tags["name"]; name != "" {
   516  						host.CPU.Processors[name] = val
   517  					}
   518  				case "serialNumber":
   519  					host.SerialNumber = val
   520  				case "version":
   521  					host.OS.Version = val
   522  				case "versionCaption", "uname":
   523  					host.OS.Caption = val
   524  				}
   525  			case float64:
   526  				switch m.Name {
   527  				case "speed":
   528  					if iface != nil {
   529  						iface.LinkSpeed = int64(val)
   530  					}
   531  				}
   532  			}
   533  		}
   534  		GetIfaceBits := func(netType string, ifaceId string, iface *HostInterface, host string, tags []opentsdb.TagSet) error {
   535  			metric := "os.net." + netType + ".bytes"
   536  			if netType == "" {
   537  				metric = "os.net.bytes"
   538  			}
   539  			for _, ts := range tags {
   540  				if ts["iface"] != ifaceId {
   541  					continue
   542  				}
   543  				dir, ok := ts["direction"]
   544  				if !ok {
   545  					continue
   546  				}
   547  				val, timestamp, _ := s.Search.GetLastInt64(metric, ts.String(), true)
   548  				if dir == "in" {
   549  					iface.Inbps = val * 8
   550  				}
   551  				if dir == "out" {
   552  					iface.Outbps = val * 8
   553  				}
   554  				iface.StatsLastUpdated = timestamp
   555  				iface.Type = netType
   556  			}
   557  			return nil
   558  		}
   559  		GetIfaceSpeed := func(netType string, ifaceId string, iface *HostInterface, host string, tags []opentsdb.TagSet) error {
   560  			metric := "os.net." + netType + ".ifspeed"
   561  			if netType == "" {
   562  				metric = "os.net.ifspeed"
   563  			}
   564  			for _, ts := range tags {
   565  				if ts["iface"] != ifaceId {
   566  					continue
   567  				}
   568  				val, timestamp, err := s.Search.GetLastInt64(metric, ts.String(), false)
   569  				if !oldOrErr(timestamp, err) {
   570  					iface.LinkSpeed = val
   571  				}
   572  			}
   573  			return nil
   574  		}
   575  		for ifaceId, iface := range host.Interfaces {
   576  			if err := GetIfaceBits("", ifaceId, iface, host.Name, osNetBytesTags[host.Name]); err != nil {
   577  				return nil, err
   578  			}
   579  			if err := GetIfaceBits("virtual", ifaceId, iface, host.Name, osNetVirtualBytesTags[host.Name]); err != nil {
   580  				return nil, err
   581  			}
   582  			if err := GetIfaceBits("bond", ifaceId, iface, host.Name, osNetBondBytesTags[host.Name]); err != nil {
   583  				return nil, err
   584  			}
   585  			if err := GetIfaceBits("tunnel", ifaceId, iface, host.Name, osNetTunnelBytesTags[host.Name]); err != nil {
   586  				return nil, err
   587  			}
   588  			if err := GetIfaceBits("other", ifaceId, iface, host.Name, osNetOtherBytesTags[host.Name]); err != nil {
   589  				return nil, err
   590  			}
   591  			if err := GetIfaceSpeed("", ifaceId, iface, host.Name, osNetIfSpeedTags[host.Name]); err != nil {
   592  				return nil, err
   593  			}
   594  			if err := GetIfaceSpeed("virtual", ifaceId, iface, host.Name, osNetVirtualIfSpeedTags[host.Name]); err != nil {
   595  				return nil, err
   596  			}
   597  			if err := GetIfaceSpeed("bond", ifaceId, iface, host.Name, osNetBondIfSpeedTags[host.Name]); err != nil {
   598  				return nil, err
   599  			}
   600  			if err := GetIfaceSpeed("tunnel", ifaceId, iface, host.Name, osNetTunnelIfSpeedTags[host.Name]); err != nil {
   601  				return nil, err
   602  			}
   603  			if err := GetIfaceSpeed("other", ifaceId, iface, host.Name, osNetOtherIfSpeedTags[host.Name]); err != nil {
   604  				return nil, err
   605  			}
   606  		}
   607  		host.Clean()
   608  	}
   609  	return hosts, nil
   610  }
   611  
   612  func statusString(val, goodVal int64, goodName, badName string) string {
   613  	if val == goodVal {
   614  		return goodName
   615  	}
   616  	return badName
   617  }
   618  
   619  func processHostIncidents(host *HostData, states States, silences SilenceTester) {
   620  	for ak, state := range states {
   621  		if stateHost, ok := state.AlertKey.Group()["host"]; !ok {
   622  			continue
   623  		} else if stateHost != host.Name {
   624  			continue
   625  		}
   626  		silenced := silences(ak)
   627  		is := IncidentStatus{
   628  			IncidentID:         state.Id,
   629  			Active:             state.IsActive(),
   630  			AlertKey:           state.AlertKey,
   631  			Status:             state.CurrentStatus,
   632  			StatusTime:         state.Last().Time.Unix(),
   633  			Subject:            state.Subject,
   634  			Silenced:           silenced != nil,
   635  			LastAbnormalStatus: state.LastAbnormalStatus,
   636  			LastAbnormalTime:   state.LastAbnormalTime,
   637  			NeedsAck:           state.NeedAck,
   638  		}
   639  		host.OpenIncidents = append(host.OpenIncidents, is)
   640  	}
   641  }
   642  
   643  // Cisco Discovery Protocol
   644  type CDPCacheEntry struct {
   645  	DeviceID   string
   646  	DevicePort string
   647  }
   648  
   649  type CDPCacheEntries []CDPCacheEntry
   650  
   651  type HostInterface struct {
   652  	Description      string          `json:",omitempty"`
   653  	IPAddresses      []string        `json:",omitempty"`
   654  	RemoteMacs       []string        `json:",omitempty"`
   655  	CDPCacheEntries  CDPCacheEntries `json:",omitempty"`
   656  	Inbps            int64
   657  	LinkSpeed        int64  `json:",omitempty"`
   658  	MAC              string `json:",omitempty"`
   659  	Master           string `json:",omitempty"`
   660  	Name             string `json:",omitempty"`
   661  	Outbps           int64
   662  	StatsLastUpdated int64
   663  	Type             string
   664  }
   665  
   666  type Disk struct {
   667  	UsedBytes        int64
   668  	TotalBytes       int64
   669  	Label            string `json:",omitempty"`
   670  	StatsLastUpdated int64
   671  }
   672  
   673  type MemoryModule struct {
   674  	// Maybe this should be a bool but that might be limiting
   675  	Status            string
   676  	StatusLastUpdated int64
   677  	Size              string
   678  }
   679  
   680  type ChassisComponent struct {
   681  	Status            string
   682  	StatusLastUpdated int64
   683  }
   684  
   685  type PowerSupply struct {
   686  	Status                     string
   687  	StatusLastUpdated          int64
   688  	Amps                       float64
   689  	AmpsLastUpdated            int64
   690  	Volts                      float64
   691  	VoltsLastUpdated           int64
   692  	metadata.HWPowerSupplyMeta //Should be renamed to Meta
   693  }
   694  
   695  type PhysicalDisk struct {
   696  	Status            string
   697  	StatusLastUpdated int64
   698  	metadata.HWDiskMeta
   699  }
   700  
   701  type VirtualDisk struct {
   702  	Status            string
   703  	StatusLastUpdated int64
   704  }
   705  
   706  type Controller struct {
   707  	Status            string
   708  	StatusLastUpdated int64
   709  	metadata.HWControllerMeta
   710  }
   711  
   712  type Temp struct {
   713  	Celsius            float64
   714  	Status             string
   715  	StatusLastUpdated  int64
   716  	CelsiusLastUpdated int64
   717  }
   718  
   719  type ICMPData struct {
   720  	TimedOut               bool
   721  	TimedOutLastUpdated    int64
   722  	DNSResolved            bool
   723  	DNSResolvedLastUpdated int64
   724  	RTTMS                  float64
   725  	RTTLastUpdated         int64
   726  }
   727  
   728  func newHostData() *HostData {
   729  	hd := &HostData{}
   730  	hd.CPU.Processors = make(map[string]string)
   731  	hd.Interfaces = make(map[string]*HostInterface)
   732  	hd.Disks = make(map[string]*Disk)
   733  	hd.Services = make(map[string]*ServiceStatus)
   734  	hd.Processes = make(map[string]*Process)
   735  	hd.ICMPData = make(map[string]*ICMPData)
   736  	hd.Hardware = &Hardware{}
   737  	hd.Hardware.ChassisComponents = make(map[string]*ChassisComponent)
   738  	hd.Hardware.Temps = make(map[string]*Temp)
   739  	hd.Hardware.PowerSupplies = make(map[string]*PowerSupply)
   740  	hd.Hardware.Memory = make(map[string]*MemoryModule)
   741  	hd.Hardware.Storage.PhysicalDisks = make(map[string]*PhysicalDisk)
   742  	hd.Hardware.Storage.VirtualDisks = make(map[string]*VirtualDisk)
   743  	hd.Hardware.Storage.Controllers = make(map[string]*Controller)
   744  	hd.Hardware.Storage.Batteries = make(map[string]*Battery)
   745  	return hd
   746  }
   747  
   748  // Clean sets certain maps to nil so they don't get marshalled in JSON
   749  // The logic is to remove ones that monitored devices might lack, such
   750  // as hardware information
   751  func (hd *HostData) Clean() {
   752  	if len(hd.CPU.Processors) == 0 {
   753  		hd.CPU.Processors = nil
   754  	}
   755  	if len(hd.Disks) == 0 {
   756  		hd.Disks = nil
   757  	}
   758  	if len(hd.Services) == 0 {
   759  		hd.Services = nil
   760  	}
   761  	if len(hd.Processes) == 0 {
   762  		hd.Processes = nil
   763  	}
   764  	hwLen := len(hd.Hardware.ChassisComponents) +
   765  		len(hd.Hardware.Memory) +
   766  		len(hd.Hardware.Storage.PhysicalDisks) +
   767  		len(hd.Hardware.Storage.VirtualDisks) +
   768  		len(hd.Hardware.Storage.Controllers) +
   769  		len(hd.Hardware.PowerSupplies) +
   770  		len(hd.Hardware.Temps) +
   771  		len(hd.Hardware.Storage.Batteries)
   772  	if hwLen == 0 {
   773  		hd.Hardware = nil
   774  	}
   775  }
   776  
   777  type Hardware struct {
   778  	Memory            map[string]*MemoryModule     `json:",omitempty"`
   779  	ChassisComponents map[string]*ChassisComponent `json:",omitempty"`
   780  	Storage           struct {
   781  		Controllers   map[string]*Controller   `json:",omitempty"`
   782  		PhysicalDisks map[string]*PhysicalDisk `json:",omitempty"`
   783  		VirtualDisks  map[string]*VirtualDisk  `json:",omitempty"`
   784  		Batteries     map[string]*Battery
   785  	}
   786  	Temps             map[string]*Temp
   787  	PowerSupplies     map[string]*PowerSupply `json:",omitempty"`
   788  	BoardPowerReading *BoardPowerReading
   789  }
   790  
   791  type BoardPowerReading struct {
   792  	Watts            int64
   793  	WattsLastUpdated int64
   794  }
   795  
   796  type VM struct {
   797  	Host                  string `json:",omitempty"`
   798  	PowerState            string `json:",omitempty"`
   799  	PowerStateLastUpdated int64  `json:",omitempty"`
   800  }
   801  
   802  type Battery struct {
   803  	Status            string
   804  	StatusLastUpdated int64
   805  }
   806  
   807  type ServiceStatus struct {
   808  	Running            bool
   809  	RunningLastUpdated int64
   810  }
   811  
   812  type Process struct {
   813  	CPUPercentUsed              float64
   814  	CPUPercentLastUpdated       int64
   815  	UsedRealBytes               int64
   816  	UsedRealBytesLastUpdated    int64
   817  	UsedVirtualBytes            int64
   818  	UsedVirtualBytesLastUpdated int64
   819  	Count                       int64
   820  	CountLastUpdated            int64
   821  }
   822  
   823  type HostData struct {
   824  	CPU struct {
   825  		Logical          int64 `json:",omitempty"`
   826  		Physical         int64 `json:",omitempty"`
   827  		PercentUsed      float64
   828  		StatsLastUpdated int64
   829  		Processors       map[string]string `json:",omitempty"`
   830  	}
   831  	ICMPData      map[string]*ICMPData
   832  	Disks         map[string]*Disk
   833  	OpenIncidents []IncidentStatus
   834  	Interfaces    map[string]*HostInterface
   835  	UptimeSeconds int64     `json:",omitempty"`
   836  	Manufacturer  string    `json:",omitempty"`
   837  	Hardware      *Hardware `json:",omitempty"`
   838  	Memory        struct {
   839  		TotalBytes       float64
   840  		UsedBytes        float64
   841  		StatsLastUpdated int64
   842  	}
   843  	Processes map[string]*Process       `json:",omitempty"`
   844  	Services  map[string]*ServiceStatus `json:",omitempty"`
   845  	Model     string                    `json:",omitempty"`
   846  	Name      string                    `json:",omitempty"`
   847  	OS        struct {
   848  		Caption string `json:",omitempty"`
   849  		Version string `json:",omitempty"`
   850  	}
   851  	SerialNumber string   `json:",omitempty"`
   852  	VM           *VM      `json:",omitempty"`
   853  	Guests       []string `json:",omitempty"`
   854  }