misc: bug fixes and improvements for stats Fuse fs
Added syslogs Added support for symlinks Relocated make commands in a local Makefile Dumping stats on index instead of paths Updated README Added go.mod and go.sum with relevant dependencies for the module Type: fix Change-Id: I2c91317939b2f4d765771ab7038372ae27d3109d Signed-off-by: Arthur de Kerhor <arthurdekerhor@gmail.com>
This commit is contained in:

committed by
Beno�t Ganne

parent
2f64790c59
commit
9cfbd3b786
28
Makefile
28
Makefile
@ -220,7 +220,6 @@ help:
|
||||
@echo " docs - Build the Sphinx documentation"
|
||||
@echo " docs-venv - Build the virtual environment for the Sphinx docs"
|
||||
@echo " docs-clean - Remove the generated files from the Sphinx docs"
|
||||
@echo " stats-fs-help - Help to build the stats segment file system"
|
||||
@echo ""
|
||||
@echo "Make Arguments:"
|
||||
@echo " V=[0|1] - set build verbosity level"
|
||||
@ -669,33 +668,6 @@ featurelist: centos-pyyaml
|
||||
checkfeaturelist: centos-pyyaml
|
||||
@extras/scripts/fts.py --validate --all
|
||||
|
||||
|
||||
# Build vpp_stats_fs
|
||||
|
||||
.PHONY: stats-fs-install
|
||||
stats-fs-install:
|
||||
@extras/vpp_stats_fs/install.sh install
|
||||
|
||||
.PHONY: stats-fs-start
|
||||
stats-fs-start:
|
||||
@extras/vpp_stats_fs/install.sh start
|
||||
|
||||
.PHONY: stats-fs-cleanup
|
||||
stats-fs-cleanup:
|
||||
@extras/vpp_stats_fs/install.sh cleanup
|
||||
|
||||
.PHONY: stats-fs-help
|
||||
stats-fs-help:
|
||||
@extras/vpp_stats_fs/install.sh help
|
||||
|
||||
.PHONY: stats-fs-force-unmount
|
||||
stats-fs-force-unmount:
|
||||
@extras/vpp_stats_fs/install.sh unmount
|
||||
|
||||
.PHONY: stats-fs-stop
|
||||
stats-fs-stop:
|
||||
@extras/vpp_stats_fs/install.sh stop
|
||||
|
||||
#
|
||||
# Build the documentation
|
||||
#
|
||||
|
26
extras/vpp_stats_fs/Makefile
Normal file
26
extras/vpp_stats_fs/Makefile
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
# Build vpp_stats_fs
|
||||
|
||||
.PHONY: install
|
||||
install:
|
||||
@./install.sh install
|
||||
|
||||
.PHONY: start
|
||||
start:
|
||||
@./install.sh start
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@./install.sh clean
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
@./install.sh help
|
||||
|
||||
.PHONY: force-unmount
|
||||
force-unmount:
|
||||
@./install.sh unmount
|
||||
|
||||
.PHONY: stop
|
||||
stop:
|
||||
@./install.sh stop
|
@ -1,61 +1,113 @@
|
||||
# VPP stats segment FUSE filesystem {#stats_fs_doc}
|
||||
|
||||
The statfs binary allows to create a FUSE filesystem to expose and to browse the stats segment.
|
||||
Is is leaned on the Go-FUSE library and requires Go-VPP stats bindings to work.
|
||||
It relies on the Go-FUSE library and requires Go-VPP stats bindings to work.
|
||||
|
||||
The binary mounts a filesystem on the local machine whith the data from the stats segments.
|
||||
The counters can be opened and read as files (e.g. in a Unix shell).
|
||||
Note that the value of a counter is determined when the corresponding file is opened (as for /proc/interrupts).
|
||||
|
||||
Directories regularly update their contents so that new counters get added to the filesystem.
|
||||
Directories update their contents on epoch changes so that new counters get added to the filesystem.
|
||||
|
||||
## Prerequisites (for building)
|
||||
|
||||
**GoVPP** library (master branch)
|
||||
**Go-FUSE** library
|
||||
vpp, vppapi
|
||||
|
||||
## Building
|
||||
|
||||
Here, we add the Go librairies before building the binary
|
||||
```bash
|
||||
go mod init stats_fs
|
||||
go get git.fd.io/govpp.git@master
|
||||
go get git.fd.io/govpp.git/adapter/statsclient@master
|
||||
go get github.com/hanwen/go-fuse/v2
|
||||
go build
|
||||
```
|
||||
The script `install.sh` is responsible for buildiing and installing the filesystem.
|
||||
|
||||
## Usage
|
||||
|
||||
The basic usage is:
|
||||
The local Makefile contains targets for all the possible intercations with the stats_f binary.
|
||||
|
||||
### Help
|
||||
A basic help menu
|
||||
```bash
|
||||
sudo ./statfs <MOUNT_POINT> &
|
||||
make help
|
||||
```
|
||||
|
||||
### Install
|
||||
Building the binary
|
||||
```bash
|
||||
make install
|
||||
```
|
||||
|
||||
### Start
|
||||
Starts the filesystem. Requires a running VPP instance using the default socket /run/vpp/stats.sock.
|
||||
|
||||
May require a privileged user (sudo)
|
||||
```bash
|
||||
make start
|
||||
```
|
||||
|
||||
### Stop
|
||||
Stops and unmounts the filesystem if it is not busy.
|
||||
|
||||
May require a privileged user (sudo)
|
||||
```bash
|
||||
make stop
|
||||
```
|
||||
|
||||
### Force unmount
|
||||
Forces the unmount of the filesystem even if it is busy.
|
||||
|
||||
May require a privileged user (sudo)
|
||||
```bash
|
||||
make force-unmount
|
||||
```
|
||||
|
||||
### Cleanup
|
||||
Cleaning stats_fs binary.
|
||||
|
||||
May require a privileged user (sudo).
|
||||
```bash
|
||||
make clean
|
||||
```
|
||||
**Options:**
|
||||
- debug \<true|false\> (default is false)
|
||||
- socket \<statSocket\> (default is /run/vpp/stats.sock)
|
||||
|
||||
## Browsing the filesystem
|
||||
|
||||
The default mountpoint is /run/vpp/stats_fs_dir.
|
||||
You can browse the filesystem as a regular user.
|
||||
Example:
|
||||
|
||||
```bash
|
||||
cd /path/to/mountpoint
|
||||
cd /run/vpp/stats_fs_dir
|
||||
cd sys/node
|
||||
ls -al
|
||||
cat names
|
||||
```
|
||||
|
||||
## Unmounting the file system
|
||||
## Building and mounting the filesystem manually
|
||||
|
||||
For more modularity, you can build and mount the filesystem manually.
|
||||
|
||||
### Building
|
||||
Inside the local directory, you can build the go binary:
|
||||
```bash
|
||||
go build
|
||||
```
|
||||
|
||||
### Mounting
|
||||
Then, ou can mount the filesystem with the local binary.
|
||||
|
||||
May require a privileged user (sudo).
|
||||
|
||||
The basic usage is:
|
||||
```bash
|
||||
./stats_fs <MOUNT_POINT>
|
||||
```
|
||||
|
||||
**Options:**
|
||||
- debug \<true|false\> (default is false)
|
||||
- socket \<statSocket\> (default is /run/vpp/stats.sock) : VPP socket for stats
|
||||
|
||||
|
||||
### Unmounting the file system
|
||||
|
||||
You can unmount the filesystem with the fusermount command.
|
||||
|
||||
May require a privileged user (sudo)
|
||||
|
||||
```bash
|
||||
sudo fusermount -u /path/to/mountpoint
|
||||
fusermount -u /path/to/mountpoint
|
||||
```
|
||||
|
||||
To force the unmount even if the resource is busy, add the -z option:
|
||||
```bash
|
||||
sudo fusermount -uz /path/to/mountpoint
|
||||
fusermount -uz /path/to/mountpoint
|
||||
```
|
||||
|
@ -25,6 +25,8 @@ package main
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"log/syslog"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
@ -36,20 +38,32 @@ import (
|
||||
"github.com/hanwen/go-fuse/v2/fs"
|
||||
)
|
||||
|
||||
func LogMsg(msg string) {
|
||||
fmt.Fprint(os.Stderr, msg)
|
||||
log.Print(msg)
|
||||
}
|
||||
|
||||
func main() {
|
||||
syslogger, err := syslog.New(syslog.LOG_ERR|syslog.LOG_DAEMON, "statsfs")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
log.SetOutput(syslogger)
|
||||
|
||||
statsSocket := flag.String("socket", statsclient.DefaultSocketName, "Path to VPP stats socket")
|
||||
debug := flag.Bool("debug", false, "print debugging messages.")
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() < 1 {
|
||||
fmt.Fprintf(os.Stderr, "usage: %s MOUNTPOINT\n", os.Args[0])
|
||||
LogMsg(fmt.Sprintf("usage: %s MOUNTPOINT\n", os.Args[0]))
|
||||
os.Exit(2)
|
||||
}
|
||||
//Conection to the stat segment socket.
|
||||
sc := statsclient.NewStatsClient(*statsSocket)
|
||||
fmt.Printf("Waiting for the VPP socket to be available. Be sure a VPP instance is running.\n")
|
||||
fmt.Println("Waiting for the VPP socket to be available. Be sure a VPP instance is running.")
|
||||
c, err := core.ConnectStats(sc)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to connect to the stats socket: %v\n", err)
|
||||
LogMsg(fmt.Sprintf("Failed to connect to the stats socket: %v\n", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
defer c.Disconnect()
|
||||
@ -57,7 +71,7 @@ func main() {
|
||||
//Creating the filesystem instance
|
||||
root, err := NewStatsFileSystem(sc)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "NewStatsFileSystem failed: %v\n", err)
|
||||
LogMsg(fmt.Sprintf("NewStatsFileSystem failed: %v\n", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@ -67,7 +81,7 @@ func main() {
|
||||
opts.AllowOther = true
|
||||
server, err := fs.Mount(flag.Arg(0), root, opts)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Mount fail: %v\n", err)
|
||||
LogMsg(fmt.Sprintf("Mount fail: %v\n", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@ -86,6 +100,6 @@ func main() {
|
||||
if err == nil || !strings.Contains(err.Error(), "Device or resource busy") {
|
||||
break
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Unmount fail: %v\n", err)
|
||||
LogMsg(fmt.Sprintf("Unmount fail: %v\n", err))
|
||||
}
|
||||
}
|
||||
|
8
extras/vpp_stats_fs/go.mod
Normal file
8
extras/vpp_stats_fs/go.mod
Normal file
@ -0,0 +1,8 @@
|
||||
module stats_fs
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
git.fd.io/govpp.git v0.3.6-0.20210601140839-da95997338b7 // indirect
|
||||
github.com/hanwen/go-fuse/v2 v2.1.0 // indirect
|
||||
)
|
42
extras/vpp_stats_fs/go.sum
Normal file
42
extras/vpp_stats_fs/go.sum
Normal file
@ -0,0 +1,42 @@
|
||||
git.fd.io/govpp.git v0.3.6-0.20210601140839-da95997338b7 h1:IPy+QyEmQxFbVRFolJ4ofP+ZLN4HfzmK+QCPycmaINc=
|
||||
git.fd.io/govpp.git v0.3.6-0.20210601140839-da95997338b7/go.mod h1:OCVd4W8SH+666KRQoMj6PM+oipLDZAHhqMz9B1TGbgI=
|
||||
github.com/bennyscetbun/jsongo v1.1.0/go.mod h1:suxbVmjBV8+A2BBAM5EYVh6Uj8j3rqJhzWf3hv7Ff8U=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ftrvxmtrx/fd v0.0.0-20150925145434-c6d800382fff h1:zk1wwii7uXmI0znwU+lqg+wFL9G5+vm5I+9rv2let60=
|
||||
github.com/ftrvxmtrx/fd v0.0.0-20150925145434-c6d800382fff/go.mod h1:yUhRXHewUVJ1k89wHKP68xfzk7kwXUx/DV1nx4EBMbw=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/hanwen/go-fuse v1.0.0 h1:GxS9Zrn6c35/BnfiVsZVWmsG803xwE7eVRDvcf/BEVc=
|
||||
github.com/hanwen/go-fuse v1.0.0/go.mod h1:unqXarDXqzAk0rt98O2tVndEPIpUgLD9+rwFisZH3Ok=
|
||||
github.com/hanwen/go-fuse/v2 v2.1.0 h1:+32ffteETaLYClUj0a3aHjZ1hOPxxaNEHiZiujuDaek=
|
||||
github.com/hanwen/go-fuse/v2 v2.1.0/go.mod h1:oRyA5eK+pvJyv5otpO/DgccS8y/RvYMaO00GgRLGryc=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
github.com/lunixbochs/struc v0.0.0-20200521075829-a4cb8d33dbbe h1:ewr1srjRCmcQogPQ/NCx6XCk6LGVmsVCc9Y3vvPZj+Y=
|
||||
github.com/lunixbochs/struc v0.0.0-20200521075829-a4cb8d33dbbe/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
|
||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.1.0/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200610111108-226ff32320da h1:bGb80FudwxpeucJUjPYJXuJ8Hk91vNtfvrymzwiei38=
|
||||
golang.org/x/sys v0.0.0-20200610111108-226ff32320da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
@ -1,3 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2021 Cisco Systems and/or its affiliates.
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@ -11,8 +13,6 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
# A simple script that installs stats_fs, a Fuse file system
|
||||
# for the stats segment
|
||||
|
||||
@ -21,6 +21,7 @@ set -eo pipefail
|
||||
OPT_ARG=${1:-}
|
||||
|
||||
STATS_FS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"/
|
||||
cd "${STATS_FS_DIR}"/../../
|
||||
VPP_DIR=$(pwd)/
|
||||
BUILD_ROOT=${VPP_DIR}build-root/
|
||||
BINARY_DIR=${BUILD_ROOT}install-vpp-native/vpp/bin/
|
||||
@ -75,6 +76,12 @@ function install_fuse() {
|
||||
apt-get install fuse -y
|
||||
}
|
||||
|
||||
function install_nohup() {
|
||||
echo "Installing nohup"
|
||||
apt-get update
|
||||
apt-get install nohup -y
|
||||
}
|
||||
|
||||
function install_go_dep() {
|
||||
echo "Installing Go dependencies"
|
||||
if [[ ! -x "$(command -v go)" ]]; then
|
||||
@ -84,11 +91,11 @@ function install_go_dep() {
|
||||
|
||||
if [ ! -e "go.mod" ]; then
|
||||
go mod init stats_fs
|
||||
# We require a specific govpp commit for compatibility
|
||||
go get git.fd.io/govpp.git@da95997338b77811bc2ea850db393c652b3bd18e
|
||||
go get git.fd.io/govpp.git/adapter/statsclient@da95997338b77811bc2ea850db393c652b3bd18e
|
||||
go get github.com/hanwen/go-fuse/v2
|
||||
fi
|
||||
# master required
|
||||
go get git.fd.io/govpp.git@master
|
||||
go get git.fd.io/govpp.git/adapter/statsclient@master
|
||||
go get github.com/hanwen/go-fuse/v2
|
||||
}
|
||||
|
||||
# Resolve stats_fs dependencies and builds the binary
|
||||
@ -114,6 +121,10 @@ function install_statfs() {
|
||||
install_fuse
|
||||
fi
|
||||
|
||||
if [[ ! -x "$(command -v nohup)" ]]; then
|
||||
install_nohup
|
||||
fi
|
||||
|
||||
if [ ! -d "${STATS_FS_DIR}" ]; then
|
||||
echo "${STATS_FS_DIR} directory does not exist"
|
||||
exit 1
|
||||
@ -137,6 +148,11 @@ function start_statfs() {
|
||||
EXE_DIR=$DEBUG_DIR
|
||||
fi
|
||||
|
||||
if [[ $(pidof "${EXE_DIR}"stats_fs) ]]; then
|
||||
echo "The service stats_fs has already been launched"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mountpoint="${RUN_DIR}stats_fs_dir"
|
||||
|
||||
if [[ -x "$(command -v ${EXE_DIR}stats_fs)" ]] ; then
|
||||
@ -148,6 +164,7 @@ function start_statfs() {
|
||||
fi
|
||||
|
||||
echo "stats_fs is not installed, use 'make stats-fs-install' first"
|
||||
exit 1
|
||||
}
|
||||
|
||||
function stop_statfs() {
|
||||
@ -165,7 +182,8 @@ function stop_statfs() {
|
||||
PID=$(pidof "${EXE_DIR}"stats_fs)
|
||||
kill "$PID"
|
||||
if [[ $(pidof "${EXE_DIR}"stats_fs) ]]; then
|
||||
echo "Can't unmount the file system: Device or resource busy"
|
||||
echo "Check your syslog file (default is /var/log/syslog)."
|
||||
echo "It may be that the file system is busy."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@ -196,12 +214,6 @@ function cleanup() {
|
||||
|
||||
cd "${STATS_FS_DIR}"
|
||||
|
||||
if [ -e "go.mod" ]; then
|
||||
rm -f go.mod
|
||||
fi
|
||||
if [ -e "go.sum" ]; then
|
||||
rm -f go.sum
|
||||
fi
|
||||
if [ -e "stats_fs" ]; then
|
||||
rm -f stats_fs
|
||||
fi
|
||||
@ -224,14 +236,14 @@ function cleanup() {
|
||||
# Show available commands
|
||||
function help() {
|
||||
cat <<__EOF__
|
||||
Stats_fs installer
|
||||
Stats-fs installer
|
||||
|
||||
stats-fs-install - Installs requirements (Go, GoVPP, GoFUSE) and builds stats_fs
|
||||
stats-fs-start - Launches the stats_fs binary and creates a mountpoint
|
||||
stats-fs-cleanup - Removes stats_fs binary and deletes go module
|
||||
stats-fs-stop - Stops the executable, unmounts the file system
|
||||
and removes the mountpoint directory
|
||||
stats-fs-force-unmount - Forces the unmount of the filesystem even if it is busy
|
||||
install - Installs requirements (Go, GoVPP, GoFUSE) and builds stats_fs
|
||||
start - Launches the stats_fs binary and creates a mountpoint
|
||||
clean - Removes stats_fs binary
|
||||
stop - Stops the executable, unmounts the file system
|
||||
and removes the mountpoint directory
|
||||
force-unmount - Forces the unmount of the filesystem even if it is busy
|
||||
|
||||
__EOF__
|
||||
}
|
||||
@ -246,7 +258,7 @@ function resolve_option() {
|
||||
"install")
|
||||
install_statfs
|
||||
;;
|
||||
"cleanup")
|
||||
"clean")
|
||||
cleanup
|
||||
;;
|
||||
"unmount")
|
||||
|
@ -22,7 +22,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
@ -36,57 +36,58 @@ import (
|
||||
)
|
||||
|
||||
func updateDir(ctx context.Context, n *fs.Inode, cl *statsclient.StatsClient, dirPath string) syscall.Errno {
|
||||
list, err := cl.ListStats(dirPath)
|
||||
stats, err := cl.PrepareDir(dirPath)
|
||||
if err != nil {
|
||||
log.Println("list stats failed:", err)
|
||||
LogMsg(fmt.Sprintf("Listing stats index failed: %v\n", err))
|
||||
return syscall.EAGAIN
|
||||
}
|
||||
|
||||
if list == nil {
|
||||
n.ForgetPersistent()
|
||||
return syscall.ENOENT
|
||||
}
|
||||
n.Operations().(*dirNode).epoch = stats.Epoch
|
||||
|
||||
for _, path := range list {
|
||||
localPath := strings.TrimPrefix(path, dirPath)
|
||||
dir, base := filepath.Split(localPath)
|
||||
n.RmAllChildren()
|
||||
|
||||
for _, entry := range stats.Entries {
|
||||
localPath := strings.TrimPrefix(string(entry.Name), dirPath)
|
||||
dirPath, base := filepath.Split(localPath)
|
||||
|
||||
parent := n
|
||||
for _, component := range strings.Split(dir, "/") {
|
||||
for _, component := range strings.Split(dirPath, "/") {
|
||||
if len(component) == 0 {
|
||||
continue
|
||||
}
|
||||
child := parent.GetChild(component)
|
||||
if child == nil {
|
||||
child = parent.NewPersistentInode(ctx, &dirNode{client: cl, lastUpdate: time.Now()},
|
||||
child = parent.NewInode(ctx, &dirNode{client: cl, epoch: stats.Epoch},
|
||||
fs.StableAttr{Mode: fuse.S_IFDIR})
|
||||
parent.AddChild(component, child, true)
|
||||
} else {
|
||||
child.Operations().(*dirNode).epoch = stats.Epoch
|
||||
}
|
||||
|
||||
parent = child
|
||||
}
|
||||
|
||||
filename := strings.Replace(base, " ", "_", -1)
|
||||
child := parent.GetChild(filename)
|
||||
if child == nil {
|
||||
child := parent.NewPersistentInode(ctx, &statNode{client: cl, path: path}, fs.StableAttr{})
|
||||
child := parent.NewPersistentInode(ctx, &statNode{client: cl, index: entry.Index}, fs.StableAttr{})
|
||||
parent.AddChild(filename, child, true)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func getCounterContent(path string, client *statsclient.StatsClient) (content string, status syscall.Errno) {
|
||||
func getCounterContent(index uint32, client *statsclient.StatsClient) (content string, status syscall.Errno) {
|
||||
content = ""
|
||||
//We add '$' because we deal with regexp here
|
||||
res, err := client.DumpStats(path + "$")
|
||||
statsDir, err := client.PrepareDirOnIndex(index)
|
||||
if err != nil {
|
||||
LogMsg(fmt.Sprintf("Dumping stats on index failed: %v\n", err))
|
||||
return content, syscall.EAGAIN
|
||||
}
|
||||
if res == nil {
|
||||
if len(statsDir.Entries) != 1 {
|
||||
return content, syscall.ENOENT
|
||||
}
|
||||
|
||||
result := res[0]
|
||||
result := statsDir.Entries[0]
|
||||
if result.Data == nil {
|
||||
return content, 0
|
||||
}
|
||||
@ -131,38 +132,46 @@ func getCounterContent(path string, client *statsclient.StatsClient) (content st
|
||||
return content, fs.OK
|
||||
}
|
||||
|
||||
type rootNode struct {
|
||||
fs.Inode
|
||||
client *statsclient.StatsClient
|
||||
lastUpdate time.Time
|
||||
}
|
||||
|
||||
var _ = (fs.NodeOnAdder)((*rootNode)(nil))
|
||||
|
||||
func (root *rootNode) OnAdd(ctx context.Context) {
|
||||
updateDir(ctx, &root.Inode, root.client, "/")
|
||||
root.lastUpdate = time.Now()
|
||||
}
|
||||
|
||||
//The dirNode structure represents directories
|
||||
type dirNode struct {
|
||||
fs.Inode
|
||||
client *statsclient.StatsClient
|
||||
lastUpdate time.Time
|
||||
client *statsclient.StatsClient
|
||||
epoch int64
|
||||
}
|
||||
|
||||
var _ = (fs.NodeOpendirer)((*dirNode)(nil))
|
||||
var _ = (fs.NodeGetattrer)((*dirNode)(nil))
|
||||
var _ = (fs.NodeOnAdder)((*dirNode)(nil))
|
||||
|
||||
func (dn *dirNode) OnAdd(ctx context.Context) {
|
||||
if dn.Inode.IsRoot() {
|
||||
updateDir(ctx, &dn.Inode, dn.client, "/")
|
||||
}
|
||||
}
|
||||
|
||||
func (dn *dirNode) Getattr(ctx context.Context, f fs.FileHandle, out *fuse.AttrOut) syscall.Errno {
|
||||
out.Mtime = uint64(time.Now().Unix())
|
||||
out.Atime = out.Mtime
|
||||
out.Ctime = out.Mtime
|
||||
return 0
|
||||
}
|
||||
|
||||
func (dn *dirNode) Opendir(ctx context.Context) syscall.Errno {
|
||||
//We do not update a directory more than once a second, as counters are rarely added/deleted.
|
||||
if time.Now().Sub(dn.lastUpdate) < time.Second {
|
||||
return 0
|
||||
var status syscall.Errno = syscall.F_OK
|
||||
var sleepTime time.Duration = 10 * time.Millisecond
|
||||
newEpoch, inProgress := dn.client.GetEpoch()
|
||||
for inProgress {
|
||||
newEpoch, inProgress = dn.client.GetEpoch()
|
||||
time.Sleep(sleepTime)
|
||||
sleepTime = sleepTime * 2
|
||||
}
|
||||
|
||||
//directoryPath is the path to the current directory from root
|
||||
directoryPath := "/" + dn.Inode.Path(nil) + "/"
|
||||
status := updateDir(ctx, &dn.Inode, dn.client, directoryPath)
|
||||
dn.lastUpdate = time.Now()
|
||||
//We check that the directory epoch is up to date
|
||||
if dn.epoch != newEpoch {
|
||||
//directoryPath is the path to the current directory from root
|
||||
directoryPath := path.Clean("/" + dn.Inode.Path(nil) + "/")
|
||||
status = updateDir(ctx, &dn.Inode, dn.client, directoryPath)
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
@ -170,16 +179,26 @@ func (dn *dirNode) Opendir(ctx context.Context) syscall.Errno {
|
||||
type statNode struct {
|
||||
fs.Inode
|
||||
client *statsclient.StatsClient
|
||||
path string
|
||||
index uint32
|
||||
}
|
||||
|
||||
var _ = (fs.NodeOpener)((*statNode)(nil))
|
||||
var _ = (fs.NodeGetattrer)((*statNode)(nil))
|
||||
|
||||
func (fh *statNode) Getattr(ctx context.Context, f fs.FileHandle, out *fuse.AttrOut) syscall.Errno {
|
||||
out.Mtime = uint64(time.Now().Unix())
|
||||
out.Atime = out.Mtime
|
||||
out.Ctime = out.Mtime
|
||||
return 0
|
||||
}
|
||||
|
||||
//When a file is opened, the correpsonding counter value is dumped and a file handle is created
|
||||
func (sn *statNode) Open(ctx context.Context, flags uint32) (fs.FileHandle, uint32, syscall.Errno) {
|
||||
content, status := getCounterContent(sn.path, sn.client)
|
||||
content, status := getCounterContent(sn.index, sn.client)
|
||||
if status == syscall.ENOENT {
|
||||
sn.Inode.ForgetPersistent()
|
||||
_, parent := sn.Inode.Parent()
|
||||
parent.RmChild(sn.Inode.Path(parent))
|
||||
|
||||
}
|
||||
return &statFH{data: []byte(content)}, fuse.FOPEN_DIRECT_IO, status
|
||||
}
|
||||
@ -203,5 +222,5 @@ func (fh *statFH) Read(ctx context.Context, data []byte, off int64) (fuse.ReadRe
|
||||
|
||||
//NewStatsFileSystem creates the fs for the stat segment.
|
||||
func NewStatsFileSystem(sc *statsclient.StatsClient) (root fs.InodeEmbedder, err error) {
|
||||
return &rootNode{client: sc}, nil
|
||||
return &dirNode{client: sc}, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user