This repository was archived by the owner on Oct 27, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 28
Added monitoring function for link022 #81
Merged
Merged
Changes from all commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
eca8ad2
Add a single radio ap config file for test on emulator
zhangtianyang d076f55
Update certificates for test and add a new script for generating test…
zhangtianyang 74a2e4a
Merge remote-tracking branch 'upstream/master'
zhangtianyang 721111f
add a function to fetch AP state periodic
zhangtianyang 5dcf857
implemented collector which can convert AP state to Prometheus metrics
zhangtianyang aaa2316
add a HTTP server to expose metrics
zhangtianyang 831e057
1. Delete use_model in getRequest to separate each leaf node into
zhangtianyang 07cee6a
add ctrl_interface in hostapd.conf
zhangtianyang b0d4901
1. delete useless debug message and config
zhangtianyang f46f0e9
delete unnecessary code
zhangtianyang 2f57ece
Add unit test for prometheus exporter
zhangtianyang b9a7af5
use a query friendly way process array type
zhangtianyang 6bae6f6
add device states to LINK022's gNMI server
zhangtianyang dc45900
Merge remote-tracking branch 'upstream/master'
zhangtianyang 59454a2
used a new way to implement type conversion
zhangtianyang 821e5b8
add support to JSON data
zhangtianyang a5f639f
added support for JSON-IETF data.
zhangtianyang c485fcd
solve leafref when create new node
zhangtianyang 302c35a
fixed a bug in calculating cpu usage
zhangtianyang d3e53a6
added ap info monitoring.
zhangtianyang 3e80b56
fixed a bug in test which caused
zhangtianyang 066c0ff
added README for prometheus exporter
zhangtianyang c93a11a
Add prometheus configuring instruction in prometheus exporter README.
zhangtianyang 4f367fd
fixed issues reported by Go Report Card
zhangtianyang dca750e
Now getting hostname from os instead of hard-coding.
zhangtianyang 44cdde0
added license headers.
zhangtianyang e5a46fe
corrected typos
zhangtianyang File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,256 @@ | ||
/* Copyright 2017 Google Inc. | ||
|
||
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 | ||
|
||
https://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 monitoring | ||
|
||
import ( | ||
ctx "context" | ||
"errors" | ||
"fmt" | ||
"io/ioutil" | ||
"os" | ||
"regexp" | ||
"strconv" | ||
"strings" | ||
"time" | ||
|
||
log "github.com/golang/glog" | ||
"github.com/google/gnxi/utils/xpath" | ||
"github.com/google/link022/agent/context" | ||
"github.com/google/link022/agent/gnmi" | ||
"github.com/google/link022/agent/syscmd" | ||
) | ||
|
||
const ( | ||
statesUpdateDelay = 15 * time.Second | ||
systemClockTick = 100 | ||
physicalMemoryPath = "/access-points/access-point[hostname=$hostname]/system/memory/state/physical" | ||
cpuUsagePath = "/access-points/access-point[hostname=$hostname]/system/cpus/cpu[index=$index]/state/total/instant" | ||
channelPath = "/access-points/access-point[hostname=$hostname]/radios/radio[id=$id]/state/channel" | ||
widthPath = "/access-points/access-point[hostname=$hostname]/radios/radio[id=$id]/state/channel-width" | ||
frequencyPath = "/access-points/access-point[hostname=$hostname]/radios/radio[id=$id]/state/operating-frequency" | ||
txpowerPath = "/access-points/access-point[hostname=$hostname]/radios/radio[id=$id]/state/transmit-power" | ||
selfMemPath = "/access-points/access-point[hostname=$hostname]/system/processes/process[pid=$pid]/state/memory-usage" | ||
selfCPUPath = "/access-points/access-point[hostname=$hostname]/system/processes/process[pid=$pid]/state/cpu-utilization" | ||
) | ||
|
||
var cmdRunner = syscmd.Runner() | ||
|
||
// UpdateDeviceStatus periodically collect AP device stats | ||
// and update their corresponding nodes in OpenConfig Model tree. | ||
func UpdateDeviceStatus(bkgdContext ctx.Context, gnmiServer *gnmi.Server) { | ||
deviceConfig := context.GetDeviceConfig() | ||
hostName := deviceConfig.Hostname | ||
wLANINTFName := deviceConfig.WLANINTFName | ||
for { | ||
select { | ||
case <-bkgdContext.Done(): | ||
return | ||
case <-time.After(statesUpdateDelay): | ||
} | ||
|
||
if err := updateMemoryInfo(gnmiServer, hostName); err != nil { | ||
log.Errorf("Error in updating memory info: %v", err) | ||
} | ||
if err := updateCPUInfo(gnmiServer, hostName); err != nil { | ||
log.Errorf("Error in updating CPU info: %v", err) | ||
} | ||
if err := updateAPInfo(gnmiServer, hostName, wLANINTFName); err != nil { | ||
log.Errorf("Error in updating AP info: %v", err) | ||
} | ||
} | ||
} | ||
|
||
func updateMemoryInfo(s *gnmi.Server, hostName string) error { | ||
b, err := ioutil.ReadFile("/proc/meminfo") | ||
if err != nil { | ||
return err | ||
} | ||
memStr := string(b) | ||
reFree := regexp.MustCompile("MemTotal:\\s+(\\d+)") | ||
match := reFree.FindStringSubmatch(memStr) | ||
if len(match) != 2 { | ||
return errors.New("No Memory Free info in /proc/meminfo") | ||
} | ||
strPath := strings.Replace(physicalMemoryPath, "$hostname", hostName, 1) | ||
pbPath, err := xpath.ToGNMIPath(strPath) | ||
if err != nil { | ||
return err | ||
} | ||
physicalMemory, err := strconv.ParseInt(match[1], 10, 64) | ||
if err != nil { | ||
return err | ||
} | ||
stateOpt := gnmi.GNXIStateOptGenerator(pbPath, uint64(physicalMemory*1024), gnmi.InternalUpdateState) | ||
if err = s.InternalUpdate(stateOpt); err != nil { | ||
return err | ||
} | ||
|
||
pid := os.Getpid() | ||
spid := fmt.Sprint(pid) | ||
filePath := fmt.Sprintf("/proc/%v/status", pid) | ||
b, err = ioutil.ReadFile(filePath) | ||
if err != nil { | ||
log.Errorf("failed open %v: %v", filePath, err) | ||
return err | ||
} | ||
memStr = string(b) | ||
reSelfMem := regexp.MustCompile("VmRSS:\\s+(\\d+)") | ||
match = reSelfMem.FindStringSubmatch(memStr) | ||
if len(match) != 2 { | ||
return fmt.Errorf("No Memory info in: %v", filePath) | ||
} | ||
p := strings.Replace(selfMemPath, "$pid", spid, 1) | ||
p = strings.Replace(p, "$hostname", hostName, 1) | ||
pbPath, err = xpath.ToGNMIPath(p) | ||
if err != nil { | ||
return err | ||
} | ||
selfMemory, err := strconv.ParseInt(match[1], 10, 64) | ||
if err != nil { | ||
return err | ||
} | ||
stateOpt = gnmi.GNXIStateOptGenerator(pbPath, uint64(selfMemory*1024), gnmi.InternalUpdateState) | ||
if err = s.InternalUpdate(stateOpt); err != nil { | ||
log.Errorf("update state failed: %v", err) | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func updateCPUInfo(s *gnmi.Server, hostName string) error { | ||
pid := os.Getpid() | ||
spid := fmt.Sprint(pid) | ||
filePath := fmt.Sprintf("/proc/%v/stat", pid) | ||
b0, err := ioutil.ReadFile(filePath) | ||
if err != nil { | ||
log.Errorf("failed open %v: %v", filePath, err) | ||
return err | ||
} | ||
time.Sleep(1 * time.Second) | ||
b1, err := ioutil.ReadFile(filePath) | ||
if err != nil { | ||
log.Errorf("failed open %v: %v", filePath, err) | ||
return err | ||
} | ||
cpuStr0 := strings.Split(string(b0), " ") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might be better to use regex instead of doing spiting and using hard-coded index. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This file simply contains 52 numbers. Their positions are fixed. So it's better to find them by index. |
||
cpuStr1 := strings.Split(string(b1), " ") | ||
if len(cpuStr0) < 14 || len(cpuStr1) < 14 { | ||
return errors.New("cpu info not correct") | ||
} | ||
up0, err := strconv.ParseInt(cpuStr0[13], 10, 64) | ||
if err != nil { | ||
log.Errorf("failed convert string to int: %v", err) | ||
return err | ||
} | ||
up1, err := strconv.ParseInt(cpuStr1[13], 10, 64) | ||
if err != nil { | ||
log.Errorf("failed convert string to int: %v", err) | ||
return err | ||
} | ||
cpuinfo, err := ioutil.ReadFile("/proc/cpuinfo") | ||
if err != nil { | ||
log.Errorf("failed open %v: %v", "/proc/cpuinfo", err) | ||
return err | ||
} | ||
cpuCount := strings.Count(string(cpuinfo), "processor") | ||
cpuUtil := (up1 - up0) / (systemClockTick * int64(cpuCount)) | ||
p := strings.Replace(selfCPUPath, "$pid", spid, 1) | ||
p = strings.Replace(p, "$hostname", hostName, 1) | ||
pbPath, err := xpath.ToGNMIPath(p) | ||
if err != nil { | ||
return err | ||
} | ||
stateOpt := gnmi.GNXIStateOptGenerator(pbPath, uint8(cpuUtil), gnmi.InternalUpdateState) | ||
if err = s.InternalUpdate(stateOpt); err != nil { | ||
log.Errorf("update state failed: %v", err) | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func updateAPInfo(s *gnmi.Server, hostName string, wLANINTFName string) error { | ||
apInfoString, err := cmdRunner.GetAPStates() | ||
if err != nil { | ||
return err | ||
} | ||
// If one interface has multiple ssid, match the first one | ||
apRegex := regexp.MustCompile("Interface\\s([\\w-_]+)[\\S\\s]*?ssid\\s([\\w-_]+)[\\S\\s]*?channel\\s([\\d]+)[\\S\\s]*?width:\\s([\\d]+)[\\S\\s]*?txpower\\s([\\d]+)") | ||
apInfos := apRegex.FindAllStringSubmatch(apInfoString, -1) | ||
for _, apInfo := range apInfos { | ||
wlanName := apInfo[1] | ||
channelStr := apInfo[3] | ||
widthStr := apInfo[4] | ||
txpowerStr := apInfo[5] | ||
|
||
// Because radio info is not in IW command's output. | ||
// The radio id is hard-coded here | ||
phyIDStr := "1" | ||
|
||
if strings.Compare(wlanName, wLANINTFName) != 0 { | ||
continue | ||
} | ||
|
||
p := strings.Replace(channelPath, "$id", phyIDStr, 1) | ||
p = strings.Replace(p, "$hostname", hostName, 1) | ||
pbPath, err := xpath.ToGNMIPath(p) | ||
if err != nil { | ||
return fmt.Errorf("convert %v to GNMI path failed: %v", p, err) | ||
} | ||
channel, err := strconv.ParseInt(channelStr, 10, 8) | ||
if err != nil { | ||
log.Errorf("failed convert string to int: %v", err) | ||
return err | ||
} | ||
stateOpt := gnmi.GNXIStateOptGenerator(pbPath, uint8(channel), gnmi.InternalUpdateState) | ||
if err = s.InternalUpdate(stateOpt); err != nil { | ||
return fmt.Errorf("update state failed: %v", err) | ||
} | ||
|
||
p = strings.Replace(widthPath, "$id", phyIDStr, 1) | ||
p = strings.Replace(p, "$hostname", hostName, 1) | ||
pbPath, err = xpath.ToGNMIPath(p) | ||
if err != nil { | ||
return fmt.Errorf("convert %v to GNMI path failed: %v", p, err) | ||
} | ||
width, err := strconv.ParseInt(widthStr, 10, 8) | ||
if err != nil { | ||
return fmt.Errorf("failed convert string to int: %v", err) | ||
} | ||
stateOpt = gnmi.GNXIStateOptGenerator(pbPath, uint8(width), gnmi.InternalUpdateState) | ||
if err = s.InternalUpdate(stateOpt); err != nil { | ||
log.Errorf("update state failed: %v", err) | ||
return err | ||
} | ||
|
||
p = strings.Replace(txpowerPath, "$id", phyIDStr, 1) | ||
p = strings.Replace(p, "$hostname", hostName, 1) | ||
pbPath, err = xpath.ToGNMIPath(p) | ||
if err != nil { | ||
return fmt.Errorf("convert %v to GNMI path failed: %v", p, err) | ||
} | ||
txpower, err := strconv.ParseInt(txpowerStr, 10, 8) | ||
if err != nil { | ||
return fmt.Errorf("failed convert string to int: %v", err) | ||
} | ||
stateOpt = gnmi.GNXIStateOptGenerator(pbPath, uint8(txpower), gnmi.InternalUpdateState) | ||
if err = s.InternalUpdate(stateOpt); err != nil { | ||
return fmt.Errorf("update state failed: %v", err) | ||
} | ||
} | ||
|
||
return nil | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.