From 27777bd89016a057e995b4c3d0447eecebb7b345 Mon Sep 17 00:00:00 2001 From: Arthur de Kerhor Date: Wed, 24 Mar 2021 07:32:07 -0700 Subject: [PATCH] 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 (cherry picked from commit 9cfbd3b7869db3ca5131c6fd0c0f77b787fa4312) --- Makefile | 28 ----------- extras/vpp_stats_fs/Makefile | 26 ++++++++++ extras/vpp_stats_fs/README.md | 98 +++++++++++++++++++++++++++--------- extras/vpp_stats_fs/cmd.go | 26 +++++++--- extras/vpp_stats_fs/go.mod | 8 +++ extras/vpp_stats_fs/go.sum | 42 ++++++++++++++++ extras/vpp_stats_fs/install.sh | 54 ++++++++++++-------- extras/vpp_stats_fs/stats_fs.go | 107 +++++++++++++++++++++++----------------- 8 files changed, 267 insertions(+), 122 deletions(-) create mode 100644 extras/vpp_stats_fs/Makefile create mode 100644 extras/vpp_stats_fs/go.mod create mode 100644 extras/vpp_stats_fs/go.sum diff --git a/Makefile b/Makefile index b57ed5c06e8..2bc236c5356 100644 --- a/Makefile +++ b/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 # diff --git a/extras/vpp_stats_fs/Makefile b/extras/vpp_stats_fs/Makefile new file mode 100644 index 00000000000..0aded0c40c3 --- /dev/null +++ b/extras/vpp_stats_fs/Makefile @@ -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 \ No newline at end of file diff --git a/extras/vpp_stats_fs/README.md b/extras/vpp_stats_fs/README.md index c5ec8127d8a..52610feba94 100755 --- a/extras/vpp_stats_fs/README.md +++ b/extras/vpp_stats_fs/README.md @@ -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) +The script `install.sh` is responsible for buildiing and installing the filesystem. -**GoVPP** library (master branch) -**Go-FUSE** library -vpp, vppapi +## Usage -## Building +The local Makefile contains targets for all the possible intercations with the stats_f binary. -Here, we add the Go librairies before building the binary +### Help +A basic help menu ```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 +make help ``` -## Usage +### Install +Building the binary +```bash +make install +``` -The basic usage is: +### Start +Starts the filesystem. Requires a running VPP instance using the default socket /run/vpp/stats.sock. + +May require a privileged user (sudo) ```bash -sudo ./statfs & +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 \ (default is false) - - socket \ (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 +``` + +**Options:** + - debug \ (default is false) + - socket \ (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 ``` diff --git a/extras/vpp_stats_fs/cmd.go b/extras/vpp_stats_fs/cmd.go index 826b011b00d..38d1b3db62f 100644 --- a/extras/vpp_stats_fs/cmd.go +++ b/extras/vpp_stats_fs/cmd.go @@ -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)) } } diff --git a/extras/vpp_stats_fs/go.mod b/extras/vpp_stats_fs/go.mod new file mode 100644 index 00000000000..0a284bb8aec --- /dev/null +++ b/extras/vpp_stats_fs/go.mod @@ -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 +) diff --git a/extras/vpp_stats_fs/go.sum b/extras/vpp_stats_fs/go.sum new file mode 100644 index 00000000000..05bcc7a53be --- /dev/null +++ b/extras/vpp_stats_fs/go.sum @@ -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= diff --git a/extras/vpp_stats_fs/install.sh b/extras/vpp_stats_fs/install.sh index 6249e63c6eb..c800ad33356 100755 --- a/extras/vpp_stats_fs/install.sh +++ b/extras/vpp_stats_fs/install.sh @@ -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") diff --git a/extras/vpp_stats_fs/stats_fs.go b/extras/vpp_stats_fs/stats_fs.go index a9b8ae77633..80c15096234 100644 --- a/extras/vpp_stats_fs/stats_fs.go +++ b/extras/vpp_stats_fs/stats_fs.go @@ -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 + + n.RmAllChildren() - for _, path := range list { - localPath := strings.TrimPrefix(path, dirPath) - dir, base := filepath.Split(localPath) + 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 { +//The dirNode structure represents directories +type dirNode struct { fs.Inode - client *statsclient.StatsClient - lastUpdate time.Time + client *statsclient.StatsClient + epoch int64 } -var _ = (fs.NodeOnAdder)((*rootNode)(nil)) +var _ = (fs.NodeOpendirer)((*dirNode)(nil)) +var _ = (fs.NodeGetattrer)((*dirNode)(nil)) +var _ = (fs.NodeOnAdder)((*dirNode)(nil)) -func (root *rootNode) OnAdd(ctx context.Context) { - updateDir(ctx, &root.Inode, root.client, "/") - root.lastUpdate = time.Now() +func (dn *dirNode) OnAdd(ctx context.Context) { + if dn.Inode.IsRoot() { + updateDir(ctx, &dn.Inode, dn.client, "/") + } } -//The dirNode structure represents directories -type dirNode struct { - fs.Inode - client *statsclient.StatsClient - lastUpdate time.Time +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 } -var _ = (fs.NodeOpendirer)((*dirNode)(nil)) - 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 } -- 2.16.6