diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..e392a0b6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,55 @@ + +# Create snmpcollector-last.tar.gz, which includes the binary + +FROM ubuntu:16.04 as build +WORKDIR /app/src/snmpcollector +RUN apt update -y && apt upgrade -y +RUN apt-get install -y locales +RUN apt-get install -y curl git gcc +RUN curl -sL https://deb.nodesource.com/setup_8.x -o nodesource_setup.sh +RUN bash nodesource_setup.sh +RUN apt-get install -y nodejs +RUN curl -O https://storage.googleapis.com/golang/go1.9.1.linux-amd64.tar.gz +RUN tar -xvf go1.9.1.linux-amd64.tar.gz -C /usr/local +ENV PATH=$PATH:/usr/local/go/bin +ENV GOROOT=/usr/local/go +ENV GOPATH=/app +ENV PATH=$PATH:$GOROOT/bin +ENV PATH=$GOPATH/bin:$GOROOT/bin:$PATH + + +COPY . /app/src/snmpcollector/ + + +RUN go run build.go setup +# RUN go get github.com/tools/godep +# RUN godep restore + +# RUN npm install +# RUN PATH=$(npm bin):$PATH +# RUN npm run build:prod + +# RUN npm run build:static +RUN go run build.go build-static + +RUN go run build.go pkg-min-tar + + + +# Create the Dockerfile for the SnmpCollector + +FROM alpine:latest + +COPY --from=build /app/src/snmpcollector/dist/snmpcollector.tar.gz / + +RUN tar zxvf snmpcollector.tar.gz -C . + + +WORKDIR /opt/snmpcollector + +COPY ../../docker ./conf +COPY ../../docker / + +# RUN mkdir ./log + +ENTRYPOINT ["/start.sh"] \ No newline at end of file diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 21cb7b9e..7ed24822 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,5 +1,5 @@ { - "ImportPath": "github.com/toni-moreno/snmpcollector", + "ImportPath": "snmpcollector", "GoVersion": "go1.8", "GodepVersion": "v79", "Packages": [ diff --git a/build.go b/build.go index 9b486241..a787965a 100644 --- a/build.go +++ b/build.go @@ -226,7 +226,7 @@ func createMinTar() { runPrint("cp", "bin/snmpcollector", filepath.Join(packageRoot, "/opt/snmpcollector/bin")) runPrint("cp", "bin/snmpcollector.md5", filepath.Join(packageRoot, "/opt/snmpcollector/bin")) runPrint("cp", "-a", filepath.Join(workingDir, "public")+"/.", filepath.Join(packageRoot, "/opt/snmpcollector/public")) - tarname := fmt.Sprintf("dist/snmpcollector-%s-%s_%s_%s.tar.gz", version, getGitSha(), runtime.GOOS, runtime.GOARCH) + tarname := "dist/snmpcollector.tar.gz" runPrint("tar", "zcvf", tarname, "-C", packageRoot, ".") runPrint("rm", "-rf", packageRoot) } @@ -366,9 +366,9 @@ func build(pkg string, tags []string, flags []string) { func ldflags(flags []string) string { var b bytes.Buffer b.WriteString("-w") - b.WriteString(fmt.Sprintf(" -X github.com/toni-moreno/snmpcollector/pkg/agent.Version=%s", version)) - b.WriteString(fmt.Sprintf(" -X github.com/toni-moreno/snmpcollector/pkg/agent.Commit=%s", getGitSha())) - b.WriteString(fmt.Sprintf(" -X github.com/toni-moreno/snmpcollector/pkg/agent.BuildStamp=%d", buildStamp())) + b.WriteString(fmt.Sprintf(" -X snmpcollector/pkg/agent.Version=%s", version)) + b.WriteString(fmt.Sprintf(" -X snmpcollector/pkg/agent.Commit=%s", getGitSha())) + b.WriteString(fmt.Sprintf(" -X snmpcollector/pkg/agent.BuildStamp=%d", buildStamp())) for _, f := range flags { b.WriteString(fmt.Sprintf(" %s", f)) } @@ -384,9 +384,9 @@ func rmr(paths ...string) { func clean() { // rmr("bin", "Godeps/_workspace/pkg", "Godeps/_workspace/bin") - rmr("public") + // disable ui: rmr("public") //rmr("tmp") - rmr(filepath.Join(os.Getenv("GOPATH"), fmt.Sprintf("pkg/%s_%s/github.com/toni-moreno/snmpcollector", goos, goarch))) + rmr(filepath.Join(os.Getenv("GOPATH"), fmt.Sprintf("pkg/%s_%s/snmpcollector", goos, goarch))) } func setBuildEnv() { diff --git a/docker/Dockerfile b/docker/Dockerfile deleted file mode 100644 index 43e5294e..00000000 --- a/docker/Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -FROM alpine:latest -MAINTAINER Toni Moreno <toni.moreno@gmail.com> - -ADD ./snmpcollector-last.tar.gz / - -VOLUME ["/opt/snmpcollector/conf", "/opt/snmpcollector/log"] - -EXPOSE 8090 - -WORKDIR /opt/snmpcollector -COPY ./config.toml ./conf/ -COPY ./start.sh / - -ENTRYPOINT ["/start.sh"] diff --git a/docker/build-docker.sh b/docker/build-docker.sh index fa28c41e..dc97a495 100755 --- a/docker/build-docker.sh +++ b/docker/build-docker.sh @@ -13,6 +13,7 @@ if [ ! -f dist/snmpcollector-${VERSION}-${COMMIT}_${GOOS:-linux}_${GOARCH:-amd64 then echo "building binary...." npm run build:static + # npm run build go run build.go pkg-min-tar else echo "skiping build..." @@ -26,7 +27,7 @@ cp conf/sample.config.toml docker/config.toml cd docker -sudo docker build --label version="${VERSION}" --label commitid="${COMMIT}" -t tonimoreno/snmpcollector:${VERSION} -t tonimoreno/snmpcollector:latest . +docker build --label version="${VERSION}" --label commitid="${COMMIT}" -t tonimoreno/snmpcollector:${VERSION} -t tonimoreno/snmpcollector:latest . rm snmpcollector-last.tar.gz rm config.toml rm /tmp/goenv.tmp diff --git a/docker/config.toml b/docker/config.toml new file mode 100644 index 00000000..9ffcb0be --- /dev/null +++ b/docker/config.toml @@ -0,0 +1,82 @@ + +############################ +# General Config +############################ + +[general] + # InstanceID will be a string identifying the collector , It will be useful when + # more than one running on the same server, this ID will be shown in the WebUI. + instanceID = "WAN Communicactions" + # datadir set the directory where the data will be placed , also sqlite db if set as db engine + # if not set the default datadir will be placed in the configuration directory + # datadir = "/var/lib/snmpcollector" + + # there are as many logs as devices configured + # logdir set the Directory path for each device individual log, default is /var/log/snmpcollector + # logdir = "/var/log/somelogpath" + # NOTE: main process log is now sent to standard output + # logLevel set the main process log level + # valid values: panic,fatal,error,warn,info,debug + logLevel = "warn" + + ############################ + # DataBase Config + ############################ + +[database] +#type sets the sql backend +#valid values sqlite3,mysql + type = "sqlite3" +# these parameters are only for mysql (use mysql_setup.sql before) +# host = 127.0.0.1:3306 +# user = "snmpcoluser" +# password = "snmpcolpass" +# name sets the database name + name = "snmpcollector" +#sqllogfile sets the name for a file in the log/ directory where sql backend will write all SQL transactions +#sqllogfile = "sql.log" + # debug adds extra verbosity to the SQL log + debug = false + + + +############################ +# Self Monitorig Config +############################ +#config for sending self monitoring metrics to our default influx db +# Sent Measurements will be <prefix>selfmon_gvm with the following fields +# runtime_goroutines +# *mem.alloc +# *mem.mallocs +# *mem.frees +# *gc.total_pause_ns +# *memory.heap +# *gc.pause_per_second +# *gc.pause_per_interval +# *gc.gc_per_second +# *gc.gc_per_interval + +[selfmon] + #enable true/false enable/disable self monitoring + enabled = true + #send data Frequency + freq = 60 + #prefix for measurement naming + prefix = "" + #inherit device tags + inheritdevicetags = true + #adds extra tags to the measurement config should be set as a csv - tag=value1,tag2=value2,...,tagN=valN + extratags = [ "instance=snmpcollector01" ] + +############################ +# Embedded WebServer Config +############################ + +[http] + #port where webserver will listen waiting for connections + port = 8090 + #Admin credentials to access to the SnmpCollector agent + adminuser = "adm1" + adminpassword = "adm1pass" + #When more than one instance you will need customize the cookie_id allowing navigate to all instances + cookieid ="my_instance_cookie" diff --git a/docker/development/Dockerfile b/docker/development/Dockerfile new file mode 100644 index 00000000..e5e0f60e --- /dev/null +++ b/docker/development/Dockerfile @@ -0,0 +1,24 @@ +FROM ubuntu:16.04 as build +WORKDIR /app/src/snmpcollector + +RUN apt update -y && apt upgrade -y +RUN apt-get install -y locales +RUN apt-get install -y curl git gcc +RUN curl -sL https://deb.nodesource.com/setup_8.x -o nodesource_setup.sh +RUN bash nodesource_setup.sh +RUN apt-get install -y nodejs +RUN curl -O https://storage.googleapis.com/golang/go1.12.1.linux-amd64.tar.gz +RUN tar -xvf go1.12.1.linux-amd64.tar.gz -C /usr/local +RUN rm go1.12.1.linux-amd64.tar.gz +ENV PATH=$PATH:/usr/local/go/bin +ENV GOROOT=/usr/local/go +ENV GOPATH=/app +ENV PATH=$PATH:$GOROOT/bin +ENV PATH=$GOPATH/bin:$GOROOT/bin:$PATH +COPY ./setup.sh . +RUN bash setup.sh +COPY . . +RUN mv docker/start-dev.sh / + +ENTRYPOINT [ "/start-dev.sh" ] + diff --git a/docker/production/Dockerfile b/docker/production/Dockerfile new file mode 100644 index 00000000..28a51817 --- /dev/null +++ b/docker/production/Dockerfile @@ -0,0 +1,55 @@ + +# Create snmpcollector-last.tar.gz, which includes the binary + +FROM ubuntu:16.04 as build +WORKDIR /app/src/snmpcollector +RUN apt update -y && apt upgrade -y +RUN apt-get install -y locales +RUN apt-get install -y curl git gcc +RUN curl -sL https://deb.nodesource.com/setup_8.x -o nodesource_setup.sh +RUN bash nodesource_setup.sh +RUN apt-get install -y nodejs +RUN curl -O https://storage.googleapis.com/golang/go1.12.1.linux-amd64.tar.gz +RUN tar -xvf go1.12.1.linux-amd64.tar.gz -C /usr/local +ENV PATH=$PATH:/usr/local/go/bin +ENV GOROOT=/usr/local/go +ENV GOPATH=/app +ENV PATH=$PATH:$GOROOT/bin +ENV PATH=$GOPATH/bin:$GOROOT/bin:$PATH + +COPY ./setup.sh . +RUN bash setup.sh + +COPY . /app/src/snmpcollector/ + +# RUN go get github.com/tools/godep +# RUN godep restore + +RUN npm install +RUN PATH=$(npm bin):$PATH +#RUN npm run build:prod + +RUN npm run build:static +# RUN go run build.go build-static + +RUN go run build.go pkg-min-tar + + + +# Create the Dockerfile for the SnmpCollector + +FROM alpine:latest + +COPY --from=build /app/src/snmpcollector/dist/snmpcollector.tar.gz / + +RUN tar zxvf snmpcollector.tar.gz -C . + + +WORKDIR /opt/snmpcollector + +COPY ./docker/config.toml ./conf +COPY ./docker/start.sh / + +# RUN mkdir ./log + +ENTRYPOINT ["/start.sh"] \ No newline at end of file diff --git a/docker/start-dev.sh b/docker/start-dev.sh new file mode 100755 index 00000000..fa62b027 --- /dev/null +++ b/docker/start-dev.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +rm ./conf/snmpcollector.db +go run build.go build + + + +./bin/snmpcollector > ./log/stdout.log 2> ./log/stderr.log diff --git a/docker/start.sh b/docker/start.sh index 957b0463..9573d238 100755 --- a/docker/start.sh +++ b/docker/start.sh @@ -1,2 +1,21 @@ #!/bin/sh + +if [ "$APP_ENV" = "development" ] +then + echo "MODE: Developement" + if [ -f "./conf/snmpcollector.db" ] + then + rm ./conf/snmpcollector.db + fi +fi + + +if ! [ -x "./bin/snmpcollector" ] +then + go run build.go build + echo "Status: build complete" +fi + + + ./bin/snmpcollector > ./log/stdout.log 2> ./log/stderr.log diff --git a/docker/toni-moreno/Dockerfile b/docker/toni-moreno/Dockerfile new file mode 100644 index 00000000..e56a92a2 --- /dev/null +++ b/docker/toni-moreno/Dockerfile @@ -0,0 +1,30 @@ + + +FROM ubuntu:16.04 + +COPY .. /app +RUN apt-get update && apt-get install -y locales +RUN apt-get install -y golang curl +RUN curl -sL https://deb.nodesource.com/setup_8.x -o nodesource_setup.sh +RUN bash nodesource_setup.sh +RUN apt-get install -y nodejs +RUN npm install +RUN PATH=$(npm bin):$PATH +RUN npm run build:prod + + +FROM alpine:latest + +MAINTAINER Toni Moreno <toni.moreno@gmail.com> + +ADD ./docker/snmpcollector-last.tar.gz / + +VOLUME ["/opt/snmpcollector/conf", "/opt/snmpcollector/log"] + +EXPOSE 8090 + +WORKDIR /opt/snmpcollector +COPY ./docker/config.toml ./conf +COPY ./docker/start.sh / + +ENTRYPOINT ["/start.sh"] diff --git a/log/.gitignore b/log/.gitignore deleted file mode 100644 index 5e7d2734..00000000 --- a/log/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# Ignore everything in this directory -* -# Except this file -!.gitignore diff --git a/package.json b/package.json index 1398c767..cc7d2935 100644 --- a/package.json +++ b/package.json @@ -7,25 +7,28 @@ "version": "0.8.0", "repository": { "type": "git", - "url": "http://github.com/toni-moreno/snmpcollector.git" + "url": "http://snmpcollector.git" }, "bugs": { - "url": "http://github.com/toni-moreno/snmpcollector/issues" + "url": "http://snmpcollector/issues" }, "license": "Apache-2.0", "angular-cli": {}, "scripts": { "clean": "rm -rf public", - "start": "concurrently \"bra run > ./log/braout.log 2>&1\" \"ng serve --proxy-config proxy.conf.json\" ", + "start": "concurrently \"bra run > ./log/braout.log 2>&1\" \"ng serve --port=4201 --proxy-config proxy.conf.json\" ", + "build:frontend": "node --max-old-space-size=8192 node_modules/@angular/cli/bin/ng build", + "build:frontend:prod": "node --max-old-space-size=8192 node_modules/@angular/cli/bin/ng build --prod", "lint": "tslint \"src/**/*.ts\"", "build:dev": "go run build.go build && ng build", - "build:prod": "go run build.go build && ng build --prod", + "build:prod": "go run build.go build && npm run build:frontend:prod", "build": "npm run clean && npm run build:prod", - "build:static": "go run build.go build-static && ng build --prod", + "build:static": "go run build.go build-static && npm run build:frontend:prod", "postbuild": "go run build.go latest", "test": "ng test", "pree2e": "webdriver-manager update", - "e2e": "protractor" + "e2e": "protractor", + "s": "npm run start" }, "private": true, "dependencies": { @@ -44,7 +47,7 @@ "es6-shim": "^0.35.0", "file-saver": "^1.3.3", "jquery": "~> 3.0.0", - "lodash": ">=4.17.5", + "lodash": "4.17.5", "moment": "^2.13.0", "ngx-bootstrap": "1.9.3", "reflect-metadata": "~0.1.8", @@ -57,7 +60,7 @@ "@angular/compiler-cli": "4.3.6", "@types/file-saver": "~0.0.0", "@types/jasmine": "2.5.38", - "@types/lodash": "^4.14.44", + "@types/lodash": "4.14.44", "@types/node": "8.0.28", "codelyzer": "3.2.0", "concurrently": "^3.0.0", diff --git a/packaging/deb/systemd/snmpcollector.service b/packaging/deb/systemd/snmpcollector.service index 7687ed60..56ba6167 100644 --- a/packaging/deb/systemd/snmpcollector.service +++ b/packaging/deb/systemd/snmpcollector.service @@ -1,6 +1,6 @@ [Unit] Description=SnmpCollector Agent -Documentation=http://github.com/toni-moreno/snmpcollector +Documentation=http://snmpcollector Wants=network-online.target After=network-online.target diff --git a/packaging/rpm/systemd/snmpcollector.service b/packaging/rpm/systemd/snmpcollector.service index da6e7134..21206d80 100644 --- a/packaging/rpm/systemd/snmpcollector.service +++ b/packaging/rpm/systemd/snmpcollector.service @@ -1,6 +1,6 @@ [Unit] Description=SnmpCollector Agent -Documentation=http://github.com/toni-moreno/snmpcollector +Documentation=http://snmpcollector Wants=network-online.target After=network-online.target diff --git a/pkg/agent/agent.go b/pkg/agent/agent.go index a6288480..71962a6a 100644 --- a/pkg/agent/agent.go +++ b/pkg/agent/agent.go @@ -2,15 +2,16 @@ package agent import ( "fmt" + "snmpcollector/pkg/rabbitmq" "sync" "time" "github.com/Sirupsen/logrus" - "github.com/toni-moreno/snmpcollector/pkg/agent/bus" - "github.com/toni-moreno/snmpcollector/pkg/agent/device" - "github.com/toni-moreno/snmpcollector/pkg/agent/output" - "github.com/toni-moreno/snmpcollector/pkg/agent/selfmon" - "github.com/toni-moreno/snmpcollector/pkg/config" + "snmpcollector/pkg/agent/bus" + "snmpcollector/pkg/agent/device" + "snmpcollector/pkg/agent/output" + "snmpcollector/pkg/agent/selfmon" + "snmpcollector/pkg/config" ) var ( @@ -279,6 +280,10 @@ func AddDeviceInRuntime(k string, cfg *config.SnmpDeviceCfg) { // send a db map to initialize each one its own db if needed outdb, _ := dev.GetOutSenderFromMap(influxdb) outdb.Init() + + // init rabbitmq + rabbitmq.Init() + outdb.StartSender(&senderWg) mutex.Lock() diff --git a/pkg/agent/device/measgather.go b/pkg/agent/device/measgather.go index 3223465a..e85cdb7e 100644 --- a/pkg/agent/device/measgather.go +++ b/pkg/agent/device/measgather.go @@ -4,7 +4,7 @@ import ( "sync" "time" - "github.com/toni-moreno/snmpcollector/pkg/data/measurement" + "snmpcollector/pkg/data/measurement" ) func (d *SnmpDevice) measConcurrentGatherAndSend() { @@ -14,7 +14,7 @@ func (d *SnmpDevice) measConcurrentGatherAndSend() { wg.Add(1) go func(m *measurement.Measurement) { defer wg.Done() - bpts, _ := d.Influx.BP() + d.Debugf("-------Processing measurement : %s", m.ID) nGets, nProcs, nErrs, _ := m.GetData() @@ -24,19 +24,28 @@ func (d *SnmpDevice) measConcurrentGatherAndSend() { m.ComputeEvaluatedMetrics(d.VarMap) //prepare batchpoint - metSent, metError, measSent, measError, points := m.GetInfluxPoint(d.TagMap) + metSent, metError, measSent, measError, pointsArray := m.GetInfluxPoint(d.TagMap) d.stats.AddMeasStats(metSent, metError, measSent, measError) startInfluxStats := time.Now() - if bpts != nil { - (*bpts).AddPoints(points) - //send data - d.Influx.Send(bpts) - } else { - d.Warnf("Can not send data to the output DB becaouse of batchpoint creation error") + + for _, points := range pointsArray { + + bpts, _ := d.Influx.BP() + + if bpts != nil { + (*bpts).AddPoints(points) + //send data + d.Influx.Send(bpts) + } else { + d.Warnf("Can not send data to the output DB becaouse of batchpoint creation error") + } + } + elapsedInfluxStats := time.Since(startInfluxStats) d.stats.AddSentDuration(startInfluxStats, elapsedInfluxStats) + }(m) } wg.Wait() @@ -63,11 +72,16 @@ func (d *SnmpDevice) measSeqGatherAndSend() { m.ComputeEvaluatedMetrics(d.VarMap) //prepare batchpoint - metSent, metError, measSent, measError, points := m.GetInfluxPoint(d.TagMap) + metSent, metError, measSent, measError, pointsArray := m.GetInfluxPoint(d.TagMap) d.stats.AddMeasStats(metSent, metError, measSent, measError) - if bpts != nil { - (*bpts).AddPoints(points) + + for _, points := range pointsArray { + if bpts != nil { + (*bpts).AddPoints(points) + } } + + } elapsedSnmpStats := time.Since(startSnmpStats) diff --git a/pkg/agent/device/snmpdevice.go b/pkg/agent/device/snmpdevice.go index 5f437207..c8263519 100644 --- a/pkg/agent/device/snmpdevice.go +++ b/pkg/agent/device/snmpdevice.go @@ -1,24 +1,26 @@ package device import ( + "encoding/json" "fmt" + "io" "os" "strconv" - - "encoding/json" "strings" "sync" "time" + "snmpcollector/pkg/agent/bus" + "snmpcollector/pkg/agent/output" + "snmpcollector/pkg/agent/selfmon" + "snmpcollector/pkg/config" + "snmpcollector/pkg/data/measurement" + "snmpcollector/pkg/data/snmp" + "snmpcollector/pkg/data/utils" + "github.com/Sirupsen/logrus" "github.com/soniah/gosnmp" - "github.com/toni-moreno/snmpcollector/pkg/agent/bus" - "github.com/toni-moreno/snmpcollector/pkg/agent/output" - "github.com/toni-moreno/snmpcollector/pkg/agent/selfmon" - "github.com/toni-moreno/snmpcollector/pkg/config" - "github.com/toni-moreno/snmpcollector/pkg/data/measurement" - "github.com/toni-moreno/snmpcollector/pkg/data/snmp" - "github.com/toni-moreno/snmpcollector/pkg/data/utils" + "gopkg.in/natefinch/lumberjack.v2" ) var ( @@ -68,6 +70,7 @@ type SnmpDevice struct { DeviceActive bool DeviceConnected bool StateDebug bool + Exited bool Node *bus.Node `json:"-"` isStopped chan bool `json:"-"` @@ -124,6 +127,7 @@ func (d *SnmpDevice) getBasicStats() *DevStat { stat.DeviceConnected = d.DeviceConnected stat.NumMeasurements = len(d.Measurements) stat.NumMetrics = sum + stat.Exited = d.Exited if d.SysInfo != nil { stat.SysDescription = d.SysInfo.SysDescr } else { @@ -190,6 +194,7 @@ func (d *SnmpDevice) SnmpReset(mode string) { // StopGather send signal to stop the Gathering process func (d *SnmpDevice) StopGather() { d.Node.SendMsg(&bus.Message{Type: "syncexit"}) + // d.Node.SendMsg(&bus.Message{Type: "exit"}) <-d.isStopped d.log.Info("Exiting from StopGather process...") } @@ -266,7 +271,7 @@ func (d *SnmpDevice) InitDevMeasurements() { continue } //creating a new measurement runtime object and asigning to array - imeas, err := measurement.New(mVal, d.log, c, d.cfg.DisableBulk) + imeas, err := measurement.New(mVal, d.log, c, d.cfg.DisableBulk, d.cfg.SpecificInterfaceFilters, d.cfg.SpecificMetricFilters) if err != nil { d.Errorf("Error on measurement initialization Error: %s", err) continue @@ -322,8 +327,11 @@ Init does the following - Initialize not set variables to some defaults - Initialize logfile for this device -- Initialize comunication channels and initial device state +- Initialize communication channels and initial device state + */ + + func (d *SnmpDevice) Init(c *config.SnmpDeviceCfg) error { if c == nil { return fmt.Errorf("Error on initialice device, configuration struct is nil") @@ -344,12 +352,26 @@ func (d *SnmpDevice) Init(c *config.SnmpDeviceCfg) error { d.cfg.LogLevel = "info" } - f, _ := os.OpenFile(d.cfg.LogFile, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644) + //f, _ := os.OpenFile(d.cfg.LogFile, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644) d.log = logrus.New() - d.log.Out = f + + + lumberjackLogger := &lumberjack.Logger{ + Filename: d.cfg.LogFile, + MaxSize: 5, + MaxBackups: 2, + MaxAge: 1, + } + + d.log.Out = lumberjackLogger + io.MultiWriter(os.Stdout, lumberjackLogger) + //d.log.Out = f + + l, _ := logrus.ParseLevel(d.cfg.LogLevel) d.log.Level = l d.CurLogLevel = d.log.Level.String() + //Formatter for time customFormatter := new(logrus.TextFormatter) customFormatter.TimestampFormat = "2006-01-02 15:04:05" @@ -470,6 +492,7 @@ func (d *SnmpDevice) LeaveBus(b *bus.Bus) { // End The Opposite of Init() uninitialize all variables func (d *SnmpDevice) End() { + d.Exited = true d.Node.Close() for _, val := range d.snmpClientMap { snmp.Release(val) @@ -727,12 +750,38 @@ func (d *SnmpDevice) startGatherGo(wg *sync.WaitGroup) { d.Infof("Beginning gather process for device on host (%s)", d.cfg.Host) t := time.NewTicker(time.Duration(d.cfg.Freq) * time.Second) - for { + + //_, err := agent.MainConfig.Database.GetSnmpDeviceCfgByID(d.cfg.ID) + //if err != nil { + // deviceStillExists = false + // } + + + + for d.stats.Exited != true { t = d.gatherAndProcessData(t, false) + + // if (d.cfg.UpdateFltFreq - d.Stats.ReloadLoopsPending) > 2 { + // _, exists := cfg.SnmpDevice[d.cfg.ID] + // if exists != true { + // break + // } + // } + + LOOP: - for { + for d.stats.Exited != true { + + // if (d.cfg.UpdateFltFreq - d.Stats.ReloadLoopsPending) > 2 { + // _, exists := cfg.SnmpDevice[d.cfg.ID] + // if exists != true { + // break + // } + // } + + select { case <-t.C: break LOOP @@ -748,7 +797,14 @@ func (d *SnmpDevice) startGatherGo(wg *sync.WaitGroup) { case "syncexit": d.Infof("invoked Syncronous EXIT from SNMP Gather process ") d.isStopped <- true - return + d.rtData.Lock() + d.DeviceActive = false + d.cfg.Active = false + d.stats.Exited = true + d.Exited = true + d.rtData.Unlock() + break + // return case "filterupdate": d.rtData.Lock() d.setReloadLoopsPending(1) @@ -799,6 +855,15 @@ func (d *SnmpDevice) startGatherGo(wg *sync.WaitGroup) { d.statsData.Lock() d.Stats = d.getBasicStats() d.statsData.Unlock() + + if d.Stats.Exited == true { + break + } } + + if d.Stats.Exited == true { + break + } + } } diff --git a/pkg/agent/device/stats.go b/pkg/agent/device/stats.go index a6f2b61a..0aa0b521 100644 --- a/pkg/agent/device/stats.go +++ b/pkg/agent/device/stats.go @@ -5,7 +5,7 @@ import ( "time" "github.com/Sirupsen/logrus" - "github.com/toni-moreno/snmpcollector/pkg/agent/selfmon" + "snmpcollector/pkg/agent/selfmon" ) // DevStatType a device stat type @@ -79,6 +79,7 @@ type DevStat struct { NumMeasurements int SysDescription string NumMetrics int + Exited bool } // Init initializes the device stat object diff --git a/pkg/agent/output/influx.go b/pkg/agent/output/influx.go index e912ec0a..e25ea4e7 100644 --- a/pkg/agent/output/influx.go +++ b/pkg/agent/output/influx.go @@ -3,14 +3,15 @@ package output import ( "fmt" "math/rand" + "snmpcollector/pkg/rabbitmq" "strings" "sync" "time" "github.com/Sirupsen/logrus" "github.com/influxdata/influxdb/client/v2" - "github.com/toni-moreno/snmpcollector/pkg/config" - "github.com/toni-moreno/snmpcollector/pkg/data/utils" + "snmpcollector/pkg/config" + "snmpcollector/pkg/data/utils" ) var ( @@ -212,7 +213,11 @@ func (db *InfluxDB) Init() { log.Infof("Initializing influxdb with id = [ %s ]", db.cfg.ID) log.Infof("Connecting to: %s", db.cfg.Host) + + db.iChan = make(chan *client.BatchPoints, db.cfg.BufferSize) + + db.chExit = make(chan bool) if err := db.Connect(); err != nil { log.Errorln("failed connecting to: ", db.cfg.Host) @@ -282,20 +287,37 @@ func (db *InfluxDB) sendBatchPoint(data *client.BatchPoints, enqueueonerror bool np := len((*data).Points()) //number of total fields nf := 0 + + var rabbitMqInfluxPointsStored = make(map[string]int) + for _, v := range (*data).Points() { - fields, err := v.Fields() - if err != nil { + + fields, errFields := v.Fields() + if errFields != nil { log.Debug("Some error happened when trying to get fields for point... ") } else { nf += len(fields) } + + + // prepare rabbitmq messages + tags := v.Tags() + pointStr := v.String() + + pointMeasurement := v.Name() + + pointStored := rabbitmq.GetInfluxPointStored(pointMeasurement, tags) + rabbitMqInfluxPointsStored[pointStored] = 1 // we just need the key, we wont send point stored twice + log.Info(" pointStr: %s, %s, %s", pointStr, pointMeasurement, tags) } + + // keep trying until we get it (don't drop the data) startSend := time.Now() err := db.client.Write(*data) elapsedSend := time.Since(startSend) - bufferPercent = (float32(len(db.iChan)) * 100.0) / float32(db.cfg.BufferSize) + bufferPercent = float32(len(db.iChan) * 100.0) / float32(db.cfg.BufferSize) if err != nil { db.stats.WriteErrUpdate(elapsedSend, bufferPercent) log.Errorf("ERROR on Write batchPoint in DB %s (%d points) | elapsed : %s | Error: %s ", db.cfg.ID, np, elapsedSend.String(), err) @@ -310,6 +332,12 @@ func (db *InfluxDB) sendBatchPoint(data *client.BatchPoints, enqueueonerror bool } else { log.Debugf("OK on Write batchPoint in DB %s (%d points) | elapsed : %s ", db.cfg.ID, np, elapsedSend.String()) db.stats.WriteOkUpdate(int64(np), int64(nf), elapsedSend, bufferPercent) + + + // publish through rabbitmq + rabbitmq.PublishInfluxPointStored(rabbitMqInfluxPointsStored) + + } } @@ -319,34 +347,36 @@ func (db *InfluxDB) startSenderGo(r int, wg *sync.WaitGroup) { time.Sleep(5) log.Infof("beginning Influx Sender thread: [%s]", db.cfg.ID) for { - select { - case <-db.chExit: - //need to flush all data - - chanlen := len(db.iChan) // get number of entries in the batchpoint channel - log.Infof("Flushing %d batchpoints of data in OutDB %s ", chanlen, db.cfg.ID) - for i := 0; i < chanlen; i++ { - //flush them - data := <-db.iChan - //this process only will work if backend is running ok elsewhere points will be lost - db.sendBatchPoint(data, false) - } - log.Infof("EXIT from Influx sender process for device [%s] ", db.cfg.ID) - db.SetStartedAs(false) - return - case data := <-db.iChan: - if data == nil { - log.Warn("null influx input") - continue - } - if db.client == nil { - log.Warn("db Client not initialized yet!!!!!") - continue - } - db.sendBatchPoint(data, true) + select { + case <-db.chExit: + //need to flush all data + + chanlen := len(db.iChan) // get number of entries in the batchpoint channel + log.Infof("Flushing %d batchpoints of data in OutDB %s ", chanlen, db.cfg.ID) + for i := 0; i < chanlen; i++ { + //flush them + data := <-db.iChan + //this process only will work if backend is running ok elsewhere points will be lost + db.sendBatchPoint(data, false) + } + + log.Infof("EXIT from Influx sender process for device [%s] ", db.cfg.ID) + db.SetStartedAs(false) + return + case data := <-db.iChan: + if data == nil { + log.Warn("null influx input") + continue + } + if db.client == nil { + log.Warn("db Client not initialized yet!!!!!") + continue + } + + db.sendBatchPoint(data, true) + } - } } } diff --git a/pkg/agent/selfmon/selfmon.go b/pkg/agent/selfmon/selfmon.go index 454fcffa..f2b6353e 100644 --- a/pkg/agent/selfmon/selfmon.go +++ b/pkg/agent/selfmon/selfmon.go @@ -9,8 +9,8 @@ import ( "github.com/Sirupsen/logrus" "github.com/influxdata/influxdb/client/v2" - "github.com/toni-moreno/snmpcollector/pkg/agent/output" - "github.com/toni-moreno/snmpcollector/pkg/config" + "snmpcollector/pkg/agent/output" + "snmpcollector/pkg/config" ) var ( diff --git a/pkg/config/database.go b/pkg/config/database.go index e5ec047c..696b64eb 100644 --- a/pkg/config/database.go +++ b/pkg/config/database.go @@ -12,6 +12,7 @@ import ( "github.com/go-xorm/core" "github.com/go-xorm/xorm" + // _ needed to sqlite3 _ "github.com/mattn/go-sqlite3" ) @@ -121,6 +122,86 @@ func (dbc *DatabaseCfg) InitDB() { if err = dbc.x.Sync(new(OidConditionCfg)); err != nil { log.Fatalf("Fail to sync database OidConditionCfg: %v\n", err) } + + SetupDefaultDbData(dbc) +} + +// SetupDefaultDbData initialize our database with mandatory default values +func SetupDefaultDbData(dbc *DatabaseCfg) { + + // create default monitoring InfluxDB + + var id string + hastInfluxDb, existsError := dbc.x.Table("influx_cfg").Where("ID = ?", "influx").Cols("ID").Get(&id) + + if existsError != nil { + log.Fatalf("Error on determine wether monitioring InfluxDB exists, error: %s", existsError) + } + + + var influxHost = os.Getenv("INFLUXDB_HOST") + if influxHost == "" { + influxHost = "localhost" // default + } + + var influxDB = os.Getenv("INFLUXDB_DB") + if influxDB == "" { + influxDB = "monitoring" // default + } + + var influxUser = os.Getenv("INFLUXDB_USER") + if influxUser == "" { + influxUser = "admin" // default + } + + var influxPassword = os.Getenv("INFLUXDB_USER_PASSWORD") + if influxPassword == "" { + influxPassword = "admin" // default + } + + if !hastInfluxDb { + // create default influx db + + monitoringInfluxDB := InfluxCfg{ + ID: "influx", + Host: influxHost, + Port: 8086, + DB: influxDB, + User: influxUser, + Password: influxPassword, + Retention: "autogen", + Precision: "s", + Timeout: 30, + UserAgent: "snmpcollector", + EnableSSL: false, + InsecureSkipVerify: true, + BufferSize: 1000, + } + + affected, err := dbc.AddInfluxCfg(monitoringInfluxDB) + if err != nil { + log.Fatalf("Error on insert monitoring InfluxDB, affected : %+v , error: %s", affected, err) + } + } else { + // update default influx db + + var defaultInfluxDb, err = dbc.GetInfluxCfgByID("influx") + if err != nil { + log.Fatalf("Error on update default InfluxDB with env variables, error: %s", err) + } else { + + defaultInfluxDb.Host = influxHost + defaultInfluxDb.DB = influxDB + defaultInfluxDb.User = influxUser + defaultInfluxDb.Password = influxPassword + + var _, err = dbc.UpdateInfluxCfg(defaultInfluxDb.ID, defaultInfluxDb) + if err != nil { + log.Fatalf("Error on update default InfluxDB with env variables, error: %s", err) + } + } + + } } // CatalogVar2Map return interface map from variable table diff --git a/pkg/config/dbconfig.go b/pkg/config/dbconfig.go index 61d0c3a2..f7c213b7 100644 --- a/pkg/config/dbconfig.go +++ b/pkg/config/dbconfig.go @@ -44,6 +44,8 @@ type SnmpDeviceCfg struct { //Filters for measurements MeasurementGroups []string `xorm:"-"` MeasFilters []string `xorm:"-"` + SpecificInterfaceFilters string `xorm:"specific_interface_filters"` + SpecificMetricFilters string `xorm:"specific_metric_filters"` } // InfluxCfg is the main configuration for any InfluxDB TSDB diff --git a/pkg/config/measurementcfg.go b/pkg/config/measurementcfg.go index 95fe763f..393af1ea 100644 --- a/pkg/config/measurementcfg.go +++ b/pkg/config/measurementcfg.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - "github.com/toni-moreno/snmpcollector/pkg/data/utils" + "snmpcollector/pkg/data/utils" ) //MeasurementCfg the measurement configuration @@ -14,6 +14,7 @@ type MeasurementCfg struct { Name string `xorm:"name" binding:"Required"` GetMode string `xorm:"getmode" binding:"In(value,indexed,indexed_it)"` //value ,indexed (direct tag), indexed_it ( indirect_tag) IndexOID string `xorm:"indexoid"` //only valid if Indexed (direct or indirect) + IndexDescrOID string `xorm:"index_descr_oid"` // translation for indices TagOID string `xorm:"tagoid"` //only valid if inderecta TAG indexeded IndexTag string `xorm:"indextag"` IndexTagFormat string `xorm:"indextagformat"` @@ -26,6 +27,7 @@ type MeasurementCfg struct { EvalMetric []*SnmpMetricCfg `xorm:"-" json:"-"` OidCondMetric []*SnmpMetricCfg `xorm:"-" json:"-"` Description string `xorm:"description"` + Encoding string `xorm:"encoding"` } //CheckComputedMetricVars check for computed metrics based on check if variable definition exist diff --git a/pkg/config/oidconditioncfg.go b/pkg/config/oidconditioncfg.go index da20b27b..2c9b69b7 100644 --- a/pkg/config/oidconditioncfg.go +++ b/pkg/config/oidconditioncfg.go @@ -7,7 +7,7 @@ import ( "strings" "github.com/Knetic/govaluate" - "github.com/toni-moreno/snmpcollector/pkg/data/utils" + "snmpcollector/pkg/data/utils" ) // OidConditionCfg condition config for filters and metrics @@ -18,6 +18,7 @@ type OidConditionCfg struct { CondType string `xorm:"cond_type"` CondValue string `xorm:"cond_value"` Description string `xorm:"description"` + Encoding string `xorm:"encoding"` } // Init Initialize a OIDConditionCfg diff --git a/pkg/config/snmpdevicecfg.go b/pkg/config/snmpdevicecfg.go index 600e5d98..8ce4ac3a 100644 --- a/pkg/config/snmpdevicecfg.go +++ b/pkg/config/snmpdevicecfg.go @@ -1,6 +1,9 @@ package config -import "fmt" +import ( + "fmt" + "strconv" +) /*************************** SNMPDevice Services @@ -39,6 +42,17 @@ func (dbc *DatabaseCfg) GetSnmpDeviceCfgMap(filter string) (map[string]*SnmpDevi return devcfgmap, err } +func (dbc *DatabaseCfg) GetNumberOfSnmpDevices() (int, error) { + var err error + // var devices []*SnmpDeviceCfg + // counts, err := dbc.x.Count(&devices) + // counts, err := dbc.x.SQL("SELECT count(*) AS total from snmp_device_cfg").Count(&devices) + result, err := dbc.x.QueryString("SELECT count(*) AS total from snmp_device_cfg") + counts, _ := strconv.Atoi(result[0]["total"]) + return counts, err +} + + /*GetSnmpDeviceCfgArray generate an array of devices with all its information */ func (dbc *DatabaseCfg) GetSnmpDeviceCfgArray(filter string) ([]*SnmpDeviceCfg, error) { var err error @@ -94,11 +108,17 @@ func (dbc *DatabaseCfg) GetSnmpDeviceCfgArray(filter string) ([]*SnmpDeviceCfg, /*AddSnmpDeviceCfg for adding new devices*/ func (dbc *DatabaseCfg) AddSnmpDeviceCfg(dev SnmpDeviceCfg) (int64, error) { + var err error var affected, newmg, newft int64 session := dbc.x.NewSession() defer session.Close() + _, err = dbc.GetSnmpDeviceCfgByID(dev.ID) + if err == nil { //device exist already in the database + return dbc.UpdateSnmpDeviceCfg(dev.ID, dev) + } + affected, err = session.Insert(dev) if err != nil { session.Rollback() diff --git a/pkg/data/filter/customfilter.go b/pkg/data/filter/customfilter.go index a59e5876..8ba9285f 100644 --- a/pkg/data/filter/customfilter.go +++ b/pkg/data/filter/customfilter.go @@ -2,7 +2,7 @@ package filter import ( "github.com/Sirupsen/logrus" - "github.com/toni-moreno/snmpcollector/pkg/config" + "snmpcollector/pkg/config" ) // CustomFilter a filter created from a device query, usuali used only with the DeviceOrigin , but could be used on more than one but diff --git a/pkg/data/filter/oidfilter.go b/pkg/data/filter/oidfilter.go index 8a31eac7..8ade87b4 100644 --- a/pkg/data/filter/oidfilter.go +++ b/pkg/data/filter/oidfilter.go @@ -2,14 +2,15 @@ package filter import ( "fmt" + "net" "regexp" "strconv" "strings" "github.com/Sirupsen/logrus" "github.com/soniah/gosnmp" - "github.com/toni-moreno/snmpcollector/pkg/data/snmp" - "github.com/toni-moreno/snmpcollector/pkg/data/utils" + "snmpcollector/pkg/data/snmp" + "snmpcollector/pkg/data/utils" ) // OidFilter a new Oid condition filter @@ -18,13 +19,14 @@ type OidFilter struct { OidCond string TypeCond string ValueCond string + Encoding string log *logrus.Logger Walk func(string, gosnmp.WalkFunc) error `json:"-"` } // NewOidFilter create a new filter for OID conditions -func NewOidFilter(oidcond string, typecond string, value string, l *logrus.Logger) *OidFilter { - return &OidFilter{OidCond: oidcond, TypeCond: typecond, ValueCond: value, log: l} +func NewOidFilter(oidcond string, typecond string, value string, l *logrus.Logger, encoding string) *OidFilter { + return &OidFilter{OidCond: oidcond, TypeCond: typecond, ValueCond: value, log: l, Encoding: encoding} } // Init initialize @@ -88,7 +90,15 @@ func (of *OidFilter) Update() error { cond = !matched case of.TypeCond == "match": //m.log.Debugf("PDU: %+v", pdu) - str := snmp.PduVal2str(pdu) + + str := "" + if of.Encoding == "MAC" { + var bytes = pdu.Value.([]byte) + str = net.HardwareAddr(bytes).String() + } else { + str = snmp.PduVal2str(pdu) + } + re, err := regexp.Compile(of.ValueCond) if err != nil { of.log.Warnf("OIDFILTER [%s] Evaluated notmatch condition value: %s | filter: %s | ERROR : %s", of.OidCond, str, of.ValueCond, err) diff --git a/pkg/data/filter/oidmultiplefilter.go b/pkg/data/filter/oidmultiplefilter.go index 9134e501..f974522d 100644 --- a/pkg/data/filter/oidmultiplefilter.go +++ b/pkg/data/filter/oidmultiplefilter.go @@ -5,7 +5,7 @@ import ( "github.com/Knetic/govaluate" "github.com/Sirupsen/logrus" "github.com/soniah/gosnmp" - "github.com/toni-moreno/snmpcollector/pkg/config" + "snmpcollector/pkg/config" ) // OidMultipleFilter a new Oid condition filter @@ -61,7 +61,7 @@ func (of *OidMultipleFilter) Init(arg ...interface{}) error { } of.condMap[par] = &oidcond of.log.Debugf("OIDMULTIPLEFILTER [%s] get %s Filter %+v", of.EvalCondition, par, oidcond) - f := NewOidFilter(oidcond.OIDCond, oidcond.CondType, oidcond.CondValue, of.log) + f := NewOidFilter(oidcond.OIDCond, oidcond.CondType, oidcond.CondValue, of.log, oidcond.Encoding) f.Init(of.Walk) of.oidFilterMap[par] = f } diff --git a/pkg/data/impexp/exportdata.go b/pkg/data/impexp/exportdata.go index 522a992e..99c5e3cf 100644 --- a/pkg/data/impexp/exportdata.go +++ b/pkg/data/impexp/exportdata.go @@ -7,8 +7,8 @@ import ( "github.com/Knetic/govaluate" "github.com/Sirupsen/logrus" - "github.com/toni-moreno/snmpcollector/pkg/agent" - "github.com/toni-moreno/snmpcollector/pkg/config" + "snmpcollector/pkg/agent" + "snmpcollector/pkg/config" ) var ( diff --git a/pkg/data/impexp/import.go b/pkg/data/impexp/import.go index 710e157f..3c0bf131 100644 --- a/pkg/data/impexp/import.go +++ b/pkg/data/impexp/import.go @@ -8,7 +8,7 @@ import ( "time" "github.com/go-macaron/binding" - "github.com/toni-moreno/snmpcollector/pkg/config" + "snmpcollector/pkg/config" ) // ImportCheck returns and ExportData type with error summary diff --git a/pkg/data/measurement/influxpoint.go b/pkg/data/measurement/influxpoint.go index 93fdf834..3e949578 100644 --- a/pkg/data/measurement/influxpoint.go +++ b/pkg/data/measurement/influxpoint.go @@ -7,12 +7,13 @@ import ( ) //GetInfluxPoint get points from measuremnetsl -func (m *Measurement) GetInfluxPoint(hostTags map[string]string) (int64, int64, int64, int64, []*client.Point) { +func (m *Measurement) GetInfluxPoint(hostTags map[string]string) (int64, int64, int64, int64, map[string][]*client.Point) { var metSent int64 var metError int64 var measSent int64 var measError int64 - var ptarray []*client.Point + + var points = make(map[string][]*client.Point) switch m.cfg.GetMode { case "value": @@ -32,15 +33,63 @@ func (m *Measurement) GetInfluxPoint(hostTags map[string]string) (int64, int64, } m.Debugf("FIELDS:%+v", Fields) - pt, err := client.NewPoint(m.cfg.Name, Tags, Fields, t) - if err != nil { - m.Warnf("error in influx point building:%s", err) - measError++ - } else { - m.Debugf("GENERATED INFLUX POINT[%s] value: %+v", m.cfg.Name, pt) - ptarray = append(ptarray, pt) - measSent++ - k.Valid = true + for fieldsKey, fieldsV := range Fields { + + switch fieldType := fieldsV.(type) { + case map[string]interface{}: + + for fieldK, fieldV := range fieldType { + + if fieldV != nil { + + // handle sub indexes + + var indexField = make(map[string]interface{}) + + indexField[fieldsKey] = fieldV + if fieldK != "" { + indexField["subIndexesValues"] = fieldK + Tags["subIndexes"] = fieldK + } + + + pt, err := client.NewPoint(m.cfg.Name, Tags, indexField, t) + if err != nil { + m.Warnf("error in influx point building:%s", err) + measError++ + } else { + m.Debugf("GENERATED INFLUX POINT[%s] value: %+v", m.cfg.Name, pt) + + var ptarray = points[fieldK] + ptarray = append(ptarray, pt) + points[fieldK] = ptarray + + measSent++ + k.Valid = true + } + } + } + + default: + // handle unknown type + + pt, err := client.NewPoint(m.cfg.Name, Tags, Fields, t) + if err != nil { + m.Warnf("error in influx point building:%s", err) + measError++ + } else { + m.Debugf("GENERATED INFLUX POINT[%s] value: %+v", m.cfg.Name, pt) + + var ptarray = points[""] + ptarray = append(ptarray, pt) + points[""] = ptarray + + measSent++ + k.Valid = true + } + + } + } case "indexed", "indexed_it": @@ -64,20 +113,70 @@ func (m *Measurement) GetInfluxPoint(hostTags map[string]string) (int64, int64, } //here we can chek Fields names prior to send data m.Debugf("FIELDS:%+v TAGS:%+v", Fields, Tags) - pt, err := client.NewPoint(m.cfg.Name, Tags, Fields, t) - if err != nil { - m.Warnf("error in influx point creation :%s", err) - measError++ - } else { - m.Debugf("GENERATED INFLUX POINT[%s] index [%s]: %+v", m.cfg.Name, idx, pt) - ptarray = append(ptarray, pt) - measSent++ - vIdx.Valid = true + + for fieldsKey, fieldsV := range Fields { + + switch fieldType := fieldsV.(type) { + case map[string]interface{}: + + for fieldK, fieldV := range fieldType { + + if fieldV != nil { + + // handle sub indexes + + var indexField = make(map[string]interface{}) + + indexField[fieldsKey] = fieldV + if fieldK != "" { + indexField["subIndexesValues"] = fieldK + Tags["subIndexes"] = fieldK + } + + pt, err := client.NewPoint(m.cfg.Name, Tags, indexField, t) + + if err != nil { + m.Warnf("error in influx point creation :%s", err) + measError++ + } else { + m.Debugf("GENERATED INFLUX POINT[%s] index [%s]: %+v", m.cfg.Name, idx, pt) + + var ptarray = points[fieldK] + ptarray = append(ptarray, pt) + points[fieldK] = ptarray + + measSent++ + vIdx.Valid = true + } + } + } + + default: + // handle unknown type + + pt, err := client.NewPoint(m.cfg.Name, Tags, Fields, t) + if err != nil { + m.Warnf("error in influx point creation :%s", err) + measError++ + } else { + m.Debugf("GENERATED INFLUX POINT[%s] index [%s]: %+v", m.cfg.Name, idx, pt) + + var ptarray = points[""] + ptarray = append(ptarray, pt) + points[""] = ptarray + + measSent++ + vIdx.Valid = true + } + + } + } + } } - return metSent, metError, measSent, measError, ptarray + return metSent, metError, measSent, measError, points } diff --git a/pkg/data/measurement/measurement.go b/pkg/data/measurement/measurement.go index 90c45247..8ade8b42 100644 --- a/pkg/data/measurement/measurement.go +++ b/pkg/data/measurement/measurement.go @@ -2,17 +2,20 @@ package measurement import ( "fmt" + "net" + "regexp" + "strings" "strconv" "time" "github.com/Sirupsen/logrus" "github.com/soniah/gosnmp" - "github.com/toni-moreno/snmpcollector/pkg/config" - "github.com/toni-moreno/snmpcollector/pkg/data/filter" - "github.com/toni-moreno/snmpcollector/pkg/data/metric" - "github.com/toni-moreno/snmpcollector/pkg/data/snmp" - "github.com/toni-moreno/snmpcollector/pkg/data/utils" + "snmpcollector/pkg/config" + "snmpcollector/pkg/data/filter" + "snmpcollector/pkg/data/metric" + "snmpcollector/pkg/data/snmp" + "snmpcollector/pkg/data/utils" ) var ( @@ -42,10 +45,12 @@ type Measurement struct { snmpOids []string OidSnmpMap map[string]*metric.SnmpMetric `json:"-"` //snmpMetric mapped with real OID's AllIndexedLabels map[string]string //`json:"-"` //all available values on the remote device + AllIfDescrLabels map[string]string CurIndexedLabels map[string]string //`json:"-"` idxPosInOID int idx2PosInOID int curIdxPos int //used in Walk functions could be variable depending on the Index (or IndexTag) + curIfDescrxPos int //used in Walk functions could be variable depending on the IfDescr FilterCfg *config.MeasFilterCfg Filter filter.Filter log *logrus.Logger @@ -53,11 +58,13 @@ type Measurement struct { DisableBulk bool `json:"-"` GetData func() (int64, int64, int64, error) `json:"-"` Walk func(string, gosnmp.WalkFunc) error `json:"-"` + deviceSpecificInterfaceFilters string + deviceSpecificMetricFilters string } //New creates object with config , log + goSnmp client -func New(c *config.MeasurementCfg, l *logrus.Logger, cli *gosnmp.GoSNMP, db bool) (*Measurement, error) { - m := &Measurement{ID: c.ID, MName: c.Name, cfg: c, log: l, snmpClient: cli, DisableBulk: db} +func New(c *config.MeasurementCfg, l *logrus.Logger, cli *gosnmp.GoSNMP,db bool, deviceSpecificInterfaceFilters string, deviceSpecificMetricFilters string) (*Measurement, error) { + m := &Measurement{ID: c.ID, MName: c.Name, cfg: c, log: l, snmpClient: cli, DisableBulk: db, deviceSpecificInterfaceFilters: deviceSpecificInterfaceFilters, deviceSpecificMetricFilters: deviceSpecificMetricFilters} err := m.Init() return m, err } @@ -106,6 +113,20 @@ func (m *Measurement) Init() error { m.AllIndexedLabels = il //Final Selected Indexes are All Indexed m.CurIndexedLabels = m.AllIndexedLabels + + + + // ifDescr labels + if len(m.cfg.IndexDescrOID) > 0 { + ifDescr, ifDescrErr := m.loadIfDescrLabels(m.cfg.IndexDescrOID) + if ifDescrErr != nil { + m.Errorf("Error while trying to load ifDescr Labels on for measurement ERROR: %s", err) + return err + } + m.AllIfDescrLabels = ifDescr + } + + } /******************************** @@ -177,7 +198,7 @@ func (m *Measurement) AddFilter(f *config.MeasFilterCfg) error { return fmt.Errorf("Error invalid Multiple Condition Filter : %s", err) } } else { - m.Filter = filter.NewOidFilter(cond.OIDCond, cond.CondType, cond.CondValue, m.log) + m.Filter = filter.NewOidFilter(cond.OIDCond, cond.CondType, cond.CondValue, m.log, cond.Encoding) err = m.Filter.Init(m.Walk) if err != nil { return fmt.Errorf("Error invalid OID condition Filter : %s", err) @@ -224,6 +245,21 @@ func (m *Measurement) UpdateFilter() (bool, error) { return false, err } m.AllIndexedLabels = il + + + // ifDescr labels + if len(m.cfg.IndexDescrOID) > 0 { + ifDescr, ifDescrErr := m.loadIfDescrLabels(m.cfg.IndexDescrOID) + if ifDescrErr != nil { + m.Errorf("Error while trying to load ifDescr Labels on for measurement ERROR: %s", err) + return false, err + } + m.AllIfDescrLabels = ifDescr + } + + + + if m.Filter == nil { m.Debugf("There is no filter configured in this measurement %s", m.cfg.ID) //check if curindexed different of AllIndexed @@ -310,20 +346,90 @@ func (m *Measurement) SnmpWalkData() (int64, int64, int64, error) { errors++ return nil //if error return the bulk process will stop } + + if metr, ok := m.OidSnmpMap[pdu.Name]; ok { m.Debugf("OK measurement %s SNMP RESULT OID %s MetricFound", pdu.Name, pdu.Value) processed++ - metr.SetRawData(pdu, now) + metr.SetRawData(pdu, nil, "", now) } else { - m.Debugf("returned OID from device: %s Not Found in measurement /metr list: %+v", pdu.Name, m.cfg.ID) + var subSet = false + var pduOids = strings.Split(pdu.Name, ".") + + var mapKey = "" + + for _, oid := range pduOids { + + if mapKey == "" { + if oid != "" { + mapKey = "." + oid + } + continue + } + + if mapVal, ok := m.OidSnmpMap[mapKey]; ok { + + var subOids = strings.Split(pdu.Name, mapKey) + var subOid = strings.Join(subOids,"") + + + subSet = true + + + m.Debugf("OK measurement %s SNMP RESULT OID %s MetricFound", pdu.Name, pdu.Value) + + if len(m.cfg.IndexDescrOID) > 0 && m.AllIfDescrLabels != nil { + var lastOidIndex = subOids[len(subOids) -1] + var lastOids = strings.Split(lastOidIndex, ".") + var lastOid = lastOids[len(lastOids) -1] + if _, present := m.AllIfDescrLabels[lastOid]; present { + subOid = m.AllIfDescrLabels[lastOid] + } + } + + processed++ + mapVal.SetRawData(pdu, subOids, subOid, now) + + break + } + mapKey += "." + oid + } + + + if !subSet { + m.Debugf("returned OID from device: %s Not Found in measurement /metr list: %+v", pdu.Name, m.cfg.ID) + } } + return nil } + for _, v := range m.cfg.FieldMetric { - if err := m.Walk(v.BaseOID, setRawData); err != nil { - m.Errorf("SNMP WALK (%s) for OID (%s) get error: %s\n", m.snmpClient.Target, v.BaseOID, err) - errors += int64(m.MetricTable.Len()) + var filtered = false + + if len(m.deviceSpecificMetricFilters) > 0 { // check if field metric should be used + if strings.Contains(m.deviceSpecificMetricFilters, v.FieldName) != true { + filtered = true + } + } + if !filtered { + var walkAll = len(m.deviceSpecificInterfaceFilters) <= 0 + if walkAll { + if err := m.Walk(v.BaseOID, setRawData); err != nil { + m.Errorf("SNMP WALK (%s) for OID (%s) get error: %s\n", m.snmpClient.Target, v.BaseOID, err) + errors += int64(m.MetricTable.Len()) + } + } else { + // load only available indices + for labelKey := range m.AllIndexedLabels { + var labelOID = v.BaseOID + "." + labelKey + if err := m.Walk(labelOID, setRawData); err != nil { + m.Errorf("SNMP WALK (%s) for OID (%s) get error: %s\n", m.snmpClient.Target, v.BaseOID, err) + errors += int64(m.MetricTable.Len()) + } + } + } } } @@ -472,7 +578,7 @@ func (m *Measurement) SnmpGetData() (int64, int64, int64, error) { if metr, ok := m.OidSnmpMap[oid]; ok { m.Debugf("OK measurement %s SNMP result OID: %s MetricFound: %+v ", m.cfg.ID, oid, val) sent++ - metr.SetRawData(pdu, now) + metr.SetRawData(pdu, nil, "", now) } else { m.Errorf("OID %s Not Found in measurement %s", oid, m.cfg.ID) } @@ -508,7 +614,17 @@ func (m *Measurement) loadIndexedLabels() (map[string]string, error) { name := "ErrorOnGetIdxValue" switch pdu.Type { case gosnmp.OctetString: - name = string(pdu.Value.([]byte)) + var bytes = pdu.Value.([]byte) + + if m.cfg.Encoding == "MAC" { + var mac = net.HardwareAddr(bytes).String() + name = mac // name = string(bytes) + } else { + name = string(bytes) + } + + + m.Debugf("Got the following OctetString index for [%s/%s]", suffix, name) case gosnmp.Counter32, gosnmp.Counter64, gosnmp.Gauge32, gosnmp.Uinteger32: name = strconv.FormatUint(snmp.PduVal2UInt64(pdu), 10) @@ -526,6 +642,30 @@ func (m *Measurement) loadIndexedLabels() (map[string]string, error) { default: m.Errorf("Error in IndexedLabel IndexLabel %s ERR: Not String or numeric or IPAddress Value", m.cfg.IndexOID) } + + + // TODO: implement measurement filter + if len(m.deviceSpecificInterfaceFilters) > 0 { + var filterConditionValues = strings.Split(m.deviceSpecificInterfaceFilters, ";") + var matched = false + for _, filterConditionValue := range filterConditionValues { + if matched { + continue + } + re, err := regexp.Compile(strings.ToUpper(filterConditionValue)) + if err != nil { + m.Warnf("OIDFILTER [%s] Evaluated notmatch condition value: %s | filter: %s | ERROR : %s", name, filterConditionValue, err) + return nil + } + matched = re.MatchString(strings.ToUpper(name)) + } + + if !matched { + return nil + } + } + + allindex[suffix] = name return nil } @@ -584,3 +724,65 @@ func (m *Measurement) loadIndexedLabels() (map[string]string, error) { } return allindexIt, nil } + + + +func (m *Measurement) loadIfDescrLabels(ifDescrOID string) (map[string]string, error) { + + m.Debugf("Looking up loadIfDescrLabels %s ", m.cfg.ID) + + allIfDescr := make(map[string]string) + + setRawData := func(pdu gosnmp.SnmpPDU) error { + m.Debugf("received SNMP pdu:%+v", pdu) + if pdu.Value == nil { + m.Warnf("no value retured by pdu :%+v", pdu) + return nil //if error return the bulk process will stop + } + if len(pdu.Name) < m.curIfDescrxPos+1 { + m.Warnf("Received PDU OID smaller than minimal index(%d) positionretured by pdu :%+v", m.curIdxPos, pdu) + return nil //if error return the bulk process will stop + } + //i := strings.LastIndex(pdu.Name, ".") + suffix := pdu.Name[m.curIfDescrxPos+1:] + + if m.cfg.IndexAsValue == true { + allIfDescr[suffix] = suffix + return nil + } + name := "ErrorOnGetIdxValue" + switch pdu.Type { + case gosnmp.OctetString: + var bytes = pdu.Value.([]byte) + name = string(bytes) + m.Debugf("Got the following OctetString index for [%s/%s]", suffix, name) + case gosnmp.Counter32, gosnmp.Counter64, gosnmp.Gauge32, gosnmp.Uinteger32: + name = strconv.FormatUint(snmp.PduVal2UInt64(pdu), 10) + m.Debugf("Got the following Numeric index for [%s/%s]", suffix, name) + case gosnmp.Integer: + name = strconv.FormatInt(snmp.PduVal2Int64(pdu), 10) + m.Debugf("Got the following Numeric index for [%s/%s]", suffix, name) + case gosnmp.IPAddress: + var err error + name, err = snmp.PduVal2IPaddr(pdu) + m.Debugf("Got the following IPaddress index for [%s/%s]", suffix, name) + if err != nil { + m.Errorf("Error on loadIfDescr Labels IPAddress to string conversion: %s", err) + } + default: + m.Errorf("Error in loadIfDescrLabels %s ERR: Not String or numeric or IPAddress Value", m.cfg.IndexOID) + } + allIfDescr[suffix] = name + return nil + } + + m.curIfDescrxPos = len(ifDescrOID) + err := m.Walk(ifDescrOID, setRawData) + + if err != nil { + m.Errorf("loadIfDescrLabels - SNMP WALK error: %s", err) + return allIfDescr, err + } + + return allIfDescr, nil +} diff --git a/pkg/data/measurement/metrictable.go b/pkg/data/measurement/metrictable.go index beed476c..668c7287 100644 --- a/pkg/data/measurement/metrictable.go +++ b/pkg/data/measurement/metrictable.go @@ -4,8 +4,8 @@ import ( "fmt" "github.com/Sirupsen/logrus" - "github.com/toni-moreno/snmpcollector/pkg/config" - "github.com/toni-moreno/snmpcollector/pkg/data/metric" + "snmpcollector/pkg/config" + "snmpcollector/pkg/data/metric" ) // MetricRow Measurment row type diff --git a/pkg/data/metric/snmpmetric.go b/pkg/data/metric/snmpmetric.go index 7ca1fa2f..7bd9df40 100644 --- a/pkg/data/metric/snmpmetric.go +++ b/pkg/data/metric/snmpmetric.go @@ -3,6 +3,8 @@ package metric import ( "encoding/json" "fmt" + "github.com/getsentry/raven-go" + "reflect" "math" "regexp" @@ -13,9 +15,9 @@ import ( "github.com/Knetic/govaluate" "github.com/Sirupsen/logrus" "github.com/soniah/gosnmp" - "github.com/toni-moreno/snmpcollector/pkg/config" - "github.com/toni-moreno/snmpcollector/pkg/data/filter" - "github.com/toni-moreno/snmpcollector/pkg/data/snmp" + "snmpcollector/pkg/config" + "snmpcollector/pkg/data/filter" + "snmpcollector/pkg/data/snmp" ) var ( @@ -48,16 +50,16 @@ const ( type SnmpMetric struct { cfg *config.SnmpMetricCfg Valid bool //indicate if has been updated in the last gathered process - CookedValue interface{} - CurValue interface{} - LastValue interface{} + CookedValue map[string]interface{} + CurValue map[string]interface{} + LastValue map[string]interface{} CurTime time.Time LastTime time.Time ElapsedTime float64 Compute func(arg ...interface{}) `json:"-"` Scale func() `json:"-"` Convert func() `json:"-"` - SetRawData func(pdu gosnmp.SnmpPDU, now time.Time) `json:"-"` + SetRawData func(pdu gosnmp.SnmpPDU, subIndexes []string, subIndex string, now time.Time) `json:"-"` RealOID string Report int //if false this metric won't be sent to the output buffer (is just taken as a coomputed input for other metrics) //for STRINGPARSER/MULTISTRINGPARSER @@ -93,6 +95,9 @@ func (s *SnmpMetric) GetID() string { // New create a new snmpmetric with a specific logger func New(c *config.SnmpMetricCfg, l *logrus.Logger) (*SnmpMetric, error) { metric := &SnmpMetric{log: l} + metric.CookedValue = make(map[string]interface{}) + metric.CurValue = make(map[string]interface{}) + metric.LastValue = make(map[string]interface{}) err := metric.Init(c) return metric, err } @@ -106,167 +111,235 @@ func (s *SnmpMetric) SetLogger(l *logrus.Logger) { func (s *SnmpMetric) convertFromUInteger() { //check first the rigth - switch vt := s.CookedValue.(type) { - case uint64: - //everything ok - break - default: - s.log.Errorf("ERROR: expected value on metric %s type UINT64 and got %T type ( %+v) type \n", s.cfg.ID, vt, s.CookedValue) - return - } - //the only acceptable conversions - // signed integer 64 -> float64 - // signet integer 64 -> boolean ( true if value != 0 ) - switch s.cfg.Conversion { - case config.INTEGER: - return - case config.FLOAT: - s.CookedValue = float64(s.CookedValue.(uint64)) - return - case config.BOOLEAN: - if s.CookedValue.(uint64) != 0 { - s.CookedValue = true - } else { - s.CookedValue = false + for cookedValueKey, cookedValue := range s.CookedValue { + + if cookedValue == nil { + delete(s.CookedValue, cookedValueKey) + continue } - return - case config.STRING: - s.CookedValue = strconv.FormatUint(s.CookedValue.(uint64), 10) - return - default: - s.log.Errorf("Bad conversion: requested %s from %T type", s.cfg.Conversion.GetString(), s.CookedValue) + + var reflectType = reflect.TypeOf(cookedValue) + s.log.Info("cookedValue type: ", reflectType) + + switch vt := cookedValue.(type) { + case int64: + //everything ok + break + case uint64: + s.CookedValue[cookedValueKey] = int64(cookedValue.(uint64)) + cookedValue = s.CookedValue[cookedValueKey] + break + case float64: + s.CookedValue[cookedValueKey] = int64(cookedValue.(float64)) + cookedValue = s.CookedValue[cookedValueKey] + break + default: + s.log.Errorf("ERROR: expected value on metric %s type UINT64 and got %T type ( %+v) type \n", s.cfg.ID, vt, s.CookedValue) + // var msg = fmt.Sprintf("ERROR: expected value on metric %s type UINT64 and got %T type ( %+v) type \n", s.cfg.ID, vt, s.CookedValue) + // raven.CaptureMessage(msg, map[string]string{"category": "typecast"}) + continue + } + //the only acceptable conversions + // signed integer 64 -> float64 + // signet integer 64 -> boolean ( true if value != 0 ) + switch s.cfg.Conversion { + case config.INTEGER: + continue + case config.FLOAT: + s.CookedValue[cookedValueKey] = float64(cookedValue.(int64)) + continue + case config.BOOLEAN: + if cookedValue.(int64) != 0 { + s.CookedValue[cookedValueKey] = true + } else { + s.CookedValue[cookedValueKey]= false + } + continue + case config.STRING: + s.CookedValue[cookedValueKey] = strconv.FormatInt(cookedValue.(int64), 10) + continue + default: + s.log.Errorf("Bad conversion: requested %s from %T type", s.cfg.Conversion.GetString(), s.CookedValue) + } + } } func (s *SnmpMetric) convertFromInteger() { - switch vt := s.CookedValue.(type) { - case int64: - //everything ok - break - default: - s.log.Errorf("ERROR: expected value on metric %s type INT64 and got %T ( %+v) type \n", s.cfg.ID, vt, s.CookedValue) - return - } - //the only acceptable conversions - // signed integer 64 -> float64 - // signet integer 64 -> boolean ( true if value != 0 ) - switch s.cfg.Conversion { - case config.INTEGER: - return - case config.FLOAT: - s.CookedValue = float64(s.CookedValue.(int64)) - return - case config.BOOLEAN: - if s.CookedValue.(int64) != 0 { - s.CookedValue = true - } else { - s.CookedValue = false + for cookedValueKey, cookedValue := range s.CookedValue { + + + if cookedValue == nil { + delete(s.CookedValue, cookedValueKey) + continue } - return - case config.STRING: - s.CookedValue = strconv.FormatInt(s.CookedValue.(int64), 10) - return - default: - s.log.Errorf("Bad conversion: requested %s from %T type", s.cfg.Conversion.GetString(), s.CookedValue) + + var reflectType = reflect.TypeOf(cookedValue) + s.log.Info("cookedValue type: ", reflectType) + + switch vt := cookedValue.(type) { + case int64: + //everything ok + break + case uint64: + s.CookedValue[cookedValueKey] = int64(cookedValue.(uint64)) + cookedValue = s.CookedValue[cookedValueKey] + break + case float64: + s.CookedValue[cookedValueKey] = int64(cookedValue.(float64)) + cookedValue = s.CookedValue[cookedValueKey] + break + default: + s.log.Errorf("ERROR: expected value on metric %s type INT64 and got %T ( %+v) type \n", s.cfg.ID, vt, s.CookedValue) + // var msg = fmt.Sprintf("ERROR: expected value on metric %s type INT64 and got %T ( %+v) type \n", s.cfg.ID, vt, s.CookedValue) + // raven.CaptureMessage(msg, map[string]string{"category": "typecast"}) + continue + } + //the only acceptable conversions + // signed integer 64 -> float64 + // signet integer 64 -> boolean ( true if value != 0 ) + switch s.cfg.Conversion { + case config.INTEGER: + continue + case config.FLOAT: + s.CookedValue[cookedValueKey] = float64(cookedValue.(int64)) + continue + case config.BOOLEAN: + if cookedValue.(int64) != 0 { + s.CookedValue[cookedValueKey] = true + } else { + s.CookedValue[cookedValueKey] = false + } + continue + case config.STRING: + s.CookedValue[cookedValueKey]= strconv.FormatInt(cookedValue.(int64), 10) + continue + default: + s.log.Errorf("Bad conversion: requested %s from %T type", s.cfg.Conversion.GetString(), s.CookedValue) + } + } } func (s *SnmpMetric) convertFromFloat() { - switch vt := s.CookedValue.(type) { - case float64: - //everything ok - break - default: - s.log.Errorf("ERROR: expected value on metric %s type Float64 and got %T type ( %+v) \n", s.cfg.ID, vt, s.CookedValue) - return - } - //the only acceptable conversions - // signed float -> int64 (will do rounded value) - switch s.cfg.Conversion { - case config.INTEGER: - s.CookedValue = int64(math.Round(s.CookedValue.(float64))) - return - case config.FLOAT: - return - case config.BOOLEAN: - if s.CookedValue.(float64) != 0.0 { - s.CookedValue = true - } else { - s.CookedValue = false + for cookedValueKey, cookedValue := range s.CookedValue { + + if cookedValue == nil { + delete(s.CookedValue, cookedValueKey) + continue + } + + switch vt := cookedValue.(type) { + case float64: + //everything ok + break + default: + s.log.Errorf("ERROR: expected value on metric %s type Float64 and got %T type ( %+v) \n", s.cfg.ID, vt, s.CookedValue) + var msg = fmt.Sprintf("ERROR: expected value on metric %s type Float64 and got %T type ( %+v) \n", s.cfg.ID, vt, s.CookedValue) + raven.CaptureMessage(msg, map[string]string{"category": "typecast"}) + continue + } + //the only acceptable conversions + // signed float -> int64 (will do rounded value) + switch s.cfg.Conversion { + case config.INTEGER: + s.CookedValue[cookedValueKey] = int64(math.Round(cookedValue.(float64))) + continue + case config.FLOAT: + continue + case config.BOOLEAN: + if cookedValue.(float64) != 0.0 { + s.CookedValue[cookedValueKey] = true + } else { + s.CookedValue[cookedValueKey] = false + } + continue + case config.STRING: + s.CookedValue[cookedValueKey] = strconv.FormatFloat(cookedValue.(float64), 'f', -1, 64) + continue + default: + s.log.Errorf("Bad conversion: requested on metric %s: to type %s from %T type", s.cfg.ID, s.cfg.Conversion.GetString(), s.CookedValue) } - return - case config.STRING: - s.CookedValue = strconv.FormatFloat(s.CookedValue.(float64), 'f', -1, 64) - return - default: - s.log.Errorf("Bad conversion: requested on metric %s: to type %s from %T type", s.cfg.ID, s.cfg.Conversion.GetString(), s.CookedValue) } + } func (s *SnmpMetric) convertFromString() { - switch vt := s.CookedValue.(type) { - case string: - //everything ok - break - default: - s.log.Errorf("ERROR: expected value on metric %s type STRING and got %T type ( %+v) type \n", s.cfg.ID, vt, s.CookedValue) - return - } - //the only acceptable conversions - // string -> int64 - // string -> float (the default) - // string -> boolean - // string -> string - switch s.cfg.Conversion { - case config.STRING: - return - case config.INTEGER: - value, err := strconv.ParseInt(s.CookedValue.(string), 10, 64) - if err != nil { - s.log.Warnf("Error parsing Integer from String %s metric %s : error: %s", s.CookedValue.(string), s.cfg.ID, err) - return - } - s.CookedValue = value - return - case config.FLOAT: - value, err := strconv.ParseFloat(s.CookedValue.(string), 64) - if err != nil { - s.log.Warnf("Error parsing float from String %s metric %s : error: %s", s.CookedValue.(string), s.cfg.ID, err) - return + for cookedValueKey, cookedValue := range s.CookedValue { + + switch vt := cookedValue.(type) { + case string: + //everything ok + break + default: + s.log.Errorf("ERROR: expected value on metric %s type STRING and got %T type ( %+v) type \n", s.cfg.ID, vt, s.CookedValue) + // var msg = fmt.Sprintf("ERROR: expected value on metric %s type STRING and got %T type ( %+v) type \n", s.cfg.ID, vt, s.CookedValue) + // raven.CaptureMessage(msg, map[string]string{"category": "typecast"}) + continue } - s.CookedValue = value - return - case config.BOOLEAN: - value, err := strconv.ParseBool(s.CookedValue.(string)) - if err != nil { - s.log.Warnf("Error parsing Boolean from String %s metric %s : error: %s", s.CookedValue.(string), s.cfg.ID, err) - return + //the only acceptable conversions + // string -> int64 + // string -> float (the default) + // string -> boolean + // string -> string + switch s.cfg.Conversion { + case config.STRING: + continue + case config.INTEGER: + value, err := strconv.ParseInt(cookedValue.(string), 10, 64) + if err != nil { + s.log.Warnf("Error parsing Integer from String %s metric %s : error: %s", cookedValue.(string), s.cfg.ID, err) + continue + } + s.CookedValue[cookedValueKey] = value + continue + case config.FLOAT: + value, err := strconv.ParseFloat(cookedValue.(string), 64) + if err != nil { + s.log.Warnf("Error parsing float from String %s metric %s : error: %s", cookedValue.(string), s.cfg.ID, err) + continue + } + s.CookedValue[cookedValueKey] = value + continue + case config.BOOLEAN: + value, err := strconv.ParseBool(cookedValue.(string)) + if err != nil { + s.log.Warnf("Error parsing Boolean from String %s metric %s : error: %s", cookedValue.(string), s.cfg.ID, err) + continue + } + s.CookedValue[cookedValueKey] = value + continue + default: + s.log.Errorf("Bad conversion: requested on metric %s: to type %s from %T type", s.cfg.ID, s.cfg.Conversion.GetString(), s.CookedValue) } - s.CookedValue = value - return - default: - s.log.Errorf("Bad conversion: requested on metric %s: to type %s from %T type", s.cfg.ID, s.cfg.Conversion.GetString(), s.CookedValue) } + } func (s *SnmpMetric) convertFromAny() { - switch s.CookedValue.(type) { - case float32, float64: - s.convertFromFloat() - return - case uint64, uint32: - s.convertFromUInteger() - return - case int64, int32: - s.convertFromInteger() - return - case string: - s.convertFromString() - return - case bool: - s.log.Errorf("Bad conversion: requested on metric %s: to type %s from %T type", s.cfg.ID, s.cfg.Conversion.GetString(), s.CookedValue) - default: + + for _, cookedValue := range s.CookedValue { + + switch cookedValue.(type) { + case float32, float64: + s.convertFromFloat() + continue + case uint64, uint32: + s.convertFromUInteger() + continue + case int64, int32: + s.convertFromInteger() + continue + case string: + s.convertFromString() + continue + case bool: + s.log.Errorf("Bad conversion: requested on metric %s: to type %s from %T type", s.cfg.ID, s.cfg.Conversion.GetString(), s.CookedValue) + default: + } } + + } // Init Initialice a new snmpmetric object with the specific configuration @@ -284,18 +357,23 @@ func (s *SnmpMetric) Init(c *config.SnmpMetricCfg) error { } if s.cfg.Scale != 0.0 || s.cfg.Shift != 0.0 { s.Scale = func() { - switch v := s.CookedValue.(type) { - case uint64: - //should return Integer - val := (s.cfg.Scale * float64(s.CookedValue.(uint64))) + s.cfg.Shift - s.CookedValue = uint64(math.Round(val)) - case float64: - //should return float - s.CookedValue = float64((s.cfg.Scale * float64(s.CookedValue.(float64))) + s.cfg.Shift) - case string: - s.log.Errorf("Error Trying to Scale Function from non numbered STRING type value : %s ", s.CookedValue) - default: - s.log.Errorf("Error Trying to Scale Function from unknown type %T value: %#+v", v, s.CookedValue) + + for cookedValueKey, cookedValue := range s.CookedValue { + + switch v := cookedValue.(type) { + case uint64: + //should return Integer + val := (s.cfg.Scale * float64(cookedValue.(uint64))) + s.cfg.Shift + s.CookedValue[cookedValueKey] = uint64(math.Round(val)) + case float64: + //should return float + s.CookedValue[cookedValueKey] = float64((s.cfg.Scale * float64(cookedValue.(float64))) + s.cfg.Shift) + case string: + s.log.Errorf("Error Trying to Scale Function from non numbered STRING type value : %s ", s.CookedValue) + default: + s.log.Errorf("Error Trying to Scale Function from unknown type %T value: %#+v", v, s.CookedValue) + } + } } @@ -303,354 +381,481 @@ func (s *SnmpMetric) Init(c *config.SnmpMetricCfg) error { s.Scale = func() { } } - switch s.cfg.DataSrcType { - case "CONDITIONEVAL": - //select - cond, err := dbc.GetOidConditionCfgByID(s.cfg.ExtraData) - if err != nil { - s.log.Errorf("Error getting CONDITIONEVAL [id: %s ] data : %s", s.cfg.ExtraData, err) - } - //get Regexp - if cond.IsMultiple == true { - s.condflt = filter.NewOidMultipleFilter(cond.OIDCond, s.log) - } else { - s.condflt = filter.NewOidFilter(cond.OIDCond, cond.CondType, cond.CondValue, s.log) - } - s.Compute = func(arg ...interface{}) { - s.condflt.Init(arg...) - s.condflt.Update() - s.CookedValue = s.condflt.Count() - s.CurTime = time.Now() - s.Valid = true - } - //Sign - //set Process Data - case "TIMETICKS": //Cooked TimeTicks - s.SetRawData = func(pdu gosnmp.SnmpPDU, now time.Time) { - val := snmp.PduVal2Int64(pdu) - s.CookedValue = val / 100 //now data in secoonds - s.CurTime = now - s.Scale() - s.convertFromInteger() - s.Valid = true - } - //Signed Integers - case "INTEGER", "Integer32": - s.SetRawData = func(pdu gosnmp.SnmpPDU, now time.Time) { - s.CookedValue = snmp.PduVal2Int64(pdu) - s.CurTime = now - s.Scale() - s.convertFromInteger() - s.Valid = true - } - //Unsigned Integers - case "Counter32", "Gauge32", "Counter64", "TimeTicks", "UInteger32", "Unsigned32": - s.SetRawData = func(pdu gosnmp.SnmpPDU, now time.Time) { - s.CookedValue = snmp.PduVal2UInt64(pdu) - s.CurTime = now - s.Scale() - s.convertFromUInteger() - s.Valid = true - } - case "COUNTER32": //Increment computed - s.SetRawData = func(pdu gosnmp.SnmpPDU, now time.Time) { - //first time only set values and reassign itself to the complete method this will avoi to send invalid data - val := snmp.PduVal2UInt64(pdu) - s.CurValue = val - s.CurTime = now - s.Valid = true - s.SetRawData = func(pdu gosnmp.SnmpPDU, now time.Time) { - val := snmp.PduVal2UInt64(pdu) - s.LastTime = s.CurTime - s.LastValue = s.CurValue - s.CurValue = val + if len(s.CookedValue) == 0 { + s.CookedValue[""] = nil // init values for setting raw data methods + } + + for cookedValueKey := range s.CookedValue { + + switch s.cfg.DataSrcType { + case "CONDITIONEVAL": + //select + cond, err := dbc.GetOidConditionCfgByID(s.cfg.ExtraData) + if err != nil { + s.log.Errorf("Error getting CONDITIONEVAL [id: %s ] data : %s", s.cfg.ExtraData, err) + } + //get Regexp + if cond.IsMultiple == true { + s.condflt = filter.NewOidMultipleFilter(cond.OIDCond, s.log) + } else { + s.condflt = filter.NewOidFilter(cond.OIDCond, cond.CondType, cond.CondValue, s.log, cond.Encoding) + } + s.Compute = func(arg ...interface{}) { + s.condflt.Init(arg...) + s.condflt.Update() + s.CookedValue[cookedValueKey] = s.condflt.Count() + s.CurTime = time.Now() + s.Valid = true + } + //Sign + //set Process Data + case "TIMETICKS": //Cooked TimeTicks + s.SetRawData = func(pdu gosnmp.SnmpPDU, subIndexes []string, subIndex string, now time.Time) { + var valKey = cookedValueKey + if subIndexes != nil { + valKey = subIndex + } + val := snmp.PduVal2Int64(pdu) + s.CookedValue[valKey] = val / 100 //now data in secoonds s.CurTime = now - s.Compute() s.Scale() - s.Convert() + s.convertFromInteger() s.Valid = true } - } - if s.cfg.GetRate == true { - s.Compute = func(arg ...interface{}) { - s.ElapsedTime = s.CurTime.Sub(s.LastTime).Seconds() - if s.CurValue.(uint64) < s.LastValue.(uint64) { - s.CookedValue = float64(math.MaxUint32-s.LastValue.(uint64)+s.CurValue.(uint64)) / s.ElapsedTime - } else { - s.CookedValue = float64(s.CurValue.(uint64)-s.LastValue.(uint64)) / s.ElapsedTime + + //Signed Integers + case "INTEGER", "Integer32": + s.SetRawData = func(pdu gosnmp.SnmpPDU, subIndexes []string, subIndex string, now time.Time) { + var valKey = cookedValueKey + if subIndexes != nil { + valKey = subIndex } + s.CookedValue[valKey] = snmp.PduVal2Int64(pdu) + s.CurTime = now + s.Scale() + s.convertFromInteger() + s.Valid = true } - s.Convert = s.convertFromFloat - } else { - s.Compute = func(arg ...interface{}) { - s.ElapsedTime = s.CurTime.Sub(s.LastTime).Seconds() - if s.CurValue.(uint64) < s.LastValue.(uint64) { - s.CookedValue = math.MaxUint32 - s.LastValue.(uint64) + s.CurValue.(uint64) - } else { - s.CookedValue = s.CurValue.(uint64) - s.LastValue.(uint64) + //Unsigned Integers + case "Counter32", "Gauge32", "Counter64", "TimeTicks", "UInteger32", "Unsigned32": + s.SetRawData = func(pdu gosnmp.SnmpPDU, subIndexes []string, subIndex string, now time.Time) { + var valKey = cookedValueKey + if subIndexes != nil { + valKey = subIndex } - } - s.Convert = s.convertFromUInteger - } - case "COUNTER64": //Increment computed - s.SetRawData = func(pdu gosnmp.SnmpPDU, now time.Time) { - //log.Debugf("========================================>COUNTER64: first time :%s ", s.RealOID) - //first time only set values and reassign itself to the complete method - val := snmp.PduVal2UInt64(pdu) - s.CurValue = val - s.CurTime = now - s.Valid = true - s.SetRawData = func(pdu gosnmp.SnmpPDU, now time.Time) { - //log.Debugf("========================================>COUNTER64: the other time:%s", s.RealOID) - val := snmp.PduVal2UInt64(pdu) - s.LastTime = s.CurTime - s.LastValue = s.CurValue - s.CurValue = val + s.CookedValue[valKey] = uint64(snmp.PduVal2UInt64(pdu)) s.CurTime = now - s.Compute() s.Scale() - s.Convert() + s.convertFromUInteger() s.Valid = true } - } - if s.cfg.GetRate == true { - s.Compute = func(arg ...interface{}) { - s.ElapsedTime = s.CurTime.Sub(s.LastTime).Seconds() - if s.CurValue.(uint64) < s.LastValue.(uint64) { - s.CookedValue = float64(math.MaxUint64-s.LastValue.(uint64)+s.CurValue.(uint64)) / s.ElapsedTime - } else { - s.CookedValue = float64(s.CurValue.(uint64)-s.LastValue.(uint64)) / s.ElapsedTime + case "COUNTER32": //Increment computed + s.SetRawData = func(pdu gosnmp.SnmpPDU, subIndexes []string, subIndex string, now time.Time) { + //first time only set values and reassign itself to the complete method this will avoi to send invalid data + + + var valKey = cookedValueKey + if subIndexes != nil { + valKey = subIndex + } + + val := snmp.PduVal2UInt64(pdu) + s.CurValue[cookedValueKey] = val + s.CurTime = now + s.Valid = true + + s.SetRawData = func(pdu gosnmp.SnmpPDU, subIndexes []string, subIndex string, now time.Time) { + + valKey = cookedValueKey + if subIndexes != nil { + valKey = subIndex + } + + val := snmp.PduVal2UInt64(pdu) + s.LastTime = s.CurTime + s.LastValue[valKey] = s.CurValue[valKey] + s.CurValue[valKey]= val + s.CurTime = now + s.Compute() + s.Scale() + s.Convert() + s.Valid = true } } - s.Convert = s.convertFromFloat - } else { - s.Compute = func(arg ...interface{}) { - s.ElapsedTime = s.CurTime.Sub(s.LastTime).Seconds() - if s.CurValue.(uint64) < s.LastValue.(uint64) { - s.CookedValue = math.MaxUint64 - s.LastValue.(uint64) + s.CurValue.(uint64) - } else { - s.CookedValue = s.CurValue.(uint64) - s.LastValue.(uint64) + if s.cfg.GetRate == true { + s.Compute = func(arg ...interface{}) { + s.ElapsedTime = s.CurTime.Sub(s.LastTime).Seconds() + if s.CurValue[cookedValueKey].(uint64) < s.LastValue[cookedValueKey].(uint64) { + s.CookedValue[cookedValueKey] = float64(math.MaxUint32-s.LastValue[cookedValueKey].(uint64)+s.CurValue[cookedValueKey].(uint64)) / s.ElapsedTime + } else { + s.CookedValue[cookedValueKey] = float64(s.CurValue[cookedValueKey].(uint64)-s.LastValue[cookedValueKey].(uint64)) / s.ElapsedTime + } } + s.Convert = s.convertFromFloat + } else { + s.Compute = func(arg ...interface{}) { + s.ElapsedTime = s.CurTime.Sub(s.LastTime).Seconds() + if s.CurValue[cookedValueKey].(uint64) < s.LastValue[cookedValueKey].(uint64) { + s.CookedValue[cookedValueKey] = math.MaxUint32 - s.LastValue[cookedValueKey].(uint64) + s.CurValue[cookedValueKey].(uint64) + } else { + s.CookedValue[cookedValueKey] = s.CurValue[cookedValueKey].(uint64) - s.LastValue[cookedValueKey].(uint64) + } + } + s.Convert = s.convertFromUInteger } - s.Convert = s.convertFromUInteger - } - case "COUNTERXX": //Generic Counter With Unknown range or buggy counters that Like Non negative derivative - s.SetRawData = func(pdu gosnmp.SnmpPDU, now time.Time) { - //first time only set values and reassign itself to the complete method this will avoi to send invalid data - val := snmp.PduVal2UInt64(pdu) - s.CurValue = val - s.CurTime = now - s.Valid = true - s.log.Debugf("FIRST RAW(post-compute): %T - %#+v", s.CookedValue, s.CookedValue) - s.SetRawData = func(pdu gosnmp.SnmpPDU, now time.Time) { + case "COUNTER64": //Increment computed + s.SetRawData = func(pdu gosnmp.SnmpPDU, subIndexes []string, subIndex string, now time.Time) { + //log.Debugf("========================================>COUNTER64: first time :%s ", s.RealOID) + //first time only set values and reassign itself to the complete method + + var valKey = cookedValueKey + if subIndexes != nil { + valKey = subIndex + } + + val := snmp.PduVal2UInt64(pdu) - s.LastTime = s.CurTime - s.LastValue = s.CurValue - s.CurValue = val + s.CurValue[valKey] = val s.CurTime = now - s.Compute() s.Valid = true - } - } - if s.cfg.GetRate == true { - s.Compute = func(arg ...interface{}) { - s.ElapsedTime = s.CurTime.Sub(s.LastTime).Seconds() - if s.CurValue.(uint64) >= s.LastValue.(uint64) { - s.CookedValue = float64(s.CurValue.(uint64)-s.LastValue.(uint64)) / s.ElapsedTime + s.SetRawData = func(pdu gosnmp.SnmpPDU, subIndexes []string, subIndex string, now time.Time) { + //log.Debugf("========================================>COUNTER64: the other time:%s", s.RealOID) + + valKey = cookedValueKey + if subIndexes != nil { + valKey = subIndex + } + + val := snmp.PduVal2UInt64(pdu) + s.LastTime = s.CurTime + s.LastValue[valKey] = s.CurValue[valKey] + s.CurValue[valKey] = val + s.CurTime = now + s.Compute() s.Scale() s.Convert() - } else { - // Else => nothing to do last value will be sent - s.log.Warnf("Warning Negative COUNTER increment [current: %d | last: %d ] last value will be sent %f", s.CurValue, s.LastValue, s.CookedValue) + s.Valid = true } } - s.Convert = s.convertFromFloat - } else { - s.Compute = func(arg ...interface{}) { - s.ElapsedTime = s.CurTime.Sub(s.LastTime).Seconds() - if s.CurValue.(uint64) >= s.LastValue.(uint64) { - s.CookedValue = s.CurValue.(uint64) - s.LastValue.(uint64) - s.Scale() - s.Convert() - } else { - // Else => nothing to do last value will be sent - s.log.Warnf("Warning Negative COUNTER increment [current: %d | last: %d ] last value will be sent %f", s.CurValue, s.LastValue, s.CookedValue) + if s.cfg.GetRate == true { + s.Compute = func(arg ...interface{}) { + s.ElapsedTime = s.CurTime.Sub(s.LastTime).Seconds() + if s.CurValue[cookedValueKey].(uint64) < s.LastValue[cookedValueKey].(uint64) { + s.CookedValue[cookedValueKey] = float64(math.MaxUint64-s.LastValue[cookedValueKey].(uint64)+s.CurValue[cookedValueKey].(uint64)) / s.ElapsedTime + } else { + s.CookedValue[cookedValueKey] = float64(s.CurValue[cookedValueKey].(uint64)-s.LastValue[cookedValueKey].(uint64)) / s.ElapsedTime + } } + s.Convert = s.convertFromFloat + } else { + s.Compute = func(arg ...interface{}) { + s.ElapsedTime = s.CurTime.Sub(s.LastTime).Seconds() + if s.CurValue[cookedValueKey].(uint64) < s.LastValue[cookedValueKey].(uint64) { + s.CookedValue[cookedValueKey] = math.MaxUint64 - s.LastValue[cookedValueKey].(uint64) + s.CurValue[cookedValueKey].(uint64) + } else { + s.CookedValue[cookedValueKey] = s.CurValue[cookedValueKey].(uint64) - s.LastValue[cookedValueKey].(uint64) + } + } + s.Convert = s.convertFromUInteger } - s.Convert = s.convertFromUInteger - } - case "BITS": - s.SetRawData = func(pdu gosnmp.SnmpPDU, now time.Time) { - barray := snmp.PduVal2BoolArray(pdu) - names := []string{} - for i, b := range barray { - if b { - names = append(names, s.cfg.Names[i]) + case "COUNTERXX": //Generic Counter With Unknown range or buggy counters that Like Non negative derivative + s.SetRawData = func(pdu gosnmp.SnmpPDU, subIndexes []string, subIndex string, now time.Time) { + //first time only set values and reassign itself to the complete method this will avoi to send invalid data + + var valKey = cookedValueKey + if subIndexes != nil { + valKey = subIndex + } + + val := snmp.PduVal2UInt64(pdu) + s.CurValue[cookedValueKey] = val + s.CurTime = now + s.Valid = true + s.log.Debugf("FIRST RAW(post-compute): %T - %#+v", s.CookedValue, s.CookedValue) + s.SetRawData = func(pdu gosnmp.SnmpPDU, subIndexes []string, subIndex string, now time.Time) { + + valKey = cookedValueKey + if subIndexes != nil { + valKey = subIndex + } + + val := snmp.PduVal2UInt64(pdu) + s.LastTime = s.CurTime + s.LastValue[valKey] = s.CurValue[valKey] + s.CurValue[valKey] = val + s.CurTime = now + s.Compute() + s.Valid = true } } - s.CookedValue = strings.Join(names, ",") - s.CurTime = now - s.Valid = true - s.log.Debugf("SETRAW BITS %+v, RESULT %s", s.cfg.Names, s.CookedValue) - } - case "BITSCHK": - s.SetRawData = func(pdu gosnmp.SnmpPDU, now time.Time) { - barray := snmp.PduVal2BoolArray(pdu) - index, _ := strconv.Atoi(s.cfg.ExtraData) - b := barray[index] - if b { - s.CookedValue = 1.0 + if s.cfg.GetRate == true { + s.Compute = func(arg ...interface{}) { + s.ElapsedTime = s.CurTime.Sub(s.LastTime).Seconds() + if s.CurValue[cookedValueKey].(uint64) >= s.LastValue[cookedValueKey].(uint64) { + s.CookedValue[cookedValueKey] = float64(s.CurValue[cookedValueKey].(uint64)-s.LastValue[cookedValueKey].(uint64)) / s.ElapsedTime + s.Scale() + s.Convert() + } else { + // Else => nothing to do last value will be sent + s.log.Warnf("Warning Negative COUNTER increment [current: %d | last: %d ] last value will be sent %f", s.CurValue[cookedValueKey], s.LastValue[cookedValueKey], s.CookedValue[cookedValueKey]) + } + } + s.Convert = s.convertFromFloat } else { - s.CookedValue = 0.0 + s.Compute = func(arg ...interface{}) { + s.ElapsedTime = s.CurTime.Sub(s.LastTime).Seconds() + if s.CurValue[cookedValueKey].(uint64) >= s.LastValue[cookedValueKey].(uint64) { + s.CookedValue[cookedValueKey] = s.CurValue[cookedValueKey].(uint64) - s.LastValue[cookedValueKey].(uint64) + s.Scale() + s.Convert() + } else { + // Else => nothing to do last value will be sent + s.log.Warnf("Warning Negative COUNTER increment [current: %d | last: %d ] last value will be sent %f", s.CurValue[cookedValueKey], s.LastValue[cookedValueKey], s.CookedValue[cookedValueKey]) + } + } + s.Convert = s.convertFromUInteger } - s.Convert() - s.CurTime = now - s.Valid = true - s.log.Debugf("BITS CHECK bit %+v, Position %d , RESULT %t", barray, index, s.CookedValue) - } - case "ENUM": - s.SetRawData = func(pdu gosnmp.SnmpPDU, now time.Time) { - idx := snmp.PduVal2Int64(pdu) - if val, ok := s.cfg.Names[int(idx)]; ok { - s.CookedValue = val - } else { - s.CookedValue = strconv.Itoa(int(idx)) + case "BITS": + s.SetRawData = func(pdu gosnmp.SnmpPDU, subIndexes []string, subIndex string, now time.Time) { + + var valKey = cookedValueKey + if subIndexes != nil { + valKey = subIndex + } + + barray := snmp.PduVal2BoolArray(pdu) + names := []string{} + for i, b := range barray { + if b { + names = append(names, s.cfg.Names[i]) + } + } + s.CookedValue[valKey] = strings.Join(names, ",") + s.CurTime = now + s.Valid = true + s.log.Debugf("SETRAW BITS %+v, RESULT %s", s.cfg.Names, s.CookedValue) } - s.Valid = true - s.log.Debugf("SETRAW ENUM %+v, RESULT %s", s.cfg.Names, s.CookedValue) - } - case "OCTETSTRING": - switch s.cfg.Conversion { + case "BITSCHK": + s.SetRawData = func(pdu gosnmp.SnmpPDU, subIndexes []string, subIndex string, now time.Time) { + var valKey = cookedValueKey + if subIndexes != nil { + valKey = subIndex + } - case config.INTEGER: - s.SetRawData = func(pdu gosnmp.SnmpPDU, now time.Time) { - val, err := snmp.PduValHexString2Uint(pdu) - s.CookedValue = val + barray := snmp.PduVal2BoolArray(pdu) + index, _ := strconv.Atoi(s.cfg.ExtraData) + b := barray[index] + if b { + s.CookedValue[valKey] = 1.0 + } else { + s.CookedValue[valKey] = 0.0 + } + s.Convert() s.CurTime = now - if err != nil { - s.log.Warnf("Error on HexString to UINT conversion: %s", err) - return + s.Valid = true + s.log.Debugf("BITS CHECK bit %+v, Position %d , RESULT %t", barray, index, s.CookedValue) + } + case "ENUM": + s.SetRawData = func(pdu gosnmp.SnmpPDU, subIndexes []string, subIndex string, now time.Time) { + + var valKey = cookedValueKey + if subIndexes != nil { + valKey = subIndex + } + + idx := snmp.PduVal2Int64(pdu) + if val, ok := s.cfg.Names[int(idx)]; ok { + s.CookedValue[valKey] = val + } else { + s.CookedValue[valKey] = strconv.Itoa(int(idx)) } s.Valid = true + s.log.Debugf("SETRAW ENUM %+v, RESULT %s", s.cfg.Names, s.CookedValue) } - //For compatibility purposes with previous versions - case config.FLOAT: - s.log.Errorf("WARNING ON SNMPMETRIC ( %s ): You are using version >=0.8 version without database upgrade: you should upgrade the DB by executing this SQL on your database \"update snmp_metric_cfg set Conversion=3 where datasrctype='OCTETSTRING';\", to avoid this message ", s.cfg.ID) - fallthrough - case config.STRING: - s.SetRawData = func(pdu gosnmp.SnmpPDU, now time.Time) { - s.CookedValue = snmp.PduVal2str(pdu) + case "OCTETSTRING": + switch s.cfg.Conversion { + + case config.INTEGER: + s.SetRawData = func(pdu gosnmp.SnmpPDU, subIndexes []string, subIndex string, now time.Time) { + + var valKey = cookedValueKey + if subIndexes != nil { + valKey = subIndex + } + + val, err := snmp.PduValHexString2Uint(pdu) + s.CookedValue[valKey] = val + s.CurTime = now + if err != nil { + s.log.Warnf("Error on HexString to UINT conversion: %s", err) + return + } + s.Valid = true + } + //For compatibility purposes with previous versions + case config.FLOAT: + s.log.Errorf("WARNING ON SNMPMETRIC ( %s ): You are using version >=0.8 version without database upgrade: you should upgrade the DB by executing this SQL on your database \"update snmp_metric_cfg set Conversion=3 where datasrctype='OCTETSTRING';\", to avoid this message ", s.cfg.ID) + fallthrough + case config.STRING: + s.SetRawData = func(pdu gosnmp.SnmpPDU, subIndexes []string, subIndex string, now time.Time) { + + var valKey = cookedValueKey + if subIndexes != nil { + valKey = subIndex + } + + s.CookedValue[valKey] = snmp.PduVal2str(pdu) + s.CurTime = now + s.Valid = true + } + default: + s.log.Errorf("WARNING ON SNMPMETRIC ( %s ): Invalid conversion mode from OCTETSTRING to %s", s.cfg.ID, s.cfg.Conversion.GetString()) + s.SetRawData = func(pdu gosnmp.SnmpPDU, subIndexes []string, subIndex string, now time.Time) { + + var valKey = cookedValueKey + if subIndexes != nil { + valKey = subIndex + } + + s.CookedValue[valKey] = snmp.PduVal2str(pdu) + s.CurTime = now + s.Valid = true + } + } + + case "OID": + s.SetRawData = func(pdu gosnmp.SnmpPDU, subIndexes []string, subIndex string, now time.Time) { + + var valKey = cookedValueKey + if subIndexes != nil { + valKey = subIndex + } + + s.CookedValue[valKey] = snmp.PduVal2OID(pdu) s.CurTime = now s.Valid = true } - default: - s.log.Errorf("WARNING ON SNMPMETRIC ( %s ): Invalid conversion mode from OCTETSTRING to %s", s.cfg.ID, s.cfg.Conversion.GetString()) - s.SetRawData = func(pdu gosnmp.SnmpPDU, now time.Time) { - s.CookedValue = snmp.PduVal2str(pdu) + case "IpAddress": + s.SetRawData = func(pdu gosnmp.SnmpPDU, subIndexes []string, subIndex string, now time.Time) { + + var valKey = cookedValueKey + if subIndexes != nil { + valKey = subIndex + } + + s.CookedValue[valKey], _ = snmp.PduVal2IPaddr(pdu) s.CurTime = now s.Valid = true } - } + case "HWADDR": + s.SetRawData = func(pdu gosnmp.SnmpPDU, subIndexes []string, subIndex string, now time.Time) { - case "OID": - s.SetRawData = func(pdu gosnmp.SnmpPDU, now time.Time) { - s.CookedValue = snmp.PduVal2OID(pdu) - s.CurTime = now - s.Valid = true - } - case "IpAddress": - s.SetRawData = func(pdu gosnmp.SnmpPDU, now time.Time) { - s.CookedValue, _ = snmp.PduVal2IPaddr(pdu) - s.CurTime = now - s.Valid = true - } - case "HWADDR": - s.SetRawData = func(pdu gosnmp.SnmpPDU, now time.Time) { - s.CookedValue, _ = snmp.PduVal2Hwaddr(pdu) - s.CurTime = now - s.Valid = true - } - case "STRINGPARSER": - //get Regexp - re, err := regexp.Compile(s.cfg.ExtraData) - if err != nil { - return fmt.Errorf("Error on initialice STRINGPARSER, invalind Regular Expression : %s", s.cfg.ExtraData) - } - s.re = re - //set Process Data - s.SetRawData = func(pdu gosnmp.SnmpPDU, now time.Time) { - str := snmp.PduVal2str(pdu) - retarray := s.re.FindStringSubmatch(str) - if len(retarray) < 2 { - s.log.Warnf("Error for metric [%s] parsing REGEXG [%s] on string [%s] without capturing group", s.cfg.ID, s.cfg.ExtraData, str) - return - } - //retarray[0] contains full string - if len(retarray[1]) == 0 { - s.log.Warnf("Error for metric [%s] parsing REGEXG [%s] on string [%s] cause void capturing group", s.cfg.ID, s.cfg.ExtraData, str) - return - } - s.CookedValue = retarray[1] - s.CurTime = now - s.convertFromString() - //s.Scale() <-only valid if Integer or Float - s.Valid = true - } - case "MULTISTRINGPARSER": - //get Regexp - re, err := regexp.Compile(s.cfg.ExtraData) - if err != nil { - return fmt.Errorf("Error on initialice MULTISTRINGPARSER, invalind Regular Expression : %s", s.cfg.ExtraData) - } - s.re = re + var valKey = cookedValueKey + if subIndexes != nil { + valKey = subIndex + } - mm, err := s.cfg.GetMultiStringTagFieldMap() - if err != nil { - return fmt.Errorf("Error on initialice MULTISTRINGPARSER, invalind Field/Tag definition Format : %s", err) - } - s.mm = mm - //set Process Data - s.SetRawData = func(pdu gosnmp.SnmpPDU, now time.Time) { - str := snmp.PduVal2str(pdu) - s.CookedValue = str - s.CurTime = now - s.Valid = true - } - case "STRINGEVAL": + s.CookedValue[valKey], _ = snmp.PduVal2Hwaddr(pdu) + s.CurTime = now + s.Valid = true + } + case "STRINGPARSER": + //get Regexp + re, err := regexp.Compile(s.cfg.ExtraData) + if err != nil { + return fmt.Errorf("Error on initialice STRINGPARSER, invalind Regular Expression : %s", s.cfg.ExtraData) + } + s.re = re + //set Process Data + s.SetRawData = func(pdu gosnmp.SnmpPDU, subIndexes []string, subIndex string, now time.Time) { - expression, err := govaluate.NewEvaluableExpression(s.cfg.ExtraData) - if err != nil { - s.log.Errorf("Error on initialice STRINGEVAL, evaluation : %s : ERROR : %s", s.cfg.ExtraData, err) - return err - } - s.expr = expression - //set Process Data - s.Compute = func(arg ...interface{}) { - parameters := arg[0].(map[string]interface{}) - s.log.Debugf("Evaluating Metric %s with eval expresion [%s] with parameters %+v", s.cfg.ID, s.cfg.ExtraData, parameters) - result, err := s.expr.Evaluate(parameters) + var valKey = cookedValueKey + if subIndexes != nil { + valKey = subIndex + } + + str := snmp.PduVal2str(pdu) + retarray := s.re.FindStringSubmatch(str) + if len(retarray) < 2 { + s.log.Warnf("Error for metric [%s] parsing REGEXG [%s] on string [%s] without capturing group", s.cfg.ID, s.cfg.ExtraData, str) + return + } + //retarray[0] contains full string + if len(retarray[1]) == 0 { + s.log.Warnf("Error for metric [%s] parsing REGEXG [%s] on string [%s] cause void capturing group", s.cfg.ID, s.cfg.ExtraData, str) + return + } + s.CookedValue[valKey] = retarray[1] + s.CurTime = now + s.convertFromString() + //s.Scale() <-only valid if Integer or Float + s.Valid = true + } + case "MULTISTRINGPARSER": + //get Regexp + re, err := regexp.Compile(s.cfg.ExtraData) + if err != nil { + return fmt.Errorf("Error on initialice MULTISTRINGPARSER, invalind Regular Expression : %s", s.cfg.ExtraData) + } + s.re = re + + mm, err := s.cfg.GetMultiStringTagFieldMap() + if err != nil { + return fmt.Errorf("Error on initialice MULTISTRINGPARSER, invalind Field/Tag definition Format : %s", err) + } + s.mm = mm + //set Process Data + s.SetRawData = func(pdu gosnmp.SnmpPDU, subIndexes []string, subIndex string, now time.Time) { + + var valKey = cookedValueKey + if subIndexes != nil { + valKey = subIndex + } + + str := snmp.PduVal2str(pdu) + s.CookedValue[valKey] = str + s.CurTime = now + s.Valid = true + } + case "STRINGEVAL": + + expression, err := govaluate.NewEvaluableExpression(s.cfg.ExtraData) if err != nil { - s.log.Errorf("Error in metric %s On EVAL string: %s : ERROR : %s", s.cfg.ID, s.cfg.ExtraData, err) - return - } - //Influxdb has not support for NaN,Inf values - //https://github.com/influxdata/influxdb/issues/4089 - switch v := result.(type) { - case float64: - if math.IsNaN(v) || math.IsInf(v, 0) { - s.log.Warnf("Warning in metric %s On EVAL string: %s : Value is not a valid Floating Pint (NaN/Inf) : %f", s.cfg.ID, s.cfg.ExtraData, v) + s.log.Errorf("Error on initialice STRINGEVAL, evaluation : %s : ERROR : %s", s.cfg.ExtraData, err) + return err + } + s.expr = expression + //set Process Data + s.Compute = func(arg ...interface{}) { + parameters := arg[0].(map[string]interface{}) + s.log.Debugf("Evaluating Metric %s with eval expresion [%s] with parameters %+v", s.cfg.ID, s.cfg.ExtraData, parameters) + result, err := s.expr.Evaluate(parameters) + if err != nil { + s.log.Errorf("Error in metric %s On EVAL string: %s : ERROR : %s", s.cfg.ID, s.cfg.ExtraData, err) return } + //Influxdb has not support for NaN,Inf values + //https://github.com/influxdata/influxdb/issues/4089 + switch v := result.(type) { + case float64: + if math.IsNaN(v) || math.IsInf(v, 0) { + s.log.Warnf("Warning in metric %s On EVAL string: %s : Value is not a valid Floating Pint (NaN/Inf) : %f", s.cfg.ID, s.cfg.ExtraData, v) + return + } + } + s.CookedValue[cookedValueKey] = result + //conversion depends onthe type of the evaluted data. + s.CurTime = time.Now() + s.Scale() + s.Convert() //default + s.Valid = true } - s.CookedValue = result - //conversion depends onthe type of the evaluted data. - s.CurTime = time.Now() - s.Scale() - s.Convert() //default - s.Valid = true } + } + + return nil } @@ -673,82 +878,94 @@ func (s *SnmpMetric) GetEvaluableVariables(params map[string]interface{}) { func (s *SnmpMetric) addSingleField(mid string, fields map[string]interface{}) int64 { - if s.Report == OnNonZeroReport { - if s.CookedValue == 0.0 { - s.log.Debugf("REPORT on non zero in METRIC ID [%s] from MEASUREMENT[ %s ] won't be reported to the output backend", s.cfg.ID, mid) - return 0 + if s.Report == OnNonZeroReport { + if s.CookedValue[""] == 0.0 { + s.log.Debugf("REPORT on non zero in METRIC ID [%s] from MEASUREMENT[ %s ] won't be reported to the output backend", s.cfg.ID, mid) + return 0 + } } - } - //assuming float Cooked Values - s.log.Debugf("generating field for %s value %#v ", s.cfg.FieldName, s.CookedValue) - s.log.Debugf("DEBUG METRIC %+v", s) - fields[s.cfg.FieldName] = s.CookedValue + //assuming float Cooked Values + s.log.Debugf("generating field for %s value %#v ", s.cfg.FieldName, s.CookedValue) + s.log.Debugf("DEBUG METRIC %+v", s) + fields[s.cfg.FieldName] = s.CookedValue + + return 0 } func (s *SnmpMetric) addSingleTag(mid string, tags map[string]string) int64 { - var tag string - switch v := s.CookedValue.(type) { - case string: - tag = v - default: - s.log.Debugf("ERROR wrong type %T for ID [%s] from MEASUREMENT[ %s ] when converting to TAG(STRING) won't be reported to the output backend", v, s.cfg.ID, mid) - return 1 - } - //I don't know if a OnNonZeroReport could have sense in any configuration. - if s.Report == OnNonZeroReport { - if tag == "0" { - s.log.Debugf("REPORT on non zero in METRIC ID [%s] from MEASUREMENT[ %s ] won't be reported to the output backend", s.cfg.ID, mid) - return 0 + for _, cookedValue := range s.CookedValue { + var tag string + switch v := cookedValue.(type) { + case string: + tag = v + default: + s.log.Debugf("ERROR wrong type %T for ID [%s] from MEASUREMENT[ %s ] when converting to TAG(STRING) won't be reported to the output backend", v, s.cfg.ID, mid) + return 1 + } + //I don't know if a OnNonZeroReport could have sense in any configuration. + if s.Report == OnNonZeroReport { + if tag == "0" { + s.log.Debugf("REPORT on non zero in METRIC ID [%s] from MEASUREMENT[ %s ] won't be reported to the output backend", s.cfg.ID, mid) + return 0 + } } + s.log.Debugf("generating Tag for Metric: %s : tagname: %s", s.cfg.FieldName, tag) + tags[s.cfg.FieldName] = tag } - s.log.Debugf("generating Tag for Metric: %s : tagname: %s", s.cfg.FieldName, tag) - tags[s.cfg.FieldName] = tag + + return 0 } func (s *SnmpMetric) computeMultiStringParserValues() { - ni := len(s.mm) - var str string - switch v := s.CookedValue.(type) { - case string: - str = s.CookedValue.(string) - default: - s.log.Warnf("Error for metric [%s] Type value is not string is %T", s.cfg.ID, v) - return - } - retarray := s.re.FindStringSubmatch(str) - if len(retarray) < ni { - s.log.Warnf("Error for metric [%s] parsing REGEXG [%s] on string [%s] without capturing group", s.cfg.ID, s.cfg.ExtraData, str) - return - } - //retarray[0] contains full string - if len(retarray[1]) == 0 { - s.log.Warnf("Error for metric [%s] parsing REGEXG [%s] on string [%s] cause void capturing group", s.cfg.ID, s.cfg.ExtraData, str) - return - } - for k, i := range s.mm { - s.log.Debugf("Parsing Metric %s MULTISTRING %d value %s (part %s)", s.cfg.ID, k, retarray[0], retarray[k+1]) - - var err error - bitstr := retarray[k+1] - switch i.IConv { - case "STR": - i.Value = bitstr - case "INT": - i.Value, err = strconv.ParseInt(bitstr, 10, 64) - case "BL": - i.Value, err = strconv.ParseBool(bitstr) - case "FP": - i.Value, err = strconv.ParseFloat(bitstr, 64) + for _, cookedValue := range s.CookedValue { + + ni := len(s.mm) + var str string + switch v := cookedValue.(type) { + case string: + str = cookedValue.(string) + default: + s.log.Warnf("Error for metric [%s] Type value is not string is %T", s.cfg.ID, v) + return } - if err != nil { - s.log.Warnf("Error for Metric %s MULTISTRINGPARSER Field [%s|%s|%s] Coversion from [%s] error: %s", s.cfg.ID, i.IType, i.IName, i.IConv, bitstr, err) - i.Value = nil + + retarray := s.re.FindStringSubmatch(str) + if len(retarray) < ni { + s.log.Warnf("Error for metric [%s] parsing REGEXG [%s] on string [%s] without capturing group", s.cfg.ID, s.cfg.ExtraData, str) + return + } + //retarray[0] contains full string + if len(retarray[1]) == 0 { + s.log.Warnf("Error for metric [%s] parsing REGEXG [%s] on string [%s] cause void capturing group", s.cfg.ID, s.cfg.ExtraData, str) + return + } + for k, i := range s.mm { + s.log.Debugf("Parsing Metric %s MULTISTRING %d value %s (part %s)", s.cfg.ID, k, retarray[0], retarray[k+1]) + + var err error + bitstr := retarray[k+1] + switch i.IConv { + case "STR": + i.Value = bitstr + case "INT": + i.Value, err = strconv.ParseInt(bitstr, 10, 64) + case "BL": + i.Value, err = strconv.ParseBool(bitstr) + case "FP": + i.Value, err = strconv.ParseFloat(bitstr, 64) + } + if err != nil { + s.log.Warnf("Error for Metric %s MULTISTRINGPARSER Field [%s|%s|%s] Coversion from [%s] error: %s", s.cfg.ID, i.IType, i.IName, i.IConv, bitstr, err) + i.Value = nil + } } } + + } func (s *SnmpMetric) addMultiStringParserValues(tags map[string]string, fields map[string]interface{}) int64 { diff --git a/pkg/data/snmp/snmp.go b/pkg/data/snmp/snmp.go index aebe1e03..360241b2 100644 --- a/pkg/data/snmp/snmp.go +++ b/pkg/data/snmp/snmp.go @@ -10,7 +10,7 @@ import ( "github.com/Sirupsen/logrus" "github.com/soniah/gosnmp" - "github.com/toni-moreno/snmpcollector/pkg/config" + "snmpcollector/pkg/config" ) var ( @@ -381,6 +381,10 @@ func PduVal2Int64(pdu gosnmp.SnmpPDU) int64 { val = int64(value) case uint64: val = int64(value) + case float32: + val = int64(value) + case float64: + val = int64(value) case string: // for testing and other apps - numbers may appear as strings var err error @@ -477,7 +481,7 @@ const ( // Release release the GoSNMP object func Release(client *gosnmp.GoSNMP) { if client != nil { - client.Conn.Close() + // defer client.Conn.Close() } } diff --git a/pkg/main b/pkg/main new file mode 100755 index 00000000..15160b3a Binary files /dev/null and b/pkg/main differ diff --git a/pkg/main.go b/pkg/main.go index cf98b76a..29e65ca7 100644 --- a/pkg/main.go +++ b/pkg/main.go @@ -3,28 +3,29 @@ package main import ( "flag" "fmt" + "github.com/newrelic/go-agent" "os/signal" + "snmpcollector/pkg/rabbitmq" "syscall" "github.com/Sirupsen/logrus" "github.com/spf13/viper" - "io/ioutil" "os" "path/filepath" "strconv" "time" - "github.com/toni-moreno/snmpcollector/pkg/agent" - "github.com/toni-moreno/snmpcollector/pkg/agent/bus" - "github.com/toni-moreno/snmpcollector/pkg/agent/device" - "github.com/toni-moreno/snmpcollector/pkg/agent/output" - "github.com/toni-moreno/snmpcollector/pkg/agent/selfmon" - "github.com/toni-moreno/snmpcollector/pkg/config" - "github.com/toni-moreno/snmpcollector/pkg/data/impexp" - "github.com/toni-moreno/snmpcollector/pkg/data/measurement" - "github.com/toni-moreno/snmpcollector/pkg/data/snmp" - "github.com/toni-moreno/snmpcollector/pkg/webui" + "snmpcollector/pkg/agent" + "snmpcollector/pkg/agent/bus" + "snmpcollector/pkg/agent/device" + "snmpcollector/pkg/agent/output" + "snmpcollector/pkg/agent/selfmon" + "snmpcollector/pkg/config" + "snmpcollector/pkg/data/impexp" + "snmpcollector/pkg/data/measurement" + "snmpcollector/pkg/data/snmp" + "snmpcollector/pkg/webui" ) var ( @@ -77,7 +78,6 @@ func flags() *flag.FlagSet { }) fmt.Fprintf(os.Stderr, "\nAll settings can be set in config file: %s\n", configFile) os.Exit(1) - } return &f } @@ -168,6 +168,33 @@ func init() { bus.SetLogger(log) // log.Infof("Set Default directories : \n - Exec: %s\n - Config: %s\n -Logs: %s\n -Home: %s\n", appdir, confDir, logDir, homeDir) + + + // init sentry + if len(os.Getenv("SENTRY_DSN")) > 0 { + // raven.SetDSN(os.Getenv("SENTRY_DSN")) + } + + + + + + // init newrelic + + if os.Getenv("NEW_RELIC_AGENT_ENABLED") == "true" { + + config := newrelic.NewConfig(os.Getenv("APP_NAME"), os.Getenv("NR_APP_KEY")) + _, newRelicError := newrelic.NewApplication(config) + + if newRelicError != nil { + fmt.Printf("error on init rew relic: %s", err) + } + + } + + + + } func main() { @@ -196,12 +223,16 @@ func main() { } }() + // Delete Database in Development + agent.MainConfig.Database.InitDB() measurement.SetDB(&agent.MainConfig.Database) impexp.SetDB(&agent.MainConfig.Database) agent.Start() + rabbitmq.SetLogger(log) + webui.WebServer(filepath.Join(homeDir, "public"), httpPort, &agent.MainConfig.HTTP, agent.MainConfig.General.InstanceID) } diff --git a/pkg/rabbitmq/rabbitmq-sender.go b/pkg/rabbitmq/rabbitmq-sender.go new file mode 100644 index 00000000..684c9de1 --- /dev/null +++ b/pkg/rabbitmq/rabbitmq-sender.go @@ -0,0 +1,220 @@ +package rabbitmq + +import ( + "github.com/Sirupsen/logrus" + "github.com/streadway/amqp" + "os" + "sort" +) + +var ( + log *logrus.Logger + influxPointStoredChannel *amqp.Channel + influxPointStoredQueue amqp.Queue + rabbitMqConnection amqp.Connection +) + + +func failOnError(err error, msg string) { + if err != nil { + log.Fatalf("%s: %s", msg, err) + } +} + +func SetLogger(l *logrus.Logger) { + log = l +} + + +func Init() { + if rabbitMqConnection.IsClosed() || influxPointStoredChannel == nil { + initConnection() + } +} + +func initConnection() { + if os.Getenv("RABBITMQ_ENABLED") != "true" { + return + } + + rabbitmqUrl := os.Getenv("RABBITMQ_URL") + if rabbitmqUrl == "" { + rabbitmqUrl = "amqp://rabbitmq:rabbitmq@rabbitmq/" // default + } + + if rabbitMqConnection.IsClosed() { + defer rabbitMqConnection.Close() + } + + rabbitMqConnection, err := amqp.Dial(rabbitmqUrl) + failOnError(err, "Failed to connect to RabbitMQ") + // defer rabbitMqConnection.Close() + + + influxPointStoredChannel, err = rabbitMqConnection.Channel() + failOnError(err, "Failed to open a channel") + // defer influxPointStoredChannel.Close() + + + influxPointStoredQueue, err = influxPointStoredChannel.QueueDeclare( + "snmp_collector.influxpoint.stored", // name + false, + false, + false, + false, + nil, + ) + failOnError(err, "Failed to declare a queue") + +} + + +func GetInfluxPointStored(measurement string, tags map[string]string) string { + if os.Getenv("RABBITMQ_ENABLED") != "true" { + return "" + } + + body := "{ \"measurement\": \"" + measurement + "\"" + + sortedKeys := sortKeys(tags) // be sure that body is always the same, key order could be different in map + + + body += ", \"tags\": [" + i := 0 + for _, tagKey := range sortedKeys { + if tagKey == "subIndexesTag" { + continue // we don't ned sub index tags for identification + } + + if i > 0 { + body += ", " + } + body += "{ \"" + tagKey + "\": \"" + tags[tagKey] + "\" }" + i++ + } + + body += " ]}" + return body +} + +func PublishInfluxPointStored(influxPointsStored map[string]int) { + if os.Getenv("RABBITMQ_ENABLED") != "true" { + return + } + + msg := "{ \"stored\": [" + i := 0 + for influxPoint := range influxPointsStored { + if i > 0 { + msg += ", " + } + msg += " " + influxPoint + i++ + } + msg += "]}" + + + // https://dzone.com/articles/try-and-catch-in-golang + Block{ + Try: func() { + + if influxPointStoredChannel == nil { + initConnection() + } + + err := influxPointStoredChannel.Publish( + "snmp_collector.influxpoint", + influxPointStoredQueue.Name, + false, + false, + amqp.Publishing{ + ContentType: "text/plain", + Body: []byte(msg), + }) + log.Printf(" [x] Sent %s", msg) + + if err != nil { + Throw(err) + } + + }, + Catch: func(e Exception) { + closeConnection() + + }, + }.Do() + +} + + +func closeConnection() { + Block{ + Try: func() { + if influxPointStoredChannel != nil { + defer influxPointStoredChannel.Close() + } + defer rabbitMqConnection.Close() + }, + Catch: func(e Exception) { + + }, + Finally: func() { + influxPointStoredChannel = nil + }, + }.Do() +} + + + + +func sortKeys(m map[string]string) ([]string) { + keys := make([]string, len(m)) + i := 0 + for k := range m { + keys[i] = k + i++ + } + sort.Strings(keys) + return keys +} + + + +type Block struct { + Try func() + Catch func(Exception) + Finally func() +} + +type Exception interface{} + +func Throw(up Exception) { + panic(up) +} + +func (tcf Block) Do() { + if tcf.Finally != nil { + + defer tcf.Finally() + } + if tcf.Catch != nil { + defer func() { + if r := recover(); r != nil { + tcf.Catch(r) + } + }() + } + tcf.Try() +} + + + + + + + + + + + + diff --git a/pkg/webui/apicfg-customfilter.go b/pkg/webui/apicfg-customfilter.go index 2497ede3..0df25ca8 100644 --- a/pkg/webui/apicfg-customfilter.go +++ b/pkg/webui/apicfg-customfilter.go @@ -2,8 +2,8 @@ package webui import ( "github.com/go-macaron/binding" - "github.com/toni-moreno/snmpcollector/pkg/agent" - "github.com/toni-moreno/snmpcollector/pkg/config" + "snmpcollector/pkg/agent" + "snmpcollector/pkg/config" "gopkg.in/macaron.v1" ) diff --git a/pkg/webui/apicfg-impexp.go b/pkg/webui/apicfg-impexp.go index 49d7b7a2..90a5e1f7 100644 --- a/pkg/webui/apicfg-impexp.go +++ b/pkg/webui/apicfg-impexp.go @@ -8,7 +8,7 @@ import ( "os" "github.com/go-macaron/binding" - "github.com/toni-moreno/snmpcollector/pkg/data/impexp" + "snmpcollector/pkg/data/impexp" "gopkg.in/macaron.v1" ) diff --git a/pkg/webui/apicfg-influxerver.go b/pkg/webui/apicfg-influxerver.go index 8215a907..20da6dd7 100644 --- a/pkg/webui/apicfg-influxerver.go +++ b/pkg/webui/apicfg-influxerver.go @@ -4,9 +4,9 @@ import ( "time" "github.com/go-macaron/binding" - "github.com/toni-moreno/snmpcollector/pkg/agent" - "github.com/toni-moreno/snmpcollector/pkg/agent/output" - "github.com/toni-moreno/snmpcollector/pkg/config" + "snmpcollector/pkg/agent" + "snmpcollector/pkg/agent/output" + "snmpcollector/pkg/config" "gopkg.in/macaron.v1" ) diff --git a/pkg/webui/apicfg-measfilters.go b/pkg/webui/apicfg-measfilters.go index b0afc21f..a2032a17 100644 --- a/pkg/webui/apicfg-measfilters.go +++ b/pkg/webui/apicfg-measfilters.go @@ -4,9 +4,9 @@ import ( "fmt" "github.com/go-macaron/binding" - "github.com/toni-moreno/snmpcollector/pkg/agent" - "github.com/toni-moreno/snmpcollector/pkg/config" - "github.com/toni-moreno/snmpcollector/pkg/data/filter" + "snmpcollector/pkg/agent" + "snmpcollector/pkg/config" + "snmpcollector/pkg/data/filter" "gopkg.in/macaron.v1" ) diff --git a/pkg/webui/apicfg-measgroup.go b/pkg/webui/apicfg-measgroup.go index 857b7cf0..48acbbee 100644 --- a/pkg/webui/apicfg-measgroup.go +++ b/pkg/webui/apicfg-measgroup.go @@ -2,8 +2,8 @@ package webui import ( "github.com/go-macaron/binding" - "github.com/toni-moreno/snmpcollector/pkg/agent" - "github.com/toni-moreno/snmpcollector/pkg/config" + "snmpcollector/pkg/agent" + "snmpcollector/pkg/config" "gopkg.in/macaron.v1" ) diff --git a/pkg/webui/apicfg-measurement.go b/pkg/webui/apicfg-measurement.go index 8cb82699..66c7c3d5 100644 --- a/pkg/webui/apicfg-measurement.go +++ b/pkg/webui/apicfg-measurement.go @@ -2,8 +2,8 @@ package webui import ( "github.com/go-macaron/binding" - "github.com/toni-moreno/snmpcollector/pkg/agent" - "github.com/toni-moreno/snmpcollector/pkg/config" + "snmpcollector/pkg/agent" + "snmpcollector/pkg/config" "gopkg.in/macaron.v1" ) diff --git a/pkg/webui/apicfg-oidcondition.go b/pkg/webui/apicfg-oidcondition.go index 3f117cd2..4051ad7f 100644 --- a/pkg/webui/apicfg-oidcondition.go +++ b/pkg/webui/apicfg-oidcondition.go @@ -2,8 +2,8 @@ package webui import ( "github.com/go-macaron/binding" - "github.com/toni-moreno/snmpcollector/pkg/agent" - "github.com/toni-moreno/snmpcollector/pkg/config" + "snmpcollector/pkg/agent" + "snmpcollector/pkg/config" "gopkg.in/macaron.v1" ) diff --git a/pkg/webui/apicfg-snmpdevice.go b/pkg/webui/apicfg-snmpdevice.go index b4458d97..12d00544 100644 --- a/pkg/webui/apicfg-snmpdevice.go +++ b/pkg/webui/apicfg-snmpdevice.go @@ -2,10 +2,9 @@ package webui import ( "github.com/go-macaron/binding" - "github.com/toni-moreno/snmpcollector/pkg/agent" - "github.com/toni-moreno/snmpcollector/pkg/config" - "github.com/toni-moreno/snmpcollector/pkg/data/snmp" "gopkg.in/macaron.v1" + "snmpcollector/pkg/agent" + "snmpcollector/pkg/config" ) // NewAPICfgSnmpDevice SnmpDevice REST API creator @@ -15,6 +14,7 @@ func NewAPICfgSnmpDevice(m *macaron.Macaron) error { // Data sources m.Group("/api/cfg/snmpdevice", func() { + m.Get("/count", reqSignedIn, GetNrOfSNMPDevices) m.Get("/", reqSignedIn, GetSNMPDevices) m.Post("/", reqSignedIn, bind(config.SnmpDeviceCfg{}), AddSNMPDevice) m.Post("/:mode", reqSignedIn, bind(config.SnmpDeviceCfg{}), AddSNMPDevice) @@ -35,6 +35,18 @@ type DeviceStatMap struct { IsRuntime bool } +// GetNrOfSNMPDevices Return number of snmpdevice to frontend +func GetNrOfSNMPDevices(ctx *Context) { + count, err := agent.MainConfig.Database.GetNumberOfSnmpDevices() + if err != nil { + ctx.JSON(500, err.Error()) + log.Errorf("Error on GetNrOfSNMPDevices :%+s", err) + return + } + ctx.JSON(200, count) + log.Debugf("Getting GetNrOfSNMPDevices %d", count) +} + // GetSNMPDevices Return snmpdevice list to frontend func GetSNMPDevices(ctx *Context) { devcfgarray, err := agent.MainConfig.Database.GetSnmpDeviceCfgArray("") @@ -54,15 +66,21 @@ func GetSNMPDevices(ctx *Context) { } func addDeviceOnline(mode string, id string, dev *config.SnmpDeviceCfg) error { + + + // DISABLED: we also want to add offline devices to the collector + + /* //First doing Ping log.Infof("trying to ping device %s : %+v", dev.ID, dev) - _, sysinfo, err := snmp.GetClient(dev, log, "ping", false, 0) if err != nil { log.Debugf("ERROR on query device : %s", err) return err } log.Info("Device Ping ok : %#v", sysinfo) + */ + // Next updating database switch mode { case "add": @@ -85,6 +103,7 @@ func addDeviceOnline(mode string, id string, dev *config.SnmpDeviceCfg) error { return nil } + // AddSNMPDevice Insert new snmpdevice to de internal BBDD --pending-- func AddSNMPDevice(ctx *Context, dev config.SnmpDeviceCfg) { mode := ctx.Params(":mode") diff --git a/pkg/webui/apicfg-snmpmetric.go b/pkg/webui/apicfg-snmpmetric.go index 46aa528f..3f1f6cc0 100644 --- a/pkg/webui/apicfg-snmpmetric.go +++ b/pkg/webui/apicfg-snmpmetric.go @@ -2,8 +2,8 @@ package webui import ( "github.com/go-macaron/binding" - "github.com/toni-moreno/snmpcollector/pkg/agent" - "github.com/toni-moreno/snmpcollector/pkg/config" + "snmpcollector/pkg/agent" + "snmpcollector/pkg/config" "gopkg.in/macaron.v1" ) diff --git a/pkg/webui/apicfg-varcatalog.go b/pkg/webui/apicfg-varcatalog.go index ce29ae90..4ebbdbd9 100644 --- a/pkg/webui/apicfg-varcatalog.go +++ b/pkg/webui/apicfg-varcatalog.go @@ -2,8 +2,8 @@ package webui import ( "github.com/go-macaron/binding" - "github.com/toni-moreno/snmpcollector/pkg/agent" - "github.com/toni-moreno/snmpcollector/pkg/config" + "snmpcollector/pkg/agent" + "snmpcollector/pkg/config" "gopkg.in/macaron.v1" ) diff --git a/pkg/webui/apirt-agent.go b/pkg/webui/apirt-agent.go index 828fb67b..6392a305 100644 --- a/pkg/webui/apirt-agent.go +++ b/pkg/webui/apirt-agent.go @@ -5,9 +5,9 @@ import ( "time" "github.com/go-macaron/binding" - "github.com/toni-moreno/snmpcollector/pkg/agent" - "github.com/toni-moreno/snmpcollector/pkg/config" - "github.com/toni-moreno/snmpcollector/pkg/data/snmp" + "snmpcollector/pkg/agent" + "snmpcollector/pkg/config" + "snmpcollector/pkg/data/snmp" "gopkg.in/macaron.v1" ) diff --git a/pkg/webui/apirt-device.go b/pkg/webui/apirt-device.go index 388527e8..3b7dd460 100644 --- a/pkg/webui/apirt-device.go +++ b/pkg/webui/apirt-device.go @@ -4,7 +4,7 @@ import ( //"github.com/go-macaron/binding" "strconv" - "github.com/toni-moreno/snmpcollector/pkg/agent" + "snmpcollector/pkg/agent" "gopkg.in/macaron.v1" ) diff --git a/pkg/webui/webserver.go b/pkg/webui/webserver.go index 083a8646..eea20352 100644 --- a/pkg/webui/webserver.go +++ b/pkg/webui/webserver.go @@ -12,7 +12,7 @@ import ( "os" "github.com/Sirupsen/logrus" - "github.com/toni-moreno/snmpcollector/pkg/config" + "snmpcollector/pkg/config" "gopkg.in/macaron.v1" ) diff --git a/setup.sh b/setup.sh new file mode 100644 index 00000000..f83d8162 --- /dev/null +++ b/setup.sh @@ -0,0 +1,8 @@ +go get -v github.com/tools/godep +go get -v github.com/blang/semver +go get -v github.com/mattn/go-sqlite3 +go install -v github.com/mattn/go-sqlite3 +go get -v gopkg.in/natefinch/lumberjack.v2 +go get -v github.com/getsentry/raven-go +go get -v github.com/streadway/amqp +go get -v github.com/newrelic/go-agent diff --git a/src/common/validation.service.ts b/src/common/validation.service.ts index 86da209b..c11be529 100644 --- a/src/common/validation.service.ts +++ b/src/common/validation.service.ts @@ -161,7 +161,7 @@ export class ValidationService { if (control.value){ // Regex to check valid IP or hostname // From https://stackoverflow.com/questions/106179/regular-expression-to-match-dns-hostname-or-ip-address - if (control.value.toString().match(/^[a-z\d]([a-z\d\-]{0,61}[a-z\d])?(\.[a-z\d]([a-z\d\-]{0,61}[a-z\d])?)*$/i)) { + if (control.value.toString().match(/^[a-z\d]([a-z\d\-\_]{0,61}[a-z\d])?(\.[a-z\d]([a-z\d\-\_]{0,61}[a-z\d])?)*$/i)) { return null; } else { return { 'invalidFQDNHost': true }; diff --git a/src/home/about-modal.ts b/src/home/about-modal.ts index d0b11c1b..1eaa064a 100644 --- a/src/home/about-modal.ts +++ b/src/home/about-modal.ts @@ -19,7 +19,7 @@ import { WindowRef } from '../common/windowref'; <h4 class="text-primary"> <b>SNMPCollector</b> </h4> <span> SNMPCollector is a full featured generic SNMP data collector with web administration interface. It is an open Source tool which has as main goal simplify the configuration for getting data from any device with SNMP protocol support and send resulting data to an InfluxDB backend</span> <div class="text-right"> - <a href="javaScript:void(0);" (click)="link('https://github.com/toni-moreno/snmpcollector')" class="text-link"> More info <i class="glyphicon glyphicon-plus-sign"></i></a> + <a href="javaScript:void(0);" (click)="link('https://snmpcollector')" class="text-link"> More info <i class="glyphicon glyphicon-plus-sign"></i></a> </div> <hr/> <h4> Release information </h4> diff --git a/src/home/home.html b/src/home/home.html index 551c8bfb..e7707d62 100644 --- a/src/home/home.html +++ b/src/home/home.html @@ -27,7 +27,7 @@ <a role="button" class="glyphicon glyphicon-info-sign" (click)="showAboutModal()"></a> </li> <li class="active"> - <a href="javascript:void(0);" role="button" (click)="link('https://github.com/toni-moreno/snmpcollector/wiki')" class="glyphicon glyphicon-question-sign"></a> + <a href="javascript:void(0);" role="button" (click)="link('https://snmpcollector/wiki')" class="glyphicon glyphicon-question-sign"></a> </li> <li class="active"> <a role="button" class="glyphicon glyphicon-off" (click)="logout()"></a> @@ -70,7 +70,7 @@ <span class="title-menu"><i class="glyphicon glyphicon-question-sign" style="padding-right: 8px"></i>Others</span> </li> <li> - <a href="https://github.com/toni-moreno/snmpcollector/wiki" role="button">Wiki</a> + <a href="https://snmpcollector/wiki" role="button">Wiki</a> </li> <li> <a role="button" (click)="showAboutModal()">About</a> diff --git a/src/influxmeas/influxmeascfg.component.ts b/src/influxmeas/influxmeascfg.component.ts index 14a04ee6..9298a7fb 100644 --- a/src/influxmeas/influxmeascfg.component.ts +++ b/src/influxmeas/influxmeascfg.component.ts @@ -35,6 +35,8 @@ export class InfluxMeasCfgComponent { public isRequesting : boolean; public counterItems : number = null; public counterErrors: any = []; + public myTexts = {}; + public mySettings = {}; itemsPerPageOptions : any = ItemsPerPageOptions; editmode: string; //list , create, modify @@ -122,9 +124,11 @@ export class InfluxMeasCfgComponent { controlArray.push({'ID': 'TagOID', 'defVal' : '', 'Validators' : Validators.compose([ValidationService.OIDValidator, Validators.required])}); case 'indexed': controlArray.push({'ID': 'IndexOID', 'defVal' : '', 'Validators' : Validators.compose([ValidationService.OIDValidator, Validators.required])}); + controlArray.push({'ID': 'IndexDescrOID', 'defVal' : '', 'Validators' : Validators.compose([ValidationService.OIDValidator, Validators.required])}); controlArray.push({'ID': 'IndexTag', 'defVal' : '', 'Validators' : Validators.required}); controlArray.push({'ID': 'IndexTagFormat', 'defVal' : ''}); - controlArray.push({'ID': 'IndexAsValue', 'defVal' : 'false', 'Validators' : Validators.required}); + controlArray.push({'ID': 'IndexAsValue', 'defVal' : 'false', 'Validators' : Validators.required}) + controlArray.push({'ID': 'Encoding', 'defVal' : ''}); break; default: break; diff --git a/src/influxmeas/influxmeaseditor.html b/src/influxmeas/influxmeaseditor.html index 753efe2a..159372de 100644 --- a/src/influxmeas/influxmeaseditor.html +++ b/src/influxmeas/influxmeaseditor.html @@ -73,6 +73,15 @@ <h4 style="display:inline"> </div> </div> + <div class="form-group" *ngIf="influxmeasForm.controls.IndexOID"> + <label class="control-label col-sm-2" for="IndexDescrOID">IndexDescrOID</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="The index OID to translate the indices"></i> + <div class="col-sm-9"> + <input formControlName="IndexDescrOID" id="IndexDescrOID" [ngModel]="influxmeasForm.value.IndexDescrOID"/> + <control-messages [control]="influxmeasForm.controls.IndexOID"></control-messages> + </div> + </div> + <div class="form-group" *ngIf="influxmeasForm.controls.TagOID"> <label class="control-label col-sm-2" for="TagOID">TagOID</label> <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="The Tag OID will allow us to get real Tag Name not provided in the IndexOID"></i> @@ -112,6 +121,23 @@ <h4 style="display:inline"> </div> </div> + <div class="form-group"> + <label class="control-label col-sm-2" for="Encoding">Encoding</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Encoding for indexes which are for example represented as mac addresses"></i> + + + <div class="col-sm-9"> + <select formControlName="Encoding" id="Encoding" [ngModel]="influxmeasForm.value.Encoding"> + <option value="">No Encoding</option> + <option value="MAC">MAC</option> + </select> + <control-messages [control]="influxmeasForm.controls.Encoding"></control-messages> + </div> + + + + </div> + <div class="form-group"> <label class="control-label col-sm-2" for="Fields">Fields</label> <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="List of metrics to associate with the measurement {{influxmeasForm.value.ID}}"></i> diff --git a/src/snmpdevice/snmpdevicecfg.component.ts b/src/snmpdevice/snmpdevicecfg.component.ts index bb9a2e41..4f64a12c 100644 --- a/src/snmpdevice/snmpdevicecfg.component.ts +++ b/src/snmpdevice/snmpdevicecfg.component.ts @@ -142,6 +142,8 @@ export class SnmpDeviceCfgComponent { MeasurementGroups: [this.snmpdevForm ? this.snmpdevForm.value.MeasurementGroups : null], MeasFilters: [this.snmpdevForm ? this.snmpdevForm.value.MeasFilters : null], Description: [this.snmpdevForm ? this.snmpdevForm.value.Description : ''], + SpecificInterfaceFilters: [this.snmpdevForm ? this.snmpdevForm.value.SpecificInterfaceFilters : ''], + SpecificMetricFilters: [this.snmpdevForm ? this.snmpdevForm.value.SpecificMetricFilters : ''] }); } diff --git a/src/snmpdevice/snmpdeviceeditor.html b/src/snmpdevice/snmpdeviceeditor.html index ab123e33..82ad1d96 100644 --- a/src/snmpdevice/snmpdeviceeditor.html +++ b/src/snmpdevice/snmpdeviceeditor.html @@ -3,407 +3,537 @@ <h2>{{defaultConfig.name}}</h2> </test-connection-modal> <div #blocker style="position: absolute; top: 50%;"></div> <ng-container [ngSwitch]="editmode"> - <ng-template ngSwitchCase="list"> + <ng-template ngSwitchCase="list"> <test-modal #viewModal titleName='Device'></test-modal> - <test-modal #viewModalDelete titleName='Deleting:' [customMessage]="['Deleting this SNMP Device will affect the following components','Deleting this SNMP Device will NOT affect any component. Safe delete']" [customMessageClass]="['alert alert-danger','alert alert-success']" - [showValidation]="true" [textValidation]="'Delete'" [controlSize]="true" (validationClicked)="deleteSnmpDevice($event)"> + <test-modal #viewModalDelete titleName='Deleting:' + [customMessage]="['Deleting this SNMP Device will affect the following components','Deleting this SNMP Device will NOT affect any component. Safe delete']" + [customMessageClass]="['alert alert-danger','alert alert-success']" + [showValidation]="true" [textValidation]="'Delete'" [controlSize]="true" + (validationClicked)="deleteSnmpDevice($event)"> </test-modal> - <export-file-modal #exportFileModal [showValidation]="true" [exportType]="defaultConfig['slug']" [textValidation]="'Export'" titleName='Exporting:'></export-file-modal> - <table-list #listTableComponent [typeComponent]="defaultConfig['slug']" [data]="data" [columns]="defaultConfig['table-columns']" [counterItems]="counterItems" [counterErrors]="counterErrors" [selectedArray]="selectedArray" [isRequesting]="isRequesting" [tableRole]="tableRole" - [roleActions]="overrideRoleActions" [extraActions]="extraActions" overrideEditEnabled="true" [tableAvailableActions]="tableAvailableActions" (customClicked)="customActions($event)" - (extraActionClicked)="onExtraActionClicked($event)"></table-list> - </ng-template> - <ng-template ngSwitchDefault> + <export-file-modal #exportFileModal [showValidation]="true" [exportType]="defaultConfig['slug']" + [textValidation]="'Export'" titleName='Exporting:'></export-file-modal> + <table-list #listTableComponent [typeComponent]="defaultConfig['slug']" [data]="data" + [columns]="defaultConfig['table-columns']" [counterItems]="counterItems" [counterErrors]="counterErrors" + [selectedArray]="selectedArray" [isRequesting]="isRequesting" [tableRole]="tableRole" + [roleActions]="overrideRoleActions" [extraActions]="extraActions" overrideEditEnabled="true" + [tableAvailableActions]="tableAvailableActions" (customClicked)="customActions($event)" + (extraActionClicked)="onExtraActionClicked($event)"></table-list> + </ng-template> + <ng-template ngSwitchDefault> <test-filter-modal #viewTestFilterModal titleName='Filter connection:' [formValues]="snmpdevForm.value"> </test-filter-modal> - <form [formGroup]="snmpdevForm" class="form-horizontal" (ngSubmit)="editmode === 'create' ? saveSnmpDev(snmpdevForm.value) : updateSnmpDev(false,snmpdevForm.value)"> + <form [formGroup]="snmpdevForm" class="form-horizontal" + (ngSubmit)="editmode === 'create' ? saveSnmpDev(snmpdevForm.value) : updateSnmpDev(false,snmpdevForm.value)"> <ng-container> <div class="row well well-sm"> <h4 style="display:inline"> - <i class="glyphicon glyphicon-cog text-info"></i> {{ editmode | uppercase}} - </h4> - <div class="pull-right" style="margin-right: 20px"> - <div style="display:inline" tooltip='Create Custom Filter' container=body> <button class="btn btn-primary" type="button" (click)="showFilterModal()" [disabled]="!snmpdevForm.valid"> <i class="glyphicon glyphicon-random"></i></button></div> - <div style="display:inline" tooltip='Test Connection' container=body><button class="btn btn-info" type="button" (click)="showTestConnectionModal(snmpdevForm.value)" [disabled]="!snmpdevForm.valid"> <i class="glyphicon glyphicon-flash"></i></button></div> - <div style="display:inline" tooltip='Submit' container=body><button class="btn btn-success" type="submit" [disabled]="!snmpdevForm.valid"> <i class="glyphicon glyphicon-ok-circle"></i></button></div> - <div style="display:inline" tooltip='Reset' container=body><button class="btn btn-warning" type="reset" [disabled]="!snmpdevForm.dirty"><i class="glyphicon glyphicon-ban-circle"></i> </button></div> - <div style="display:inline" tooltip='Cancel' container=body><button class="btn btn-danger" type="button" (click)="cancelEdit()"><i class="glyphicon glyphicon-remove-circle"></i></button></div> - <div style="display:inline; margin-left: 10px; border-left: 1px solid #337ab7" tooltip='Submit and apply changes on runtime' container=body ><button class="btn btn-success" style="margin-left: 10px;" type="button" (click)="editmode === 'create' ? saveSnmpDev(snmpdevForm.value,'full') : updateSnmpDev(false,snmpdevForm.value,'full')"><i class="glyphicon glyphicon-adjust"></i></button></div> + <i class="glyphicon glyphicon-cog text-info"></i> {{ editmode | uppercase}} + </h4> + <div class="pull-right" style="margin-right: 20px"> + <div style="display:inline" tooltip='Create Custom Filter' container=body> + <button class="btn btn-primary" type="button" (click)="showFilterModal()" [disabled]="!snmpdevForm.valid"> + <i class="glyphicon glyphicon-random"></i></button> + </div> + <div style="display:inline" tooltip='Test Connection' container=body> + <button class="btn btn-info" type="button" (click)="showTestConnectionModal(snmpdevForm.value)" + [disabled]="!snmpdevForm.valid"><i class="glyphicon glyphicon-flash"></i></button> + </div> + <div style="display:inline" tooltip='Submit' container=body> + <button class="btn btn-success" type="submit" [disabled]="!snmpdevForm.valid"><i + class="glyphicon glyphicon-ok-circle"></i></button> + </div> + <div style="display:inline" tooltip='Reset' container=body> + <button class="btn btn-warning" type="reset" [disabled]="!snmpdevForm.dirty"><i + class="glyphicon glyphicon-ban-circle"></i></button> + </div> + <div style="display:inline" tooltip='Cancel' container=body> + <button class="btn btn-danger" type="button" (click)="cancelEdit()"><i + class="glyphicon glyphicon-remove-circle"></i></button> + </div> + <div style="display:inline; margin-left: 10px; border-left: 1px solid #337ab7" + tooltip='Submit and apply changes on runtime' container=body> + <button class="btn btn-success" style="margin-left: 10px;" type="button" + (click)="editmode === 'create' ? saveSnmpDev(snmpdevForm.value,'full') : updateSnmpDev(false,snmpdevForm.value,'full')"> + <i class="glyphicon glyphicon-adjust"></i></button> + </div> + </div> </div> - </div> - </ng-container> - <div class="form-fixed-height"> + </ng-container> + <div class="form-fixed-height"> <div class="well well-sm"> <span class="editsection"> Core Settings </span> <div class="form-group" style="margin-top: 25px"> - <label class="control-label col-sm-2" for="ID">ID</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Unique identifier of SNMP Device"></i> - <div class="col-sm-9"> - <input formControlName="ID" id="ID" [ngModel]="snmpdevForm.value.ID" /> - <control-messages [control]="snmpdevForm.controls.ID"></control-messages> - </div> - </div> - <div class="form-group"> - <label class="control-label col-sm-2" for="Active">Active</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Active on Collector reboot"></i> - <div class="col-sm-9"> - <select formControlName="Active" id="Active" [ngModel]="snmpdevForm.value.Active"> - <option value="true">True</option> - <option value="false">False</option> - </select> - <control-messages [control]="snmpdevForm.controls.Active"></control-messages> + <label class="control-label col-sm-2" for="ID">ID</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="Unique identifier of SNMP Device"></i> + <div class="col-sm-9"> + <input formControlName="ID" id="ID" [ngModel]="snmpdevForm.value.ID"/> + <control-messages [control]="snmpdevForm.controls.ID"></control-messages> + </div> + </div> + <div class="form-group"> + <label class="control-label col-sm-2" for="Active">Active</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="Active on Collector reboot"></i> + <div class="col-sm-9"> + <select formControlName="Active" id="Active" [ngModel]="snmpdevForm.value.Active"> + <option value="true">True</option> + <option value="false">False</option> + </select> + <control-messages [control]="snmpdevForm.controls.Active"></control-messages> + </div> + </div> </div> - </div> - </div> - <div class="well well-sm"> + <div class="well well-sm"> <span class="editsection"> Device Settings </span> - <div class="form-group" style="margin-top: 25px"> - <label class="control-label col-sm-2" for="host">Host</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Host (IP or FQDN) of SNMP device to connnect by SNMP protocol"></i> - <div class="col-sm-9"> - <input formControlName="Host" id="Host" [ngModel]="snmpdevForm.value.Host" /> - <control-messages [control]="snmpdevForm.controls.Host"></control-messages> - </div> - </div> + <div class="form-group" style="margin-top: 25px"> + <label class="control-label col-sm-2" for="host">Host</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="Host (IP or FQDN) of SNMP device to connnect by SNMP protocol"></i> + <div class="col-sm-9"> + <input formControlName="Host" id="Host" [ngModel]="snmpdevForm.value.Host"/> + <control-messages [control]="snmpdevForm.controls.Host"></control-messages> + </div> + </div> - <div class="form-group"> - <label class="control-label col-sm-2" for="Port">Port</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Connection port to the device using SNMP protocol"></i> - <div class="col-sm-9"> - <input formControlName="Port" id="Port" [ngModel]="snmpdevForm.value.Port" /> - <control-messages [control]="snmpdevForm.controls.Port"></control-messages> - </div> - </div> + <div class="form-group"> + <label class="control-label col-sm-2" for="Port">Port</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="Connection port to the device using SNMP protocol"></i> + <div class="col-sm-9"> + <input formControlName="Port" id="Port" [ngModel]="snmpdevForm.value.Port"/> + <control-messages [control]="snmpdevForm.controls.Port"></control-messages> + </div> + </div> - <div class="form-group"> - <label class="control-label col-sm-2" for="Timeout">Timeout</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Timeout for the SNMP Query"></i> - <div class="col-sm-9"> - <input formControlName="Timeout" id="Timeout" [ngModel]="snmpdevForm.value.Timeout" /> - <control-messages [control]="snmpdevForm.controls.Timeout"></control-messages> - </div> - </div> + <div class="form-group"> + <label class="control-label col-sm-2" for="Timeout">Timeout</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="Timeout for the SNMP Query"></i> + <div class="col-sm-9"> + <input formControlName="Timeout" id="Timeout" [ngModel]="snmpdevForm.value.Timeout"/> + <control-messages [control]="snmpdevForm.controls.Timeout"></control-messages> + </div> + </div> - <div class="form-group"> - <label class="control-label col-sm-2" for="Retries">Retries</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Set the number of retries to attempt within timeout"></i> - <div class="col-sm-9"> - <input formControlName="Retries" id="Retries" [ngModel]="snmpdevForm.value.Retries" /> - <control-messages [control]="snmpdevForm.controls.Retries"></control-messages> - </div> - </div> + <div class="form-group"> + <label class="control-label col-sm-2" for="Retries">Retries</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="Set the number of retries to attempt within timeout"></i> + <div class="col-sm-9"> + <input formControlName="Retries" id="Retries" [ngModel]="snmpdevForm.value.Retries"/> + <control-messages [control]="snmpdevForm.controls.Retries"></control-messages> + </div> + </div> - <div class="form-group"> - <label class="control-label col-sm-2" for="SystemOIDs">Alternate System OID's</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Set OID to get like MIB-2::System Info (NEEDED TO CHECK connectivity to the device!!! for non MIB-2 based devices)"></i> - <div class="col-sm-9"> - <input formControlName="SystemOIDs" id="SystemOIDs" [ngModel]="snmpdevForm.value.SystemOIDs" /> - <control-messages [control]="snmpdevForm.controls.SystemOIDs"></control-messages> - </div> - </div> + <div class="form-group"> + <label class="control-label col-sm-2" for="SystemOIDs">Alternate System OID's</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" + tooltip="Set OID to get like MIB-2::System Info (NEEDED TO CHECK connectivity to the device!!! for non MIB-2 based devices)"></i> + <div class="col-sm-9"> + <input formControlName="SystemOIDs" id="SystemOIDs" [ngModel]="snmpdevForm.value.SystemOIDs"/> + <control-messages [control]="snmpdevForm.controls.SystemOIDs"></control-messages> + </div> + </div> - </div> - <div class="well well-sm"> + </div> + <div class="well well-sm"> <span class="editsection"> Debug Settings </span> - <div class="form-group" style="margin-top: 25px"> - <label class="control-label col-sm-2" for="LogLevel">Log level</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Severity log level"></i> - <div class="col-sm-9"> - <select formControlName="LogLevel" id="LogLevel" [ngModel]="snmpdevForm.value.LogLevel"> - <option value="panic">Panic</option> - <option value="fatal">Fatal</option> - <option value="error">Error</option> - <option value="warn">Warning</option> - <option selected="selected" value="info">Info</option> - <option value="debug">Debug</option> - </select> - <control-messages [control]="snmpdevForm.controls.LogLevel"></control-messages> - </div> - </div> - <div class="form-group"> - <label class="control-label col-sm-2" for="SnmpDebug">SnmpDebug</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Show SNMP debug"></i> - <div class="col-sm-9"> - <select formControlName="SnmpDebug" id="SnmpDebug" [ngModel]="snmpdevForm.value.SnmpDebug"> - <option value="true">True</option> - <option value="false">False</option> - </select> - <control-messages [control]="snmpdevForm.controls.SnmpDebug"></control-messages> + <div class="form-group" style="margin-top: 25px"> + <label class="control-label col-sm-2" for="LogLevel">Log level</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="Severity log level"></i> + <div class="col-sm-9"> + <select formControlName="LogLevel" id="LogLevel" [ngModel]="snmpdevForm.value.LogLevel"> + <option value="panic">Panic</option> + <option value="fatal">Fatal</option> + <option value="error">Error</option> + <option value="warn">Warning</option> + <option selected="selected" value="info">Info</option> + <option value="debug">Debug</option> + </select> + <control-messages [control]="snmpdevForm.controls.LogLevel"></control-messages> + </div> + </div> + <div class="form-group"> + <label class="control-label col-sm-2" for="SnmpDebug">SnmpDebug</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="Show SNMP debug"></i> + <div class="col-sm-9"> + <select formControlName="SnmpDebug" id="SnmpDebug" [ngModel]="snmpdevForm.value.SnmpDebug"> + <option value="true">True</option> + <option value="false">False</option> + </select> + <control-messages [control]="snmpdevForm.controls.SnmpDebug"></control-messages> + </div> + </div> </div> - </div> - </div> - <div class="well well-sm"> + <div class="well well-sm"> <span class="editsection"> Polling Settings </span> - <div class="form-group" style="margin-top: 25px"> - <label class="control-label col-sm-2" for="SnmpVersion">SnmpVersion</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="SNMP Version (1,2c,3)"></i> - <div class="col-sm-9"> - <select formControlName="SnmpVersion" id="SnmpVersion" (click)="setDynamicFields(snmpdevForm.value.SnmpVersion)" [ngModel]="snmpdevForm.value.SnmpVersion"> - <option value="1">1</option> - <option value="2c" selected="selected">2c</option> - <option value="3">3</option> - </select> - <control-messages [control]="snmpdevForm.controls.SnmpVersion"></control-messages> - </div> - </div> - - <div class="form-group" *ngIf="snmpdevForm.value.SnmpVersion != '1' "> - <label class="control-label col-sm-2" for="DisableBulk">DisableBulk</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Active on Collector reboot"></i> - <div class="col-sm-9"> - <select formControlName="DisableBulk" id="DisableBulk" [ngModel]="snmpdevForm.value.DisableBulk"> - <option value="true">True</option> - <option value="false">False</option> - </select> - <control-messages [control]="snmpdevForm.controls.DisableBulk"></control-messages> - </div> - </div> - - <div class="form-group" *ngIf="snmpdevForm.value.SnmpVersion != '1' "> - <label class="control-label col-sm-2" for="MaxRepetitions" >MaxRepetitions</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Set the MaxRepetitions value for BULKWALK SNMP Queries (valid ranges is 1-255) default 50"></i> - <div class="col-sm-9"> - <input formControlName="MaxRepetitions" id="MaxRepetitions" [ngModel]="snmpdevForm.value.MaxRepetitions"/> - <control-messages [control]="snmpdevForm.controls.MaxRepetitions"></control-messages> - </div> - </div> - - <div class="form-group" *ngIf="snmpdevForm.value.SnmpVersion != '3' && snmpdevForm.controls.Community"> - <label class="control-label col-sm-2" for="Community">Community</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Community for authentication"></i> - <div class="col-sm-9"> - <input #Community formControlName="Community" id="Community" type="password" [ngModel]="snmpdevForm.value.Community"/> - <i style="margin-left:-25px; margin-right:6px" [ngClass]="Community.type === 'password' ? ['glyphicon glyphicon-eye-open text-primary'] : ['glyphicon glyphicon-eye-close text-primary']" passwordToggle [input]="Community"> </i> - <control-messages [control]="snmpdevForm.controls.Community"></control-messages> - </div> - </div> - - <div *ngIf="snmpdevForm.value.SnmpVersion == 3"> - <div class="form-group" *ngIf="snmpdevForm.controls.V3SecLevel"> - <label class="control-label col-sm-2" for="V3SecLevel">V3SecLevel</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Authentification security request mode"></i> - <div class="col-sm-9"> - <select formControlName="V3SecLevel" id="V3SecLevel" (click)="setDynamicFields(snmpdevForm.value.V3SecLevel)" [ngModel]="snmpdevForm.value.V3SecLevel"> - <option value="NoAuthNoPriv">NoAuthNoPriv</option> - <option value="AuthNoPriv">AuthNoPriv</option> - <option value="AuthPriv">AuthPriv</option> - </select> - <control-messages [control]="snmpdevForm.controls.V3SecLevel"></control-messages> + <div class="form-group" style="margin-top: 25px"> + <label class="control-label col-sm-2" for="SnmpVersion">SnmpVersion</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="SNMP Version (1,2c,3)"></i> + <div class="col-sm-9"> + <select formControlName="SnmpVersion" id="SnmpVersion" + (click)="setDynamicFields(snmpdevForm.value.SnmpVersion)" + [ngModel]="snmpdevForm.value.SnmpVersion"> + <option value="1">1</option> + <option value="2c" selected="selected">2c</option> + <option value="3">3</option> + </select> + <control-messages [control]="snmpdevForm.controls.SnmpVersion"></control-messages> + </div> </div> - </div> - <div class="form-group" *ngIf="snmpdevForm.controls.V3AuthUser"> - <label class="control-label col-sm-2" for="V3AuthUser">V3AuthUser</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Authentication user"></i> - <div class="col-sm-9"> - <input formControlName="V3AuthUser" id="V3AuthUser" [ngModel]="snmpdevForm.value.V3AuthUser"/> - <control-messages [control]="snmpdevForm.controls.V3AuthUser"></control-messages> + <div class="form-group" *ngIf="snmpdevForm.value.SnmpVersion != '1' "> + <label class="control-label col-sm-2" for="DisableBulk">DisableBulk</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="Active on Collector reboot"></i> + <div class="col-sm-9"> + <select formControlName="DisableBulk" id="DisableBulk" [ngModel]="snmpdevForm.value.DisableBulk"> + <option value="true">True</option> + <option value="false">False</option> + </select> + <control-messages [control]="snmpdevForm.controls.DisableBulk"></control-messages> + </div> </div> - </div> - <div class="form-group" *ngIf="snmpdevForm.controls.V3AuthPass"> - <label class="control-label col-sm-2" for="V3AuthPass">V3AuthPass</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Authentication password"></i> - <div class="col-sm-9"> - <input #inputV3AuthPass formControlName="V3AuthPass" id="V3AuthPass" type="password" [ngModel]="snmpdevForm.value.V3AuthPass"/> - <i style="margin-left:-25px; margin-right:6px" [ngClass]="inputV3AuthPass.type === 'password' ? ['glyphicon glyphicon-eye-open text-primary'] : ['glyphicon glyphicon-eye-close text-primary']" passwordToggle [input]="inputV3AuthPass"> </i> - <control-messages [control]="snmpdevForm.controls.V3AuthPass"></control-messages> + <div class="form-group" *ngIf="snmpdevForm.value.SnmpVersion != '1' "> + <label class="control-label col-sm-2" for="MaxRepetitions">MaxRepetitions</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" + tooltip="Set the MaxRepetitions value for BULKWALK SNMP Queries (valid ranges is 1-255) default 50"></i> + <div class="col-sm-9"> + <input formControlName="MaxRepetitions" id="MaxRepetitions" [ngModel]="snmpdevForm.value.MaxRepetitions"/> + <control-messages [control]="snmpdevForm.controls.MaxRepetitions"></control-messages> + </div> </div> - </div> - <div class="form-group" *ngIf="snmpdevForm.controls.V3AuthProt"> - <label class="control-label col-sm-2" for="V3AuthProt">V3AuthProt</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Authentication protocol"></i> - <div class="col-sm-9"> - <select formControlName="V3AuthProt" id="V3AuthProt" [ngModel]="snmpdevForm.value.V3AuthProt"> - <option value="MD5">MD5</option> - <option value="SHA">SHA</option> - </select> - <control-messages [control]="snmpdevForm.controls.V3AuthProt"></control-messages> + <div class="form-group" *ngIf="snmpdevForm.value.SnmpVersion != '3' && snmpdevForm.controls.Community"> + <label class="control-label col-sm-2" for="Community">Community</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="Community for authentication"></i> + <div class="col-sm-9"> + <input #Community formControlName="Community" id="Community" type="password" + [ngModel]="snmpdevForm.value.Community"/> + <i style="margin-left:-25px; margin-right:6px" + [ngClass]="Community.type === 'password' ? ['glyphicon glyphicon-eye-open text-primary'] : ['glyphicon glyphicon-eye-close text-primary']" + passwordToggle [input]="Community"> </i> + <control-messages [control]="snmpdevForm.controls.Community"></control-messages> + </div> </div> - </div> - <div class="form-group" *ngIf="snmpdevForm.controls.V3PrivPass"> - <label class="control-label col-sm-2" for="V3PrivPass">V3PrivPass</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Privacy password"></i> - <div class="col-sm-9"> - <input #inputV3PrivPass formControlName="V3PrivPass" id="V3PrivPass" type="password" [ngModel]="snmpdevForm.value.V3PrivPass"/> - <i style="margin-left:-25px; margin-right:6px" [ngClass]="inputV3PrivPass.type === 'password' ? ['glyphicon glyphicon-eye-open text-primary'] : ['glyphicon glyphicon-eye-close text-primary']" passwordToggle [input]="inputV3PrivPass"> </i> - <control-messages [control]="snmpdevForm.controls.V3PrivPass"></control-messages> - </div> - </div> + <div *ngIf="snmpdevForm.value.SnmpVersion == 3"> + <div class="form-group" *ngIf="snmpdevForm.controls.V3SecLevel"> + <label class="control-label col-sm-2" for="V3SecLevel">V3SecLevel</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="Authentification security request mode"></i> + <div class="col-sm-9"> + <select formControlName="V3SecLevel" id="V3SecLevel" + (click)="setDynamicFields(snmpdevForm.value.V3SecLevel)" + [ngModel]="snmpdevForm.value.V3SecLevel"> + <option value="NoAuthNoPriv">NoAuthNoPriv</option> + <option value="AuthNoPriv">AuthNoPriv</option> + <option value="AuthPriv">AuthPriv</option> + </select> + <control-messages [control]="snmpdevForm.controls.V3SecLevel"></control-messages> + </div> + </div> - <div class="form-group" *ngIf="snmpdevForm.controls.V3PrivProt"> - <label class="control-label col-sm-2" for="V3PrivProt">V3PrivProt</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Privacy Protocol"></i> - <div class="col-sm-9"> - <select formControlName="V3PrivProt" id="V3PrivProt" [ngModel]="snmpdevForm.value.V3PrivProt"> - <option value="AES">AES</option> - <option value="DES">DES</option> - </select> - <control-messages [control]="snmpdevForm.controls.V3PrivProt"></control-messages> - </div> - </div> + <div class="form-group" *ngIf="snmpdevForm.controls.V3AuthUser"> + <label class="control-label col-sm-2" for="V3AuthUser">V3AuthUser</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="Authentication user"></i> + <div class="col-sm-9"> + <input formControlName="V3AuthUser" id="V3AuthUser" [ngModel]="snmpdevForm.value.V3AuthUser"/> + <control-messages [control]="snmpdevForm.controls.V3AuthUser"></control-messages> + </div> + </div> - <div class="form-group" *ngIf="snmpdevForm.controls.V3ContextEngineID"> - <label class="control-label col-sm-2" for="V3ContextEngineID">V3ContextEngineID</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="SNMPV3 ContextEngineID in ScopedPDU (equivalent to the net-snmp -E paramenter)"></i> - <div class="col-sm-9"> - <input formControlName="V3ContextEngineID" id="V3ContextEngineID" [ngModel]="snmpdevForm.value.V3ContextEngineID" /> - <control-messages [control]="snmpdevForm.controls.V3ContextEngineID"></control-messages> - </div> - </div> + <div class="form-group" *ngIf="snmpdevForm.controls.V3AuthPass"> + <label class="control-label col-sm-2" for="V3AuthPass">V3AuthPass</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="Authentication password"></i> + <div class="col-sm-9"> + <input #inputV3AuthPass formControlName="V3AuthPass" id="V3AuthPass" type="password" + [ngModel]="snmpdevForm.value.V3AuthPass"/> + <i style="margin-left:-25px; margin-right:6px" + [ngClass]="inputV3AuthPass.type === 'password' ? ['glyphicon glyphicon-eye-open text-primary'] : ['glyphicon glyphicon-eye-close text-primary']" + passwordToggle [input]="inputV3AuthPass"> </i> + <control-messages [control]="snmpdevForm.controls.V3AuthPass"></control-messages> + </div> + </div> + + <div class="form-group" *ngIf="snmpdevForm.controls.V3AuthProt"> + <label class="control-label col-sm-2" for="V3AuthProt">V3AuthProt</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="Authentication protocol"></i> + <div class="col-sm-9"> + <select formControlName="V3AuthProt" id="V3AuthProt" [ngModel]="snmpdevForm.value.V3AuthProt"> + <option value="MD5">MD5</option> + <option value="SHA">SHA</option> + </select> + <control-messages [control]="snmpdevForm.controls.V3AuthProt"></control-messages> + </div> + </div> + + <div class="form-group" *ngIf="snmpdevForm.controls.V3PrivPass"> + <label class="control-label col-sm-2" for="V3PrivPass">V3PrivPass</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="Privacy password"></i> + <div class="col-sm-9"> + <input #inputV3PrivPass formControlName="V3PrivPass" id="V3PrivPass" type="password" + [ngModel]="snmpdevForm.value.V3PrivPass"/> + <i style="margin-left:-25px; margin-right:6px" + [ngClass]="inputV3PrivPass.type === 'password' ? ['glyphicon glyphicon-eye-open text-primary'] : ['glyphicon glyphicon-eye-close text-primary']" + passwordToggle [input]="inputV3PrivPass"> </i> + <control-messages [control]="snmpdevForm.controls.V3PrivPass"></control-messages> + </div> + </div> + + <div class="form-group" *ngIf="snmpdevForm.controls.V3PrivProt"> + <label class="control-label col-sm-2" for="V3PrivProt">V3PrivProt</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="Privacy Protocol"></i> + <div class="col-sm-9"> + <select formControlName="V3PrivProt" id="V3PrivProt" [ngModel]="snmpdevForm.value.V3PrivProt"> + <option value="AES">AES</option> + <option value="DES">DES</option> + </select> + <control-messages [control]="snmpdevForm.controls.V3PrivProt"></control-messages> + </div> + </div> + + <div class="form-group" *ngIf="snmpdevForm.controls.V3ContextEngineID"> + <label class="control-label col-sm-2" for="V3ContextEngineID">V3ContextEngineID</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" + tooltip="SNMPV3 ContextEngineID in ScopedPDU (equivalent to the net-snmp -E paramenter)"></i> + <div class="col-sm-9"> + <input formControlName="V3ContextEngineID" id="V3ContextEngineID" + [ngModel]="snmpdevForm.value.V3ContextEngineID"/> + <control-messages [control]="snmpdevForm.controls.V3ContextEngineID"></control-messages> + </div> + </div> - <div class="form-group" *ngIf="snmpdevForm.controls.V3ContextName"> - <label class="control-label col-sm-2" for="V3ContextName">V3ContextName</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="SNMPV3 ContextName in ScopedPDU ( equivalent to the net-snmp -n parameter)"></i> - <div class="col-sm-9"> - <input formControlName="V3ContextName" id="V3ContextName" [ngModel]="snmpdevForm.value.V3ContextName" /> - <control-messages [control]="snmpdevForm.controls.V3ContextName"></control-messages> + <div class="form-group" *ngIf="snmpdevForm.controls.V3ContextName"> + <label class="control-label col-sm-2" for="V3ContextName">V3ContextName</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" + tooltip="SNMPV3 ContextName in ScopedPDU ( equivalent to the net-snmp -n parameter)"></i> + <div class="col-sm-9"> + <input formControlName="V3ContextName" id="V3ContextName" [ngModel]="snmpdevForm.value.V3ContextName"/> + <control-messages [control]="snmpdevForm.controls.V3ContextName"></control-messages> + </div> + </div> </div> - </div> - </div> - <div class="form-group"> - <label class="control-label col-sm-2" for="Freq">Freq</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Frequency of snmp data polling (in seconds)"></i> - <div class="col-sm-9"> - <input formControlName="Freq" id="Freq" [ngModel]="snmpdevForm.value.Freq" /> - <control-messages [control]="snmpdevForm.controls.Freq"></control-messages> - </div> - </div> + <div class="form-group"> + <label class="control-label col-sm-2" for="Freq">Freq</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="Frequency of snmp data polling (in seconds)"></i> + <div class="col-sm-9"> + <input formControlName="Freq" id="Freq" [ngModel]="snmpdevForm.value.Freq"/> + <control-messages [control]="snmpdevForm.controls.Freq"></control-messages> + </div> + </div> - <div class="form-group"> - <label class="control-label col-sm-2" for="UpdateFltFreq">UpdateFltFreq</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Number of snmp data gather cycles the collector will take to update indexes and filters on (indexed/snmptable) measurements of snmp data polling (time will be this number*freq seconds) <br> Set this valie to -1 to disable indexes and filters updates "></i> - <div class="col-sm-9"> - <input formControlName="UpdateFltFreq" id="UpdateFltFreq" [ngModel]="snmpdevForm.value.UpdateFltFreq" /> - <control-messages [control]="snmpdevForm.controls.UpdateFltFreq"></control-messages> - </div> - </div> + <div class="form-group"> + <label class="control-label col-sm-2" for="UpdateFltFreq">UpdateFltFreq</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" + tooltip="Number of snmp data gather cycles the collector will take to update indexes and filters on (indexed/snmptable) measurements of snmp data polling (time will be this number*freq seconds) <br> Set this valie to -1 to disable indexes and filters updates "></i> + <div class="col-sm-9"> + <input formControlName="UpdateFltFreq" id="UpdateFltFreq" [ngModel]="snmpdevForm.value.UpdateFltFreq"/> + <control-messages [control]="snmpdevForm.controls.UpdateFltFreq"></control-messages> + </div> + </div> - <div class="form-group"> - <label class="control-label col-sm-2" for="ConcurrentGather">ConcurrentGather</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Open a new snmp connection for each measurement and send concurrent queries over the device "></i> - <div class="col-sm-9"> - <select formControlName="ConcurrentGather" id="ConcurrentGather" [ngModel]="snmpdevForm.value.ConcurrentGather"> - <option value="true">True</option> - <option value="false">False</option> - </select> - <control-messages [control]="snmpdevForm.controls.ConcurrentGather"></control-messages> + <div class="form-group"> + <label class="control-label col-sm-2" for="ConcurrentGather">ConcurrentGather</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" + tooltip="Open a new snmp connection for each measurement and send concurrent queries over the device "></i> + <div class="col-sm-9"> + <select formControlName="ConcurrentGather" id="ConcurrentGather" + [ngModel]="snmpdevForm.value.ConcurrentGather"> + <option value="true">True</option> + <option value="false">False</option> + </select> + <control-messages [control]="snmpdevForm.controls.ConcurrentGather"></control-messages> + </div> + </div> </div> - </div> - </div> - <div class="well well-sm"> + <div class="well well-sm"> <span class="editsection"> Data Settings </span> - <div class="form-group" style="margin-top: 25px"> - <label class="control-label col-sm-2" for="OutDB">InfluxDB Server</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="InfluxDB server"></i> - <div class="col-sm-9"> - <ss-multiselect-dropdown [options]="selectinfluxservers" formControlName="OutDB" [texts]="myTexts" [settings]="mySettingsInflux" [ngModel]="snmpdevForm.value.OutDB"></ss-multiselect-dropdown> - <control-messages [control]="snmpdevForm.controls.OutDB"></control-messages> - </div> - </div> + <div class="form-group" style="margin-top: 25px"> + <label class="control-label col-sm-2" for="OutDB">InfluxDB Server</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="InfluxDB server"></i> + <div class="col-sm-9"> + <ss-multiselect-dropdown [options]="selectinfluxservers" formControlName="OutDB" [texts]="myTexts" + [settings]="mySettingsInflux" + [ngModel]="snmpdevForm.value.OutDB"></ss-multiselect-dropdown> + <control-messages [control]="snmpdevForm.controls.OutDB"></control-messages> + </div> + </div> - <div class="form-group"> - <label class="control-label col-sm-2" for="DeviceTagName">Device Tag Name</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Tag's value to identify type of device in InfluxDB"></i> - <div class="col-sm-9"> - <input formControlName="DeviceTagName" id="DeviceTagName" placeholder="device, host, switch..." [ngModel]="snmpdevForm.value.DeviceTagName"/> - <control-messages [control]="snmpdevForm.controls.DeviceTagName"></control-messages> - </div> - </div> + <div class="form-group"> + <label class="control-label col-sm-2" for="DeviceTagName">Device Tag Name</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="Tag's value to identify type of device in InfluxDB"></i> + <div class="col-sm-9"> + <input formControlName="DeviceTagName" id="DeviceTagName" placeholder="device, host, switch..." + [ngModel]="snmpdevForm.value.DeviceTagName"/> + <control-messages [control]="snmpdevForm.controls.DeviceTagName"></control-messages> + </div> + </div> - <div class="form-group"> - <label class="control-label col-sm-2" for="DeviceTagValue">Device Tag Value</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Tag's value to identify the device in InfluxDB"></i> - <div class="col-sm-9"> - <select formControlName="DeviceTagValue" id="DeviceTagValue" [ngModel]="snmpdevForm.value.DeviceTagValue"> - <option selected="selected" value="id">Id - {{snmpdevForm.controls.ID.value}}</option> - <option value="host">Host - {{snmpdevForm.controls.Host.value}}</option> - </select> - <control-messages [control]="snmpdevForm.controls.DeviceTagValue"></control-messages> - </div> - </div> + <div class="form-group"> + <label class="control-label col-sm-2" for="DeviceTagValue">Device Tag Value</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="Tag's value to identify the device in InfluxDB"></i> + <div class="col-sm-9"> + <select formControlName="DeviceTagValue" id="DeviceTagValue" [ngModel]="snmpdevForm.value.DeviceTagValue"> + <option selected="selected" value="id">Id - {{snmpdevForm.controls.ID.value}}</option> + <option value="host">Host - {{snmpdevForm.controls.Host.value}}</option> + </select> + <control-messages [control]="snmpdevForm.controls.DeviceTagValue"></control-messages> + </div> + </div> - <div class="form-group"> - <label class="control-label col-sm-2" for="ExtraTags">ExtraTags</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Tag's value to identify the device in InfluxDB"></i> - <div class="col-sm-9"> - <input formControlName="ExtraTags" id="ExtraTags" [ngModel]="snmpdevForm.value.ExtraTags" /> - <control-messages [control]="snmpdevForm.controls.ExtraTags"></control-messages> - </div> - </div> - <div class="form-group"> - <label class="control-label col-sm-2" for="DeviceVars">Override Device Vars</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="List of var catalog to override the default set value"></i> - <div class="col-sm-9"> - <div> - <ss-multiselect-dropdown [options]="selectvarcatalogs" [texts]="myTexts" [settings]="mySettings" [(ngModel)]="selectedVars" [ngModelOptions]="{standalone: true}" (ngModelChange)="onChangevarsArray($event,test)"></ss-multiselect-dropdown> - <control-messages [control]="snmpdevForm.controls.DeviceVars"></control-messages> + <div class="form-group"> + <label class="control-label col-sm-2" for="ExtraTags">ExtraTags</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="Tag's value to identify the device in InfluxDB"></i> + <div class="col-sm-9"> + <input formControlName="ExtraTags" id="ExtraTags" [ngModel]="snmpdevForm.value.ExtraTags"/> + <control-messages [control]="snmpdevForm.controls.ExtraTags"></control-messages> + </div> </div> - </div> - </div> - <div class="form-group" *ngIf="varsArray.length > 0"> - <label class="control-label col-sm-2" for="Report">Vars</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Set the value to override the default one"></i> - <div class="col-sm-9"> - <div class="input-group list-group"> - <div *ngFor="let varSingle of varsArray; let i = index"> - <div class="input-group" style="background: none"> - <div class="input-group-addon"> - <span>{{varSingle.ID}}</span> + <div class="form-group"> + <label class="control-label col-sm-2" for="DeviceVars">Override Device Vars</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="List of var catalog to override the default set value"></i> + <div class="col-sm-9"> + <div> + <ss-multiselect-dropdown [options]="selectvarcatalogs" [texts]="myTexts" [settings]="mySettings" + [(ngModel)]="selectedVars" [ngModelOptions]="{standalone: true}" + (ngModelChange)="onChangevarsArray($event,test)"></ss-multiselect-dropdown> + <control-messages [control]="snmpdevForm.controls.DeviceVars"></control-messages> + </div> + </div> + </div> + <div class="form-group" *ngIf="varsArray.length > 0"> + <label class="control-label col-sm-2" for="Report">Vars</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="Set the value to override the default one"></i> + <div class="col-sm-9"> + <div class="input-group list-group"> + <div *ngFor="let varSingle of varsArray; let i = index"> + <div class="input-group" style="background: none"> + <div class="input-group-addon"> + <span>{{varSingle.ID}}</span> + </div> + <input #mytem [(ngModel)]="varsArray[i].value" [ngModelOptions]="{standalone: true}"/> </div> - <input #mytem [(ngModel)]="varsArray[i].value" [ngModelOptions]="{standalone: true}"/> + </div> </div> </div> </div> - </div> - </div> - <div class="form-group"> - <label class="control-label col-sm-2" for="MeasurementGroups">Measurement Groups</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Selection of Measurent Groups associated with the device {{snmpdevForm.value.id}}"></i> - <div class="col-sm-9"> - <ss-multiselect-dropdown [options]="selectgroups" formControlName="MeasurementGroups" [texts]="myTexts" [settings]="mySettings" [ngModel]="snmpdevForm.value.MeasurementGroups"></ss-multiselect-dropdown> - <control-messages [control]="snmpdevForm.controls.MeasurementGroups"></control-messages> - </div> - </div> + <div class="form-group"> + <label class="control-label col-sm-2" for="MeasurementGroups">Measurement Groups</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" + tooltip="Selection of Measurent Groups associated with the device {{snmpdevForm.value.id}}"></i> + <div class="col-sm-9"> + <ss-multiselect-dropdown [options]="selectgroups" formControlName="MeasurementGroups" [texts]="myTexts" + [settings]="mySettings" + [ngModel]="snmpdevForm.value.MeasurementGroups"></ss-multiselect-dropdown> + <control-messages [control]="snmpdevForm.controls.MeasurementGroups"></control-messages> + </div> + </div> - <div class="form-group"> - <label class="control-label col-sm-2" for="MeasFilters">Measurement Filters</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Selection of filters to use with the device {{snmpdevForm.value.id}}"></i> - <div class="col-sm-9"> - <ss-multiselect-dropdown [options]="selectfilters" formControlName="MeasFilters" [texts]="myTexts" [settings]="mySettings" [ngModel]="snmpdevForm.value.MeasFilters"></ss-multiselect-dropdown> - <control-messages [control]="snmpdevForm.controls.MeasFilters"></control-messages> + <div class="form-group"> + <label class="control-label col-sm-2" for="MeasFilters">Measurement Filters</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" + tooltip="Selection of filters to use with the device {{snmpdevForm.value.id}}"></i> + <div class="col-sm-9"> + <ss-multiselect-dropdown [options]="selectfilters" formControlName="MeasFilters" [texts]="myTexts" + [settings]="mySettings" + [ngModel]="snmpdevForm.value.MeasFilters"></ss-multiselect-dropdown> + <control-messages [control]="snmpdevForm.controls.MeasFilters"></control-messages> + </div> + </div> </div> - </div> - </div> - <div class="well well-sm"> + <div class="well well-sm"> + <span class="editsection"> Extra Settings </span> - <div class="form-group" style="margin-top: 25px"> - <label class="control-label col-sm-2" for="Description">Description</label> - <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" tooltipAnimation="true" tooltip="Description of the SNMP Device"></i> - <div class="col-sm-9"> - <textarea class="form-control" style="width: 50%" rows="2" formControlName="Description" id="Description" [ngModel]="snmpdevForm.value.Description"> </textarea> - <control-messages [control]="snmpdevForm.controls.Description"></control-messages> + <div class="form-group" style="margin-top: 25px"> + <label class="control-label col-sm-2" for="Description">Description</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="Description of the SNMP Device"></i> + <div class="col-sm-9"> + <textarea class="form-control" style="width: 50%" rows="2" formControlName="Description" id="Description" + [ngModel]="snmpdevForm.value.Description"> </textarea> + <control-messages [control]="snmpdevForm.controls.Description"></control-messages> + </div> + </div> + + + </div> + + + <div class="well well-sm"> + + <span class="editsection"> + Device Filters + </span> + + + <div class="form-group" style="margin-top: 25px"> + <label class="control-label col-sm-2" for="ExtraTags">Interface Filter</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="Semicolon ; separated regex which can be used, that only specific interfaces should be used"></i> + <div class="col-sm-9"> + <textarea rows="4" style="width: 100%" class="form-control" formControlName="SpecificInterfaceFilters" id="SpecificInterfaceFilters" [ngModel]="snmpdevForm.value.SpecificInterfaceFilters"> </textarea> + <control-messages [control]="snmpdevForm.controls.SpecificInterfaceFilters"></control-messages> + </div> + </div> + + <div class="form-group"> + <label class="control-label col-sm-2" for="ExtraTags">Metric Filter</label> + <i placement="top" style="float: left" class="info control-label glyphicon glyphicon-info-sign" + tooltipAnimation="true" tooltip="Semicolon ; separated regex which can be used, that only specific metric (name field) should be used"></i> + <div class="col-sm-9"> + <textarea rows="4" style="width: 100%" class="form-control" formControlName="SpecificMetricFilters" id="SpecificMetricFilters" [ngModel]="snmpdevForm.value.SpecificMetricFilters"> </textarea> + <control-messages [control]="snmpdevForm.controls.SpecificMetricFilters"></control-messages> + </div> + </div> + + </div> + + </div> - </div> - </div> </form> </ng-template> </ng-container> diff --git a/src/tsconfig.json b/src/tsconfig.json index 1cf713a3..0d0f07cf 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -4,6 +4,7 @@ "declaration": false, "emitDecoratorMetadata": true, "experimentalDecorators": true, + "skipLibCheck": true, "lib": ["es6", "dom"], "mapRoot": "./", "module": "es6", diff --git a/vendor/github.com/soniah/gosnmp/walk.go b/vendor/github.com/soniah/gosnmp/walk.go index 2a363eb2..d84b0459 100644 --- a/vendor/github.com/soniah/gosnmp/walk.go +++ b/vendor/github.com/soniah/gosnmp/walk.go @@ -73,6 +73,8 @@ RequestLoop: } break RequestLoop } + + if v.Name == oid { return fmt.Errorf("OID not increasing: %s", v.Name) } diff --git a/vendor/github.com/spf13/viper/nohup.out b/vendor/github.com/spf13/viper/nohup.out deleted file mode 100644 index 8973bf27..00000000 --- a/vendor/github.com/spf13/viper/nohup.out +++ /dev/null @@ -1 +0,0 @@ -QProcess::start: Process is already running