This is an automated email from the ASF dual-hosted git repository.

friede pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new b31e00330d Add TM integration and separate docker-compose services for 
Varnish (#7746)
b31e00330d is described below

commit b31e00330d6d3735d812cfeb737ff86c28c070c8
Author: AbdelrahmanElawady 
<[email protected]>
AuthorDate: Sat Sep 9 01:11:11 2023 +0300

    Add TM integration and separate docker-compose services for Varnish (#7746)
    
    * Add TM integration and separate docker-compose services for Varnish
    
    * Add vstats parser tests
    
    * Get interface from TM and remove concurrency
    
    * Remove . and / from interface names
---
 cache-config/t3c-apply/torequest/torequest.go      |   8 +-
 ..._ops_ort.crontab => docker-compose.varnish.yml} |  19 +++-
 .../profiles/011-VARNISH_EDGE_TIER_CACHE.json      |   8 +-
 .../profiles/021-VARNISH_MID_TIER_CACHE.json       |   8 +-
 infrastructure/cdn-in-a-box/varnish/Dockerfile     |   9 ++
 infrastructure/cdn-in-a-box/varnish/run.sh         |   1 +
 .../cdn-in-a-box/varnish/traffic_ops_ort.crontab   |   2 +-
 infrastructure/cdn-in-a-box/varnish/vstats.go      | 112 +++++++++++++++++++++
 traffic_monitor/cache/vstats.go                    |  79 +++++++++++++++
 traffic_monitor/cache/vstats_test.go               |  60 +++++++++++
 10 files changed, 300 insertions(+), 6 deletions(-)

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/varnish/traffic_ops_ort.crontab 
b/infrastructure/cdn-in-a-box/docker-compose.varnish.yml
similarity index 64%
copy from infrastructure/cdn-in-a-box/varnish/traffic_ops_ort.crontab
copy to infrastructure/cdn-in-a-box/docker-compose.varnish.yml
index d830ed0062..f79ba0cff9 100644
--- a/infrastructure/cdn-in-a-box/varnish/traffic_ops_ort.crontab
+++ b/infrastructure/cdn-in-a-box/docker-compose.varnish.yml
@@ -14,5 +14,22 @@
 # 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'
 
-*/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
+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..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
@@ -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/?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 35d7051d82..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
@@ -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/?inf.name=${interface_name}";
                },
                {
                        "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..4e68f1aafe 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 -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/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..10c871717f
--- /dev/null
+++ b/infrastructure/cdn-in-a-box/varnish/vstats.go
@@ -0,0 +1,112 @@
+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 (
+       "encoding/json"
+       "flag"
+       "fmt"
+       "log"
+       "net/http"
+       "os"
+       "os/exec"
+       "strconv"
+       "strings"
+)
+
+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 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))
+
+       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")
+       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 getStats(w http.ResponseWriter, r *http.Request) {
+       inf := r.URL.Query().Get("inf.name")
+       if inf == "" {
+               // assume default eth0?
+               inf = "eth0"
+       }
+       inf = strings.ReplaceAll(inf, ".", "")
+       inf = strings.ReplaceAll(inf, "/", "")
+       vstats := getSystemData(inf)
+       encoder := json.NewEncoder(w)
+       err := encoder.Encode(vstats)
+       if err != nil {
+               log.Printf("failed to write Varnish stats: %s", err)
+       }
+}
+
+func main() {
+       var port int
+       flag.IntVar(&port, "port", 2000, "port to run vstats on")
+
+       flag.Parse()
+       http.HandleFunc("/", getStats)
+
+       listenAddress := fmt.Sprintf(":%d", port)
+       if err := http.ListenAndServe(listenAddress, 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{}}
+}
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")
+       }
+}

Reply via email to