From f66ef6225c17e8507e25d750136f9f0e33a0fb98 Mon Sep 17 00:00:00 2001 From: AbdelrahmanElawady Date: Fri, 25 Aug 2023 21:54:01 +0300 Subject: [PATCH 1/4] Add TM integration and separate docker-compose services for Varnish --- cache-config/t3c-apply/torequest/torequest.go | 8 +- .../cdn-in-a-box/docker-compose.varnish.yml | 35 +++++ .../profiles/011-VARNISH_EDGE_TIER_CACHE.json | 8 +- .../profiles/021-VARNISH_MID_TIER_CACHE.json | 8 +- .../cdn-in-a-box/varnish/Dockerfile | 9 ++ infrastructure/cdn-in-a-box/varnish/run.sh | 1 + .../varnish/traffic_ops_ort.crontab | 2 +- infrastructure/cdn-in-a-box/varnish/vstats.go | 129 ++++++++++++++++++ traffic_monitor/cache/vstats.go | 79 +++++++++++ 9 files changed, 274 insertions(+), 5 deletions(-) create mode 100644 infrastructure/cdn-in-a-box/docker-compose.varnish.yml create mode 100644 infrastructure/cdn-in-a-box/varnish/vstats.go create mode 100644 traffic_monitor/cache/vstats.go diff --git a/cache-config/t3c-apply/torequest/torequest.go b/cache-config/t3c-apply/torequest/torequest.go index ba9b443682..6da59c2657 100644 --- a/cache-config/t3c-apply/torequest/torequest.go +++ b/cache-config/t3c-apply/torequest/torequest.go @@ -91,6 +91,7 @@ type RestartData struct { TrafficServerRestart bool // a trafficserver restart is required RemapConfigReload bool // remap.config should be reloaded HitchReload bool // hitch should be reloaded + VarnishReload bool // varnish should be reloaded } type ConfigFile struct { @@ -522,6 +523,7 @@ func (r *TrafficOpsReq) replaceCfgFile(cfg *ConfigFile) (*FileRestartData, error ntpdRestart := cfg.Name == "ntpd.conf" sysCtlReload := cfg.Name == "sysctl.conf" hitchReload := cfg.Name == "hitch.conf" + varnishReload := cfg.Name == "default.vcl" log.Debugf("Reload state after %s: remap.config: %t reload: %t restart: %t ntpd: %t sysctl: %t", cfg.Name, remapConfigReload, trafficCtlReload, trafficServerRestart, ntpdRestart, sysCtlReload) @@ -535,6 +537,7 @@ func (r *TrafficOpsReq) replaceCfgFile(cfg *ConfigFile) (*FileRestartData, error TrafficServerRestart: trafficServerRestart, RemapConfigReload: remapConfigReload, HitchReload: hitchReload, + VarnishReload: varnishReload, }, }, nil } @@ -814,6 +817,7 @@ func (r *TrafficOpsReq) CheckReloadRestart(data []FileRestartData) RestartData { rd.TrafficServerRestart = rd.TrafficServerRestart || changedFile.TrafficServerRestart rd.RemapConfigReload = rd.RemapConfigReload || changedFile.RemapConfigReload rd.HitchReload = rd.HitchReload || changedFile.HitchReload + rd.VarnishReload = rd.VarnishReload || changedFile.VarnishReload } return rd } @@ -1153,7 +1157,7 @@ func (r *TrafficOpsReq) StartServices(syncdsUpdate *UpdateStatus, metaData *t3cu // If check-reload does not know about these and we do, then we should initiate // a reload as well if serviceNeeds != t3cutil.ServiceNeedsRestart && serviceNeeds != t3cutil.ServiceNeedsReload { - if r.TrafficCtlReload || r.RemapConfigReload { + if r.TrafficCtlReload || r.RemapConfigReload || r.VarnishReload { log.Infof("ATS config files unchanged, we updated files via t3c-apply, ATS needs reload") serviceNeeds = t3cutil.ServiceNeedsReload } @@ -1215,7 +1219,7 @@ func (r *TrafficOpsReq) StartServices(syncdsUpdate *UpdateStatus, metaData *t3cu reloadCommand := config.TSHome + config.TrafficCtl reloadArgs := []string{"config", "reload"} if cfg.CacheType == "varnish" { - reloadCommand = "varnishreload" + reloadCommand = "/usr/sbin/varnishreload" reloadArgs = []string{} } if _, _, err := util.ExecCommand(reloadCommand, reloadArgs...); err != nil { diff --git a/infrastructure/cdn-in-a-box/docker-compose.varnish.yml b/infrastructure/cdn-in-a-box/docker-compose.varnish.yml new file mode 100644 index 0000000000..f79ba0cff9 --- /dev/null +++ b/infrastructure/cdn-in-a-box/docker-compose.varnish.yml @@ -0,0 +1,35 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# This compose file will run cache servers as Varnish instead of ATS. +# +# docker-compose -f docker-compose.yml -f docker-compose.varnish.yml up +# + +--- +version: '3.8' + +services: + edge: + build: + dockerfile: infrastructure/cdn-in-a-box/varnish/Dockerfile + mid-01: + build: + dockerfile: infrastructure/cdn-in-a-box/varnish/Dockerfile + mid-02: + build: + dockerfile: infrastructure/cdn-in-a-box/varnish/Dockerfile \ No newline at end of file diff --git a/infrastructure/cdn-in-a-box/traffic_ops_data/profiles/011-VARNISH_EDGE_TIER_CACHE.json b/infrastructure/cdn-in-a-box/traffic_ops_data/profiles/011-VARNISH_EDGE_TIER_CACHE.json index 16fb280843..184c11227b 100644 --- a/infrastructure/cdn-in-a-box/traffic_ops_data/profiles/011-VARNISH_EDGE_TIER_CACHE.json +++ b/infrastructure/cdn-in-a-box/traffic_ops_data/profiles/011-VARNISH_EDGE_TIER_CACHE.json @@ -155,11 +155,17 @@ "secure": false, "value": "1000" }, + { + "configFile": "rascal.properties", + "name": "health.polling.format", + "secure": false, + "value": "vstats" + }, { "configFile": "rascal.properties", "name": "health.polling.url", "secure": false, - "value": "http://${hostname}/_astats?application=&inf.name=${interface_name}" + "value": "http://${hostname}:2000/" }, { "configFile": "storage.config", diff --git a/infrastructure/cdn-in-a-box/traffic_ops_data/profiles/021-VARNISH_MID_TIER_CACHE.json b/infrastructure/cdn-in-a-box/traffic_ops_data/profiles/021-VARNISH_MID_TIER_CACHE.json index 35d7051d82..906c98f36e 100644 --- a/infrastructure/cdn-in-a-box/traffic_ops_data/profiles/021-VARNISH_MID_TIER_CACHE.json +++ b/infrastructure/cdn-in-a-box/traffic_ops_data/profiles/021-VARNISH_MID_TIER_CACHE.json @@ -155,11 +155,17 @@ "secure": false, "value": "1000" }, + { + "configFile": "rascal.properties", + "name": "health.polling.format", + "secure": false, + "value": "vstats" + }, { "configFile": "rascal.properties", "name": "health.polling.url", "secure": false, - "value": "http://${hostname}/_astats?application=&inf.name=${interface_name}" + "value": "http://${hostname}:2000/" }, { "configFile": "storage.config", diff --git a/infrastructure/cdn-in-a-box/varnish/Dockerfile b/infrastructure/cdn-in-a-box/varnish/Dockerfile index 9b5840d7b5..e6f89188e4 100644 --- a/infrastructure/cdn-in-a-box/varnish/Dockerfile +++ b/infrastructure/cdn-in-a-box/varnish/Dockerfile @@ -17,6 +17,13 @@ ARG BASE_IMAGE=rockylinux \ RHEL_VERSION=8 + +FROM golang:1.20-alpine AS vstats-builder + +COPY infrastructure/cdn-in-a-box/varnish/vstats.go / + +RUN go build -o /vstats /vstats.go + FROM ${BASE_IMAGE}:${RHEL_VERSION} AS common-varnish-cache-config-layers ARG RHEL_VERSION=8 # Makes RHEL_VERSION available at runtime @@ -51,6 +58,8 @@ RUN rpm -Uvh /$(basename $ORT_RPM) &&\ COPY infrastructure/cdn-in-a-box/varnish/traffic_ops_ort.crontab /etc/cron.d/traffic_ops_ort-cron-template +COPY --from=vstats-builder /vstats /usr/local/bin + ENV CACHE_SERVER_TYPE=VARNISH CMD /run.sh diff --git a/infrastructure/cdn-in-a-box/varnish/run.sh b/infrastructure/cdn-in-a-box/varnish/run.sh index eb9ccd4f62..964397d6f1 100755 --- a/infrastructure/cdn-in-a-box/varnish/run.sh +++ b/infrastructure/cdn-in-a-box/varnish/run.sh @@ -84,6 +84,7 @@ debug_binary="${!debug_variable_name}" if ! type -p "$debug_binary"; then t3c apply --cache=varnish --trafficserver-home=/opt/cache --run-mode=badass --traffic-ops-url="$TO_URL" --traffic-ops-user="$TO_USER" --traffic-ops-password="$TO_PASSWORD" --git=yes -vv || { echo "Failed"; } fi +vstats --check-interval 1 >> /var/log/vstats.log 2>&1 & envsubst < "/etc/cron.d/traffic_ops_ort-cron-template" > "/etc/cron.d/traffic_ops_ort-cron" && rm -f "/etc/cron.d/traffic_ops_ort-cron-template" chmod "0644" "/etc/cron.d/traffic_ops_ort-cron" && crontab "/etc/cron.d/traffic_ops_ort-cron" diff --git a/infrastructure/cdn-in-a-box/varnish/traffic_ops_ort.crontab b/infrastructure/cdn-in-a-box/varnish/traffic_ops_ort.crontab index d830ed0062..c47d08376d 100644 --- a/infrastructure/cdn-in-a-box/varnish/traffic_ops_ort.crontab +++ b/infrastructure/cdn-in-a-box/varnish/traffic_ops_ort.crontab @@ -15,4 +15,4 @@ # specific language governing permissions and limitations # under the License. -*/1 * * * * root t3c apply --cache=varnish --run-mode=syncds --traffic-ops-url=$TO_URL --traffic-ops-user=$TO_USER --traffic-ops-password=$TO_PASSWORD --git=yes -vv --cache-host-name=$(hostname -s) >> /var/log/ort.log 2>&1 +*/1 * * * * root t3c apply --cache=varnish --run-mode=syncds --traffic-ops-url=$TO_URL --trafficserver-home=/opt/cache --traffic-ops-user=$TO_USER --traffic-ops-password=$TO_PASSWORD --git=yes -vv --cache-host-name=$(hostname -s) >> /var/log/ort.log 2>&1 diff --git a/infrastructure/cdn-in-a-box/varnish/vstats.go b/infrastructure/cdn-in-a-box/varnish/vstats.go new file mode 100644 index 0000000000..936784aa2b --- /dev/null +++ b/infrastructure/cdn-in-a-box/varnish/vstats.go @@ -0,0 +1,129 @@ +package main + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "log" + "net/http" + "os" + "os/exec" + "strconv" + "strings" + "sync" + "time" +) + +type app struct { + mu *sync.Mutex + vstats vstats + checkInterval time.Duration +} + +type vstats struct { + ProcLoadavg string `json:"proc.loadavg"` + ProcNetDev string `json:"proc.net.dev"` + InfSpeed int64 `json:"inf_speed"` + NotAvailable bool `json:"not_available"` + // TODO: stats +} + +func (a *app) getSystemData(ctx context.Context) { + ticker := time.NewTicker(a.checkInterval) + for { + select { + case <-ticker.C: + var vstats vstats + loadavg, err := os.ReadFile("/proc/loadavg") + if err != nil { + log.Printf("failed to read /proc/loadavg: %s\n", err) + } + vstats.ProcLoadavg = strings.TrimSpace(string(loadavg)) + + procNetDev, err := os.ReadFile("/proc/net/dev") + if err != nil { + log.Printf("failed to read /proc/net/dev: %s\n", err) + } + parts := strings.Split(string(procNetDev), "\n") + // 3 because first two are columns headers and 2 is loopback interface + vstats.ProcNetDev = strings.TrimSpace(parts[3]) + + infSpeedFile := fmt.Sprintf("/sys/class/net/%s/speed", strings.Split(vstats.ProcNetDev, ":")[0]) + speedStr, err := os.ReadFile(infSpeedFile) + if err != nil { + log.Printf("failed to read %s: %s\n", infSpeedFile, err) + } + speed, err := strconv.ParseInt(strings.TrimSpace(string(speedStr)), 10, 64) + if err != nil { + log.Printf("failed to convert speed '%s' to int: %s\n", speedStr, err) + } + vstats.InfSpeed = speed + + cmd := exec.Command("systemctl", "status", "varnish.service") + err = cmd.Run() + if err != nil { + log.Printf("failed to run systemctl: %s\n", err) + } + if cmd.ProcessState.ExitCode() != 0 { + vstats.NotAvailable = true + } + + a.mu.Lock() + a.vstats = vstats + a.mu.Unlock() + + case <-ctx.Done(): + break + } + } +} + +func (a *app) getStats(w http.ResponseWriter, r *http.Request) { + a.mu.Lock() + defer a.mu.Unlock() + encoder := json.NewEncoder(w) + err := encoder.Encode(a.vstats) + if err != nil { + log.Printf("failed to write Varnish stats: %s", err) + } +} + +func main() { + var checkInterval int + flag.IntVar(&checkInterval, "check-interval", 1, "the duration in seconds to get system data and poll Varnish cache") + + flag.Parse() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + app := app{ + mu: &sync.Mutex{}, + checkInterval: time.Duration(checkInterval) * time.Second, + } + go app.getSystemData(ctx) + + http.HandleFunc("/", app.getStats) + + if err := http.ListenAndServe(":2000", nil); err != nil { + log.Printf("server stopped %s", err) + } +} diff --git a/traffic_monitor/cache/vstats.go b/traffic_monitor/cache/vstats.go new file mode 100644 index 0000000000..c977dedc9c --- /dev/null +++ b/traffic_monitor/cache/vstats.go @@ -0,0 +1,79 @@ +package cache + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import ( + "errors" + "fmt" + "io" + + "github.com/apache/trafficcontrol/lib/go-log" + "github.com/apache/trafficcontrol/traffic_monitor/todata" + jsoniter "github.com/json-iterator/go" +) + +func init() { + registerDecoder("vstats", vstatsParse, vstatsPrecompute) +} + +// Vstats holds Varnish cache statistics +type Vstats struct { + ProcNetDev string `json:"proc.net.dev"` + ProcLoadAvg string `json:"proc.loadavg"` + NotAvailable bool `json:"not_available"` + InfSpeed int64 `json:"inf_speed"` + Stats map[string]interface{} `json:"stats"` +} + +func vstatsParse(cacheName string, r io.Reader, _ interface{}) (Statistics, map[string]interface{}, error) { + var stats Statistics + + if r == nil { + log.Warnf("%s handler got nil reader", cacheName) + return stats, nil, errors.New("handler got nil reader") + } + + var vstats Vstats + json := jsoniter.ConfigFastest + + if err := json.NewDecoder(r).Decode(&vstats); err != nil { + return stats, nil, fmt.Errorf("failed to decode reader data: %w", err) + } + if err := stats.AddInterfaceFromRawLine(vstats.ProcNetDev); err != nil { + return stats, nil, fmt.Errorf("failed to add interface data %s: %w", vstats.ProcNetDev, err) + } + + if loadAvg, err := LoadavgFromRawLine(vstats.ProcLoadAvg); err != nil { + return stats, nil, fmt.Errorf("failed to read average load data %s: %w", vstats.ProcLoadAvg, err) + } else { + stats.Loadavg = loadAvg + } + + stats.NotAvailable = vstats.NotAvailable + inf := stats.Interfaces["eth0"] + inf.Speed = vstats.InfSpeed + stats.Interfaces["eth0"] = inf + + return stats, vstats.Stats, nil +} + +func vstatsPrecompute(cacheName string, data todata.TOData, stats Statistics, miscStats map[string]interface{}) PrecomputedData { + return PrecomputedData{DeliveryServiceStats: map[string]*DSStat{}} +} From 1d3a6392e393d14e3f8db536569c6a09ae7a7d24 Mon Sep 17 00:00:00 2001 From: AbdelrahmanElawady Date: Fri, 1 Sep 2023 21:12:31 +0300 Subject: [PATCH 2/4] Add vstats parser tests --- traffic_monitor/cache/vstats_test.go | 60 ++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 traffic_monitor/cache/vstats_test.go diff --git a/traffic_monitor/cache/vstats_test.go b/traffic_monitor/cache/vstats_test.go new file mode 100644 index 0000000000..d3f20aa6bc --- /dev/null +++ b/traffic_monitor/cache/vstats_test.go @@ -0,0 +1,60 @@ +package cache + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import ( + "strings" + "testing" +) + +var vstatsData = `{ + "proc.net.dev": "eth0:47907832129 14601260 0 0 0 0 0 790726 728207677726 10210700052 0 0 0 0 0 0", + "proc.loadavg": "0.30 0.12 0.21 803/863 1421", + "not_available": false, + "inf_speed": 70000, + "stats": {} +} +` + +func TestVstatsParse(t *testing.T) { + reader := strings.NewReader(vstatsData) + systemStats, statistics, err := vstatsParse("test", reader, nil) + if err != nil { + t.Errorf("got error %s", err) + } + // stats not implemented yet + if len(statistics) != 0 { + t.Errorf("expected statistics to be empty found %v", statistics) + } + load := Loadavg{One: 0.3, Five: 0.12, Fifteen: 0.21, CurrentProcesses: 803, TotalProcesses: 863, LatestPID: 1421} + inf := Interface{Speed: 70000, BytesOut: 728207677726, BytesIn: 47907832129} + if systemStats.Loadavg != load { + t.Errorf("got %v want %v", systemStats.Loadavg, load) + } + if len(systemStats.Interfaces) != 1 { + t.Errorf("expected 1 interface got %v", systemStats.Interfaces) + } + if systemStats.Interfaces["eth0"] != inf { + t.Errorf("got %v want %v", systemStats.Interfaces["eth0"], inf) + } + if systemStats.NotAvailable { + t.Errorf("expected NotAvailable to be false") + } +} From 29638850b2bdef7b71b61d8f8d7b0e4a71d95f1a Mon Sep 17 00:00:00 2001 From: AbdelrahmanElawady Date: Wed, 6 Sep 2023 22:55:10 +0300 Subject: [PATCH 3/4] Get interface from TM and remove concurrency --- .../profiles/011-VARNISH_EDGE_TIER_CACHE.json | 2 +- .../profiles/021-VARNISH_MID_TIER_CACHE.json | 2 +- infrastructure/cdn-in-a-box/varnish/run.sh | 2 +- infrastructure/cdn-in-a-box/varnish/vstats.go | 117 ++++++++---------- 4 files changed, 52 insertions(+), 71 deletions(-) diff --git a/infrastructure/cdn-in-a-box/traffic_ops_data/profiles/011-VARNISH_EDGE_TIER_CACHE.json b/infrastructure/cdn-in-a-box/traffic_ops_data/profiles/011-VARNISH_EDGE_TIER_CACHE.json index 184c11227b..e6abc723bd 100644 --- a/infrastructure/cdn-in-a-box/traffic_ops_data/profiles/011-VARNISH_EDGE_TIER_CACHE.json +++ b/infrastructure/cdn-in-a-box/traffic_ops_data/profiles/011-VARNISH_EDGE_TIER_CACHE.json @@ -165,7 +165,7 @@ "configFile": "rascal.properties", "name": "health.polling.url", "secure": false, - "value": "http://${hostname}:2000/" + "value": "http://${hostname}:2000/?inf.name=${interface_name}" }, { "configFile": "storage.config", diff --git a/infrastructure/cdn-in-a-box/traffic_ops_data/profiles/021-VARNISH_MID_TIER_CACHE.json b/infrastructure/cdn-in-a-box/traffic_ops_data/profiles/021-VARNISH_MID_TIER_CACHE.json index 906c98f36e..b095b97f71 100644 --- a/infrastructure/cdn-in-a-box/traffic_ops_data/profiles/021-VARNISH_MID_TIER_CACHE.json +++ b/infrastructure/cdn-in-a-box/traffic_ops_data/profiles/021-VARNISH_MID_TIER_CACHE.json @@ -165,7 +165,7 @@ "configFile": "rascal.properties", "name": "health.polling.url", "secure": false, - "value": "http://${hostname}:2000/" + "value": "http://${hostname}:2000/?inf.name=${interface_name}" }, { "configFile": "storage.config", diff --git a/infrastructure/cdn-in-a-box/varnish/run.sh b/infrastructure/cdn-in-a-box/varnish/run.sh index 964397d6f1..4e68f1aafe 100755 --- a/infrastructure/cdn-in-a-box/varnish/run.sh +++ b/infrastructure/cdn-in-a-box/varnish/run.sh @@ -84,7 +84,7 @@ debug_binary="${!debug_variable_name}" if ! type -p "$debug_binary"; then t3c apply --cache=varnish --trafficserver-home=/opt/cache --run-mode=badass --traffic-ops-url="$TO_URL" --traffic-ops-user="$TO_USER" --traffic-ops-password="$TO_PASSWORD" --git=yes -vv || { echo "Failed"; } fi -vstats --check-interval 1 >> /var/log/vstats.log 2>&1 & +vstats -port 2000 >> /var/log/vstats.log 2>&1 & envsubst < "/etc/cron.d/traffic_ops_ort-cron-template" > "/etc/cron.d/traffic_ops_ort-cron" && rm -f "/etc/cron.d/traffic_ops_ort-cron-template" chmod "0644" "/etc/cron.d/traffic_ops_ort-cron" && crontab "/etc/cron.d/traffic_ops_ort-cron" diff --git a/infrastructure/cdn-in-a-box/varnish/vstats.go b/infrastructure/cdn-in-a-box/varnish/vstats.go index 936784aa2b..2507cf502a 100644 --- a/infrastructure/cdn-in-a-box/varnish/vstats.go +++ b/infrastructure/cdn-in-a-box/varnish/vstats.go @@ -20,7 +20,6 @@ package main */ import ( - "context" "encoding/json" "flag" "fmt" @@ -30,16 +29,8 @@ import ( "os/exec" "strconv" "strings" - "sync" - "time" ) -type app struct { - mu *sync.Mutex - vstats vstats - checkInterval time.Duration -} - type vstats struct { ProcLoadavg string `json:"proc.loadavg"` ProcNetDev string `json:"proc.net.dev"` @@ -48,82 +39,72 @@ type vstats struct { // TODO: stats } -func (a *app) getSystemData(ctx context.Context) { - ticker := time.NewTicker(a.checkInterval) - for { - select { - case <-ticker.C: - var vstats vstats - loadavg, err := os.ReadFile("/proc/loadavg") - if err != nil { - log.Printf("failed to read /proc/loadavg: %s\n", err) - } - vstats.ProcLoadavg = strings.TrimSpace(string(loadavg)) - - procNetDev, err := os.ReadFile("/proc/net/dev") - if err != nil { - log.Printf("failed to read /proc/net/dev: %s\n", err) - } - parts := strings.Split(string(procNetDev), "\n") - // 3 because first two are columns headers and 2 is loopback interface - vstats.ProcNetDev = strings.TrimSpace(parts[3]) - - infSpeedFile := fmt.Sprintf("/sys/class/net/%s/speed", strings.Split(vstats.ProcNetDev, ":")[0]) - speedStr, err := os.ReadFile(infSpeedFile) - if err != nil { - log.Printf("failed to read %s: %s\n", infSpeedFile, err) - } - speed, err := strconv.ParseInt(strings.TrimSpace(string(speedStr)), 10, 64) - if err != nil { - log.Printf("failed to convert speed '%s' to int: %s\n", speedStr, err) - } - vstats.InfSpeed = speed - - cmd := exec.Command("systemctl", "status", "varnish.service") - err = cmd.Run() - if err != nil { - log.Printf("failed to run systemctl: %s\n", err) - } - if cmd.ProcessState.ExitCode() != 0 { - vstats.NotAvailable = true - } +func getSystemData(inf string) vstats { + var vstats vstats + loadavg, err := os.ReadFile("/proc/loadavg") + if err != nil { + log.Printf("failed to read /proc/loadavg: %s\n", err) + } + vstats.ProcLoadavg = strings.TrimSpace(string(loadavg)) - a.mu.Lock() - a.vstats = vstats - a.mu.Unlock() + procNetDev, err := os.ReadFile("/proc/net/dev") + if err != nil { + log.Printf("failed to read /proc/net/dev: %s\n", err) + } - case <-ctx.Done(): + parts := strings.Split(string(procNetDev), "\n") + for _, line := range parts { + if strings.HasPrefix(strings.TrimSpace(line), inf) { + vstats.ProcNetDev = strings.TrimSpace(line) break } } + + infSpeedFile := fmt.Sprintf("/sys/class/net/%s/speed", inf) + speedStr, err := os.ReadFile(infSpeedFile) + if err != nil { + log.Printf("failed to read %s: %s\n", infSpeedFile, err) + } + speed, err := strconv.ParseInt(strings.TrimSpace(string(speedStr)), 10, 64) + if err != nil { + log.Printf("failed to convert speed '%s' to int: %s\n", speedStr, err) + } + vstats.InfSpeed = speed + + cmd := exec.Command("systemctl", "status", "varnish.service") + err = cmd.Run() + if err != nil { + log.Printf("failed to run systemctl: %s\n", err) + } + if cmd.ProcessState.ExitCode() != 0 { + vstats.NotAvailable = true + } + return vstats } -func (a *app) getStats(w http.ResponseWriter, r *http.Request) { - a.mu.Lock() - defer a.mu.Unlock() +func getStats(w http.ResponseWriter, r *http.Request) { + inf := r.URL.Query().Get("inf.name") + if inf == "" { + // assume default eth0? + inf = "eth0" + } + vstats := getSystemData(inf) encoder := json.NewEncoder(w) - err := encoder.Encode(a.vstats) + err := encoder.Encode(vstats) if err != nil { log.Printf("failed to write Varnish stats: %s", err) } } func main() { - var checkInterval int - flag.IntVar(&checkInterval, "check-interval", 1, "the duration in seconds to get system data and poll Varnish cache") + var port int + flag.IntVar(&port, "port", 2000, "port to run vstats on") flag.Parse() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - app := app{ - mu: &sync.Mutex{}, - checkInterval: time.Duration(checkInterval) * time.Second, - } - go app.getSystemData(ctx) - - http.HandleFunc("/", app.getStats) + http.HandleFunc("/", getStats) - if err := http.ListenAndServe(":2000", nil); err != nil { + listenAddress := fmt.Sprintf(":%d", port) + if err := http.ListenAndServe(listenAddress, nil); err != nil { log.Printf("server stopped %s", err) } } From e840378acaa9514678b559f8b02b8c76952385ca Mon Sep 17 00:00:00 2001 From: AbdelrahmanElawady Date: Fri, 8 Sep 2023 17:43:35 +0300 Subject: [PATCH 4/4] Remove . and / from interface names --- infrastructure/cdn-in-a-box/varnish/vstats.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/infrastructure/cdn-in-a-box/varnish/vstats.go b/infrastructure/cdn-in-a-box/varnish/vstats.go index 2507cf502a..10c871717f 100644 --- a/infrastructure/cdn-in-a-box/varnish/vstats.go +++ b/infrastructure/cdn-in-a-box/varnish/vstats.go @@ -88,6 +88,8 @@ func getStats(w http.ResponseWriter, r *http.Request) { // assume default eth0? inf = "eth0" } + inf = strings.ReplaceAll(inf, ".", "") + inf = strings.ReplaceAll(inf, "/", "") vstats := getSystemData(inf) encoder := json.NewEncoder(w) err := encoder.Encode(vstats)