From 36cda2c2315ed96a7d200402120ae1d94bdaf8a4 Mon Sep 17 00:00:00 2001 From: chrishaylesnortal Date: Wed, 18 Mar 2020 11:48:00 -0700 Subject: [PATCH 01/12] Added placeholder and common files for opcua input. --- plugins/inputs/all/all.go | 1 + plugins/inputs/opcua/opcua.go | 3 + plugins/inputs/opcua/opcua_funcs.go | 409 ++++++++++++++++++++++++++++ plugins/inputs/opcua/utils.go | 138 ++++++++++ 4 files changed, 551 insertions(+) create mode 100644 plugins/inputs/opcua/opcua.go create mode 100644 plugins/inputs/opcua/opcua_funcs.go create mode 100644 plugins/inputs/opcua/utils.go diff --git a/plugins/inputs/all/all.go b/plugins/inputs/all/all.go index 4b38b88f7a53e..ba058be1c8132 100644 --- a/plugins/inputs/all/all.go +++ b/plugins/inputs/all/all.go @@ -116,6 +116,7 @@ import ( _ "github.com/influxdata/telegraf/plugins/inputs/nstat" _ "github.com/influxdata/telegraf/plugins/inputs/ntpq" _ "github.com/influxdata/telegraf/plugins/inputs/nvidia_smi" + _ "github.com/influxdata/telegraf/plugins/inputs/opcua" _ "github.com/influxdata/telegraf/plugins/inputs/openldap" _ "github.com/influxdata/telegraf/plugins/inputs/openntpd" _ "github.com/influxdata/telegraf/plugins/inputs/opensmtpd" diff --git a/plugins/inputs/opcua/opcua.go b/plugins/inputs/opcua/opcua.go new file mode 100644 index 0000000000000..d20d4ea8b9b33 --- /dev/null +++ b/plugins/inputs/opcua/opcua.go @@ -0,0 +1,3 @@ +package opcua + +// placeholder diff --git a/plugins/inputs/opcua/opcua_funcs.go b/plugins/inputs/opcua/opcua_funcs.go new file mode 100644 index 0000000000000..595f99ba9bbe6 --- /dev/null +++ b/plugins/inputs/opcua/opcua_funcs.go @@ -0,0 +1,409 @@ +package opcua + +import ( + "context" + "flag" + "fmt" + "log" + "time" + + "github.com/gopcua/opcua" + "github.com/gopcua/opcua/debug" + "github.com/gopcua/opcua/ua" +) + +// READ VALUE + +func readValue(address, node string) { + var ( + endpoint = flag.String("endpoint", address, "OPC UA Endpoint URL") + nodeID = flag.String("node", node, "NodeID to read") + policy = flag.String("sec-policy", "Basic256Sha256", "Security Policy URL or one of None, Basic128Rsa15, Basic256, Basic256Sha256") + mode = flag.String("sec-mode", "SignAndEncrypt", "Security Mode: one of None, Sign, SignAndEncrypt") + certFile = flag.String("cert", "./trusted/cert.pem", "Path to ./trusted/cert.pem. Required for security mode/policy != None") + keyFile = flag.String("key", "./trusted/key.pem", "Path to private ./trusted/key.pem. Required for security mode/policy != None") + ) + flag.BoolVar(&debug.Enable, "debug", false, "enable debug logging") + flag.Parse() + log.SetFlags(0) + + ctx := context.Background() + + endpoints, err := opcua.GetEndpoints(*endpoint) + if err != nil { + log.Fatal(err) + } + + log.Print("\nCalled function: opcua.GetEndpoints()\n") + + ep := opcua.SelectEndpoint(endpoints, *policy, ua.MessageSecurityModeFromString(*mode)) + if ep == nil { + log.Fatal("Failed to find suitable endpoint") + } + + log.Print("\nCalled function: opcua.SelectEndpoint()\n") + + log.Printf("\nENDPOINTS: \n%s", endpoints) + + opts := []opcua.Option{ + opcua.SecurityPolicy(*policy), + opcua.SecurityModeString(*mode), + opcua.CertificateFile(*certFile), + opcua.PrivateKeyFile(*keyFile), + opcua.AuthAnonymous(), + opcua.SecurityFromEndpoint(ep, ua.UserTokenTypeAnonymous), + } + + c := opcua.NewClient(*endpoint, opts...) + if err := c.Connect(ctx); err != nil { + log.Fatal(err) + } + defer c.Close() + + log.Print("\nCalled function: opcua.NewClient()\n") + + id, err := ua.ParseNodeID(*nodeID) + if err != nil { + log.Fatalf("invalid node id: %v", err) + } + + log.Print("\nCalled function: ua.ParseNodeID()\n") + + req := &ua.ReadRequest{ + MaxAge: 2000, + NodesToRead: []*ua.ReadValueID{ + &ua.ReadValueID{NodeID: id}, + }, + TimestampsToReturn: ua.TimestampsToReturnBoth, + } + + log.Print("\nCalled function: ua.ReadRequest()\n") + + resp, err := c.Read(req) + if err != nil { + log.Fatalf("Read failed: %s", err) + } + if resp.Results[0].Status != ua.StatusOK { + log.Fatalf("Status not OK: %v", resp.Results[0].Status) + } + log.Printf("%#v", resp.Results[0].Value.Value()) +} + +// WRITE + +func write(address, node string, newValue interface{}) { + var ( + endpoint = flag.String("endpoint", address, "OPC UA Endpoint URL") + nodeID = flag.String("node", node, "NodeID to read") + //policy = flag.String("policy", "Basic256Sha256", "Security policy: None, Basic128Rsa15, Basic256, Basic256Sha256. Default: auto") + //mode = flag.String("mode", "SignAndEncrypt", "Security mode: None, Sign, SignAndEncrypt. Default: auto") + //certFile = flag.String("cert", "./trusted/cert.pem", "Path to cert.pem. Required for security mode/policy != None") + //keyFile = flag.String("key", "./trusted/key.pem", "Path to private key.pem. Required for security mode/policy != None") + policy = flag.String("policy", "None", "Security policy: None, Basic128Rsa15, Basic256, Basic256Sha256. Default: auto") + mode = flag.String("mode", "None", "Security mode: None, Sign, SignAndEncrypt. Default: auto") + ) + flag.BoolVar(&debug.Enable, "debug", false, "enable debug logging") + flag.Parse() + log.SetFlags(0) + + ctx := context.Background() + + endpoints, err := opcua.GetEndpoints(*endpoint) + if err != nil { + log.Fatal(err) + } + ep := opcua.SelectEndpoint(endpoints, *policy, ua.MessageSecurityModeFromString(*mode)) + if ep == nil { + log.Fatal("Failed to find suitable endpoint") + } + + opts := []opcua.Option{ + opcua.SecurityPolicy(*policy), + opcua.SecurityModeString(*mode), + //opcua.CertificateFile(*certFile), + //opcua.PrivateKeyFile(*keyFile), + opcua.AuthAnonymous(), + opcua.SecurityFromEndpoint(ep, ua.UserTokenTypeAnonymous), + } + + c := opcua.NewClient(*endpoint, opts...) + if err := c.Connect(ctx); err != nil { + log.Fatal(err) + } + defer c.Close() + + id, err := ua.ParseNodeID(*nodeID) + if err != nil { + log.Fatalf("invalid node id: %v", err) + } + + v, err := ua.NewVariant(newValue) + if err != nil { + log.Fatalf("invalid value: %v", err) + } + + req := &ua.WriteRequest{ + NodesToWrite: []*ua.WriteValue{ + &ua.WriteValue{ + NodeID: id, + AttributeID: ua.AttributeIDValue, + Value: &ua.DataValue{ + EncodingMask: ua.DataValueValue, + Value: v, + }, + }, + }, + } + + resp, err := c.Write(req) + if err != nil { + log.Fatalf("Read failed: %s", err) + } + log.Printf("%v", resp.Results[0]) +} + +// SUBSCRIBE + +func subscribe(address, node string) { + var ( + endpoint = flag.String("endpoint", address, "OPC UA Endpoint URL") + policy = flag.String("policy", "Basic256Sha256", "Security policy: None, Basic128Rsa15, Basic256, Basic256Sha256. Default: auto") + mode = flag.String("mode", "SignAndEncrypt", "Security mode: None, Sign, SignAndEncrypt. Default: auto") + certFile = flag.String("cert", "./trusted/cert.pem", "Path to cert.pem. Required for security mode/policy != None") + keyFile = flag.String("key", "./trusted/key.pem", "Path to private key.pem. Required for security mode/policy != None") + nodeID = flag.String("node", node, "node id to subscribe to") + interval = flag.String("interval", opcua.DefaultSubscriptionInterval.String(), "subscription interval") + ) + flag.BoolVar(&debug.Enable, "debug", false, "enable debug logging") + flag.Parse() + log.SetFlags(0) + + subInterval, err := time.ParseDuration(*interval) + if err != nil { + log.Fatal(err) + } + + // add an arbitrary timeout to demonstrate how to stop a subscription + // with a context. + //d := 30 * time.Second + //ctx, cancel := context.WithTimeout(context.Background(), d) + ctx := context.Background() + //defer cancel() + + log.Print("\nCalled function: opcua.GetEndpoints()\n") + + endpoints, err := opcua.GetEndpoints(*endpoint) + if err != nil { + log.Fatal(err) + } + ep := opcua.SelectEndpoint(endpoints, *policy, ua.MessageSecurityModeFromString(*mode)) + if ep == nil { + log.Fatal("Failed to find suitable endpoint") + } + + log.Print("\nCalled function: opcua.SelectEndpoint()\n") + + log.Printf("\nENDPOINTS: \n%s", endpoints) + + fmt.Println("*", ep.SecurityPolicyURI, ep.SecurityMode) + + opts := []opcua.Option{ + opcua.SecurityPolicy(*policy), + opcua.SecurityModeString(*mode), + opcua.CertificateFile(*certFile), + opcua.PrivateKeyFile(*keyFile), + opcua.AuthAnonymous(), + opcua.SecurityFromEndpoint(ep, ua.UserTokenTypeAnonymous), + } + + c := opcua.NewClient(*endpoint, opts...) + if err := c.Connect(ctx); err != nil { + log.Fatal(err) + } + defer c.Close() + + log.Print("\nCalled function: opcua.NewClient()\n") + + notifyCh := make(chan *opcua.PublishNotificationData) + + sub, err := c.Subscribe(&opcua.SubscriptionParameters{ + Interval: subInterval, //500 * time.Millisecond, + }, notifyCh) + + log.Print("\nCalled function: c.Subscribe()\n") + + if err != nil { + log.Fatal(err) + } + defer sub.Cancel() + log.Printf("Created subscription with id %v", sub.SubscriptionID) + + id, err := ua.ParseNodeID(*nodeID) + if err != nil { + log.Fatal(err) + } + + log.Print("\nCalled function: ua.ParseNodeID()\n") + + // arbitrary client handle for the monitoring item + handle := uint32(42) + miCreateRequest := opcua.NewMonitoredItemCreateRequestWithDefaults(id, ua.AttributeIDValue, handle) + res, err := sub.Monitor(ua.TimestampsToReturnBoth, miCreateRequest) + if err != nil || res.Results[0].StatusCode != ua.StatusOK { + log.Fatal(err) + } + + log.Print("\nCalled function: opcua.NewMonitoredItemCreateRequestWithDefaults()\n") + + go sub.Run(ctx) // start Publish loop + + // read from subscription's notification channel until ctx is cancelled + for { + select { + case <-ctx.Done(): + log.Print("Received ctx.Done()") + return + case res := <-sub.Notifs: + if res.Error != nil { + log.Print(res.Error) + continue + } + + switch x := res.Value.(type) { + case *ua.DataChangeNotification: + for _, item := range x.MonitoredItems { + data := item.Value.Value.Value() + log.Printf("MonitoredItem with client handle %v = %v", item.ClientHandle, data) + } + + default: + log.Printf("what's this publish result? %T", res.Value) + } + } + } +} + +func subscribeMany(address string, nodes map[string]string) { + var ( + endpoint = flag.String("endpoint", address, "OPC UA Endpoint URL") + policy = flag.String("policy", "Basic256Sha256", "Security policy: None, Basic128Rsa15, Basic256, Basic256Sha256. Default: auto") + mode = flag.String("mode", "SignAndEncrypt", "Security mode: None, Sign, SignAndEncrypt. Default: auto") + certFile = flag.String("cert", "./trusted/cert.pem", "Path to cert.pem. Required for security mode/policy != None") + keyFile = flag.String("key", "./trusted/key.pem", "Path to private key.pem. Required for security mode/policy != None") + interval = flag.String("interval", opcua.DefaultSubscriptionInterval.String(), "subscription interval") + //nodeID = flag.String("node", "", "node id to subscribe to") + ) + flag.BoolVar(&debug.Enable, "debug", false, "enable debug logging") + flag.Parse() + log.SetFlags(0) + + subInterval, err := time.ParseDuration(*interval) + if err != nil { + log.Fatal(err) + } + + // add an arbitrary timeout to demonstrate how to stop a subscription + // with a context. + //d := 30 * time.Second + //ctx, cancel := context.WithTimeout(context.Background(), d) + ctx := context.Background() + //defer cancel() + + endpoints, err := opcua.GetEndpoints(*endpoint) + if err != nil { + log.Fatal(err) + } + ep := opcua.SelectEndpoint(endpoints, *policy, ua.MessageSecurityModeFromString(*mode)) + if ep == nil { + log.Fatal("Failed to find suitable endpoint") + } + + fmt.Println("*", ep.SecurityPolicyURI, ep.SecurityMode) + + opts := []opcua.Option{ + opcua.SecurityPolicy(*policy), + opcua.SecurityModeString(*mode), + opcua.CertificateFile(*certFile), + opcua.PrivateKeyFile(*keyFile), + opcua.AuthAnonymous(), + opcua.SecurityFromEndpoint(ep, ua.UserTokenTypeAnonymous), + } + + c := opcua.NewClient(*endpoint, opts...) + if err := c.Connect(ctx); err != nil { + log.Fatal(err) + } + defer c.Close() + + log.Print("\nCalled function: opcua.NewClient()\n") + + notifyCh := make(chan *opcua.PublishNotificationData) + + sub, err := c.Subscribe(&opcua.SubscriptionParameters{ + Interval: subInterval, //500 * time.Millisecond, + }, notifyCh) + + log.Print("\nCalled function: c.Subscribe()\n") + + if err != nil { + log.Fatal(err) + } + defer sub.Cancel() + log.Printf("Created subscription with id %v", sub.SubscriptionID) + + allRequests := []*ua.MonitoredItemCreateRequest{} + handleCounter := uint32(10) + handleLookup := map[uint32]string{} + + for handleName, nodeID := range nodes { + + id, err := ua.ParseNodeID(nodeID) + if err != nil { + log.Fatal(err) + } + + log.Print("\nCalled function: ua.ParseNodeID()\n") + + // arbitrary client handle for the monitoring item + handle := handleCounter + handleLookup[handle] = handleName + miCreateRequest := opcua.NewMonitoredItemCreateRequestWithDefaults(id, ua.AttributeIDValue, handle) + allRequests = append(allRequests, miCreateRequest) + + log.Print("\nCalled function: opcua.NewMonitoredItemCreateRequestWithDefaults()\n") + handleCounter++ + + } + + res, err := sub.Monitor(ua.TimestampsToReturnBoth, allRequests...) + if err != nil || res.Results[0].StatusCode != ua.StatusOK { + log.Fatal(err) + } + + go sub.Run(ctx) // start Publish loop + + // read from subscription's notification channel until ctx is cancelled + for { + select { + case <-ctx.Done(): + log.Print("Received ctx.Done()") + return + case res := <-sub.Notifs: + if res.Error != nil { + log.Print(res.Error) + continue + } + + switch x := res.Value.(type) { + case *ua.DataChangeNotification: + for _, item := range x.MonitoredItems { + data := item.Value.Value.Value() + log.Printf("MonitoredItem %s (%v) = %v", handleLookup[item.ClientHandle], item.ClientHandle, data) + } + + default: + log.Printf("what's this publish result? %T", res.Value) + } + } + } +} diff --git a/plugins/inputs/opcua/utils.go b/plugins/inputs/opcua/utils.go new file mode 100644 index 0000000000000..53e035ed04574 --- /dev/null +++ b/plugins/inputs/opcua/utils.go @@ -0,0 +1,138 @@ +package opcua + +import ( + "crypto/ecdsa" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "io/ioutil" + "log" + "math/big" + "net" + "net/url" + "os" + "strings" + "time" +) + +// SELF SIGNED CERT FUNCTIONS + +func newTempDir() (string, error) { + dir, err := ioutil.TempDir("", "ssc") + return dir, err +} + +func generateCert(host string, rsaBits int, certFile, keyFile string, dur time.Duration) { + + if len(host) == 0 { + log.Fatalf("Missing required host parameter") + } + if rsaBits == 0 { + rsaBits = 2048 + } + if len(certFile) == 0 { + certFile = "./trusted/cert.pem" + } + if len(keyFile) == 0 { + keyFile = "./trusted/key.pem" + } + + priv, err := rsa.GenerateKey(rand.Reader, rsaBits) + if err != nil { + log.Fatalf("failed to generate private key: %s", err) + } + + notBefore := time.Now() + notAfter := notBefore.Add(dur) + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + log.Fatalf("failed to generate serial number: %s", err) + } + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{"Gopcua Test Client"}, + }, + NotBefore: notBefore, + NotAfter: notAfter, + + KeyUsage: x509.KeyUsageContentCommitment | x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageDataEncipherment | x509.KeyUsageCertSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, + BasicConstraintsValid: true, + } + + hosts := strings.Split(host, ",") + for _, h := range hosts { + if ip := net.ParseIP(h); ip != nil { + template.IPAddresses = append(template.IPAddresses, ip) + } else { + template.DNSNames = append(template.DNSNames, h) + } + if uri, err := url.Parse(h); err == nil { + template.URIs = append(template.URIs, uri) + } + } + + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv) + if err != nil { + log.Fatalf("Failed to create certificate: %s", err) + } + + certOut, err := os.Create(certFile) + if err != nil { + log.Fatalf("failed to open %s for writing: %s", certFile, err) + } + if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil { + log.Fatalf("failed to write data to %s: %s", certFile, err) + } + if err := certOut.Close(); err != nil { + log.Fatalf("error closing %s: %s", certFile, err) + } + log.Printf("wrote %s\n", certFile) + + keyOut, err := os.OpenFile(keyFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + log.Printf("failed to open %s for writing: %s", keyFile, err) + return + } + if err := pem.Encode(keyOut, pemBlockForKey(priv)); err != nil { + log.Fatalf("failed to write data to %s: %s", keyFile, err) + } + if err := keyOut.Close(); err != nil { + log.Fatalf("error closing %s: %s", keyFile, err) + } + log.Printf("wrote %s\n", keyFile) +} + +func publicKey(priv interface{}) interface{} { + switch k := priv.(type) { + case *rsa.PrivateKey: + return &k.PublicKey + case *ecdsa.PrivateKey: + return &k.PublicKey + default: + return nil + } +} + +func pemBlockForKey(priv interface{}) *pem.Block { + switch k := priv.(type) { + case *rsa.PrivateKey: + return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)} + case *ecdsa.PrivateKey: + b, err := x509.MarshalECPrivateKey(k) + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err) + os.Exit(2) + } + return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b} + default: + return nil + } +} From 610087b9950af912a001b950b0c37126f7f4dd0f Mon Sep 17 00:00:00 2001 From: Chris Hayles Date: Tue, 18 Aug 2020 23:12:11 -0700 Subject: [PATCH 02/12] added input code --- go.mod | 1 + go.sum | 4 + internal/config/aws/credentials 2.go | 52 +++ internal/config/config_test 2.go | 259 +++++++++++ internal/config/testdata/inline_table 2.toml | 7 + internal/config/testdata/invalid_field 2.toml | 2 + .../testdata/single_plugin_env_vars 2.toml | 11 + internal/config/testdata/slice_comment 2.toml | 5 + .../config/testdata/subconfig/exec 2.conf | 4 + .../testdata/subconfig/memcached 2.conf | 11 + .../config/testdata/subconfig/procstat 2.conf | 2 + .../config/testdata/wrong_field_type 2.toml | 2 + internal/http_go1.12 2.go | 9 + internal/models/log_test 2.go | 70 +++ internal/models/running_input 2.go | 118 +++++ internal/models/running_output 2.go | 241 ++++++++++ internal/models/running_processor 2.go | 99 ++++ internal/models/running_processor_test 2.go | 189 ++++++++ internal/tls/config 2.go | 166 +++++++ metric/builder 2.go | 55 +++ plugins/inputs/opcua/README.md | 57 +++ plugins/inputs/opcua/{opcua.go => _opcua.go} | 0 .../opcua/{opcua_funcs.go => _opcua_funcs.go} | 0 plugins/inputs/opcua/{utils.go => _utils.go} | 0 plugins/inputs/opcua/opcua_client.go | 440 ++++++++++++++++++ plugins/inputs/opcua/opcua_client_test.go | 67 +++ plugins/inputs/opcua/opcua_util.go | 335 +++++++++++++ 27 files changed, 2206 insertions(+) create mode 100644 internal/config/aws/credentials 2.go create mode 100644 internal/config/config_test 2.go create mode 100644 internal/config/testdata/inline_table 2.toml create mode 100644 internal/config/testdata/invalid_field 2.toml create mode 100644 internal/config/testdata/single_plugin_env_vars 2.toml create mode 100644 internal/config/testdata/slice_comment 2.toml create mode 100644 internal/config/testdata/subconfig/exec 2.conf create mode 100644 internal/config/testdata/subconfig/memcached 2.conf create mode 100644 internal/config/testdata/subconfig/procstat 2.conf create mode 100644 internal/config/testdata/wrong_field_type 2.toml create mode 100644 internal/http_go1.12 2.go create mode 100644 internal/models/log_test 2.go create mode 100644 internal/models/running_input 2.go create mode 100644 internal/models/running_output 2.go create mode 100644 internal/models/running_processor 2.go create mode 100644 internal/models/running_processor_test 2.go create mode 100644 internal/tls/config 2.go create mode 100644 metric/builder 2.go create mode 100644 plugins/inputs/opcua/README.md rename plugins/inputs/opcua/{opcua.go => _opcua.go} (100%) rename plugins/inputs/opcua/{opcua_funcs.go => _opcua_funcs.go} (100%) rename plugins/inputs/opcua/{utils.go => _utils.go} (100%) create mode 100644 plugins/inputs/opcua/opcua_client.go create mode 100644 plugins/inputs/opcua/opcua_client_test.go create mode 100644 plugins/inputs/opcua/opcua_util.go diff --git a/go.mod b/go.mod index ab0cfea8238e3..f8ebcb8deda66 100644 --- a/go.mod +++ b/go.mod @@ -62,6 +62,7 @@ require ( github.com/google/go-cmp v0.4.0 github.com/google/go-github v17.0.0+incompatible github.com/google/go-querystring v1.0.0 // indirect + github.com/gopcua/opcua v0.1.11 github.com/gorilla/mux v1.6.2 github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect diff --git a/go.sum b/go.sum index c4ff76cb35d98..77a3f96639e40 100644 --- a/go.sum +++ b/go.sum @@ -292,6 +292,8 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/gopcua/opcua v0.1.11 h1:i7qDkVYRQP5LTiAEnPu09RcPSA1KGPkHsNd/TeDcZc8= +github.com/gopcua/opcua v0.1.11/go.mod h1:O2l+/u0jM6f3WKRbz5L5ep7yNpQX2l5DOagXqXNcDV8= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk= @@ -593,6 +595,7 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72 h1:+ELyKg6m8UBf0nPFSqD0mi7zUfwPyXo23HNjMnXPz7w= @@ -700,6 +703,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456 h1:ng0gs1AKnRRuEMZoTLLlbOd+C17zUDepwGQBb/n+JVg= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/internal/config/aws/credentials 2.go b/internal/config/aws/credentials 2.go new file mode 100644 index 0000000000000..1e4f91b132a3b --- /dev/null +++ b/internal/config/aws/credentials 2.go @@ -0,0 +1,52 @@ +package aws + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/client" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/credentials/stscreds" + "github.com/aws/aws-sdk-go/aws/session" +) + +type CredentialConfig struct { + Region string + AccessKey string + SecretKey string + RoleARN string + Profile string + Filename string + Token string + EndpointURL string +} + +func (c *CredentialConfig) Credentials() client.ConfigProvider { + if c.RoleARN != "" { + return c.assumeCredentials() + } else { + return c.rootCredentials() + } +} + +func (c *CredentialConfig) rootCredentials() client.ConfigProvider { + config := &aws.Config{ + Region: aws.String(c.Region), + Endpoint: &c.EndpointURL, + } + if c.AccessKey != "" || c.SecretKey != "" { + config.Credentials = credentials.NewStaticCredentials(c.AccessKey, c.SecretKey, c.Token) + } else if c.Profile != "" || c.Filename != "" { + config.Credentials = credentials.NewSharedCredentials(c.Filename, c.Profile) + } + + return session.New(config) +} + +func (c *CredentialConfig) assumeCredentials() client.ConfigProvider { + rootCredentials := c.rootCredentials() + config := &aws.Config{ + Region: aws.String(c.Region), + Endpoint: &c.EndpointURL, + } + config.Credentials = stscreds.NewCredentials(rootCredentials, c.RoleARN) + return session.New(config) +} diff --git a/internal/config/config_test 2.go b/internal/config/config_test 2.go new file mode 100644 index 0000000000000..9d42177cd8ad7 --- /dev/null +++ b/internal/config/config_test 2.go @@ -0,0 +1,259 @@ +package config + +import ( + "os" + "testing" + "time" + + "github.com/influxdata/telegraf/internal" + "github.com/influxdata/telegraf/internal/models" + "github.com/influxdata/telegraf/plugins/inputs" + "github.com/influxdata/telegraf/plugins/inputs/exec" + "github.com/influxdata/telegraf/plugins/inputs/http_listener_v2" + "github.com/influxdata/telegraf/plugins/inputs/memcached" + "github.com/influxdata/telegraf/plugins/inputs/procstat" + httpOut "github.com/influxdata/telegraf/plugins/outputs/http" + "github.com/influxdata/telegraf/plugins/parsers" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestConfig_LoadSingleInputWithEnvVars(t *testing.T) { + c := NewConfig() + err := os.Setenv("MY_TEST_SERVER", "192.168.1.1") + assert.NoError(t, err) + err = os.Setenv("TEST_INTERVAL", "10s") + assert.NoError(t, err) + c.LoadConfig("./testdata/single_plugin_env_vars.toml") + + memcached := inputs.Inputs["memcached"]().(*memcached.Memcached) + memcached.Servers = []string{"192.168.1.1"} + + filter := models.Filter{ + NameDrop: []string{"metricname2"}, + NamePass: []string{"metricname1", "ip_192.168.1.1_name"}, + FieldDrop: []string{"other", "stuff"}, + FieldPass: []string{"some", "strings"}, + TagDrop: []models.TagFilter{ + { + Name: "badtag", + Filter: []string{"othertag"}, + }, + }, + TagPass: []models.TagFilter{ + { + Name: "goodtag", + Filter: []string{"mytag"}, + }, + }, + } + assert.NoError(t, filter.Compile()) + mConfig := &models.InputConfig{ + Name: "memcached", + Filter: filter, + Interval: 10 * time.Second, + } + mConfig.Tags = make(map[string]string) + + assert.Equal(t, memcached, c.Inputs[0].Input, + "Testdata did not produce a correct memcached struct.") + assert.Equal(t, mConfig, c.Inputs[0].Config, + "Testdata did not produce correct memcached metadata.") +} + +func TestConfig_LoadSingleInput(t *testing.T) { + c := NewConfig() + c.LoadConfig("./testdata/single_plugin.toml") + + memcached := inputs.Inputs["memcached"]().(*memcached.Memcached) + memcached.Servers = []string{"localhost"} + + filter := models.Filter{ + NameDrop: []string{"metricname2"}, + NamePass: []string{"metricname1"}, + FieldDrop: []string{"other", "stuff"}, + FieldPass: []string{"some", "strings"}, + TagDrop: []models.TagFilter{ + { + Name: "badtag", + Filter: []string{"othertag"}, + }, + }, + TagPass: []models.TagFilter{ + { + Name: "goodtag", + Filter: []string{"mytag"}, + }, + }, + } + assert.NoError(t, filter.Compile()) + mConfig := &models.InputConfig{ + Name: "memcached", + Filter: filter, + Interval: 5 * time.Second, + } + mConfig.Tags = make(map[string]string) + + assert.Equal(t, memcached, c.Inputs[0].Input, + "Testdata did not produce a correct memcached struct.") + assert.Equal(t, mConfig, c.Inputs[0].Config, + "Testdata did not produce correct memcached metadata.") +} + +func TestConfig_LoadDirectory(t *testing.T) { + c := NewConfig() + err := c.LoadConfig("./testdata/single_plugin.toml") + if err != nil { + t.Error(err) + } + err = c.LoadDirectory("./testdata/subconfig") + if err != nil { + t.Error(err) + } + + memcached := inputs.Inputs["memcached"]().(*memcached.Memcached) + memcached.Servers = []string{"localhost"} + + filter := models.Filter{ + NameDrop: []string{"metricname2"}, + NamePass: []string{"metricname1"}, + FieldDrop: []string{"other", "stuff"}, + FieldPass: []string{"some", "strings"}, + TagDrop: []models.TagFilter{ + { + Name: "badtag", + Filter: []string{"othertag"}, + }, + }, + TagPass: []models.TagFilter{ + { + Name: "goodtag", + Filter: []string{"mytag"}, + }, + }, + } + assert.NoError(t, filter.Compile()) + mConfig := &models.InputConfig{ + Name: "memcached", + Filter: filter, + Interval: 5 * time.Second, + } + mConfig.Tags = make(map[string]string) + + assert.Equal(t, memcached, c.Inputs[0].Input, + "Testdata did not produce a correct memcached struct.") + assert.Equal(t, mConfig, c.Inputs[0].Config, + "Testdata did not produce correct memcached metadata.") + + ex := inputs.Inputs["exec"]().(*exec.Exec) + p, err := parsers.NewParser(&parsers.Config{ + MetricName: "exec", + DataFormat: "json", + JSONStrict: true, + }) + assert.NoError(t, err) + ex.SetParser(p) + ex.Command = "/usr/bin/myothercollector --foo=bar" + eConfig := &models.InputConfig{ + Name: "exec", + MeasurementSuffix: "_myothercollector", + } + eConfig.Tags = make(map[string]string) + + exec := c.Inputs[1].Input.(*exec.Exec) + require.NotNil(t, exec.Log) + exec.Log = nil + + assert.Equal(t, ex, c.Inputs[1].Input, + "Merged Testdata did not produce a correct exec struct.") + assert.Equal(t, eConfig, c.Inputs[1].Config, + "Merged Testdata did not produce correct exec metadata.") + + memcached.Servers = []string{"192.168.1.1"} + assert.Equal(t, memcached, c.Inputs[2].Input, + "Testdata did not produce a correct memcached struct.") + assert.Equal(t, mConfig, c.Inputs[2].Config, + "Testdata did not produce correct memcached metadata.") + + pstat := inputs.Inputs["procstat"]().(*procstat.Procstat) + pstat.PidFile = "/var/run/grafana-server.pid" + + pConfig := &models.InputConfig{Name: "procstat"} + pConfig.Tags = make(map[string]string) + + assert.Equal(t, pstat, c.Inputs[3].Input, + "Merged Testdata did not produce a correct procstat struct.") + assert.Equal(t, pConfig, c.Inputs[3].Config, + "Merged Testdata did not produce correct procstat metadata.") +} + +func TestConfig_LoadSpecialTypes(t *testing.T) { + c := NewConfig() + err := c.LoadConfig("./testdata/special_types.toml") + assert.NoError(t, err) + require.Equal(t, 1, len(c.Inputs)) + + inputHTTPListener, ok := c.Inputs[0].Input.(*http_listener_v2.HTTPListenerV2) + assert.Equal(t, true, ok) + // Tests telegraf duration parsing. + assert.Equal(t, internal.Duration{Duration: time.Second}, inputHTTPListener.WriteTimeout) + // Tests telegraf size parsing. + assert.Equal(t, internal.Size{Size: 1024 * 1024}, inputHTTPListener.MaxBodySize) + // Tests toml multiline basic strings. + assert.Equal(t, "/path/to/my/cert\n", inputHTTPListener.TLSCert) +} + +func TestConfig_FieldNotDefined(t *testing.T) { + c := NewConfig() + err := c.LoadConfig("./testdata/invalid_field.toml") + require.Error(t, err, "invalid field name") + assert.Equal(t, "Error parsing ./testdata/invalid_field.toml, line 2: field corresponding to `not_a_field' is not defined in http_listener_v2.HTTPListenerV2", err.Error()) + +} + +func TestConfig_WrongFieldType(t *testing.T) { + c := NewConfig() + err := c.LoadConfig("./testdata/wrong_field_type.toml") + require.Error(t, err, "invalid field type") + assert.Equal(t, "Error parsing ./testdata/wrong_field_type.toml, line 2: (http_listener_v2.HTTPListenerV2.Port) cannot unmarshal TOML string into int", err.Error()) + + c = NewConfig() + err = c.LoadConfig("./testdata/wrong_field_type2.toml") + require.Error(t, err, "invalid field type2") + assert.Equal(t, "Error parsing ./testdata/wrong_field_type2.toml, line 2: (http_listener_v2.HTTPListenerV2.Methods) cannot unmarshal TOML string into []string", err.Error()) +} + +func TestConfig_InlineTables(t *testing.T) { + // #4098 + c := NewConfig() + err := c.LoadConfig("./testdata/inline_table.toml") + assert.NoError(t, err) + require.Equal(t, 2, len(c.Outputs)) + + outputHTTP, ok := c.Outputs[1].Output.(*httpOut.HTTP) + assert.Equal(t, true, ok) + assert.Equal(t, map[string]string{"Authorization": "Token $TOKEN", "Content-Type": "application/json"}, outputHTTP.Headers) + assert.Equal(t, []string{"org_id"}, c.Outputs[0].Config.Filter.TagInclude) +} + +func TestConfig_SliceComment(t *testing.T) { + t.Skipf("Skipping until #3642 is resolved") + + c := NewConfig() + err := c.LoadConfig("./testdata/slice_comment.toml") + assert.NoError(t, err) + require.Equal(t, 1, len(c.Outputs)) + + outputHTTP, ok := c.Outputs[0].Output.(*httpOut.HTTP) + assert.Equal(t, []string{"test"}, outputHTTP.Scopes) + assert.Equal(t, true, ok) +} + +func TestConfig_BadOrdering(t *testing.T) { + // #3444: when not using inline tables, care has to be taken so subsequent configuration + // doesn't become part of the table. This is not a bug, but TOML syntax. + c := NewConfig() + err := c.LoadConfig("./testdata/non_slice_slice.toml") + require.Error(t, err, "bad ordering") + assert.Equal(t, "Error parsing ./testdata/non_slice_slice.toml, line 4: cannot unmarshal TOML array into string (need slice)", err.Error()) +} diff --git a/internal/config/testdata/inline_table 2.toml b/internal/config/testdata/inline_table 2.toml new file mode 100644 index 0000000000000..525fdce17e389 --- /dev/null +++ b/internal/config/testdata/inline_table 2.toml @@ -0,0 +1,7 @@ +[[outputs.http]] + headers = { Authorization = "Token $TOKEN",Content-Type = "application/json" } + taginclude = ["org_id"] + +[[outputs.http]] + headers = { Authorization = "Token $TOKEN",Content-Type = "application/json" } + taginclude = ["org_id"] diff --git a/internal/config/testdata/invalid_field 2.toml b/internal/config/testdata/invalid_field 2.toml new file mode 100644 index 0000000000000..4c718d7bbe998 --- /dev/null +++ b/internal/config/testdata/invalid_field 2.toml @@ -0,0 +1,2 @@ +[[inputs.http_listener_v2]] + not_a_field = true diff --git a/internal/config/testdata/single_plugin_env_vars 2.toml b/internal/config/testdata/single_plugin_env_vars 2.toml new file mode 100644 index 0000000000000..b1f71ea8adb78 --- /dev/null +++ b/internal/config/testdata/single_plugin_env_vars 2.toml @@ -0,0 +1,11 @@ +[[inputs.memcached]] + servers = ["$MY_TEST_SERVER"] + namepass = ["metricname1", "ip_${MY_TEST_SERVER}_name"] + namedrop = ["metricname2"] + fieldpass = ["some", "strings"] + fielddrop = ["other", "stuff"] + interval = "$TEST_INTERVAL" + [inputs.memcached.tagpass] + goodtag = ["mytag"] + [inputs.memcached.tagdrop] + badtag = ["othertag"] diff --git a/internal/config/testdata/slice_comment 2.toml b/internal/config/testdata/slice_comment 2.toml new file mode 100644 index 0000000000000..1177e5f8901e2 --- /dev/null +++ b/internal/config/testdata/slice_comment 2.toml @@ -0,0 +1,5 @@ +[[outputs.http]] + scopes = [ + # comment + "test" # comment + ] diff --git a/internal/config/testdata/subconfig/exec 2.conf b/internal/config/testdata/subconfig/exec 2.conf new file mode 100644 index 0000000000000..d621e78e08563 --- /dev/null +++ b/internal/config/testdata/subconfig/exec 2.conf @@ -0,0 +1,4 @@ +[[inputs.exec]] + # the command to run + command = "/usr/bin/myothercollector --foo=bar" + name_suffix = "_myothercollector" diff --git a/internal/config/testdata/subconfig/memcached 2.conf b/internal/config/testdata/subconfig/memcached 2.conf new file mode 100644 index 0000000000000..2cd07d15d331d --- /dev/null +++ b/internal/config/testdata/subconfig/memcached 2.conf @@ -0,0 +1,11 @@ +[[inputs.memcached]] + servers = ["192.168.1.1"] + namepass = ["metricname1"] + namedrop = ["metricname2"] + pass = ["some", "strings"] + drop = ["other", "stuff"] + interval = "5s" + [inputs.memcached.tagpass] + goodtag = ["mytag"] + [inputs.memcached.tagdrop] + badtag = ["othertag"] diff --git a/internal/config/testdata/subconfig/procstat 2.conf b/internal/config/testdata/subconfig/procstat 2.conf new file mode 100644 index 0000000000000..82708667f8e39 --- /dev/null +++ b/internal/config/testdata/subconfig/procstat 2.conf @@ -0,0 +1,2 @@ +[[inputs.procstat]] + pid_file = "/var/run/grafana-server.pid" diff --git a/internal/config/testdata/wrong_field_type 2.toml b/internal/config/testdata/wrong_field_type 2.toml new file mode 100644 index 0000000000000..237176e7e54b4 --- /dev/null +++ b/internal/config/testdata/wrong_field_type 2.toml @@ -0,0 +1,2 @@ +[[inputs.http_listener_v2]] + port = "80" diff --git a/internal/http_go1.12 2.go b/internal/http_go1.12 2.go new file mode 100644 index 0000000000000..d5b1a847f12a7 --- /dev/null +++ b/internal/http_go1.12 2.go @@ -0,0 +1,9 @@ +// +build go1.12 + +package internal + +import "net/http" + +func CloseIdleConnections(c *http.Client) { + c.CloseIdleConnections() +} diff --git a/internal/models/log_test 2.go b/internal/models/log_test 2.go new file mode 100644 index 0000000000000..d4bb6ca09b019 --- /dev/null +++ b/internal/models/log_test 2.go @@ -0,0 +1,70 @@ +package models + +import ( + "testing" + + "github.com/influxdata/telegraf/selfstat" + "github.com/stretchr/testify/require" +) + +func TestErrorCounting(t *testing.T) { + iLog := Logger{Name: "inputs.test", Errs: selfstat.Register( + "gather", + "errors", + map[string]string{"input": "test"}, + )} + iLog.Error("something went wrong") + iLog.Errorf("something went wrong") + + aLog := Logger{Name: "aggregators.test", Errs: selfstat.Register( + "aggregate", + "errors", + map[string]string{"aggregator": "test"}, + )} + aLog.Name = "aggregators.test" + aLog.Error("another thing happened") + + oLog := Logger{Name: "outputs.test", Errs: selfstat.Register( + "write", + "errors", + map[string]string{"output": "test"}, + )} + oLog.Error("another thing happened") + + pLog := Logger{Name: "processors.test", Errs: selfstat.Register( + "process", + "errors", + map[string]string{"processor": "test"}, + )} + pLog.Error("another thing happened") + + require.Equal(t, int64(2), iLog.Errs.Get()) + require.Equal(t, int64(1), aLog.Errs.Get()) + require.Equal(t, int64(1), oLog.Errs.Get()) + require.Equal(t, int64(1), pLog.Errs.Get()) +} + +func TestLogging(t *testing.T) { + log := Logger{Name: "inputs.test", Errs: selfstat.Register( + "gather", + "errors", + map[string]string{"input": "test"}, + )} + + log.Errs.Set(0) + + log.Debugf("something happened") + log.Debug("something happened") + + log.Warnf("something happened") + log.Warn("something happened") + require.Equal(t, int64(0), log.Errs.Get()) + + log.Infof("something happened") + log.Info("something happened") + require.Equal(t, int64(0), log.Errs.Get()) + + log.Errorf("something happened") + log.Error("something happened") + require.Equal(t, int64(2), log.Errs.Get()) +} diff --git a/internal/models/running_input 2.go b/internal/models/running_input 2.go new file mode 100644 index 0000000000000..c09fb1409f226 --- /dev/null +++ b/internal/models/running_input 2.go @@ -0,0 +1,118 @@ +package models + +import ( + "time" + + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/selfstat" +) + +var GlobalMetricsGathered = selfstat.Register("agent", "metrics_gathered", map[string]string{}) + +type RunningInput struct { + Input telegraf.Input + Config *InputConfig + + log telegraf.Logger + defaultTags map[string]string + + MetricsGathered selfstat.Stat + GatherTime selfstat.Stat +} + +func NewRunningInput(input telegraf.Input, config *InputConfig) *RunningInput { + tags := map[string]string{"input": config.Name} + if config.Alias != "" { + tags["alias"] = config.Alias + } + + logger := &Logger{ + Name: logName("inputs", config.Name, config.Alias), + Errs: selfstat.Register("gather", "errors", tags), + } + setLogIfExist(input, logger) + + return &RunningInput{ + Input: input, + Config: config, + MetricsGathered: selfstat.Register( + "gather", + "metrics_gathered", + tags, + ), + GatherTime: selfstat.RegisterTiming( + "gather", + "gather_time_ns", + tags, + ), + log: logger, + } +} + +// InputConfig is the common config for all inputs. +type InputConfig struct { + Name string + Alias string + Interval time.Duration + + NameOverride string + MeasurementPrefix string + MeasurementSuffix string + Tags map[string]string + Filter Filter +} + +func (r *RunningInput) metricFiltered(metric telegraf.Metric) { + metric.Drop() +} + +func (r *RunningInput) LogName() string { + return logName("inputs", r.Config.Name, r.Config.Alias) +} + +func (r *RunningInput) Init() error { + if p, ok := r.Input.(telegraf.Initializer); ok { + err := p.Init() + if err != nil { + return err + } + } + return nil +} + +func (r *RunningInput) MakeMetric(metric telegraf.Metric) telegraf.Metric { + if ok := r.Config.Filter.Select(metric); !ok { + r.metricFiltered(metric) + return nil + } + + m := makemetric( + metric, + r.Config.NameOverride, + r.Config.MeasurementPrefix, + r.Config.MeasurementSuffix, + r.Config.Tags, + r.defaultTags) + + r.Config.Filter.Modify(metric) + if len(metric.FieldList()) == 0 { + r.metricFiltered(metric) + return nil + } + + r.MetricsGathered.Incr(1) + GlobalMetricsGathered.Incr(1) + return m +} + +func (r *RunningInput) Gather(acc telegraf.Accumulator) error { + start := time.Now() + err := r.Input.Gather(acc) + elapsed := time.Since(start) + r.GatherTime.Incr(elapsed.Nanoseconds()) + return err +} + +func (r *RunningInput) SetDefaultTags(tags map[string]string) { + r.defaultTags = tags +} diff --git a/internal/models/running_output 2.go b/internal/models/running_output 2.go new file mode 100644 index 0000000000000..752cf34ef127d --- /dev/null +++ b/internal/models/running_output 2.go @@ -0,0 +1,241 @@ +package models + +import ( + "sync" + "sync/atomic" + "time" + + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/selfstat" +) + +const ( + // Default size of metrics batch size. + DEFAULT_METRIC_BATCH_SIZE = 1000 + + // Default number of metrics kept. It should be a multiple of batch size. + DEFAULT_METRIC_BUFFER_LIMIT = 10000 +) + +// OutputConfig containing name and filter +type OutputConfig struct { + Name string + Alias string + Filter Filter + + FlushInterval time.Duration + FlushJitter *time.Duration + MetricBufferLimit int + MetricBatchSize int +} + +// RunningOutput contains the output configuration +type RunningOutput struct { + // Must be 64-bit aligned + newMetricsCount int64 + droppedMetrics int64 + + Output telegraf.Output + Config *OutputConfig + MetricBufferLimit int + MetricBatchSize int + + MetricsFiltered selfstat.Stat + WriteTime selfstat.Stat + + BatchReady chan time.Time + + buffer *Buffer + log telegraf.Logger + + aggMutex sync.Mutex +} + +func NewRunningOutput( + name string, + output telegraf.Output, + config *OutputConfig, + batchSize int, + bufferLimit int, +) *RunningOutput { + tags := map[string]string{"output": config.Name} + if config.Alias != "" { + tags["alias"] = config.Alias + } + + logger := &Logger{ + Name: logName("outputs", config.Name, config.Alias), + Errs: selfstat.Register("write", "errors", tags), + } + setLogIfExist(output, logger) + + if config.MetricBufferLimit > 0 { + bufferLimit = config.MetricBufferLimit + } + if bufferLimit == 0 { + bufferLimit = DEFAULT_METRIC_BUFFER_LIMIT + } + if config.MetricBatchSize > 0 { + batchSize = config.MetricBatchSize + } + if batchSize == 0 { + batchSize = DEFAULT_METRIC_BATCH_SIZE + } + + ro := &RunningOutput{ + buffer: NewBuffer(config.Name, config.Alias, bufferLimit), + BatchReady: make(chan time.Time, 1), + Output: output, + Config: config, + MetricBufferLimit: bufferLimit, + MetricBatchSize: batchSize, + MetricsFiltered: selfstat.Register( + "write", + "metrics_filtered", + tags, + ), + WriteTime: selfstat.RegisterTiming( + "write", + "write_time_ns", + tags, + ), + log: logger, + } + + return ro +} + +func (r *RunningOutput) LogName() string { + return logName("outputs", r.Config.Name, r.Config.Alias) +} + +func (ro *RunningOutput) metricFiltered(metric telegraf.Metric) { + ro.MetricsFiltered.Incr(1) + metric.Drop() +} + +func (r *RunningOutput) Init() error { + if p, ok := r.Output.(telegraf.Initializer); ok { + err := p.Init() + if err != nil { + return err + } + + } + return nil +} + +// AddMetric adds a metric to the output. +// +// Takes ownership of metric +func (ro *RunningOutput) AddMetric(metric telegraf.Metric) { + if ok := ro.Config.Filter.Select(metric); !ok { + ro.metricFiltered(metric) + return + } + + ro.Config.Filter.Modify(metric) + if len(metric.FieldList()) == 0 { + ro.metricFiltered(metric) + return + } + + if output, ok := ro.Output.(telegraf.AggregatingOutput); ok { + ro.aggMutex.Lock() + output.Add(metric) + ro.aggMutex.Unlock() + return + } + + dropped := ro.buffer.Add(metric) + atomic.AddInt64(&ro.droppedMetrics, int64(dropped)) + + count := atomic.AddInt64(&ro.newMetricsCount, 1) + if count == int64(ro.MetricBatchSize) { + atomic.StoreInt64(&ro.newMetricsCount, 0) + select { + case ro.BatchReady <- time.Now(): + default: + } + } +} + +// Write writes all metrics to the output, stopping when all have been sent on +// or error. +func (ro *RunningOutput) Write() error { + if output, ok := ro.Output.(telegraf.AggregatingOutput); ok { + ro.aggMutex.Lock() + metrics := output.Push() + ro.buffer.Add(metrics...) + output.Reset() + ro.aggMutex.Unlock() + } + + atomic.StoreInt64(&ro.newMetricsCount, 0) + + // Only process the metrics in the buffer now. Metrics added while we are + // writing will be sent on the next call. + nBuffer := ro.buffer.Len() + nBatches := nBuffer/ro.MetricBatchSize + 1 + for i := 0; i < nBatches; i++ { + batch := ro.buffer.Batch(ro.MetricBatchSize) + if len(batch) == 0 { + break + } + + err := ro.write(batch) + if err != nil { + ro.buffer.Reject(batch) + return err + } + ro.buffer.Accept(batch) + } + return nil +} + +// WriteBatch writes a single batch of metrics to the output. +func (ro *RunningOutput) WriteBatch() error { + batch := ro.buffer.Batch(ro.MetricBatchSize) + if len(batch) == 0 { + return nil + } + + err := ro.write(batch) + if err != nil { + ro.buffer.Reject(batch) + return err + } + ro.buffer.Accept(batch) + + return nil +} + +func (r *RunningOutput) Close() { + err := r.Output.Close() + if err != nil { + r.log.Errorf("Error closing output: %v", err) + } +} + +func (r *RunningOutput) write(metrics []telegraf.Metric) error { + dropped := atomic.LoadInt64(&r.droppedMetrics) + if dropped > 0 { + r.log.Warnf("Metric buffer overflow; %d metrics have been dropped", dropped) + atomic.StoreInt64(&r.droppedMetrics, 0) + } + + start := time.Now() + err := r.Output.Write(metrics) + elapsed := time.Since(start) + r.WriteTime.Incr(elapsed.Nanoseconds()) + + if err == nil { + r.log.Debugf("Wrote batch of %d metrics in %s", len(metrics), elapsed) + } + return err +} + +func (r *RunningOutput) LogBufferStatus() { + nBuffer := r.buffer.Len() + r.log.Debugf("Buffer fullness: %d / %d metrics", nBuffer, r.MetricBufferLimit) +} diff --git a/internal/models/running_processor 2.go b/internal/models/running_processor 2.go new file mode 100644 index 0000000000000..22a7d01987769 --- /dev/null +++ b/internal/models/running_processor 2.go @@ -0,0 +1,99 @@ +package models + +import ( + "sync" + + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/selfstat" +) + +type RunningProcessor struct { + sync.Mutex + log telegraf.Logger + Processor telegraf.Processor + Config *ProcessorConfig +} + +type RunningProcessors []*RunningProcessor + +func (rp RunningProcessors) Len() int { return len(rp) } +func (rp RunningProcessors) Swap(i, j int) { rp[i], rp[j] = rp[j], rp[i] } +func (rp RunningProcessors) Less(i, j int) bool { return rp[i].Config.Order < rp[j].Config.Order } + +// FilterConfig containing a name and filter +type ProcessorConfig struct { + Name string + Alias string + Order int64 + Filter Filter +} + +func NewRunningProcessor(processor telegraf.Processor, config *ProcessorConfig) *RunningProcessor { + tags := map[string]string{"processor": config.Name} + if config.Alias != "" { + tags["alias"] = config.Alias + } + + logger := &Logger{ + Name: logName("processors", config.Name, config.Alias), + Errs: selfstat.Register("process", "errors", tags), + } + setLogIfExist(processor, logger) + + return &RunningProcessor{ + Processor: processor, + Config: config, + log: logger, + } +} + +func (rp *RunningProcessor) metricFiltered(metric telegraf.Metric) { + metric.Drop() +} + +func containsMetric(item telegraf.Metric, metrics []telegraf.Metric) bool { + for _, m := range metrics { + if item == m { + return true + } + } + return false +} + +func (r *RunningProcessor) Init() error { + if p, ok := r.Processor.(telegraf.Initializer); ok { + err := p.Init() + if err != nil { + return err + } + } + return nil +} + +func (rp *RunningProcessor) Apply(in ...telegraf.Metric) []telegraf.Metric { + rp.Lock() + defer rp.Unlock() + + ret := []telegraf.Metric{} + + for _, metric := range in { + // In processors when a filter selects a metric it is sent through the + // processor. Otherwise the metric continues downstream unmodified. + if ok := rp.Config.Filter.Select(metric); !ok { + ret = append(ret, metric) + continue + } + + rp.Config.Filter.Modify(metric) + if len(metric.FieldList()) == 0 { + rp.metricFiltered(metric) + continue + } + + // This metric should pass through the filter, so call the filter Apply + // function and append results to the output slice. + ret = append(ret, rp.Processor.Apply(metric)...) + } + + return ret +} diff --git a/internal/models/running_processor_test 2.go b/internal/models/running_processor_test 2.go new file mode 100644 index 0000000000000..c24347b8ecf8e --- /dev/null +++ b/internal/models/running_processor_test 2.go @@ -0,0 +1,189 @@ +package models + +import ( + "sort" + "testing" + "time" + + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/testutil" + + "github.com/stretchr/testify/require" +) + +// MockProcessor is a Processor with an overrideable Apply implementation. +type MockProcessor struct { + ApplyF func(in ...telegraf.Metric) []telegraf.Metric +} + +func (p *MockProcessor) SampleConfig() string { + return "" +} + +func (p *MockProcessor) Description() string { + return "" +} + +func (p *MockProcessor) Apply(in ...telegraf.Metric) []telegraf.Metric { + return p.ApplyF(in...) +} + +// TagProcessor returns a Processor whose Apply function adds the tag and +// value. +func TagProcessor(key, value string) *MockProcessor { + return &MockProcessor{ + ApplyF: func(in ...telegraf.Metric) []telegraf.Metric { + for _, m := range in { + m.AddTag(key, value) + } + return in + }, + } +} + +func TestRunningProcessor_Apply(t *testing.T) { + type args struct { + Processor telegraf.Processor + Config *ProcessorConfig + } + + tests := []struct { + name string + args args + input []telegraf.Metric + expected []telegraf.Metric + }{ + { + name: "inactive filter applies metrics", + args: args{ + Processor: TagProcessor("apply", "true"), + Config: &ProcessorConfig{ + Filter: Filter{}, + }, + }, + input: []telegraf.Metric{ + testutil.MustMetric( + "cpu", + map[string]string{}, + map[string]interface{}{ + "value": 42.0, + }, + time.Unix(0, 0), + ), + }, + expected: []telegraf.Metric{ + testutil.MustMetric( + "cpu", + map[string]string{ + "apply": "true", + }, + map[string]interface{}{ + "value": 42.0, + }, + time.Unix(0, 0), + ), + }, + }, + { + name: "filter applies", + args: args{ + Processor: TagProcessor("apply", "true"), + Config: &ProcessorConfig{ + Filter: Filter{ + NamePass: []string{"cpu"}, + }, + }, + }, + input: []telegraf.Metric{ + testutil.MustMetric( + "cpu", + map[string]string{}, + map[string]interface{}{ + "value": 42.0, + }, + time.Unix(0, 0), + ), + }, + expected: []telegraf.Metric{ + testutil.MustMetric( + "cpu", + map[string]string{ + "apply": "true", + }, + map[string]interface{}{ + "value": 42.0, + }, + time.Unix(0, 0), + ), + }, + }, + { + name: "filter doesn't apply", + args: args{ + Processor: TagProcessor("apply", "true"), + Config: &ProcessorConfig{ + Filter: Filter{ + NameDrop: []string{"cpu"}, + }, + }, + }, + input: []telegraf.Metric{ + testutil.MustMetric( + "cpu", + map[string]string{}, + map[string]interface{}{ + "value": 42.0, + }, + time.Unix(0, 0), + ), + }, + expected: []telegraf.Metric{ + testutil.MustMetric( + "cpu", + map[string]string{}, + map[string]interface{}{ + "value": 42.0, + }, + time.Unix(0, 0), + ), + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rp := &RunningProcessor{ + Processor: tt.args.Processor, + Config: tt.args.Config, + } + rp.Config.Filter.Compile() + + actual := rp.Apply(tt.input...) + require.Equal(t, tt.expected, actual) + }) + } +} + +func TestRunningProcessor_Order(t *testing.T) { + rp1 := &RunningProcessor{ + Config: &ProcessorConfig{ + Order: 1, + }, + } + rp2 := &RunningProcessor{ + Config: &ProcessorConfig{ + Order: 2, + }, + } + rp3 := &RunningProcessor{ + Config: &ProcessorConfig{ + Order: 3, + }, + } + + procs := RunningProcessors{rp2, rp3, rp1} + sort.Sort(procs) + require.Equal(t, + RunningProcessors{rp1, rp2, rp3}, + procs) +} diff --git a/internal/tls/config 2.go b/internal/tls/config 2.go new file mode 100644 index 0000000000000..185c92cd04865 --- /dev/null +++ b/internal/tls/config 2.go @@ -0,0 +1,166 @@ +package tls + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" + "strings" +) + +// ClientConfig represents the standard client TLS config. +type ClientConfig struct { + TLSCA string `toml:"tls_ca"` + TLSCert string `toml:"tls_cert"` + TLSKey string `toml:"tls_key"` + InsecureSkipVerify bool `toml:"insecure_skip_verify"` + + // Deprecated in 1.7; use TLS variables above + SSLCA string `toml:"ssl_ca"` + SSLCert string `toml:"ssl_cert"` + SSLKey string `toml:"ssl_key"` +} + +// ServerConfig represents the standard server TLS config. +type ServerConfig struct { + TLSCert string `toml:"tls_cert"` + TLSKey string `toml:"tls_key"` + TLSAllowedCACerts []string `toml:"tls_allowed_cacerts"` + TLSCipherSuites []string `toml:"tls_cipher_suites"` + TLSMinVersion string `toml:"tls_min_version"` + TLSMaxVersion string `toml:"tls_max_version"` +} + +// TLSConfig returns a tls.Config, may be nil without error if TLS is not +// configured. +func (c *ClientConfig) TLSConfig() (*tls.Config, error) { + // Support deprecated variable names + if c.TLSCA == "" && c.SSLCA != "" { + c.TLSCA = c.SSLCA + } + if c.TLSCert == "" && c.SSLCert != "" { + c.TLSCert = c.SSLCert + } + if c.TLSKey == "" && c.SSLKey != "" { + c.TLSKey = c.SSLKey + } + + // TODO: return default tls.Config; plugins should not call if they don't + // want TLS, this will require using another option to determine. In the + // case of an HTTP plugin, you could use `https`. Other plugins may need + // the dedicated option `TLSEnable`. + if c.TLSCA == "" && c.TLSKey == "" && c.TLSCert == "" && !c.InsecureSkipVerify { + return nil, nil + } + + tlsConfig := &tls.Config{ + InsecureSkipVerify: c.InsecureSkipVerify, + Renegotiation: tls.RenegotiateNever, + } + + if c.TLSCA != "" { + pool, err := makeCertPool([]string{c.TLSCA}) + if err != nil { + return nil, err + } + tlsConfig.RootCAs = pool + } + + if c.TLSCert != "" && c.TLSKey != "" { + err := loadCertificate(tlsConfig, c.TLSCert, c.TLSKey) + if err != nil { + return nil, err + } + } + + return tlsConfig, nil +} + +// TLSConfig returns a tls.Config, may be nil without error if TLS is not +// configured. +func (c *ServerConfig) TLSConfig() (*tls.Config, error) { + if c.TLSCert == "" && c.TLSKey == "" && len(c.TLSAllowedCACerts) == 0 { + return nil, nil + } + + tlsConfig := &tls.Config{} + + if len(c.TLSAllowedCACerts) != 0 { + pool, err := makeCertPool(c.TLSAllowedCACerts) + if err != nil { + return nil, err + } + tlsConfig.ClientCAs = pool + tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert + } + + if c.TLSCert != "" && c.TLSKey != "" { + err := loadCertificate(tlsConfig, c.TLSCert, c.TLSKey) + if err != nil { + return nil, err + } + } + + if len(c.TLSCipherSuites) != 0 { + cipherSuites, err := ParseCiphers(c.TLSCipherSuites) + if err != nil { + return nil, fmt.Errorf( + "could not parse server cipher suites %s: %v", strings.Join(c.TLSCipherSuites, ","), err) + } + tlsConfig.CipherSuites = cipherSuites + } + + if c.TLSMaxVersion != "" { + version, err := ParseTLSVersion(c.TLSMaxVersion) + if err != nil { + return nil, fmt.Errorf( + "could not parse tls max version %q: %v", c.TLSMaxVersion, err) + } + tlsConfig.MaxVersion = version + } + + if c.TLSMinVersion != "" { + version, err := ParseTLSVersion(c.TLSMinVersion) + if err != nil { + return nil, fmt.Errorf( + "could not parse tls min version %q: %v", c.TLSMinVersion, err) + } + tlsConfig.MinVersion = version + } + + if tlsConfig.MinVersion != 0 && tlsConfig.MaxVersion != 0 && tlsConfig.MinVersion > tlsConfig.MaxVersion { + return nil, fmt.Errorf( + "tls min version %q can't be greater then tls max version %q", tlsConfig.MinVersion, tlsConfig.MaxVersion) + } + + return tlsConfig, nil +} + +func makeCertPool(certFiles []string) (*x509.CertPool, error) { + pool := x509.NewCertPool() + for _, certFile := range certFiles { + pem, err := ioutil.ReadFile(certFile) + if err != nil { + return nil, fmt.Errorf( + "could not read certificate %q: %v", certFile, err) + } + ok := pool.AppendCertsFromPEM(pem) + if !ok { + return nil, fmt.Errorf( + "could not parse any PEM certificates %q: %v", certFile, err) + } + } + return pool, nil +} + +func loadCertificate(config *tls.Config, certFile, keyFile string) error { + cert, err := tls.LoadX509KeyPair(certFile, keyFile) + if err != nil { + return fmt.Errorf( + "could not load keypair %s:%s: %v", certFile, keyFile, err) + } + + config.Certificates = []tls.Certificate{cert} + config.BuildNameToCertificate() + return nil +} diff --git a/metric/builder 2.go b/metric/builder 2.go new file mode 100644 index 0000000000000..9a331b9a4cb36 --- /dev/null +++ b/metric/builder 2.go @@ -0,0 +1,55 @@ +package metric + +import ( + "time" + + "github.com/influxdata/telegraf" +) + +type TimeFunc func() time.Time + +type Builder struct { + TimeFunc + TimePrecision time.Duration + + *metric +} + +func NewBuilder() *Builder { + b := &Builder{ + TimeFunc: time.Now, + TimePrecision: 1 * time.Nanosecond, + } + b.Reset() + return b +} + +func (b *Builder) SetName(name string) { + b.name = name +} + +func (b *Builder) AddTag(key string, value string) { + b.metric.AddTag(key, value) +} + +func (b *Builder) AddField(key string, value interface{}) { + b.metric.AddField(key, value) +} + +func (b *Builder) SetTime(tm time.Time) { + b.tm = tm +} + +func (b *Builder) Reset() { + b.metric = &metric{ + tp: telegraf.Untyped, + } +} + +func (b *Builder) Metric() (telegraf.Metric, error) { + if b.tm.IsZero() { + b.tm = b.TimeFunc().Truncate(b.TimePrecision) + } + + return b.metric, nil +} diff --git a/plugins/inputs/opcua/README.md b/plugins/inputs/opcua/README.md new file mode 100644 index 0000000000000..407410ed83c06 --- /dev/null +++ b/plugins/inputs/opcua/README.md @@ -0,0 +1,57 @@ +# Telegraf Input Plugin: opcua_client + +The opcua_client plugin retrieves data from OPCUA slave devices + +### Configuration: + +```toml +# ## Connection Configuration +# ## +# ## The plugin supports connections to PLCs via OPCUA +# ## +# ## Device name +name = "opcua_rocks" +# +# # OPC UA Endpoint URL +endpoint = "opc.tcp://opcua.rocks:4840" +# +# ## Read Timeout +# ## add an arbitrary timeout (seconds) to demonstrate how to stop a subscription +# ## with a context. +timeout = 30 +# +# # Time Inteval, default = 100 * time.Millisecond +# # interval = "10000000" +# +# # Security policy: None, Basic128Rsa15, Basic256, Basic256Sha256. Default: auto +security_policy = "None" +# +# # Security mode: None, Sign, SignAndEncrypt. Default: auto +security_mode = "None" +# +# # Path to cert.pem. Required for security mode/policy != None +# # cert = "/etc/telegraf/cert.pem" +# +# # Path to private key.pem. Required for security mode/policy != None +# # key = "/etc/telegraf/key.pem" +# +# ## Measurements +# ## node id to subscribe to +# ## name - the variable name +# ## namespace - integer value 0 thru 3 +# ## identifier_type - s=string, i=numeric, g=guid, b=opaque +# ## identifier - tag as shown in opcua browser +# ## data_type - boolean, byte, short, int, uint, uint16, int16, uint32, int32, float, double, string, datetime, number +# ## Template - {name="", namespace="", identifier_type="", identifier="", data_type="", description=""}, +nodes = [ + {name="ProductName", namespace="0", identifier_type="i", identifier="2261", data_type="string", description="open62541 OPC UA Server"}, + {name="ProductUri", namespace="0", identifier_type="i", identifier="2262", data_type="string", description="http://open62541.org"}, + {name="ManufacturerName", namespace="0", identifier_type="i", identifier="2263", data_type="string", description="open62541"}, +] +``` +### Example Output: + +``` +$ ./telegraf -config telegraf.conf -input-filter opcua_client -test + +``` diff --git a/plugins/inputs/opcua/opcua.go b/plugins/inputs/opcua/_opcua.go similarity index 100% rename from plugins/inputs/opcua/opcua.go rename to plugins/inputs/opcua/_opcua.go diff --git a/plugins/inputs/opcua/opcua_funcs.go b/plugins/inputs/opcua/_opcua_funcs.go similarity index 100% rename from plugins/inputs/opcua/opcua_funcs.go rename to plugins/inputs/opcua/_opcua_funcs.go diff --git a/plugins/inputs/opcua/utils.go b/plugins/inputs/opcua/_utils.go similarity index 100% rename from plugins/inputs/opcua/utils.go rename to plugins/inputs/opcua/_utils.go diff --git a/plugins/inputs/opcua/opcua_client.go b/plugins/inputs/opcua/opcua_client.go new file mode 100644 index 0000000000000..c6c5e145659ea --- /dev/null +++ b/plugins/inputs/opcua/opcua_client.go @@ -0,0 +1,440 @@ +package opcua_client + +import ( + "context" + "fmt" + "log" + "net/url" + "time" + + "github.com/gopcua/opcua" + "github.com/gopcua/opcua/ua" + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/plugins/inputs" +) + +// OpcUA type +type OpcUA struct { + Name string `toml:"name"` + Endpoint string `toml:"endpoint"` + SecurityPolicy string `toml:"security_policy"` + SecurityMode string `toml:"security_mode"` + Certificate string `toml:"certificate"` + PrivateKey string `toml:"private_key"` + Username string `toml:"username"` //defaults to nil + Password string `toml:"password"` //defaults to nil + CertFile string `toml:"cert_file"` //defaults to "" + KeyFile string `toml:"key_file"` //defaults to "" + AuthMethod string `toml:"auth_method"` //defaults to "Anonymous" - accepts Anonymous, Username, Certificate + opts []opcua.Option //internally created + Interval string `toml:"time_interval"` + TimeOut int `toml:"timeout"` + NodeList []OPCTag `toml:"nodes"` + Nodes []string + NodeData []OPCData + NodeIDs []*ua.NodeID + NodeIDerror []error + state ConnectionState + + // status + ReadSuccess int + ReadError int + NumberOfTags int + + // internal values + client *opcua.Client + req *ua.ReadRequest + // nodeMonitor *monitor.NodeMonitor + // nodesData *monitor.DataChangeMessage + // nodesSubscription *monitor.Subscription + + ctx context.Context + // cancel context.CancelFunc +} + +// OPCTag type +type OPCTag struct { + Name string `toml:"name"` + Namespace string `toml:"namespace"` + IdentifierType string `toml:"identifier_type"` + Identifier string `toml:"identifier"` + DataType string `toml:"data_type"` + Description string `toml:"description"` +} + +// OPCData type +type OPCData struct { + TagName string + Value interface{} + Quality ua.StatusCode + TimeStamp string + Time string + DataType ua.TypeID +} + +// ConnectionState used for constants +type ConnectionState int + +const ( + //Disconnected constant state 0 + Disconnected ConnectionState = iota + //Connecting constant state 1 + Connecting + //Connected constant state 2 + Connected +) + +const description = `Retrieve data from OPCUA slave devices` +const sampleConfig = ` +# ## Connection Configuration +# ## +# ## The plugin supports connections to PLCs via OPCUA +# ## +# ## Device name +name = "opcua_rocks" +# +# # OPC UA Endpoint URL +endpoint = "opc.tcp://opcua.rocks:4840" +# +# ## Read Timeout +# ## add an arbitrary timeout (seconds) to demonstrate how to stop a subscription +# ## with a context. +timeout = 30 +# +# # Time Inteval, default = 10s +interval = "1s" +# +# # Security policy: None, Basic128Rsa15, Basic256, Basic256Sha256. Default: auto +security_policy = "None" +# +# # Security mode: None, Sign, SignAndEncrypt. Default: auto +security_mode = "None" +# +# # Path to cert.pem. Required for security mode/policy != None +# # cert = "/etc/telegraf/cert.pem" +# +# # Path to private key.pem. Required for security mode/policy != None +# # key = "/etc/telegraf/key.pem" +# +# ## Measurements +# ## node id to subscribe to +# ## name - the variable name +# ## namespace - integer value 0 thru 3 +# ## identifier_type - s=string, i=numeric, g=guid, b=opaque +# ## identifier - tag as shown in opcua browser +# ## data_type - boolean, byte, short, int, uint, uint16, int16, uint32, int32, float, double, string, datetime, number +# ## Template - {name="", namespace="", identifier_type="", identifier="", data_type="", description=""}, +nodes = [ + {name="ProductName", namespace="0", identifier_type="i", identifier="2261", data_type="string", description="open62541 OPC UA Server"}, + {name="ProductUri", namespace="0", identifier_type="i", identifier="2262", data_type="string", description="http://open62541.org"}, + {name="ManufacturerName", namespace="0", identifier_type="i", identifier="2263", data_type="string", description="open62541"}, +] +` + +// Description will appear directly above the plugin definition in the config file +func (o *OpcUA) Description() string { + return description +} + +// SampleConfig will populate the sample configuration portion of the plugin's configuration +func (o *OpcUA) SampleConfig() string { + return sampleConfig +} + +// Init will initialize all tags +func (o *OpcUA) Init() error { + o.state = Disconnected + + o.ctx = context.Background() + + err := o.validateEndpoint() + if err != nil { + return err + } + + err = o.InitNodes() + if err != nil { + return err + } + o.NumberOfTags = len(o.NodeList) + return nil + +} + +func (o *OpcUA) validateEndpoint() error { + //check device name + if o.Name == "" { + return fmt.Errorf("device name is empty") + } + //check device name + if o.Endpoint == "" { + return fmt.Errorf("device name is empty") + } + + _, err := url.Parse(o.Endpoint) + if err != nil { + return fmt.Errorf("endpoint url is invalid") + } + + if o.Interval == "" { + o.Interval = opcua.DefaultSubscriptionInterval.String() + } + + _, err = time.ParseDuration(o.Interval) + if err != nil { + return fmt.Errorf("fatal error with time interval") + } + + //search security policy type + switch o.SecurityPolicy { + case "None", "Basic128Rsa15", "Basic256", "Basic256Sha256", "auto": + break + default: + return fmt.Errorf("invalid security type '%s' in '%s'", o.SecurityPolicy, o.Name) + } + //search security mode type + switch o.SecurityMode { + case "None", "Sign", "SignAndEncrypt", "auto": + break + default: + return fmt.Errorf("invalid security type '%s' in '%s'", o.SecurityMode, o.Name) + } + return nil +} + +//InitNodes Method on OpcUA +func (o *OpcUA) InitNodes() error { + if len(o.NodeList) == 0 { + return nil + } + + err := o.validateOPCTags() + if err != nil { + return err + } + + return nil +} + +func (o *OpcUA) validateOPCTags() error { + nameEncountered := map[string]bool{} + for i, item := range o.NodeList { + //check empty name + if item.Name == "" { + return fmt.Errorf("empty name in '%s'", item.Name) + } + //search name duplicate + if nameEncountered[item.Name] { + return fmt.Errorf("name '%s' is duplicated in '%s'", item.Name, item.Name) + } else { + nameEncountered[item.Name] = true + } + //search identifier type + switch item.IdentifierType { + case "s", "i", "g", "b": + break + default: + return fmt.Errorf("invalid identifier type '%s' in '%s'", item.IdentifierType, item.Name) + } + // search data type + switch item.DataType { + case "boolean", "byte", "short", "int", "uint", "uint16", "int16", "uint32", "int32", "float", "double", "string", "datetime", "number": + break + default: + return fmt.Errorf("invalid data type '%s' in '%s'", item.DataType, item.Name) + } + + // build nodeid + o.Nodes = append(o.Nodes, BuildNodeID(item)) + + //parse NodeIds and NodeIds errors + nid, niderr := ua.ParseNodeID(o.Nodes[i]) + // build NodeIds and Errors + o.NodeIDs = append(o.NodeIDs, nid) + o.NodeIDerror = append(o.NodeIDerror, niderr) + // Grow NodeData for later input + o.NodeData = append(o.NodeData, OPCData{}) + } + return nil +} + +// BuildNodeID build node ID from OPC tag +func BuildNodeID(tag OPCTag) string { + return "ns=" + tag.Namespace + ";" + tag.IdentifierType + "=" + tag.Identifier +} + +// Connect to a OPCUA Slave device +func Connect(o *OpcUA) error { + u, err := url.Parse(o.Endpoint) + if err != nil { + return err + } + + switch u.Scheme { + case "opc.tcp": + o.state = Connecting + + if o.client != nil { + o.client.CloseSession() + } + + o.setupOptions() + + // endpoints, err := opcua.GetEndpoints(o.Endpoint) + // if err != nil { + // return fmt.Errorf("Endpoint Error: %s", err) + // } + + // ep := opcua.SelectEndpoint(endpoints, o.SecurityPolicy, ua.MessageSecurityModeFromString(o.SecurityMode)) + // if ep == nil { + // return fmt.Errorf("Failed to find suitable endpoint") + // } + // opts := []opcua.Option{ + // opcua.SecurityPolicy(o.SecurityPolicy), + // opcua.SecurityModeString(o.SecurityMode), + // opcua.CertificateFile(o.Certificate), + // opcua.PrivateKeyFile(o.PrivateKey), + // opcua.AuthAnonymous(), + // opcua.SecurityFromEndpoint(ep, ua.UserTokenTypeAnonymous), + // } + + o.client = opcua.NewClient(o.Endpoint, o.opts...) + if err := o.client.Connect(o.ctx); err != nil { + return fmt.Errorf("Error in Client Connection: %s", err) + } + + regResp, err := o.client.RegisterNodes(&ua.RegisterNodesRequest{ + NodesToRegister: o.NodeIDs, + }) + if err != nil { + return fmt.Errorf("RegisterNodes failed: %v", err) + } + + o.req = &ua.ReadRequest{ + MaxAge: 2000, + NodesToRead: readvalues(regResp.RegisteredNodeIDs), + TimestampsToReturn: ua.TimestampsToReturnBoth, + } + + err = o.getData() + if err != nil { + return fmt.Errorf("Get Data Failed: %v", err) + } + + default: + return fmt.Errorf("invalid opc.tcp endpoint") + } + return nil +} + +func (o *OpcUA) setupOptions() error { + + // Get a list of the endpoints for our target server + endpoints, err := opcua.GetEndpoints(o.Endpoint) + if err != nil { + log.Fatal(err) + } + + if o.CertFile == "" && o.KeyFile == "" { + if o.SecurityPolicy != "None" || o.SecurityMode != "None" { + o.CertFile, o.KeyFile = generateCert("urn:gopcua:client", 2048, o.CertFile, o.KeyFile, (365 * 24 * time.Hour)) + } + } + + o.opts = generateClientOpts(endpoints, o.CertFile, o.KeyFile, o.SecurityPolicy, o.SecurityMode, o.AuthMethod, o.Username, o.Password) + + return nil +} + +func (o *OpcUA) getData() error { + resp, err := o.client.Read(o.req) + if err != nil { + o.ReadError++ + return fmt.Errorf("RegisterNodes Read failed: %v", err) + } + o.ReadSuccess++ + for i, d := range resp.Results { + if d.Status != ua.StatusOK { + return fmt.Errorf("Status not OK: %v", d.Status) + } + o.NodeData[i].TagName = o.NodeList[i].Name + if d.Value != nil { + o.NodeData[i].Value = d.Value.Value() + o.NodeData[i].DataType = d.Value.Type() + } + o.NodeData[i].Quality = d.Status + o.NodeData[i].TimeStamp = d.ServerTimestamp.String() + o.NodeData[i].Time = d.SourceTimestamp.String() + } + return nil +} + +func readvalues(ids []*ua.NodeID) []*ua.ReadValueID { + rvids := make([]*ua.ReadValueID, len(ids)) + for i, v := range ids { + rvids[i] = &ua.ReadValueID{NodeID: v} + } + return rvids +} + +func disconnect(o *OpcUA) error { + u, err := url.Parse(o.Endpoint) + if err != nil { + return err + } + + o.ReadError = 0 + o.ReadSuccess = 0 + + switch u.Scheme { + case "opc.tcp": + o.state = Disconnected + o.client.Close() + return nil + default: + return fmt.Errorf("invalid controller") + } +} + +// Gather defines what data the plugin will gather. +func (o *OpcUA) Gather(acc telegraf.Accumulator) error { + if o.state == Disconnected { + o.state = Connecting + //o.Log.Debugf("Connecting %v", o.Servers) + err := Connect(o) + if err != nil { + o.state = Disconnected + return err + } + } + + o.state = Connected + + err := o.getData() + if err != nil && o.state == Connected { + o.state = Disconnected + disconnect(o) + return err + } + + for i, n := range o.NodeList { + fields := make(map[string]interface{}) + tags := map[string]string{ + "name": n.Name, + "type": n.DataType, + } + + fields[o.NodeData[i].TagName] = o.NodeData[i].Value + acc.AddFields(o.Name, fields, tags) + } + return nil +} + +// Add this plugin to telegraf +func init() { + inputs.Add("opcua_client", func() telegraf.Input { + return &OpcUA{ + AuthMethod: "Anonymous", + } + }) +} diff --git a/plugins/inputs/opcua/opcua_client_test.go b/plugins/inputs/opcua/opcua_client_test.go new file mode 100644 index 0000000000000..d3f48e858efd6 --- /dev/null +++ b/plugins/inputs/opcua/opcua_client_test.go @@ -0,0 +1,67 @@ +package opcua_client + +import ( + "fmt" + "reflect" + "testing" +) + +type OPCTags struct { + Name string + Namespace string + IdentifierType string + Identifier string + DataType string + Want string +} + +func TestClient1(t *testing.T) { + var testopctags = []OPCTags{ + {"ProductName", "0", "i", "2261", "string", "open62541 OPC UA Server"}, + {"ProductUri", "0", "i", "2262", "string", "http://open62541.org"}, + {"ManufacturerName", "0", "i", "2263", "string", "open62541"}, + } + + var o OpcUA + var err error + + o.Name = "testing" + o.Endpoint = "opc.tcp://opcua.rocks:4840" + o.Interval = "10ms" + o.TimeOut = 30 + o.SecurityPolicy = "None" + o.SecurityMode = "None" + for _, tags := range testopctags { + o.NodeList = append(o.NodeList, MapOPCTag(tags)) + } + err = o.Init() + if err != nil { + t.Errorf("Initialize Error: %s", err) + } + err = Connect(&o) + if err != nil { + t.Logf("Connect Error: %s", err) + } + + for i, v := range o.NodeData { + if v.Value != nil { + types := reflect.TypeOf(v.Value) + value := reflect.ValueOf(v.Value) + compare := fmt.Sprintf("%v", value.Interface()) + if compare != testopctags[i].Want { + t.Errorf("Tag %s: Values %v for type %s does not match record", o.NodeList[i].Name, value.Interface(), types) + } + } else { + t.Errorf("Tag: %s has value: %v", o.NodeList[i].Name, v.Value) + } + } +} + +func MapOPCTag(tags OPCTags) (out OPCTag) { + out.Name = tags.Name + out.Namespace = tags.Namespace + out.IdentifierType = tags.IdentifierType + out.Identifier = tags.Identifier + out.DataType = tags.DataType + return out +} diff --git a/plugins/inputs/opcua/opcua_util.go b/plugins/inputs/opcua/opcua_util.go new file mode 100644 index 0000000000000..330120dab6d66 --- /dev/null +++ b/plugins/inputs/opcua/opcua_util.go @@ -0,0 +1,335 @@ +package opcua_client + +import ( + "crypto/ecdsa" + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "io/ioutil" + "log" + "math/big" + "net" + "net/url" + "os" + "strings" + "time" + + "github.com/gopcua/opcua" + "github.com/gopcua/opcua/debug" + "github.com/gopcua/opcua/ua" + "github.com/pkg/errors" +) + +// SELF SIGNED CERT FUNCTIONS + +func newTempDir() (string, error) { + dir, err := ioutil.TempDir("", "ssc") + return dir, err +} + +func generateCert(host string, rsaBits int, certFile, keyFile string, dur time.Duration) (string, string) { + + dir, _ := newTempDir() + + if len(host) == 0 { + log.Fatalf("Missing required host parameter") + } + if rsaBits == 0 { + rsaBits = 2048 + } + if len(certFile) == 0 { + certFile = fmt.Sprintf("%s/cert.pem", dir) + } + if len(keyFile) == 0 { + keyFile = fmt.Sprintf("%s/key.pem", dir) + } + + priv, err := rsa.GenerateKey(rand.Reader, rsaBits) + if err != nil { + log.Fatalf("failed to generate private key: %s", err) + } + + notBefore := time.Now() + notAfter := notBefore.Add(dur) + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + log.Fatalf("failed to generate serial number: %s", err) + } + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{"Gopcua Test Client"}, + }, + NotBefore: notBefore, + NotAfter: notAfter, + + KeyUsage: x509.KeyUsageContentCommitment | x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageDataEncipherment | x509.KeyUsageCertSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, + BasicConstraintsValid: true, + } + + hosts := strings.Split(host, ",") + for _, h := range hosts { + if ip := net.ParseIP(h); ip != nil { + template.IPAddresses = append(template.IPAddresses, ip) + } else { + template.DNSNames = append(template.DNSNames, h) + } + if uri, err := url.Parse(h); err == nil { + template.URIs = append(template.URIs, uri) + } + } + + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv) + if err != nil { + log.Fatalf("Failed to create certificate: %s", err) + } + + certOut, err := os.Create(certFile) + if err != nil { + log.Fatalf("failed to open %s for writing: %s", certFile, err) + } + if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil { + log.Fatalf("failed to write data to %s: %s", certFile, err) + } + if err := certOut.Close(); err != nil { + log.Fatalf("error closing %s: %s", certFile, err) + } + log.Printf("wrote %s\n", certFile) + + keyOut, err := os.OpenFile(keyFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + log.Printf("failed to open %s for writing: %s", keyFile, err) + return "", "" + } + if err := pem.Encode(keyOut, pemBlockForKey(priv)); err != nil { + log.Fatalf("failed to write data to %s: %s", keyFile, err) + } + if err := keyOut.Close(); err != nil { + log.Fatalf("error closing %s: %s", keyFile, err) + } + log.Printf("wrote %s\n", keyFile) + + return certFile, keyFile +} + +func publicKey(priv interface{}) interface{} { + switch k := priv.(type) { + case *rsa.PrivateKey: + return &k.PublicKey + case *ecdsa.PrivateKey: + return &k.PublicKey + default: + return nil + } +} + +func pemBlockForKey(priv interface{}) *pem.Block { + switch k := priv.(type) { + case *rsa.PrivateKey: + return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)} + case *ecdsa.PrivateKey: + b, err := x509.MarshalECPrivateKey(k) + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err) + os.Exit(2) + } + return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b} + default: + return nil + } +} + +// OPT FUNCTIONS + +func generateClientOpts(endpoints []*ua.EndpointDescription, certFile, keyFile, policy, mode, auth, username, password string) []opcua.Option { + opts := []opcua.Option{} + appuri := "urn:gopcua:client" + + // ApplicationURI is automatically read from the cert so is not required if a cert if provided + if certFile == "" { + opts = append(opts, opcua.ApplicationURI(appuri)) + } + + if certFile == "" && keyFile == "" { + if policy != "None" || mode != "None" { + certFile, keyFile = generateCert(appuri, 2048, certFile, keyFile, (365 * 24 * time.Hour)) + } + } + + var cert []byte + if certFile != "" && keyFile != "" { + debug.Printf("Loading cert/key from %s/%s", certFile, keyFile) + c, err := tls.LoadX509KeyPair(certFile, keyFile) + if err != nil { + log.Printf("Failed to load certificate: %s", err) + } else { + pk, ok := c.PrivateKey.(*rsa.PrivateKey) + if !ok { + log.Fatalf("Invalid private key") + } + cert = c.Certificate[0] + opts = append(opts, opcua.PrivateKey(pk), opcua.Certificate(cert)) + } + } + + var secPolicy string + switch { + case policy == "auto": + // set it later + case strings.HasPrefix(policy, ua.SecurityPolicyURIPrefix): + secPolicy = policy + policy = "" + case policy == "None" || policy == "Basic128Rsa15" || policy == "Basic256" || policy == "Basic256Sha256" || policy == "Aes128_Sha256_RsaOaep" || policy == "Aes256_Sha256_RsaPss": + secPolicy = ua.SecurityPolicyURIPrefix + policy + policy = "" + default: + log.Fatalf("Invalid security policy: %s", policy) + } + + // Select the most appropriate authentication mode from server capabilities and user input + authMode, authOption := generateAuth(auth, cert, username, password) + opts = append(opts, authOption) + + var secMode ua.MessageSecurityMode + switch strings.ToLower(mode) { + case "auto": + case "none": + secMode = ua.MessageSecurityModeNone + mode = "" + case "sign": + secMode = ua.MessageSecurityModeSign + mode = "" + case "signandencrypt": + secMode = ua.MessageSecurityModeSignAndEncrypt + mode = "" + default: + log.Fatalf("Invalid security mode: %s", mode) + } + + // Allow input of only one of sec-mode,sec-policy when choosing 'None' + if secMode == ua.MessageSecurityModeNone || secPolicy == ua.SecurityPolicyURINone { + secMode = ua.MessageSecurityModeNone + secPolicy = ua.SecurityPolicyURINone + } + + // Find the best endpoint based on our input and server recommendation (highest SecurityMode+SecurityLevel) + var serverEndpoint *ua.EndpointDescription + switch { + case mode == "auto" && policy == "auto": // No user selection, choose best + for _, e := range endpoints { + if serverEndpoint == nil || (e.SecurityMode >= serverEndpoint.SecurityMode && e.SecurityLevel >= serverEndpoint.SecurityLevel) { + serverEndpoint = e + } + } + + case mode != "auto" && policy == "auto": // User only cares about mode, select highest securitylevel with that mode + for _, e := range endpoints { + if e.SecurityMode == secMode && (serverEndpoint == nil || e.SecurityLevel >= serverEndpoint.SecurityLevel) { + serverEndpoint = e + } + } + + case mode == "auto" && policy != "auto": // User only cares about policy, select highest securitylevel with that policy + for _, e := range endpoints { + if e.SecurityPolicyURI == secPolicy && (serverEndpoint == nil || e.SecurityLevel >= serverEndpoint.SecurityLevel) { + serverEndpoint = e + } + } + + default: // User cares about both + fmt.Println("secMode: ", secMode, "secPolicy:", secPolicy) + for _, e := range endpoints { + if e.SecurityPolicyURI == secPolicy && e.SecurityMode == secMode && (serverEndpoint == nil || e.SecurityLevel >= serverEndpoint.SecurityLevel) { + serverEndpoint = e + } + } + } + + if serverEndpoint == nil { // Didn't find an endpoint with matching policy and mode. + log.Printf("unable to find suitable server endpoint with selected sec-policy and sec-mode") + log.Fatalf("quitting") + } + + secPolicy = serverEndpoint.SecurityPolicyURI + secMode = serverEndpoint.SecurityMode + + // Check that the selected endpoint is a valid combo + err := validateEndpointConfig(endpoints, secPolicy, secMode, authMode) + if err != nil { + log.Fatalf("error validating input: %s", err) + } + + opts = append(opts, opcua.SecurityFromEndpoint(serverEndpoint, authMode)) + + log.Printf("Using config:\nEndpoint: %s\nSecurity mode: %s, %s\nAuth mode : %s\n", serverEndpoint.EndpointURL, serverEndpoint.SecurityPolicyURI, serverEndpoint.SecurityMode, authMode) + return opts +} + +func generateAuth(a string, cert []byte, un, pw string) (ua.UserTokenType, opcua.Option) { + var err error + + var authMode ua.UserTokenType + var authOption opcua.Option + switch strings.ToLower(a) { + case "anonymous": + authMode = ua.UserTokenTypeAnonymous + authOption = opcua.AuthAnonymous() + + case "username": + authMode = ua.UserTokenTypeUserName + + if un == "" { + if err != nil { + log.Fatalf("error reading username input: %s", err) + } + } + + if pw == "" { + if err != nil { + log.Fatalf("error reading username input: %s", err) + } + } + + authOption = opcua.AuthUsername(un, pw) + + case "certificate": + authMode = ua.UserTokenTypeCertificate + authOption = opcua.AuthCertificate(cert) + + case "issuedtoken": + // todo: this is unsupported, fail here or fail in the opcua package? + authMode = ua.UserTokenTypeIssuedToken + authOption = opcua.AuthIssuedToken([]byte(nil)) + + default: + log.Printf("unknown auth-mode, defaulting to Anonymous") + authMode = ua.UserTokenTypeAnonymous + authOption = opcua.AuthAnonymous() + + } + + return authMode, authOption +} + +func validateEndpointConfig(endpoints []*ua.EndpointDescription, secPolicy string, secMode ua.MessageSecurityMode, authMode ua.UserTokenType) error { + for _, e := range endpoints { + if e.SecurityMode == secMode && e.SecurityPolicyURI == secPolicy { + for _, t := range e.UserIdentityTokens { + if t.TokenType == authMode { + return nil + } + } + } + } + + err := errors.Errorf("server does not support an endpoint with security : %s , %s", secPolicy, secMode) + return err +} From 8bf22b6f3eefd964c3d22e61307ad944b1dbfd48 Mon Sep 17 00:00:00 2001 From: Chris Hayles Date: Wed, 19 Aug 2020 00:29:40 -0700 Subject: [PATCH 03/12] Added opcua input plugin --- internal/config/testdata/subconfig/exec 3.conf | 4 ++++ internal/config/testdata/subconfig/memcached 3.conf | 11 +++++++++++ internal/config/testdata/subconfig/procstat 3.conf | 2 ++ metric/{builder 2.go => builder.go} | 0 4 files changed, 17 insertions(+) create mode 100644 internal/config/testdata/subconfig/exec 3.conf create mode 100644 internal/config/testdata/subconfig/memcached 3.conf create mode 100644 internal/config/testdata/subconfig/procstat 3.conf rename metric/{builder 2.go => builder.go} (100%) diff --git a/internal/config/testdata/subconfig/exec 3.conf b/internal/config/testdata/subconfig/exec 3.conf new file mode 100644 index 0000000000000..d621e78e08563 --- /dev/null +++ b/internal/config/testdata/subconfig/exec 3.conf @@ -0,0 +1,4 @@ +[[inputs.exec]] + # the command to run + command = "/usr/bin/myothercollector --foo=bar" + name_suffix = "_myothercollector" diff --git a/internal/config/testdata/subconfig/memcached 3.conf b/internal/config/testdata/subconfig/memcached 3.conf new file mode 100644 index 0000000000000..2cd07d15d331d --- /dev/null +++ b/internal/config/testdata/subconfig/memcached 3.conf @@ -0,0 +1,11 @@ +[[inputs.memcached]] + servers = ["192.168.1.1"] + namepass = ["metricname1"] + namedrop = ["metricname2"] + pass = ["some", "strings"] + drop = ["other", "stuff"] + interval = "5s" + [inputs.memcached.tagpass] + goodtag = ["mytag"] + [inputs.memcached.tagdrop] + badtag = ["othertag"] diff --git a/internal/config/testdata/subconfig/procstat 3.conf b/internal/config/testdata/subconfig/procstat 3.conf new file mode 100644 index 0000000000000..82708667f8e39 --- /dev/null +++ b/internal/config/testdata/subconfig/procstat 3.conf @@ -0,0 +1,2 @@ +[[inputs.procstat]] + pid_file = "/var/run/grafana-server.pid" diff --git a/metric/builder 2.go b/metric/builder.go similarity index 100% rename from metric/builder 2.go rename to metric/builder.go From f89e30f5e57db73915c9c2ee19fffda9190e1c43 Mon Sep 17 00:00:00 2001 From: Chris Hayles Date: Wed, 19 Aug 2020 17:22:45 -0700 Subject: [PATCH 04/12] Removed unnesessary files, properties, and comments. Updated README. --- plugins/inputs/opcua/README.md | 33 ++- plugins/inputs/opcua/_opcua.go | 3 - plugins/inputs/opcua/_opcua_funcs.go | 409 --------------------------- plugins/inputs/opcua/_utils.go | 138 --------- plugins/inputs/opcua/opcua_client.go | 92 +++--- 5 files changed, 68 insertions(+), 607 deletions(-) delete mode 100644 plugins/inputs/opcua/_opcua.go delete mode 100644 plugins/inputs/opcua/_opcua_funcs.go delete mode 100644 plugins/inputs/opcua/_utils.go diff --git a/plugins/inputs/opcua/README.md b/plugins/inputs/opcua/README.md index 407410ed83c06..0a8c3b4316af8 100644 --- a/plugins/inputs/opcua/README.md +++ b/plugins/inputs/opcua/README.md @@ -5,6 +5,7 @@ The opcua_client plugin retrieves data from OPCUA slave devices ### Configuration: ```toml + # ## Connection Configuration # ## # ## The plugin supports connections to PLCs via OPCUA @@ -20,8 +21,8 @@ endpoint = "opc.tcp://opcua.rocks:4840" # ## with a context. timeout = 30 # -# # Time Inteval, default = 100 * time.Millisecond -# # interval = "10000000" +# # Time Inteval, default = 10s +time_interval = "5s" # # # Security policy: None, Basic128Rsa15, Basic256, Basic256Sha256. Default: auto security_policy = "None" @@ -29,11 +30,20 @@ security_policy = "None" # # Security mode: None, Sign, SignAndEncrypt. Default: auto security_mode = "None" # -# # Path to cert.pem. Required for security mode/policy != None -# # cert = "/etc/telegraf/cert.pem" +# # Path to cert.pem. Required for security mode/policy != None. If cert path is not supplied, self-signed cert and key will be generated. +# # certificate = "/etc/telegraf/cert.pem" +# +# # Path to private key.pem. Required for security mode/policy != None. If key path is not supplied, self-signed cert and key will be generated. +# # private_key = "/etc/telegraf/key.pem" +# +# # To authenticate using a specific ID, select chosen method from 'Certificate' or 'UserName'. Else use 'Anonymous.' Defaults to 'Anonymous' if not provided. +# # auth_method = "Anonymous" # -# # Path to private key.pem. Required for security mode/policy != None -# # key = "/etc/telegraf/key.pem" +# # Required for auth_method = "UserName" +# # username = "myusername" +# +# # Required for auth_method = "UserName" +# # password = "mypassword" # # ## Measurements # ## node id to subscribe to @@ -48,10 +58,19 @@ nodes = [ {name="ProductUri", namespace="0", identifier_type="i", identifier="2262", data_type="string", description="http://open62541.org"}, {name="ManufacturerName", namespace="0", identifier_type="i", identifier="2263", data_type="string", description="open62541"}, ] + +## Guide: +## An OPC UA node ID may resemble: "n=3,s=Temperature" +## In this example, n=3 is indicating the namespace is '3'. +## s=Temperature is indicting that the identifier type is a 'string' and the indentifier value is 'Temperature' +## This temperature node may have a current value of 79.0, which would possibly make the value a 'float'. +## To gather data from this node you would need to enter the following line into 'nodes' property above: +## {name="SomeLabel", namespace="3", identifier_type="s", identifier="Temperature", data_type="float", description="Some description."}, + ``` ### Example Output: ``` -$ ./telegraf -config telegraf.conf -input-filter opcua_client -test +opcua,host=3c70aee0901e,name=Random,type=double Random=0.018158170305814902 1597820490000000000 ``` diff --git a/plugins/inputs/opcua/_opcua.go b/plugins/inputs/opcua/_opcua.go deleted file mode 100644 index d20d4ea8b9b33..0000000000000 --- a/plugins/inputs/opcua/_opcua.go +++ /dev/null @@ -1,3 +0,0 @@ -package opcua - -// placeholder diff --git a/plugins/inputs/opcua/_opcua_funcs.go b/plugins/inputs/opcua/_opcua_funcs.go deleted file mode 100644 index 595f99ba9bbe6..0000000000000 --- a/plugins/inputs/opcua/_opcua_funcs.go +++ /dev/null @@ -1,409 +0,0 @@ -package opcua - -import ( - "context" - "flag" - "fmt" - "log" - "time" - - "github.com/gopcua/opcua" - "github.com/gopcua/opcua/debug" - "github.com/gopcua/opcua/ua" -) - -// READ VALUE - -func readValue(address, node string) { - var ( - endpoint = flag.String("endpoint", address, "OPC UA Endpoint URL") - nodeID = flag.String("node", node, "NodeID to read") - policy = flag.String("sec-policy", "Basic256Sha256", "Security Policy URL or one of None, Basic128Rsa15, Basic256, Basic256Sha256") - mode = flag.String("sec-mode", "SignAndEncrypt", "Security Mode: one of None, Sign, SignAndEncrypt") - certFile = flag.String("cert", "./trusted/cert.pem", "Path to ./trusted/cert.pem. Required for security mode/policy != None") - keyFile = flag.String("key", "./trusted/key.pem", "Path to private ./trusted/key.pem. Required for security mode/policy != None") - ) - flag.BoolVar(&debug.Enable, "debug", false, "enable debug logging") - flag.Parse() - log.SetFlags(0) - - ctx := context.Background() - - endpoints, err := opcua.GetEndpoints(*endpoint) - if err != nil { - log.Fatal(err) - } - - log.Print("\nCalled function: opcua.GetEndpoints()\n") - - ep := opcua.SelectEndpoint(endpoints, *policy, ua.MessageSecurityModeFromString(*mode)) - if ep == nil { - log.Fatal("Failed to find suitable endpoint") - } - - log.Print("\nCalled function: opcua.SelectEndpoint()\n") - - log.Printf("\nENDPOINTS: \n%s", endpoints) - - opts := []opcua.Option{ - opcua.SecurityPolicy(*policy), - opcua.SecurityModeString(*mode), - opcua.CertificateFile(*certFile), - opcua.PrivateKeyFile(*keyFile), - opcua.AuthAnonymous(), - opcua.SecurityFromEndpoint(ep, ua.UserTokenTypeAnonymous), - } - - c := opcua.NewClient(*endpoint, opts...) - if err := c.Connect(ctx); err != nil { - log.Fatal(err) - } - defer c.Close() - - log.Print("\nCalled function: opcua.NewClient()\n") - - id, err := ua.ParseNodeID(*nodeID) - if err != nil { - log.Fatalf("invalid node id: %v", err) - } - - log.Print("\nCalled function: ua.ParseNodeID()\n") - - req := &ua.ReadRequest{ - MaxAge: 2000, - NodesToRead: []*ua.ReadValueID{ - &ua.ReadValueID{NodeID: id}, - }, - TimestampsToReturn: ua.TimestampsToReturnBoth, - } - - log.Print("\nCalled function: ua.ReadRequest()\n") - - resp, err := c.Read(req) - if err != nil { - log.Fatalf("Read failed: %s", err) - } - if resp.Results[0].Status != ua.StatusOK { - log.Fatalf("Status not OK: %v", resp.Results[0].Status) - } - log.Printf("%#v", resp.Results[0].Value.Value()) -} - -// WRITE - -func write(address, node string, newValue interface{}) { - var ( - endpoint = flag.String("endpoint", address, "OPC UA Endpoint URL") - nodeID = flag.String("node", node, "NodeID to read") - //policy = flag.String("policy", "Basic256Sha256", "Security policy: None, Basic128Rsa15, Basic256, Basic256Sha256. Default: auto") - //mode = flag.String("mode", "SignAndEncrypt", "Security mode: None, Sign, SignAndEncrypt. Default: auto") - //certFile = flag.String("cert", "./trusted/cert.pem", "Path to cert.pem. Required for security mode/policy != None") - //keyFile = flag.String("key", "./trusted/key.pem", "Path to private key.pem. Required for security mode/policy != None") - policy = flag.String("policy", "None", "Security policy: None, Basic128Rsa15, Basic256, Basic256Sha256. Default: auto") - mode = flag.String("mode", "None", "Security mode: None, Sign, SignAndEncrypt. Default: auto") - ) - flag.BoolVar(&debug.Enable, "debug", false, "enable debug logging") - flag.Parse() - log.SetFlags(0) - - ctx := context.Background() - - endpoints, err := opcua.GetEndpoints(*endpoint) - if err != nil { - log.Fatal(err) - } - ep := opcua.SelectEndpoint(endpoints, *policy, ua.MessageSecurityModeFromString(*mode)) - if ep == nil { - log.Fatal("Failed to find suitable endpoint") - } - - opts := []opcua.Option{ - opcua.SecurityPolicy(*policy), - opcua.SecurityModeString(*mode), - //opcua.CertificateFile(*certFile), - //opcua.PrivateKeyFile(*keyFile), - opcua.AuthAnonymous(), - opcua.SecurityFromEndpoint(ep, ua.UserTokenTypeAnonymous), - } - - c := opcua.NewClient(*endpoint, opts...) - if err := c.Connect(ctx); err != nil { - log.Fatal(err) - } - defer c.Close() - - id, err := ua.ParseNodeID(*nodeID) - if err != nil { - log.Fatalf("invalid node id: %v", err) - } - - v, err := ua.NewVariant(newValue) - if err != nil { - log.Fatalf("invalid value: %v", err) - } - - req := &ua.WriteRequest{ - NodesToWrite: []*ua.WriteValue{ - &ua.WriteValue{ - NodeID: id, - AttributeID: ua.AttributeIDValue, - Value: &ua.DataValue{ - EncodingMask: ua.DataValueValue, - Value: v, - }, - }, - }, - } - - resp, err := c.Write(req) - if err != nil { - log.Fatalf("Read failed: %s", err) - } - log.Printf("%v", resp.Results[0]) -} - -// SUBSCRIBE - -func subscribe(address, node string) { - var ( - endpoint = flag.String("endpoint", address, "OPC UA Endpoint URL") - policy = flag.String("policy", "Basic256Sha256", "Security policy: None, Basic128Rsa15, Basic256, Basic256Sha256. Default: auto") - mode = flag.String("mode", "SignAndEncrypt", "Security mode: None, Sign, SignAndEncrypt. Default: auto") - certFile = flag.String("cert", "./trusted/cert.pem", "Path to cert.pem. Required for security mode/policy != None") - keyFile = flag.String("key", "./trusted/key.pem", "Path to private key.pem. Required for security mode/policy != None") - nodeID = flag.String("node", node, "node id to subscribe to") - interval = flag.String("interval", opcua.DefaultSubscriptionInterval.String(), "subscription interval") - ) - flag.BoolVar(&debug.Enable, "debug", false, "enable debug logging") - flag.Parse() - log.SetFlags(0) - - subInterval, err := time.ParseDuration(*interval) - if err != nil { - log.Fatal(err) - } - - // add an arbitrary timeout to demonstrate how to stop a subscription - // with a context. - //d := 30 * time.Second - //ctx, cancel := context.WithTimeout(context.Background(), d) - ctx := context.Background() - //defer cancel() - - log.Print("\nCalled function: opcua.GetEndpoints()\n") - - endpoints, err := opcua.GetEndpoints(*endpoint) - if err != nil { - log.Fatal(err) - } - ep := opcua.SelectEndpoint(endpoints, *policy, ua.MessageSecurityModeFromString(*mode)) - if ep == nil { - log.Fatal("Failed to find suitable endpoint") - } - - log.Print("\nCalled function: opcua.SelectEndpoint()\n") - - log.Printf("\nENDPOINTS: \n%s", endpoints) - - fmt.Println("*", ep.SecurityPolicyURI, ep.SecurityMode) - - opts := []opcua.Option{ - opcua.SecurityPolicy(*policy), - opcua.SecurityModeString(*mode), - opcua.CertificateFile(*certFile), - opcua.PrivateKeyFile(*keyFile), - opcua.AuthAnonymous(), - opcua.SecurityFromEndpoint(ep, ua.UserTokenTypeAnonymous), - } - - c := opcua.NewClient(*endpoint, opts...) - if err := c.Connect(ctx); err != nil { - log.Fatal(err) - } - defer c.Close() - - log.Print("\nCalled function: opcua.NewClient()\n") - - notifyCh := make(chan *opcua.PublishNotificationData) - - sub, err := c.Subscribe(&opcua.SubscriptionParameters{ - Interval: subInterval, //500 * time.Millisecond, - }, notifyCh) - - log.Print("\nCalled function: c.Subscribe()\n") - - if err != nil { - log.Fatal(err) - } - defer sub.Cancel() - log.Printf("Created subscription with id %v", sub.SubscriptionID) - - id, err := ua.ParseNodeID(*nodeID) - if err != nil { - log.Fatal(err) - } - - log.Print("\nCalled function: ua.ParseNodeID()\n") - - // arbitrary client handle for the monitoring item - handle := uint32(42) - miCreateRequest := opcua.NewMonitoredItemCreateRequestWithDefaults(id, ua.AttributeIDValue, handle) - res, err := sub.Monitor(ua.TimestampsToReturnBoth, miCreateRequest) - if err != nil || res.Results[0].StatusCode != ua.StatusOK { - log.Fatal(err) - } - - log.Print("\nCalled function: opcua.NewMonitoredItemCreateRequestWithDefaults()\n") - - go sub.Run(ctx) // start Publish loop - - // read from subscription's notification channel until ctx is cancelled - for { - select { - case <-ctx.Done(): - log.Print("Received ctx.Done()") - return - case res := <-sub.Notifs: - if res.Error != nil { - log.Print(res.Error) - continue - } - - switch x := res.Value.(type) { - case *ua.DataChangeNotification: - for _, item := range x.MonitoredItems { - data := item.Value.Value.Value() - log.Printf("MonitoredItem with client handle %v = %v", item.ClientHandle, data) - } - - default: - log.Printf("what's this publish result? %T", res.Value) - } - } - } -} - -func subscribeMany(address string, nodes map[string]string) { - var ( - endpoint = flag.String("endpoint", address, "OPC UA Endpoint URL") - policy = flag.String("policy", "Basic256Sha256", "Security policy: None, Basic128Rsa15, Basic256, Basic256Sha256. Default: auto") - mode = flag.String("mode", "SignAndEncrypt", "Security mode: None, Sign, SignAndEncrypt. Default: auto") - certFile = flag.String("cert", "./trusted/cert.pem", "Path to cert.pem. Required for security mode/policy != None") - keyFile = flag.String("key", "./trusted/key.pem", "Path to private key.pem. Required for security mode/policy != None") - interval = flag.String("interval", opcua.DefaultSubscriptionInterval.String(), "subscription interval") - //nodeID = flag.String("node", "", "node id to subscribe to") - ) - flag.BoolVar(&debug.Enable, "debug", false, "enable debug logging") - flag.Parse() - log.SetFlags(0) - - subInterval, err := time.ParseDuration(*interval) - if err != nil { - log.Fatal(err) - } - - // add an arbitrary timeout to demonstrate how to stop a subscription - // with a context. - //d := 30 * time.Second - //ctx, cancel := context.WithTimeout(context.Background(), d) - ctx := context.Background() - //defer cancel() - - endpoints, err := opcua.GetEndpoints(*endpoint) - if err != nil { - log.Fatal(err) - } - ep := opcua.SelectEndpoint(endpoints, *policy, ua.MessageSecurityModeFromString(*mode)) - if ep == nil { - log.Fatal("Failed to find suitable endpoint") - } - - fmt.Println("*", ep.SecurityPolicyURI, ep.SecurityMode) - - opts := []opcua.Option{ - opcua.SecurityPolicy(*policy), - opcua.SecurityModeString(*mode), - opcua.CertificateFile(*certFile), - opcua.PrivateKeyFile(*keyFile), - opcua.AuthAnonymous(), - opcua.SecurityFromEndpoint(ep, ua.UserTokenTypeAnonymous), - } - - c := opcua.NewClient(*endpoint, opts...) - if err := c.Connect(ctx); err != nil { - log.Fatal(err) - } - defer c.Close() - - log.Print("\nCalled function: opcua.NewClient()\n") - - notifyCh := make(chan *opcua.PublishNotificationData) - - sub, err := c.Subscribe(&opcua.SubscriptionParameters{ - Interval: subInterval, //500 * time.Millisecond, - }, notifyCh) - - log.Print("\nCalled function: c.Subscribe()\n") - - if err != nil { - log.Fatal(err) - } - defer sub.Cancel() - log.Printf("Created subscription with id %v", sub.SubscriptionID) - - allRequests := []*ua.MonitoredItemCreateRequest{} - handleCounter := uint32(10) - handleLookup := map[uint32]string{} - - for handleName, nodeID := range nodes { - - id, err := ua.ParseNodeID(nodeID) - if err != nil { - log.Fatal(err) - } - - log.Print("\nCalled function: ua.ParseNodeID()\n") - - // arbitrary client handle for the monitoring item - handle := handleCounter - handleLookup[handle] = handleName - miCreateRequest := opcua.NewMonitoredItemCreateRequestWithDefaults(id, ua.AttributeIDValue, handle) - allRequests = append(allRequests, miCreateRequest) - - log.Print("\nCalled function: opcua.NewMonitoredItemCreateRequestWithDefaults()\n") - handleCounter++ - - } - - res, err := sub.Monitor(ua.TimestampsToReturnBoth, allRequests...) - if err != nil || res.Results[0].StatusCode != ua.StatusOK { - log.Fatal(err) - } - - go sub.Run(ctx) // start Publish loop - - // read from subscription's notification channel until ctx is cancelled - for { - select { - case <-ctx.Done(): - log.Print("Received ctx.Done()") - return - case res := <-sub.Notifs: - if res.Error != nil { - log.Print(res.Error) - continue - } - - switch x := res.Value.(type) { - case *ua.DataChangeNotification: - for _, item := range x.MonitoredItems { - data := item.Value.Value.Value() - log.Printf("MonitoredItem %s (%v) = %v", handleLookup[item.ClientHandle], item.ClientHandle, data) - } - - default: - log.Printf("what's this publish result? %T", res.Value) - } - } - } -} diff --git a/plugins/inputs/opcua/_utils.go b/plugins/inputs/opcua/_utils.go deleted file mode 100644 index 53e035ed04574..0000000000000 --- a/plugins/inputs/opcua/_utils.go +++ /dev/null @@ -1,138 +0,0 @@ -package opcua - -import ( - "crypto/ecdsa" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "fmt" - "io/ioutil" - "log" - "math/big" - "net" - "net/url" - "os" - "strings" - "time" -) - -// SELF SIGNED CERT FUNCTIONS - -func newTempDir() (string, error) { - dir, err := ioutil.TempDir("", "ssc") - return dir, err -} - -func generateCert(host string, rsaBits int, certFile, keyFile string, dur time.Duration) { - - if len(host) == 0 { - log.Fatalf("Missing required host parameter") - } - if rsaBits == 0 { - rsaBits = 2048 - } - if len(certFile) == 0 { - certFile = "./trusted/cert.pem" - } - if len(keyFile) == 0 { - keyFile = "./trusted/key.pem" - } - - priv, err := rsa.GenerateKey(rand.Reader, rsaBits) - if err != nil { - log.Fatalf("failed to generate private key: %s", err) - } - - notBefore := time.Now() - notAfter := notBefore.Add(dur) - - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) - if err != nil { - log.Fatalf("failed to generate serial number: %s", err) - } - - template := x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{ - Organization: []string{"Gopcua Test Client"}, - }, - NotBefore: notBefore, - NotAfter: notAfter, - - KeyUsage: x509.KeyUsageContentCommitment | x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageDataEncipherment | x509.KeyUsageCertSign, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, - BasicConstraintsValid: true, - } - - hosts := strings.Split(host, ",") - for _, h := range hosts { - if ip := net.ParseIP(h); ip != nil { - template.IPAddresses = append(template.IPAddresses, ip) - } else { - template.DNSNames = append(template.DNSNames, h) - } - if uri, err := url.Parse(h); err == nil { - template.URIs = append(template.URIs, uri) - } - } - - derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv) - if err != nil { - log.Fatalf("Failed to create certificate: %s", err) - } - - certOut, err := os.Create(certFile) - if err != nil { - log.Fatalf("failed to open %s for writing: %s", certFile, err) - } - if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil { - log.Fatalf("failed to write data to %s: %s", certFile, err) - } - if err := certOut.Close(); err != nil { - log.Fatalf("error closing %s: %s", certFile, err) - } - log.Printf("wrote %s\n", certFile) - - keyOut, err := os.OpenFile(keyFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) - if err != nil { - log.Printf("failed to open %s for writing: %s", keyFile, err) - return - } - if err := pem.Encode(keyOut, pemBlockForKey(priv)); err != nil { - log.Fatalf("failed to write data to %s: %s", keyFile, err) - } - if err := keyOut.Close(); err != nil { - log.Fatalf("error closing %s: %s", keyFile, err) - } - log.Printf("wrote %s\n", keyFile) -} - -func publicKey(priv interface{}) interface{} { - switch k := priv.(type) { - case *rsa.PrivateKey: - return &k.PublicKey - case *ecdsa.PrivateKey: - return &k.PublicKey - default: - return nil - } -} - -func pemBlockForKey(priv interface{}) *pem.Block { - switch k := priv.(type) { - case *rsa.PrivateKey: - return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)} - case *ecdsa.PrivateKey: - b, err := x509.MarshalECPrivateKey(k) - if err != nil { - fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err) - os.Exit(2) - } - return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b} - default: - return nil - } -} diff --git a/plugins/inputs/opcua/opcua_client.go b/plugins/inputs/opcua/opcua_client.go index c6c5e145659ea..a392a204d0d26 100644 --- a/plugins/inputs/opcua/opcua_client.go +++ b/plugins/inputs/opcua/opcua_client.go @@ -15,21 +15,18 @@ import ( // OpcUA type type OpcUA struct { - Name string `toml:"name"` - Endpoint string `toml:"endpoint"` - SecurityPolicy string `toml:"security_policy"` - SecurityMode string `toml:"security_mode"` - Certificate string `toml:"certificate"` - PrivateKey string `toml:"private_key"` - Username string `toml:"username"` //defaults to nil - Password string `toml:"password"` //defaults to nil - CertFile string `toml:"cert_file"` //defaults to "" - KeyFile string `toml:"key_file"` //defaults to "" - AuthMethod string `toml:"auth_method"` //defaults to "Anonymous" - accepts Anonymous, Username, Certificate - opts []opcua.Option //internally created - Interval string `toml:"time_interval"` - TimeOut int `toml:"timeout"` - NodeList []OPCTag `toml:"nodes"` + Name string `toml:"name"` + Endpoint string `toml:"endpoint"` + SecurityPolicy string `toml:"security_policy"` + SecurityMode string `toml:"security_mode"` + Certificate string `toml:"certificate"` + PrivateKey string `toml:"private_key"` + Username string `toml:"username"` + Password string `toml:"password"` + AuthMethod string `toml:"auth_method"` + Interval string `toml:"time_interval"` + TimeOut int `toml:"timeout"` + NodeList []OPCTag `toml:"nodes"` Nodes []string NodeData []OPCData NodeIDs []*ua.NodeID @@ -44,12 +41,8 @@ type OpcUA struct { // internal values client *opcua.Client req *ua.ReadRequest - // nodeMonitor *monitor.NodeMonitor - // nodesData *monitor.DataChangeMessage - // nodesSubscription *monitor.Subscription - - ctx context.Context - // cancel context.CancelFunc + ctx context.Context + opts []opcua.Option } // OPCTag type @@ -84,7 +77,7 @@ const ( Connected ) -const description = `Retrieve data from OPCUA slave devices` +const description = `Retrieve data from OPCUA devices` const sampleConfig = ` # ## Connection Configuration # ## @@ -102,7 +95,7 @@ endpoint = "opc.tcp://opcua.rocks:4840" timeout = 30 # # # Time Inteval, default = 10s -interval = "1s" +time_interval = "5s" # # # Security policy: None, Basic128Rsa15, Basic256, Basic256Sha256. Default: auto security_policy = "None" @@ -110,11 +103,20 @@ security_policy = "None" # # Security mode: None, Sign, SignAndEncrypt. Default: auto security_mode = "None" # -# # Path to cert.pem. Required for security mode/policy != None -# # cert = "/etc/telegraf/cert.pem" +# # Path to cert.pem. Required for security mode/policy != None. If cert path is not supplied, self-signed cert and key will be generated. +# # certificate = "/etc/telegraf/cert.pem" +# +# # Path to private key.pem. Required for security mode/policy != None. If key path is not supplied, self-signed cert and key will be generated. +# # private_key = "/etc/telegraf/key.pem" +# +# # To authenticate using a specific ID, select chosen method from 'Certificate' or 'UserName'. Else use 'Anonymous.' Defaults to 'Anonymous' if not provided. +# # auth_method = "Anonymous" +# +# # Required for auth_method = "UserName" +# # username = "myusername" # -# # Path to private key.pem. Required for security mode/policy != None -# # key = "/etc/telegraf/key.pem" +# # Required for auth_method = "UserName" +# # password = "mypassword" # # ## Measurements # ## node id to subscribe to @@ -129,6 +131,15 @@ nodes = [ {name="ProductUri", namespace="0", identifier_type="i", identifier="2262", data_type="string", description="http://open62541.org"}, {name="ManufacturerName", namespace="0", identifier_type="i", identifier="2263", data_type="string", description="open62541"}, ] + +## Guide: +## An OPC UA node ID may resemble: "n=3,s=Temperature" +## In this example, n=3 is indicating the namespace is '3'. +## s=Temperature is indicting that the identifier type is a 'string' and the indentifier value is 'Temperature' +## This temperature node may have a current value of 79.0, which would possibly make the value a 'float'. +## To gather data from this node you would need to enter the following line into 'nodes' property above: +## {name="SomeLabel", namespace="3", identifier_type="s", identifier="Temperature", data_type="float", description="Some description."}, + ` // Description will appear directly above the plugin definition in the config file @@ -263,7 +274,7 @@ func BuildNodeID(tag OPCTag) string { return "ns=" + tag.Namespace + ";" + tag.IdentifierType + "=" + tag.Identifier } -// Connect to a OPCUA Slave device +// Connect to a OPCUA device func Connect(o *OpcUA) error { u, err := url.Parse(o.Endpoint) if err != nil { @@ -280,24 +291,6 @@ func Connect(o *OpcUA) error { o.setupOptions() - // endpoints, err := opcua.GetEndpoints(o.Endpoint) - // if err != nil { - // return fmt.Errorf("Endpoint Error: %s", err) - // } - - // ep := opcua.SelectEndpoint(endpoints, o.SecurityPolicy, ua.MessageSecurityModeFromString(o.SecurityMode)) - // if ep == nil { - // return fmt.Errorf("Failed to find suitable endpoint") - // } - // opts := []opcua.Option{ - // opcua.SecurityPolicy(o.SecurityPolicy), - // opcua.SecurityModeString(o.SecurityMode), - // opcua.CertificateFile(o.Certificate), - // opcua.PrivateKeyFile(o.PrivateKey), - // opcua.AuthAnonymous(), - // opcua.SecurityFromEndpoint(ep, ua.UserTokenTypeAnonymous), - // } - o.client = opcua.NewClient(o.Endpoint, o.opts...) if err := o.client.Connect(o.ctx); err != nil { return fmt.Errorf("Error in Client Connection: %s", err) @@ -335,13 +328,13 @@ func (o *OpcUA) setupOptions() error { log.Fatal(err) } - if o.CertFile == "" && o.KeyFile == "" { + if o.Certificate == "" && o.PrivateKey == "" { if o.SecurityPolicy != "None" || o.SecurityMode != "None" { - o.CertFile, o.KeyFile = generateCert("urn:gopcua:client", 2048, o.CertFile, o.KeyFile, (365 * 24 * time.Hour)) + o.Certificate, o.PrivateKey = generateCert("urn:gopcua:client", 2048, o.Certificate, o.PrivateKey, (365 * 24 * time.Hour)) } } - o.opts = generateClientOpts(endpoints, o.CertFile, o.KeyFile, o.SecurityPolicy, o.SecurityMode, o.AuthMethod, o.Username, o.Password) + o.opts = generateClientOpts(endpoints, o.Certificate, o.PrivateKey, o.SecurityPolicy, o.SecurityMode, o.AuthMethod, o.Username, o.Password) return nil } @@ -400,7 +393,6 @@ func disconnect(o *OpcUA) error { func (o *OpcUA) Gather(acc telegraf.Accumulator) error { if o.state == Disconnected { o.state = Connecting - //o.Log.Debugf("Connecting %v", o.Servers) err := Connect(o) if err != nil { o.state = Disconnected From 925887cfb2cf72208dca743ff5691f585a8ac279 Mon Sep 17 00:00:00 2001 From: Chris Hayles Date: Wed, 19 Aug 2020 17:45:38 -0700 Subject: [PATCH 05/12] Removed mystery files --- internal/config/aws/credentials 2.go | 52 ---- internal/config/config_test 2.go | 259 ------------------ internal/config/testdata/inline_table 2.toml | 7 - internal/config/testdata/invalid_field 2.toml | 2 - .../testdata/single_plugin_env_vars 2.toml | 11 - internal/config/testdata/slice_comment 2.toml | 5 - .../config/testdata/subconfig/exec 2.conf | 4 - .../config/testdata/subconfig/exec 3.conf | 4 - .../testdata/subconfig/memcached 2.conf | 11 - .../testdata/subconfig/memcached 3.conf | 11 - .../config/testdata/subconfig/procstat 2.conf | 2 - .../config/testdata/subconfig/procstat 3.conf | 2 - .../config/testdata/wrong_field_type 2.toml | 2 - internal/http_go1.12 2.go | 9 - internal/models/log_test 2.go | 70 ----- internal/models/running_input 2.go | 118 -------- internal/models/running_output 2.go | 241 ---------------- internal/models/running_processor 2.go | 99 ------- internal/models/running_processor_test 2.go | 189 ------------- internal/tls/config 2.go | 166 ----------- 20 files changed, 1264 deletions(-) delete mode 100644 internal/config/aws/credentials 2.go delete mode 100644 internal/config/config_test 2.go delete mode 100644 internal/config/testdata/inline_table 2.toml delete mode 100644 internal/config/testdata/invalid_field 2.toml delete mode 100644 internal/config/testdata/single_plugin_env_vars 2.toml delete mode 100644 internal/config/testdata/slice_comment 2.toml delete mode 100644 internal/config/testdata/subconfig/exec 2.conf delete mode 100644 internal/config/testdata/subconfig/exec 3.conf delete mode 100644 internal/config/testdata/subconfig/memcached 2.conf delete mode 100644 internal/config/testdata/subconfig/memcached 3.conf delete mode 100644 internal/config/testdata/subconfig/procstat 2.conf delete mode 100644 internal/config/testdata/subconfig/procstat 3.conf delete mode 100644 internal/config/testdata/wrong_field_type 2.toml delete mode 100644 internal/http_go1.12 2.go delete mode 100644 internal/models/log_test 2.go delete mode 100644 internal/models/running_input 2.go delete mode 100644 internal/models/running_output 2.go delete mode 100644 internal/models/running_processor 2.go delete mode 100644 internal/models/running_processor_test 2.go delete mode 100644 internal/tls/config 2.go diff --git a/internal/config/aws/credentials 2.go b/internal/config/aws/credentials 2.go deleted file mode 100644 index 1e4f91b132a3b..0000000000000 --- a/internal/config/aws/credentials 2.go +++ /dev/null @@ -1,52 +0,0 @@ -package aws - -import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/client" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws/credentials/stscreds" - "github.com/aws/aws-sdk-go/aws/session" -) - -type CredentialConfig struct { - Region string - AccessKey string - SecretKey string - RoleARN string - Profile string - Filename string - Token string - EndpointURL string -} - -func (c *CredentialConfig) Credentials() client.ConfigProvider { - if c.RoleARN != "" { - return c.assumeCredentials() - } else { - return c.rootCredentials() - } -} - -func (c *CredentialConfig) rootCredentials() client.ConfigProvider { - config := &aws.Config{ - Region: aws.String(c.Region), - Endpoint: &c.EndpointURL, - } - if c.AccessKey != "" || c.SecretKey != "" { - config.Credentials = credentials.NewStaticCredentials(c.AccessKey, c.SecretKey, c.Token) - } else if c.Profile != "" || c.Filename != "" { - config.Credentials = credentials.NewSharedCredentials(c.Filename, c.Profile) - } - - return session.New(config) -} - -func (c *CredentialConfig) assumeCredentials() client.ConfigProvider { - rootCredentials := c.rootCredentials() - config := &aws.Config{ - Region: aws.String(c.Region), - Endpoint: &c.EndpointURL, - } - config.Credentials = stscreds.NewCredentials(rootCredentials, c.RoleARN) - return session.New(config) -} diff --git a/internal/config/config_test 2.go b/internal/config/config_test 2.go deleted file mode 100644 index 9d42177cd8ad7..0000000000000 --- a/internal/config/config_test 2.go +++ /dev/null @@ -1,259 +0,0 @@ -package config - -import ( - "os" - "testing" - "time" - - "github.com/influxdata/telegraf/internal" - "github.com/influxdata/telegraf/internal/models" - "github.com/influxdata/telegraf/plugins/inputs" - "github.com/influxdata/telegraf/plugins/inputs/exec" - "github.com/influxdata/telegraf/plugins/inputs/http_listener_v2" - "github.com/influxdata/telegraf/plugins/inputs/memcached" - "github.com/influxdata/telegraf/plugins/inputs/procstat" - httpOut "github.com/influxdata/telegraf/plugins/outputs/http" - "github.com/influxdata/telegraf/plugins/parsers" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestConfig_LoadSingleInputWithEnvVars(t *testing.T) { - c := NewConfig() - err := os.Setenv("MY_TEST_SERVER", "192.168.1.1") - assert.NoError(t, err) - err = os.Setenv("TEST_INTERVAL", "10s") - assert.NoError(t, err) - c.LoadConfig("./testdata/single_plugin_env_vars.toml") - - memcached := inputs.Inputs["memcached"]().(*memcached.Memcached) - memcached.Servers = []string{"192.168.1.1"} - - filter := models.Filter{ - NameDrop: []string{"metricname2"}, - NamePass: []string{"metricname1", "ip_192.168.1.1_name"}, - FieldDrop: []string{"other", "stuff"}, - FieldPass: []string{"some", "strings"}, - TagDrop: []models.TagFilter{ - { - Name: "badtag", - Filter: []string{"othertag"}, - }, - }, - TagPass: []models.TagFilter{ - { - Name: "goodtag", - Filter: []string{"mytag"}, - }, - }, - } - assert.NoError(t, filter.Compile()) - mConfig := &models.InputConfig{ - Name: "memcached", - Filter: filter, - Interval: 10 * time.Second, - } - mConfig.Tags = make(map[string]string) - - assert.Equal(t, memcached, c.Inputs[0].Input, - "Testdata did not produce a correct memcached struct.") - assert.Equal(t, mConfig, c.Inputs[0].Config, - "Testdata did not produce correct memcached metadata.") -} - -func TestConfig_LoadSingleInput(t *testing.T) { - c := NewConfig() - c.LoadConfig("./testdata/single_plugin.toml") - - memcached := inputs.Inputs["memcached"]().(*memcached.Memcached) - memcached.Servers = []string{"localhost"} - - filter := models.Filter{ - NameDrop: []string{"metricname2"}, - NamePass: []string{"metricname1"}, - FieldDrop: []string{"other", "stuff"}, - FieldPass: []string{"some", "strings"}, - TagDrop: []models.TagFilter{ - { - Name: "badtag", - Filter: []string{"othertag"}, - }, - }, - TagPass: []models.TagFilter{ - { - Name: "goodtag", - Filter: []string{"mytag"}, - }, - }, - } - assert.NoError(t, filter.Compile()) - mConfig := &models.InputConfig{ - Name: "memcached", - Filter: filter, - Interval: 5 * time.Second, - } - mConfig.Tags = make(map[string]string) - - assert.Equal(t, memcached, c.Inputs[0].Input, - "Testdata did not produce a correct memcached struct.") - assert.Equal(t, mConfig, c.Inputs[0].Config, - "Testdata did not produce correct memcached metadata.") -} - -func TestConfig_LoadDirectory(t *testing.T) { - c := NewConfig() - err := c.LoadConfig("./testdata/single_plugin.toml") - if err != nil { - t.Error(err) - } - err = c.LoadDirectory("./testdata/subconfig") - if err != nil { - t.Error(err) - } - - memcached := inputs.Inputs["memcached"]().(*memcached.Memcached) - memcached.Servers = []string{"localhost"} - - filter := models.Filter{ - NameDrop: []string{"metricname2"}, - NamePass: []string{"metricname1"}, - FieldDrop: []string{"other", "stuff"}, - FieldPass: []string{"some", "strings"}, - TagDrop: []models.TagFilter{ - { - Name: "badtag", - Filter: []string{"othertag"}, - }, - }, - TagPass: []models.TagFilter{ - { - Name: "goodtag", - Filter: []string{"mytag"}, - }, - }, - } - assert.NoError(t, filter.Compile()) - mConfig := &models.InputConfig{ - Name: "memcached", - Filter: filter, - Interval: 5 * time.Second, - } - mConfig.Tags = make(map[string]string) - - assert.Equal(t, memcached, c.Inputs[0].Input, - "Testdata did not produce a correct memcached struct.") - assert.Equal(t, mConfig, c.Inputs[0].Config, - "Testdata did not produce correct memcached metadata.") - - ex := inputs.Inputs["exec"]().(*exec.Exec) - p, err := parsers.NewParser(&parsers.Config{ - MetricName: "exec", - DataFormat: "json", - JSONStrict: true, - }) - assert.NoError(t, err) - ex.SetParser(p) - ex.Command = "/usr/bin/myothercollector --foo=bar" - eConfig := &models.InputConfig{ - Name: "exec", - MeasurementSuffix: "_myothercollector", - } - eConfig.Tags = make(map[string]string) - - exec := c.Inputs[1].Input.(*exec.Exec) - require.NotNil(t, exec.Log) - exec.Log = nil - - assert.Equal(t, ex, c.Inputs[1].Input, - "Merged Testdata did not produce a correct exec struct.") - assert.Equal(t, eConfig, c.Inputs[1].Config, - "Merged Testdata did not produce correct exec metadata.") - - memcached.Servers = []string{"192.168.1.1"} - assert.Equal(t, memcached, c.Inputs[2].Input, - "Testdata did not produce a correct memcached struct.") - assert.Equal(t, mConfig, c.Inputs[2].Config, - "Testdata did not produce correct memcached metadata.") - - pstat := inputs.Inputs["procstat"]().(*procstat.Procstat) - pstat.PidFile = "/var/run/grafana-server.pid" - - pConfig := &models.InputConfig{Name: "procstat"} - pConfig.Tags = make(map[string]string) - - assert.Equal(t, pstat, c.Inputs[3].Input, - "Merged Testdata did not produce a correct procstat struct.") - assert.Equal(t, pConfig, c.Inputs[3].Config, - "Merged Testdata did not produce correct procstat metadata.") -} - -func TestConfig_LoadSpecialTypes(t *testing.T) { - c := NewConfig() - err := c.LoadConfig("./testdata/special_types.toml") - assert.NoError(t, err) - require.Equal(t, 1, len(c.Inputs)) - - inputHTTPListener, ok := c.Inputs[0].Input.(*http_listener_v2.HTTPListenerV2) - assert.Equal(t, true, ok) - // Tests telegraf duration parsing. - assert.Equal(t, internal.Duration{Duration: time.Second}, inputHTTPListener.WriteTimeout) - // Tests telegraf size parsing. - assert.Equal(t, internal.Size{Size: 1024 * 1024}, inputHTTPListener.MaxBodySize) - // Tests toml multiline basic strings. - assert.Equal(t, "/path/to/my/cert\n", inputHTTPListener.TLSCert) -} - -func TestConfig_FieldNotDefined(t *testing.T) { - c := NewConfig() - err := c.LoadConfig("./testdata/invalid_field.toml") - require.Error(t, err, "invalid field name") - assert.Equal(t, "Error parsing ./testdata/invalid_field.toml, line 2: field corresponding to `not_a_field' is not defined in http_listener_v2.HTTPListenerV2", err.Error()) - -} - -func TestConfig_WrongFieldType(t *testing.T) { - c := NewConfig() - err := c.LoadConfig("./testdata/wrong_field_type.toml") - require.Error(t, err, "invalid field type") - assert.Equal(t, "Error parsing ./testdata/wrong_field_type.toml, line 2: (http_listener_v2.HTTPListenerV2.Port) cannot unmarshal TOML string into int", err.Error()) - - c = NewConfig() - err = c.LoadConfig("./testdata/wrong_field_type2.toml") - require.Error(t, err, "invalid field type2") - assert.Equal(t, "Error parsing ./testdata/wrong_field_type2.toml, line 2: (http_listener_v2.HTTPListenerV2.Methods) cannot unmarshal TOML string into []string", err.Error()) -} - -func TestConfig_InlineTables(t *testing.T) { - // #4098 - c := NewConfig() - err := c.LoadConfig("./testdata/inline_table.toml") - assert.NoError(t, err) - require.Equal(t, 2, len(c.Outputs)) - - outputHTTP, ok := c.Outputs[1].Output.(*httpOut.HTTP) - assert.Equal(t, true, ok) - assert.Equal(t, map[string]string{"Authorization": "Token $TOKEN", "Content-Type": "application/json"}, outputHTTP.Headers) - assert.Equal(t, []string{"org_id"}, c.Outputs[0].Config.Filter.TagInclude) -} - -func TestConfig_SliceComment(t *testing.T) { - t.Skipf("Skipping until #3642 is resolved") - - c := NewConfig() - err := c.LoadConfig("./testdata/slice_comment.toml") - assert.NoError(t, err) - require.Equal(t, 1, len(c.Outputs)) - - outputHTTP, ok := c.Outputs[0].Output.(*httpOut.HTTP) - assert.Equal(t, []string{"test"}, outputHTTP.Scopes) - assert.Equal(t, true, ok) -} - -func TestConfig_BadOrdering(t *testing.T) { - // #3444: when not using inline tables, care has to be taken so subsequent configuration - // doesn't become part of the table. This is not a bug, but TOML syntax. - c := NewConfig() - err := c.LoadConfig("./testdata/non_slice_slice.toml") - require.Error(t, err, "bad ordering") - assert.Equal(t, "Error parsing ./testdata/non_slice_slice.toml, line 4: cannot unmarshal TOML array into string (need slice)", err.Error()) -} diff --git a/internal/config/testdata/inline_table 2.toml b/internal/config/testdata/inline_table 2.toml deleted file mode 100644 index 525fdce17e389..0000000000000 --- a/internal/config/testdata/inline_table 2.toml +++ /dev/null @@ -1,7 +0,0 @@ -[[outputs.http]] - headers = { Authorization = "Token $TOKEN",Content-Type = "application/json" } - taginclude = ["org_id"] - -[[outputs.http]] - headers = { Authorization = "Token $TOKEN",Content-Type = "application/json" } - taginclude = ["org_id"] diff --git a/internal/config/testdata/invalid_field 2.toml b/internal/config/testdata/invalid_field 2.toml deleted file mode 100644 index 4c718d7bbe998..0000000000000 --- a/internal/config/testdata/invalid_field 2.toml +++ /dev/null @@ -1,2 +0,0 @@ -[[inputs.http_listener_v2]] - not_a_field = true diff --git a/internal/config/testdata/single_plugin_env_vars 2.toml b/internal/config/testdata/single_plugin_env_vars 2.toml deleted file mode 100644 index b1f71ea8adb78..0000000000000 --- a/internal/config/testdata/single_plugin_env_vars 2.toml +++ /dev/null @@ -1,11 +0,0 @@ -[[inputs.memcached]] - servers = ["$MY_TEST_SERVER"] - namepass = ["metricname1", "ip_${MY_TEST_SERVER}_name"] - namedrop = ["metricname2"] - fieldpass = ["some", "strings"] - fielddrop = ["other", "stuff"] - interval = "$TEST_INTERVAL" - [inputs.memcached.tagpass] - goodtag = ["mytag"] - [inputs.memcached.tagdrop] - badtag = ["othertag"] diff --git a/internal/config/testdata/slice_comment 2.toml b/internal/config/testdata/slice_comment 2.toml deleted file mode 100644 index 1177e5f8901e2..0000000000000 --- a/internal/config/testdata/slice_comment 2.toml +++ /dev/null @@ -1,5 +0,0 @@ -[[outputs.http]] - scopes = [ - # comment - "test" # comment - ] diff --git a/internal/config/testdata/subconfig/exec 2.conf b/internal/config/testdata/subconfig/exec 2.conf deleted file mode 100644 index d621e78e08563..0000000000000 --- a/internal/config/testdata/subconfig/exec 2.conf +++ /dev/null @@ -1,4 +0,0 @@ -[[inputs.exec]] - # the command to run - command = "/usr/bin/myothercollector --foo=bar" - name_suffix = "_myothercollector" diff --git a/internal/config/testdata/subconfig/exec 3.conf b/internal/config/testdata/subconfig/exec 3.conf deleted file mode 100644 index d621e78e08563..0000000000000 --- a/internal/config/testdata/subconfig/exec 3.conf +++ /dev/null @@ -1,4 +0,0 @@ -[[inputs.exec]] - # the command to run - command = "/usr/bin/myothercollector --foo=bar" - name_suffix = "_myothercollector" diff --git a/internal/config/testdata/subconfig/memcached 2.conf b/internal/config/testdata/subconfig/memcached 2.conf deleted file mode 100644 index 2cd07d15d331d..0000000000000 --- a/internal/config/testdata/subconfig/memcached 2.conf +++ /dev/null @@ -1,11 +0,0 @@ -[[inputs.memcached]] - servers = ["192.168.1.1"] - namepass = ["metricname1"] - namedrop = ["metricname2"] - pass = ["some", "strings"] - drop = ["other", "stuff"] - interval = "5s" - [inputs.memcached.tagpass] - goodtag = ["mytag"] - [inputs.memcached.tagdrop] - badtag = ["othertag"] diff --git a/internal/config/testdata/subconfig/memcached 3.conf b/internal/config/testdata/subconfig/memcached 3.conf deleted file mode 100644 index 2cd07d15d331d..0000000000000 --- a/internal/config/testdata/subconfig/memcached 3.conf +++ /dev/null @@ -1,11 +0,0 @@ -[[inputs.memcached]] - servers = ["192.168.1.1"] - namepass = ["metricname1"] - namedrop = ["metricname2"] - pass = ["some", "strings"] - drop = ["other", "stuff"] - interval = "5s" - [inputs.memcached.tagpass] - goodtag = ["mytag"] - [inputs.memcached.tagdrop] - badtag = ["othertag"] diff --git a/internal/config/testdata/subconfig/procstat 2.conf b/internal/config/testdata/subconfig/procstat 2.conf deleted file mode 100644 index 82708667f8e39..0000000000000 --- a/internal/config/testdata/subconfig/procstat 2.conf +++ /dev/null @@ -1,2 +0,0 @@ -[[inputs.procstat]] - pid_file = "/var/run/grafana-server.pid" diff --git a/internal/config/testdata/subconfig/procstat 3.conf b/internal/config/testdata/subconfig/procstat 3.conf deleted file mode 100644 index 82708667f8e39..0000000000000 --- a/internal/config/testdata/subconfig/procstat 3.conf +++ /dev/null @@ -1,2 +0,0 @@ -[[inputs.procstat]] - pid_file = "/var/run/grafana-server.pid" diff --git a/internal/config/testdata/wrong_field_type 2.toml b/internal/config/testdata/wrong_field_type 2.toml deleted file mode 100644 index 237176e7e54b4..0000000000000 --- a/internal/config/testdata/wrong_field_type 2.toml +++ /dev/null @@ -1,2 +0,0 @@ -[[inputs.http_listener_v2]] - port = "80" diff --git a/internal/http_go1.12 2.go b/internal/http_go1.12 2.go deleted file mode 100644 index d5b1a847f12a7..0000000000000 --- a/internal/http_go1.12 2.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build go1.12 - -package internal - -import "net/http" - -func CloseIdleConnections(c *http.Client) { - c.CloseIdleConnections() -} diff --git a/internal/models/log_test 2.go b/internal/models/log_test 2.go deleted file mode 100644 index d4bb6ca09b019..0000000000000 --- a/internal/models/log_test 2.go +++ /dev/null @@ -1,70 +0,0 @@ -package models - -import ( - "testing" - - "github.com/influxdata/telegraf/selfstat" - "github.com/stretchr/testify/require" -) - -func TestErrorCounting(t *testing.T) { - iLog := Logger{Name: "inputs.test", Errs: selfstat.Register( - "gather", - "errors", - map[string]string{"input": "test"}, - )} - iLog.Error("something went wrong") - iLog.Errorf("something went wrong") - - aLog := Logger{Name: "aggregators.test", Errs: selfstat.Register( - "aggregate", - "errors", - map[string]string{"aggregator": "test"}, - )} - aLog.Name = "aggregators.test" - aLog.Error("another thing happened") - - oLog := Logger{Name: "outputs.test", Errs: selfstat.Register( - "write", - "errors", - map[string]string{"output": "test"}, - )} - oLog.Error("another thing happened") - - pLog := Logger{Name: "processors.test", Errs: selfstat.Register( - "process", - "errors", - map[string]string{"processor": "test"}, - )} - pLog.Error("another thing happened") - - require.Equal(t, int64(2), iLog.Errs.Get()) - require.Equal(t, int64(1), aLog.Errs.Get()) - require.Equal(t, int64(1), oLog.Errs.Get()) - require.Equal(t, int64(1), pLog.Errs.Get()) -} - -func TestLogging(t *testing.T) { - log := Logger{Name: "inputs.test", Errs: selfstat.Register( - "gather", - "errors", - map[string]string{"input": "test"}, - )} - - log.Errs.Set(0) - - log.Debugf("something happened") - log.Debug("something happened") - - log.Warnf("something happened") - log.Warn("something happened") - require.Equal(t, int64(0), log.Errs.Get()) - - log.Infof("something happened") - log.Info("something happened") - require.Equal(t, int64(0), log.Errs.Get()) - - log.Errorf("something happened") - log.Error("something happened") - require.Equal(t, int64(2), log.Errs.Get()) -} diff --git a/internal/models/running_input 2.go b/internal/models/running_input 2.go deleted file mode 100644 index c09fb1409f226..0000000000000 --- a/internal/models/running_input 2.go +++ /dev/null @@ -1,118 +0,0 @@ -package models - -import ( - "time" - - "github.com/influxdata/telegraf" - "github.com/influxdata/telegraf/selfstat" -) - -var GlobalMetricsGathered = selfstat.Register("agent", "metrics_gathered", map[string]string{}) - -type RunningInput struct { - Input telegraf.Input - Config *InputConfig - - log telegraf.Logger - defaultTags map[string]string - - MetricsGathered selfstat.Stat - GatherTime selfstat.Stat -} - -func NewRunningInput(input telegraf.Input, config *InputConfig) *RunningInput { - tags := map[string]string{"input": config.Name} - if config.Alias != "" { - tags["alias"] = config.Alias - } - - logger := &Logger{ - Name: logName("inputs", config.Name, config.Alias), - Errs: selfstat.Register("gather", "errors", tags), - } - setLogIfExist(input, logger) - - return &RunningInput{ - Input: input, - Config: config, - MetricsGathered: selfstat.Register( - "gather", - "metrics_gathered", - tags, - ), - GatherTime: selfstat.RegisterTiming( - "gather", - "gather_time_ns", - tags, - ), - log: logger, - } -} - -// InputConfig is the common config for all inputs. -type InputConfig struct { - Name string - Alias string - Interval time.Duration - - NameOverride string - MeasurementPrefix string - MeasurementSuffix string - Tags map[string]string - Filter Filter -} - -func (r *RunningInput) metricFiltered(metric telegraf.Metric) { - metric.Drop() -} - -func (r *RunningInput) LogName() string { - return logName("inputs", r.Config.Name, r.Config.Alias) -} - -func (r *RunningInput) Init() error { - if p, ok := r.Input.(telegraf.Initializer); ok { - err := p.Init() - if err != nil { - return err - } - } - return nil -} - -func (r *RunningInput) MakeMetric(metric telegraf.Metric) telegraf.Metric { - if ok := r.Config.Filter.Select(metric); !ok { - r.metricFiltered(metric) - return nil - } - - m := makemetric( - metric, - r.Config.NameOverride, - r.Config.MeasurementPrefix, - r.Config.MeasurementSuffix, - r.Config.Tags, - r.defaultTags) - - r.Config.Filter.Modify(metric) - if len(metric.FieldList()) == 0 { - r.metricFiltered(metric) - return nil - } - - r.MetricsGathered.Incr(1) - GlobalMetricsGathered.Incr(1) - return m -} - -func (r *RunningInput) Gather(acc telegraf.Accumulator) error { - start := time.Now() - err := r.Input.Gather(acc) - elapsed := time.Since(start) - r.GatherTime.Incr(elapsed.Nanoseconds()) - return err -} - -func (r *RunningInput) SetDefaultTags(tags map[string]string) { - r.defaultTags = tags -} diff --git a/internal/models/running_output 2.go b/internal/models/running_output 2.go deleted file mode 100644 index 752cf34ef127d..0000000000000 --- a/internal/models/running_output 2.go +++ /dev/null @@ -1,241 +0,0 @@ -package models - -import ( - "sync" - "sync/atomic" - "time" - - "github.com/influxdata/telegraf" - "github.com/influxdata/telegraf/selfstat" -) - -const ( - // Default size of metrics batch size. - DEFAULT_METRIC_BATCH_SIZE = 1000 - - // Default number of metrics kept. It should be a multiple of batch size. - DEFAULT_METRIC_BUFFER_LIMIT = 10000 -) - -// OutputConfig containing name and filter -type OutputConfig struct { - Name string - Alias string - Filter Filter - - FlushInterval time.Duration - FlushJitter *time.Duration - MetricBufferLimit int - MetricBatchSize int -} - -// RunningOutput contains the output configuration -type RunningOutput struct { - // Must be 64-bit aligned - newMetricsCount int64 - droppedMetrics int64 - - Output telegraf.Output - Config *OutputConfig - MetricBufferLimit int - MetricBatchSize int - - MetricsFiltered selfstat.Stat - WriteTime selfstat.Stat - - BatchReady chan time.Time - - buffer *Buffer - log telegraf.Logger - - aggMutex sync.Mutex -} - -func NewRunningOutput( - name string, - output telegraf.Output, - config *OutputConfig, - batchSize int, - bufferLimit int, -) *RunningOutput { - tags := map[string]string{"output": config.Name} - if config.Alias != "" { - tags["alias"] = config.Alias - } - - logger := &Logger{ - Name: logName("outputs", config.Name, config.Alias), - Errs: selfstat.Register("write", "errors", tags), - } - setLogIfExist(output, logger) - - if config.MetricBufferLimit > 0 { - bufferLimit = config.MetricBufferLimit - } - if bufferLimit == 0 { - bufferLimit = DEFAULT_METRIC_BUFFER_LIMIT - } - if config.MetricBatchSize > 0 { - batchSize = config.MetricBatchSize - } - if batchSize == 0 { - batchSize = DEFAULT_METRIC_BATCH_SIZE - } - - ro := &RunningOutput{ - buffer: NewBuffer(config.Name, config.Alias, bufferLimit), - BatchReady: make(chan time.Time, 1), - Output: output, - Config: config, - MetricBufferLimit: bufferLimit, - MetricBatchSize: batchSize, - MetricsFiltered: selfstat.Register( - "write", - "metrics_filtered", - tags, - ), - WriteTime: selfstat.RegisterTiming( - "write", - "write_time_ns", - tags, - ), - log: logger, - } - - return ro -} - -func (r *RunningOutput) LogName() string { - return logName("outputs", r.Config.Name, r.Config.Alias) -} - -func (ro *RunningOutput) metricFiltered(metric telegraf.Metric) { - ro.MetricsFiltered.Incr(1) - metric.Drop() -} - -func (r *RunningOutput) Init() error { - if p, ok := r.Output.(telegraf.Initializer); ok { - err := p.Init() - if err != nil { - return err - } - - } - return nil -} - -// AddMetric adds a metric to the output. -// -// Takes ownership of metric -func (ro *RunningOutput) AddMetric(metric telegraf.Metric) { - if ok := ro.Config.Filter.Select(metric); !ok { - ro.metricFiltered(metric) - return - } - - ro.Config.Filter.Modify(metric) - if len(metric.FieldList()) == 0 { - ro.metricFiltered(metric) - return - } - - if output, ok := ro.Output.(telegraf.AggregatingOutput); ok { - ro.aggMutex.Lock() - output.Add(metric) - ro.aggMutex.Unlock() - return - } - - dropped := ro.buffer.Add(metric) - atomic.AddInt64(&ro.droppedMetrics, int64(dropped)) - - count := atomic.AddInt64(&ro.newMetricsCount, 1) - if count == int64(ro.MetricBatchSize) { - atomic.StoreInt64(&ro.newMetricsCount, 0) - select { - case ro.BatchReady <- time.Now(): - default: - } - } -} - -// Write writes all metrics to the output, stopping when all have been sent on -// or error. -func (ro *RunningOutput) Write() error { - if output, ok := ro.Output.(telegraf.AggregatingOutput); ok { - ro.aggMutex.Lock() - metrics := output.Push() - ro.buffer.Add(metrics...) - output.Reset() - ro.aggMutex.Unlock() - } - - atomic.StoreInt64(&ro.newMetricsCount, 0) - - // Only process the metrics in the buffer now. Metrics added while we are - // writing will be sent on the next call. - nBuffer := ro.buffer.Len() - nBatches := nBuffer/ro.MetricBatchSize + 1 - for i := 0; i < nBatches; i++ { - batch := ro.buffer.Batch(ro.MetricBatchSize) - if len(batch) == 0 { - break - } - - err := ro.write(batch) - if err != nil { - ro.buffer.Reject(batch) - return err - } - ro.buffer.Accept(batch) - } - return nil -} - -// WriteBatch writes a single batch of metrics to the output. -func (ro *RunningOutput) WriteBatch() error { - batch := ro.buffer.Batch(ro.MetricBatchSize) - if len(batch) == 0 { - return nil - } - - err := ro.write(batch) - if err != nil { - ro.buffer.Reject(batch) - return err - } - ro.buffer.Accept(batch) - - return nil -} - -func (r *RunningOutput) Close() { - err := r.Output.Close() - if err != nil { - r.log.Errorf("Error closing output: %v", err) - } -} - -func (r *RunningOutput) write(metrics []telegraf.Metric) error { - dropped := atomic.LoadInt64(&r.droppedMetrics) - if dropped > 0 { - r.log.Warnf("Metric buffer overflow; %d metrics have been dropped", dropped) - atomic.StoreInt64(&r.droppedMetrics, 0) - } - - start := time.Now() - err := r.Output.Write(metrics) - elapsed := time.Since(start) - r.WriteTime.Incr(elapsed.Nanoseconds()) - - if err == nil { - r.log.Debugf("Wrote batch of %d metrics in %s", len(metrics), elapsed) - } - return err -} - -func (r *RunningOutput) LogBufferStatus() { - nBuffer := r.buffer.Len() - r.log.Debugf("Buffer fullness: %d / %d metrics", nBuffer, r.MetricBufferLimit) -} diff --git a/internal/models/running_processor 2.go b/internal/models/running_processor 2.go deleted file mode 100644 index 22a7d01987769..0000000000000 --- a/internal/models/running_processor 2.go +++ /dev/null @@ -1,99 +0,0 @@ -package models - -import ( - "sync" - - "github.com/influxdata/telegraf" - "github.com/influxdata/telegraf/selfstat" -) - -type RunningProcessor struct { - sync.Mutex - log telegraf.Logger - Processor telegraf.Processor - Config *ProcessorConfig -} - -type RunningProcessors []*RunningProcessor - -func (rp RunningProcessors) Len() int { return len(rp) } -func (rp RunningProcessors) Swap(i, j int) { rp[i], rp[j] = rp[j], rp[i] } -func (rp RunningProcessors) Less(i, j int) bool { return rp[i].Config.Order < rp[j].Config.Order } - -// FilterConfig containing a name and filter -type ProcessorConfig struct { - Name string - Alias string - Order int64 - Filter Filter -} - -func NewRunningProcessor(processor telegraf.Processor, config *ProcessorConfig) *RunningProcessor { - tags := map[string]string{"processor": config.Name} - if config.Alias != "" { - tags["alias"] = config.Alias - } - - logger := &Logger{ - Name: logName("processors", config.Name, config.Alias), - Errs: selfstat.Register("process", "errors", tags), - } - setLogIfExist(processor, logger) - - return &RunningProcessor{ - Processor: processor, - Config: config, - log: logger, - } -} - -func (rp *RunningProcessor) metricFiltered(metric telegraf.Metric) { - metric.Drop() -} - -func containsMetric(item telegraf.Metric, metrics []telegraf.Metric) bool { - for _, m := range metrics { - if item == m { - return true - } - } - return false -} - -func (r *RunningProcessor) Init() error { - if p, ok := r.Processor.(telegraf.Initializer); ok { - err := p.Init() - if err != nil { - return err - } - } - return nil -} - -func (rp *RunningProcessor) Apply(in ...telegraf.Metric) []telegraf.Metric { - rp.Lock() - defer rp.Unlock() - - ret := []telegraf.Metric{} - - for _, metric := range in { - // In processors when a filter selects a metric it is sent through the - // processor. Otherwise the metric continues downstream unmodified. - if ok := rp.Config.Filter.Select(metric); !ok { - ret = append(ret, metric) - continue - } - - rp.Config.Filter.Modify(metric) - if len(metric.FieldList()) == 0 { - rp.metricFiltered(metric) - continue - } - - // This metric should pass through the filter, so call the filter Apply - // function and append results to the output slice. - ret = append(ret, rp.Processor.Apply(metric)...) - } - - return ret -} diff --git a/internal/models/running_processor_test 2.go b/internal/models/running_processor_test 2.go deleted file mode 100644 index c24347b8ecf8e..0000000000000 --- a/internal/models/running_processor_test 2.go +++ /dev/null @@ -1,189 +0,0 @@ -package models - -import ( - "sort" - "testing" - "time" - - "github.com/influxdata/telegraf" - "github.com/influxdata/telegraf/testutil" - - "github.com/stretchr/testify/require" -) - -// MockProcessor is a Processor with an overrideable Apply implementation. -type MockProcessor struct { - ApplyF func(in ...telegraf.Metric) []telegraf.Metric -} - -func (p *MockProcessor) SampleConfig() string { - return "" -} - -func (p *MockProcessor) Description() string { - return "" -} - -func (p *MockProcessor) Apply(in ...telegraf.Metric) []telegraf.Metric { - return p.ApplyF(in...) -} - -// TagProcessor returns a Processor whose Apply function adds the tag and -// value. -func TagProcessor(key, value string) *MockProcessor { - return &MockProcessor{ - ApplyF: func(in ...telegraf.Metric) []telegraf.Metric { - for _, m := range in { - m.AddTag(key, value) - } - return in - }, - } -} - -func TestRunningProcessor_Apply(t *testing.T) { - type args struct { - Processor telegraf.Processor - Config *ProcessorConfig - } - - tests := []struct { - name string - args args - input []telegraf.Metric - expected []telegraf.Metric - }{ - { - name: "inactive filter applies metrics", - args: args{ - Processor: TagProcessor("apply", "true"), - Config: &ProcessorConfig{ - Filter: Filter{}, - }, - }, - input: []telegraf.Metric{ - testutil.MustMetric( - "cpu", - map[string]string{}, - map[string]interface{}{ - "value": 42.0, - }, - time.Unix(0, 0), - ), - }, - expected: []telegraf.Metric{ - testutil.MustMetric( - "cpu", - map[string]string{ - "apply": "true", - }, - map[string]interface{}{ - "value": 42.0, - }, - time.Unix(0, 0), - ), - }, - }, - { - name: "filter applies", - args: args{ - Processor: TagProcessor("apply", "true"), - Config: &ProcessorConfig{ - Filter: Filter{ - NamePass: []string{"cpu"}, - }, - }, - }, - input: []telegraf.Metric{ - testutil.MustMetric( - "cpu", - map[string]string{}, - map[string]interface{}{ - "value": 42.0, - }, - time.Unix(0, 0), - ), - }, - expected: []telegraf.Metric{ - testutil.MustMetric( - "cpu", - map[string]string{ - "apply": "true", - }, - map[string]interface{}{ - "value": 42.0, - }, - time.Unix(0, 0), - ), - }, - }, - { - name: "filter doesn't apply", - args: args{ - Processor: TagProcessor("apply", "true"), - Config: &ProcessorConfig{ - Filter: Filter{ - NameDrop: []string{"cpu"}, - }, - }, - }, - input: []telegraf.Metric{ - testutil.MustMetric( - "cpu", - map[string]string{}, - map[string]interface{}{ - "value": 42.0, - }, - time.Unix(0, 0), - ), - }, - expected: []telegraf.Metric{ - testutil.MustMetric( - "cpu", - map[string]string{}, - map[string]interface{}{ - "value": 42.0, - }, - time.Unix(0, 0), - ), - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rp := &RunningProcessor{ - Processor: tt.args.Processor, - Config: tt.args.Config, - } - rp.Config.Filter.Compile() - - actual := rp.Apply(tt.input...) - require.Equal(t, tt.expected, actual) - }) - } -} - -func TestRunningProcessor_Order(t *testing.T) { - rp1 := &RunningProcessor{ - Config: &ProcessorConfig{ - Order: 1, - }, - } - rp2 := &RunningProcessor{ - Config: &ProcessorConfig{ - Order: 2, - }, - } - rp3 := &RunningProcessor{ - Config: &ProcessorConfig{ - Order: 3, - }, - } - - procs := RunningProcessors{rp2, rp3, rp1} - sort.Sort(procs) - require.Equal(t, - RunningProcessors{rp1, rp2, rp3}, - procs) -} diff --git a/internal/tls/config 2.go b/internal/tls/config 2.go deleted file mode 100644 index 185c92cd04865..0000000000000 --- a/internal/tls/config 2.go +++ /dev/null @@ -1,166 +0,0 @@ -package tls - -import ( - "crypto/tls" - "crypto/x509" - "fmt" - "io/ioutil" - "strings" -) - -// ClientConfig represents the standard client TLS config. -type ClientConfig struct { - TLSCA string `toml:"tls_ca"` - TLSCert string `toml:"tls_cert"` - TLSKey string `toml:"tls_key"` - InsecureSkipVerify bool `toml:"insecure_skip_verify"` - - // Deprecated in 1.7; use TLS variables above - SSLCA string `toml:"ssl_ca"` - SSLCert string `toml:"ssl_cert"` - SSLKey string `toml:"ssl_key"` -} - -// ServerConfig represents the standard server TLS config. -type ServerConfig struct { - TLSCert string `toml:"tls_cert"` - TLSKey string `toml:"tls_key"` - TLSAllowedCACerts []string `toml:"tls_allowed_cacerts"` - TLSCipherSuites []string `toml:"tls_cipher_suites"` - TLSMinVersion string `toml:"tls_min_version"` - TLSMaxVersion string `toml:"tls_max_version"` -} - -// TLSConfig returns a tls.Config, may be nil without error if TLS is not -// configured. -func (c *ClientConfig) TLSConfig() (*tls.Config, error) { - // Support deprecated variable names - if c.TLSCA == "" && c.SSLCA != "" { - c.TLSCA = c.SSLCA - } - if c.TLSCert == "" && c.SSLCert != "" { - c.TLSCert = c.SSLCert - } - if c.TLSKey == "" && c.SSLKey != "" { - c.TLSKey = c.SSLKey - } - - // TODO: return default tls.Config; plugins should not call if they don't - // want TLS, this will require using another option to determine. In the - // case of an HTTP plugin, you could use `https`. Other plugins may need - // the dedicated option `TLSEnable`. - if c.TLSCA == "" && c.TLSKey == "" && c.TLSCert == "" && !c.InsecureSkipVerify { - return nil, nil - } - - tlsConfig := &tls.Config{ - InsecureSkipVerify: c.InsecureSkipVerify, - Renegotiation: tls.RenegotiateNever, - } - - if c.TLSCA != "" { - pool, err := makeCertPool([]string{c.TLSCA}) - if err != nil { - return nil, err - } - tlsConfig.RootCAs = pool - } - - if c.TLSCert != "" && c.TLSKey != "" { - err := loadCertificate(tlsConfig, c.TLSCert, c.TLSKey) - if err != nil { - return nil, err - } - } - - return tlsConfig, nil -} - -// TLSConfig returns a tls.Config, may be nil without error if TLS is not -// configured. -func (c *ServerConfig) TLSConfig() (*tls.Config, error) { - if c.TLSCert == "" && c.TLSKey == "" && len(c.TLSAllowedCACerts) == 0 { - return nil, nil - } - - tlsConfig := &tls.Config{} - - if len(c.TLSAllowedCACerts) != 0 { - pool, err := makeCertPool(c.TLSAllowedCACerts) - if err != nil { - return nil, err - } - tlsConfig.ClientCAs = pool - tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert - } - - if c.TLSCert != "" && c.TLSKey != "" { - err := loadCertificate(tlsConfig, c.TLSCert, c.TLSKey) - if err != nil { - return nil, err - } - } - - if len(c.TLSCipherSuites) != 0 { - cipherSuites, err := ParseCiphers(c.TLSCipherSuites) - if err != nil { - return nil, fmt.Errorf( - "could not parse server cipher suites %s: %v", strings.Join(c.TLSCipherSuites, ","), err) - } - tlsConfig.CipherSuites = cipherSuites - } - - if c.TLSMaxVersion != "" { - version, err := ParseTLSVersion(c.TLSMaxVersion) - if err != nil { - return nil, fmt.Errorf( - "could not parse tls max version %q: %v", c.TLSMaxVersion, err) - } - tlsConfig.MaxVersion = version - } - - if c.TLSMinVersion != "" { - version, err := ParseTLSVersion(c.TLSMinVersion) - if err != nil { - return nil, fmt.Errorf( - "could not parse tls min version %q: %v", c.TLSMinVersion, err) - } - tlsConfig.MinVersion = version - } - - if tlsConfig.MinVersion != 0 && tlsConfig.MaxVersion != 0 && tlsConfig.MinVersion > tlsConfig.MaxVersion { - return nil, fmt.Errorf( - "tls min version %q can't be greater then tls max version %q", tlsConfig.MinVersion, tlsConfig.MaxVersion) - } - - return tlsConfig, nil -} - -func makeCertPool(certFiles []string) (*x509.CertPool, error) { - pool := x509.NewCertPool() - for _, certFile := range certFiles { - pem, err := ioutil.ReadFile(certFile) - if err != nil { - return nil, fmt.Errorf( - "could not read certificate %q: %v", certFile, err) - } - ok := pool.AppendCertsFromPEM(pem) - if !ok { - return nil, fmt.Errorf( - "could not parse any PEM certificates %q: %v", certFile, err) - } - } - return pool, nil -} - -func loadCertificate(config *tls.Config, certFile, keyFile string) error { - cert, err := tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return fmt.Errorf( - "could not load keypair %s:%s: %v", certFile, keyFile, err) - } - - config.Certificates = []tls.Certificate{cert} - config.BuildNameToCertificate() - return nil -} From 32ed4dea69f9ba4b9869e9632506933211a80fd6 Mon Sep 17 00:00:00 2001 From: Chris Hayles Date: Wed, 19 Aug 2020 17:52:59 -0700 Subject: [PATCH 06/12] Added to LICENSE_OF_DEPENDENCIES.md --- docs/LICENSE_OF_DEPENDENCIES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/LICENSE_OF_DEPENDENCIES.md b/docs/LICENSE_OF_DEPENDENCIES.md index 9c2a2d71ddb83..6b800ee1743b9 100644 --- a/docs/LICENSE_OF_DEPENDENCIES.md +++ b/docs/LICENSE_OF_DEPENDENCIES.md @@ -68,6 +68,7 @@ following works: - github.com/google/go-github [BSD 3-Clause "New" or "Revised" License](https://github.com/google/go-github/blob/master/LICENSE) - github.com/google/go-querystring [BSD 3-Clause "New" or "Revised" License](https://github.com/google/go-querystring/blob/master/LICENSE) - github.com/googleapis/gax-go [BSD 3-Clause "New" or "Revised" License](https://github.com/googleapis/gax-go/blob/master/LICENSE) +- github.com/gopcua/opcua [MIT License](https://github.com/gopcua/opcua/blob/master/LICENSE) - github.com/gorilla/mux [BSD 3-Clause "New" or "Revised" License](https://github.com/gorilla/mux/blob/master/LICENSE) - github.com/hailocab/go-hostpool [MIT License](https://github.com/hailocab/go-hostpool/blob/master/LICENSE) - github.com/harlow/kinesis-consumer [MIT License](https://github.com/harlow/kinesis-consumer/blob/master/MIT-LICENSE) From 82f20c4b36ab91fbebbe32790b38535ad21a055d Mon Sep 17 00:00:00 2001 From: Chris Hayles <52332184+haylesnortal@users.noreply.github.com> Date: Thu, 20 Aug 2020 13:52:55 -0700 Subject: [PATCH 07/12] Update plugins/inputs/opcua/opcua_client.go Co-authored-by: Steven Soroka --- plugins/inputs/opcua/opcua_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/inputs/opcua/opcua_client.go b/plugins/inputs/opcua/opcua_client.go index a392a204d0d26..8be0c8eb88e1e 100644 --- a/plugins/inputs/opcua/opcua_client.go +++ b/plugins/inputs/opcua/opcua_client.go @@ -315,7 +315,7 @@ func Connect(o *OpcUA) error { } default: - return fmt.Errorf("invalid opc.tcp endpoint") + return fmt.Errorf("unsupported scheme %q in endpoint. Expected opc.tcp", u.Scheme) } return nil } From 2f734bdd2c9e2f7b944fb1d026b012578599ded5 Mon Sep 17 00:00:00 2001 From: Chris Hayles Date: Mon, 24 Aug 2020 15:02:30 -0700 Subject: [PATCH 08/12] updated --- go.mod | 6 ++++-- go.sum | 14 ++++++++++++-- plugins/inputs/opcua/opcua_client.go | 2 ++ plugins/inputs/opcua/opcua_util.go | 17 +++++++++++------ 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index f8ebcb8deda66..3e904a4039d6f 100644 --- a/go.mod +++ b/go.mod @@ -116,7 +116,8 @@ require ( github.com/sirupsen/logrus v1.4.2 github.com/soniah/gosnmp v1.25.0 github.com/streadway/amqp v0.0.0-20180528204448-e5adc2ada8b8 - github.com/stretchr/testify v1.5.1 + github.com/stretchr/objx v0.3.0 // indirect + github.com/stretchr/testify v1.6.1 github.com/tbrandon/mbserver v0.0.0-20170611213546-993e1772cc62 github.com/tedsuo/ifrit v0.0.0-20191009134036-9a97d0632f00 // indirect github.com/tidwall/gjson v1.6.0 @@ -148,7 +149,8 @@ require ( gopkg.in/ldap.v3 v3.1.0 gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce gopkg.in/olivere/elastic.v5 v5.0.70 - gopkg.in/yaml.v2 v2.2.5 + gopkg.in/yaml.v2 v2.3.0 + gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect gotest.tools v2.2.0+incompatible // indirect honnef.co/go/tools v0.0.1-2020.1.3 // indirect k8s.io/apimachinery v0.17.1 // indirect diff --git a/go.sum b/go.sum index 77a3f96639e40..113d098c93f5c 100644 --- a/go.sum +++ b/go.sum @@ -539,6 +539,8 @@ github.com/streadway/amqp v0.0.0-20180528204448-e5adc2ada8b8/go.mod h1:1WNBiOZtZ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As= +github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -546,6 +548,8 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tbrandon/mbserver v0.0.0-20170611213546-993e1772cc62 h1:Oj2e7Sae4XrOsk3ij21QjjEgAcVSeo9nkp0dI//cD2o= github.com/tbrandon/mbserver v0.0.0-20170611213546-993e1772cc62/go.mod h1:qUzPVlSj2UgxJkVbH0ZwuuiR46U8RBMDT5KLY78Ifpw= github.com/tedsuo/ifrit v0.0.0-20191009134036-9a97d0632f00 h1:mujcChM89zOHwgZBBNr5WZ77mBXP1yR+gLThGCYZgAg= @@ -592,10 +596,10 @@ golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72 h1:+ELyKg6m8UBf0nPFSqD0mi7zUfwPyXo23HNjMnXPz7w= @@ -702,8 +706,8 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456 h1:ng0gs1AKnRRuEMZoTLLlbOd+C17zUDepwGQBb/n+JVg= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -877,6 +881,12 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/plugins/inputs/opcua/opcua_client.go b/plugins/inputs/opcua/opcua_client.go index a392a204d0d26..081c9f3c0848f 100644 --- a/plugins/inputs/opcua/opcua_client.go +++ b/plugins/inputs/opcua/opcua_client.go @@ -291,6 +291,8 @@ func Connect(o *OpcUA) error { o.setupOptions() + log.Printf("opts: \n%d", len(o.opts)) + o.client = opcua.NewClient(o.Endpoint, o.opts...) if err := o.client.Connect(o.ctx); err != nil { return fmt.Errorf("Error in Client Connection: %s", err) diff --git a/plugins/inputs/opcua/opcua_util.go b/plugins/inputs/opcua/opcua_util.go index 330120dab6d66..5371c3a5e17d8 100644 --- a/plugins/inputs/opcua/opcua_util.go +++ b/plugins/inputs/opcua/opcua_util.go @@ -65,7 +65,7 @@ func generateCert(host string, rsaBits int, certFile, keyFile string, dur time.D template := x509.Certificate{ SerialNumber: serialNumber, Subject: pkix.Name{ - Organization: []string{"Gopcua Test Client"}, + Organization: []string{"Telegraf Client Self-Signed"}, }, NotBefore: notBefore, NotAfter: notAfter, @@ -153,10 +153,12 @@ func generateClientOpts(endpoints []*ua.EndpointDescription, certFile, keyFile, opts := []opcua.Option{} appuri := "urn:gopcua:client" - // ApplicationURI is automatically read from the cert so is not required if a cert if provided - if certFile == "" { - opts = append(opts, opcua.ApplicationURI(appuri)) - } + opts = append(opts, opcua.ApplicationURI(appuri)) + opts = append(opts, opcua.ApplicationName("Telegraf")) + // opts = append(opts, opcua.ProductURI("urn:telegraf:client")) + // opts = append(opts, opcua.Lifetime(time.Hour*1)) + // opts = append(opts, opcua.SessionName("telegraf-opcua")) + log.Printf("opts appuri: %d", len(opts)) if certFile == "" && keyFile == "" { if policy != "None" || mode != "None" { @@ -177,6 +179,7 @@ func generateClientOpts(endpoints []*ua.EndpointDescription, certFile, keyFile, } cert = c.Certificate[0] opts = append(opts, opcua.PrivateKey(pk), opcua.Certificate(cert)) + log.Printf("opts cert: %d", len(opts)) } } @@ -197,6 +200,7 @@ func generateClientOpts(endpoints []*ua.EndpointDescription, certFile, keyFile, // Select the most appropriate authentication mode from server capabilities and user input authMode, authOption := generateAuth(auth, cert, username, password) opts = append(opts, authOption) + log.Printf("opts authOption: %d", len(opts)) var secMode ua.MessageSecurityMode switch strings.ToLower(mode) { @@ -268,8 +272,9 @@ func generateClientOpts(endpoints []*ua.EndpointDescription, certFile, keyFile, } opts = append(opts, opcua.SecurityFromEndpoint(serverEndpoint, authMode)) + log.Printf("opts security: %d", len(opts)) - log.Printf("Using config:\nEndpoint: %s\nSecurity mode: %s, %s\nAuth mode : %s\n", serverEndpoint.EndpointURL, serverEndpoint.SecurityPolicyURI, serverEndpoint.SecurityMode, authMode) + log.Printf("Using config:\nEndpoint: %s\nSecurity mode: %s, %s\nAuth mode: %s\n", serverEndpoint.EndpointURL, serverEndpoint.SecurityPolicyURI, serverEndpoint.SecurityMode, authMode) return opts } From 814d5540de4e37a6c1dd382e40363263301abce1 Mon Sep 17 00:00:00 2001 From: Chris Hayles Date: Mon, 24 Aug 2020 15:06:34 -0700 Subject: [PATCH 09/12] Revert "updated" This reverts commit 2f734bdd2c9e2f7b944fb1d026b012578599ded5. --- go.mod | 6 ++---- go.sum | 14 ++------------ plugins/inputs/opcua/opcua_client.go | 2 -- plugins/inputs/opcua/opcua_util.go | 17 ++++++----------- 4 files changed, 10 insertions(+), 29 deletions(-) diff --git a/go.mod b/go.mod index 3e904a4039d6f..f8ebcb8deda66 100644 --- a/go.mod +++ b/go.mod @@ -116,8 +116,7 @@ require ( github.com/sirupsen/logrus v1.4.2 github.com/soniah/gosnmp v1.25.0 github.com/streadway/amqp v0.0.0-20180528204448-e5adc2ada8b8 - github.com/stretchr/objx v0.3.0 // indirect - github.com/stretchr/testify v1.6.1 + github.com/stretchr/testify v1.5.1 github.com/tbrandon/mbserver v0.0.0-20170611213546-993e1772cc62 github.com/tedsuo/ifrit v0.0.0-20191009134036-9a97d0632f00 // indirect github.com/tidwall/gjson v1.6.0 @@ -149,8 +148,7 @@ require ( gopkg.in/ldap.v3 v3.1.0 gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce gopkg.in/olivere/elastic.v5 v5.0.70 - gopkg.in/yaml.v2 v2.3.0 - gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect + gopkg.in/yaml.v2 v2.2.5 gotest.tools v2.2.0+incompatible // indirect honnef.co/go/tools v0.0.1-2020.1.3 // indirect k8s.io/apimachinery v0.17.1 // indirect diff --git a/go.sum b/go.sum index 113d098c93f5c..77a3f96639e40 100644 --- a/go.sum +++ b/go.sum @@ -539,8 +539,6 @@ github.com/streadway/amqp v0.0.0-20180528204448-e5adc2ada8b8/go.mod h1:1WNBiOZtZ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As= -github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -548,8 +546,6 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tbrandon/mbserver v0.0.0-20170611213546-993e1772cc62 h1:Oj2e7Sae4XrOsk3ij21QjjEgAcVSeo9nkp0dI//cD2o= github.com/tbrandon/mbserver v0.0.0-20170611213546-993e1772cc62/go.mod h1:qUzPVlSj2UgxJkVbH0ZwuuiR46U8RBMDT5KLY78Ifpw= github.com/tedsuo/ifrit v0.0.0-20191009134036-9a97d0632f00 h1:mujcChM89zOHwgZBBNr5WZ77mBXP1yR+gLThGCYZgAg= @@ -596,10 +592,10 @@ golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72 h1:+ELyKg6m8UBf0nPFSqD0mi7zUfwPyXo23HNjMnXPz7w= @@ -706,8 +702,8 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456 h1:ng0gs1AKnRRuEMZoTLLlbOd+C17zUDepwGQBb/n+JVg= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -881,12 +877,6 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/plugins/inputs/opcua/opcua_client.go b/plugins/inputs/opcua/opcua_client.go index 081c9f3c0848f..a392a204d0d26 100644 --- a/plugins/inputs/opcua/opcua_client.go +++ b/plugins/inputs/opcua/opcua_client.go @@ -291,8 +291,6 @@ func Connect(o *OpcUA) error { o.setupOptions() - log.Printf("opts: \n%d", len(o.opts)) - o.client = opcua.NewClient(o.Endpoint, o.opts...) if err := o.client.Connect(o.ctx); err != nil { return fmt.Errorf("Error in Client Connection: %s", err) diff --git a/plugins/inputs/opcua/opcua_util.go b/plugins/inputs/opcua/opcua_util.go index 5371c3a5e17d8..330120dab6d66 100644 --- a/plugins/inputs/opcua/opcua_util.go +++ b/plugins/inputs/opcua/opcua_util.go @@ -65,7 +65,7 @@ func generateCert(host string, rsaBits int, certFile, keyFile string, dur time.D template := x509.Certificate{ SerialNumber: serialNumber, Subject: pkix.Name{ - Organization: []string{"Telegraf Client Self-Signed"}, + Organization: []string{"Gopcua Test Client"}, }, NotBefore: notBefore, NotAfter: notAfter, @@ -153,12 +153,10 @@ func generateClientOpts(endpoints []*ua.EndpointDescription, certFile, keyFile, opts := []opcua.Option{} appuri := "urn:gopcua:client" - opts = append(opts, opcua.ApplicationURI(appuri)) - opts = append(opts, opcua.ApplicationName("Telegraf")) - // opts = append(opts, opcua.ProductURI("urn:telegraf:client")) - // opts = append(opts, opcua.Lifetime(time.Hour*1)) - // opts = append(opts, opcua.SessionName("telegraf-opcua")) - log.Printf("opts appuri: %d", len(opts)) + // ApplicationURI is automatically read from the cert so is not required if a cert if provided + if certFile == "" { + opts = append(opts, opcua.ApplicationURI(appuri)) + } if certFile == "" && keyFile == "" { if policy != "None" || mode != "None" { @@ -179,7 +177,6 @@ func generateClientOpts(endpoints []*ua.EndpointDescription, certFile, keyFile, } cert = c.Certificate[0] opts = append(opts, opcua.PrivateKey(pk), opcua.Certificate(cert)) - log.Printf("opts cert: %d", len(opts)) } } @@ -200,7 +197,6 @@ func generateClientOpts(endpoints []*ua.EndpointDescription, certFile, keyFile, // Select the most appropriate authentication mode from server capabilities and user input authMode, authOption := generateAuth(auth, cert, username, password) opts = append(opts, authOption) - log.Printf("opts authOption: %d", len(opts)) var secMode ua.MessageSecurityMode switch strings.ToLower(mode) { @@ -272,9 +268,8 @@ func generateClientOpts(endpoints []*ua.EndpointDescription, certFile, keyFile, } opts = append(opts, opcua.SecurityFromEndpoint(serverEndpoint, authMode)) - log.Printf("opts security: %d", len(opts)) - log.Printf("Using config:\nEndpoint: %s\nSecurity mode: %s, %s\nAuth mode: %s\n", serverEndpoint.EndpointURL, serverEndpoint.SecurityPolicyURI, serverEndpoint.SecurityMode, authMode) + log.Printf("Using config:\nEndpoint: %s\nSecurity mode: %s, %s\nAuth mode : %s\n", serverEndpoint.EndpointURL, serverEndpoint.SecurityPolicyURI, serverEndpoint.SecurityMode, authMode) return opts } From 26c3c6be265b35a6bfc55cd44ac70f5c064a2fbd Mon Sep 17 00:00:00 2001 From: Chris Hayles Date: Mon, 24 Aug 2020 15:57:31 -0700 Subject: [PATCH 10/12] Updated gopuca library version to resolve error in ProSys Simulator v5. --- go.mod | 4 ++-- go.sum | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index f8ebcb8deda66..ed1ce0ff461be 100644 --- a/go.mod +++ b/go.mod @@ -62,7 +62,7 @@ require ( github.com/google/go-cmp v0.4.0 github.com/google/go-github v17.0.0+incompatible github.com/google/go-querystring v1.0.0 // indirect - github.com/gopcua/opcua v0.1.11 + github.com/gopcua/opcua v0.1.12 github.com/gorilla/mux v1.6.2 github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect @@ -134,7 +134,7 @@ require ( golang.org/x/net v0.0.0-20200301022130-244492dfa37a golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a - golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 + golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd golang.org/x/text v0.3.3 golang.org/x/tools v0.0.0-20200317043434-63da46f3035e // indirect golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200205215550-e35592f146e4 diff --git a/go.sum b/go.sum index 77a3f96639e40..2c44cb5cad69d 100644 --- a/go.sum +++ b/go.sum @@ -294,6 +294,8 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gopcua/opcua v0.1.11 h1:i7qDkVYRQP5LTiAEnPu09RcPSA1KGPkHsNd/TeDcZc8= github.com/gopcua/opcua v0.1.11/go.mod h1:O2l+/u0jM6f3WKRbz5L5ep7yNpQX2l5DOagXqXNcDV8= +github.com/gopcua/opcua v0.1.12 h1:TenluCr1CPB1NHjb9tX6yprc0eUmthznXxSc5mnJPBo= +github.com/gopcua/opcua v0.1.12/go.mod h1:a6QH4F9XeODklCmWuvaOdL8v9H0d73CEKUHWVZLQyE8= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk= @@ -592,14 +594,16 @@ golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72 h1:+ELyKg6m8UBf0nPFSqD0mi7zUfwPyXo23HNjMnXPz7w= golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -702,8 +706,8 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456 h1:ng0gs1AKnRRuEMZoTLLlbOd+C17zUDepwGQBb/n+JVg= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -718,6 +722,8 @@ golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From 8ce0858b73f4c74de6386fb5c168f12f3c626175 Mon Sep 17 00:00:00 2001 From: Chris Hayles Date: Mon, 24 Aug 2020 17:57:06 -0700 Subject: [PATCH 11/12] Removed builder, updated tags and fields, removed unnecessary log output, updated self-signed cert organization. --- go.sum | 3 -- metric/builder.go | 55 ---------------------------- plugins/inputs/opcua/opcua_client.go | 11 ++++-- plugins/inputs/opcua/opcua_util.go | 15 +++----- 4 files changed, 12 insertions(+), 72 deletions(-) delete mode 100644 metric/builder.go diff --git a/go.sum b/go.sum index 2c44cb5cad69d..25d4743ad56df 100644 --- a/go.sum +++ b/go.sum @@ -292,8 +292,6 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/gopcua/opcua v0.1.11 h1:i7qDkVYRQP5LTiAEnPu09RcPSA1KGPkHsNd/TeDcZc8= -github.com/gopcua/opcua v0.1.11/go.mod h1:O2l+/u0jM6f3WKRbz5L5ep7yNpQX2l5DOagXqXNcDV8= github.com/gopcua/opcua v0.1.12 h1:TenluCr1CPB1NHjb9tX6yprc0eUmthznXxSc5mnJPBo= github.com/gopcua/opcua v0.1.12/go.mod h1:a6QH4F9XeODklCmWuvaOdL8v9H0d73CEKUHWVZLQyE8= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= @@ -594,7 +592,6 @@ golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= diff --git a/metric/builder.go b/metric/builder.go deleted file mode 100644 index 9a331b9a4cb36..0000000000000 --- a/metric/builder.go +++ /dev/null @@ -1,55 +0,0 @@ -package metric - -import ( - "time" - - "github.com/influxdata/telegraf" -) - -type TimeFunc func() time.Time - -type Builder struct { - TimeFunc - TimePrecision time.Duration - - *metric -} - -func NewBuilder() *Builder { - b := &Builder{ - TimeFunc: time.Now, - TimePrecision: 1 * time.Nanosecond, - } - b.Reset() - return b -} - -func (b *Builder) SetName(name string) { - b.name = name -} - -func (b *Builder) AddTag(key string, value string) { - b.metric.AddTag(key, value) -} - -func (b *Builder) AddField(key string, value interface{}) { - b.metric.AddField(key, value) -} - -func (b *Builder) SetTime(tm time.Time) { - b.tm = tm -} - -func (b *Builder) Reset() { - b.metric = &metric{ - tp: telegraf.Untyped, - } -} - -func (b *Builder) Metric() (telegraf.Metric, error) { - if b.tm.IsZero() { - b.tm = b.TimeFunc().Truncate(b.TimePrecision) - } - - return b.metric, nil -} diff --git a/plugins/inputs/opcua/opcua_client.go b/plugins/inputs/opcua/opcua_client.go index 8be0c8eb88e1e..a166111d71b71 100644 --- a/plugins/inputs/opcua/opcua_client.go +++ b/plugins/inputs/opcua/opcua_client.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "net/url" + "strings" "time" "github.com/gopcua/opcua" @@ -168,6 +169,9 @@ func (o *OpcUA) Init() error { return err } o.NumberOfTags = len(o.NodeList) + + o.setupOptions() + return nil } @@ -289,8 +293,6 @@ func Connect(o *OpcUA) error { o.client.CloseSession() } - o.setupOptions() - o.client = opcua.NewClient(o.Endpoint, o.opts...) if err := o.client.Connect(o.ctx); err != nil { return fmt.Errorf("Error in Client Connection: %s", err) @@ -330,7 +332,7 @@ func (o *OpcUA) setupOptions() error { if o.Certificate == "" && o.PrivateKey == "" { if o.SecurityPolicy != "None" || o.SecurityMode != "None" { - o.Certificate, o.PrivateKey = generateCert("urn:gopcua:client", 2048, o.Certificate, o.PrivateKey, (365 * 24 * time.Hour)) + o.Certificate, o.PrivateKey = generateCert("urn:telegraf:gopcua:client", 2048, o.Certificate, o.PrivateKey, (365 * 24 * time.Hour)) } } @@ -413,10 +415,11 @@ func (o *OpcUA) Gather(acc telegraf.Accumulator) error { fields := make(map[string]interface{}) tags := map[string]string{ "name": n.Name, - "type": n.DataType, + "id": BuildNodeID(n), } fields[o.NodeData[i].TagName] = o.NodeData[i].Value + fields["Quality"] = strings.TrimSpace(fmt.Sprint(o.NodeData[i].Quality)) acc.AddFields(o.Name, fields, tags) } return nil diff --git a/plugins/inputs/opcua/opcua_util.go b/plugins/inputs/opcua/opcua_util.go index 330120dab6d66..5a88ed44e0dc5 100644 --- a/plugins/inputs/opcua/opcua_util.go +++ b/plugins/inputs/opcua/opcua_util.go @@ -65,7 +65,7 @@ func generateCert(host string, rsaBits int, certFile, keyFile string, dur time.D template := x509.Certificate{ SerialNumber: serialNumber, Subject: pkix.Name{ - Organization: []string{"Gopcua Test Client"}, + Organization: []string{"Telegraf OPC UA client"}, }, NotBefore: notBefore, NotAfter: notAfter, @@ -102,7 +102,6 @@ func generateCert(host string, rsaBits int, certFile, keyFile string, dur time.D if err := certOut.Close(); err != nil { log.Fatalf("error closing %s: %s", certFile, err) } - log.Printf("wrote %s\n", certFile) keyOut, err := os.OpenFile(keyFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { @@ -115,7 +114,6 @@ func generateCert(host string, rsaBits int, certFile, keyFile string, dur time.D if err := keyOut.Close(); err != nil { log.Fatalf("error closing %s: %s", keyFile, err) } - log.Printf("wrote %s\n", keyFile) return certFile, keyFile } @@ -151,12 +149,12 @@ func pemBlockForKey(priv interface{}) *pem.Block { func generateClientOpts(endpoints []*ua.EndpointDescription, certFile, keyFile, policy, mode, auth, username, password string) []opcua.Option { opts := []opcua.Option{} - appuri := "urn:gopcua:client" + appuri := "urn:telegraf:gopcua:client" + appname := "Telegraf" // ApplicationURI is automatically read from the cert so is not required if a cert if provided - if certFile == "" { - opts = append(opts, opcua.ApplicationURI(appuri)) - } + opts = append(opts, opcua.ApplicationURI(appuri)) + opts = append(opts, opcua.ApplicationName(appname)) if certFile == "" && keyFile == "" { if policy != "None" || mode != "None" { @@ -245,7 +243,6 @@ func generateClientOpts(endpoints []*ua.EndpointDescription, certFile, keyFile, } default: // User cares about both - fmt.Println("secMode: ", secMode, "secPolicy:", secPolicy) for _, e := range endpoints { if e.SecurityPolicyURI == secPolicy && e.SecurityMode == secMode && (serverEndpoint == nil || e.SecurityLevel >= serverEndpoint.SecurityLevel) { serverEndpoint = e @@ -268,8 +265,6 @@ func generateClientOpts(endpoints []*ua.EndpointDescription, certFile, keyFile, } opts = append(opts, opcua.SecurityFromEndpoint(serverEndpoint, authMode)) - - log.Printf("Using config:\nEndpoint: %s\nSecurity mode: %s, %s\nAuth mode : %s\n", serverEndpoint.EndpointURL, serverEndpoint.SecurityPolicyURI, serverEndpoint.SecurityMode, authMode) return opts } From bb1055b9aa3fc4252113279aaea4a5b32d90581f Mon Sep 17 00:00:00 2001 From: David Reimschussel Date: Wed, 2 Sep 2020 16:13:09 -0600 Subject: [PATCH 12/12] go mod tidy --- go.sum | 1 + 1 file changed, 1 insertion(+) diff --git a/go.sum b/go.sum index 25a4c104b41bf..ed84b5f2556b4 100644 --- a/go.sum +++ b/go.sum @@ -719,6 +719,7 @@ golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6 h1:DvY3Zkh7KabQE/kfzMvYvKirSiguP9Q/veMtkYyf0o8= golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=