Opcua cleanup (#8076)
This commit is contained in:
parent
f4e5b0ef63
commit
b2ac8b4ac6
|
|
@ -9,52 +9,56 @@ Plugin minimum tested version: 1.16
|
|||
|
||||
```toml
|
||||
[[inputs.opcua]]
|
||||
## This plugin supports connections to PLCs via OPC UA.
|
||||
##
|
||||
## Device name
|
||||
name = "localhost"
|
||||
#
|
||||
## OPC UA Endpoint URL
|
||||
endpoint = "opc.tcp://localhost:4840"
|
||||
#
|
||||
## Read Timeout. Add an arbitrary timeout (seconds) to demonstrate how to stop a subscription with a context.
|
||||
timeout = 30
|
||||
#
|
||||
## Time Interval (Default = 10s)
|
||||
time_interval = "10s"
|
||||
#
|
||||
## Security policy: None, Basic128Rsa15, Basic256, Basic256Sha256.
|
||||
security_policy = "None"
|
||||
#
|
||||
## Security mode: None, Sign, SignAndEncrypt.
|
||||
security_mode = "None"
|
||||
#
|
||||
## 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 'Certificate' or 'UserName'. Default is "Anonymous"
|
||||
## auth_method = "Anonymous"
|
||||
#
|
||||
## Required for auth_method = "UserName"
|
||||
## username = "myusername"
|
||||
#
|
||||
## Required for auth_method = "UserName"
|
||||
## password = "mypassword"
|
||||
#
|
||||
## Node ID configuration
|
||||
## 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
|
||||
## Example: {name="ProductUri", namespace="0", identifier_type="i", identifier="2262", data_type="string", description="http://open62541.org"}
|
||||
nodes = [
|
||||
{name="", namespace="", identifier_type="", identifier="", data_type="", description=""},
|
||||
{name="", namespace="", identifier_type="", identifier="", data_type="", description=""},
|
||||
]
|
||||
## Device name
|
||||
# name = "localhost"
|
||||
#
|
||||
## OPC UA Endpoint URL
|
||||
# endpoint = "opc.tcp://localhost:4840"
|
||||
#
|
||||
## Maximum time allowed to establish a connect to the endpoint.
|
||||
# connect_timeout = "10s"
|
||||
#
|
||||
## Maximum time allowed for a request over the estabilished connection.
|
||||
# request_timeout = "5s"
|
||||
#
|
||||
## Security policy, one of "None", "Basic128Rsa15", "Basic256",
|
||||
## "Basic256Sha256", or "auto"
|
||||
# security_policy = "auto"
|
||||
#
|
||||
## Security mode, one of "None", "Sign", "SignAndEncrypt", or "auto"
|
||||
# security_mode = "auto"
|
||||
#
|
||||
## Path to cert.pem. Required when security mode or policy isn't "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 when security mode or policy isn't "None".
|
||||
## If key path is not supplied, self-signed cert and key will be generated.
|
||||
# private_key = "/etc/telegraf/key.pem"
|
||||
#
|
||||
## Authentication Method, one of "Certificate", "UserName", or "Anonymous". To
|
||||
## authenticate using a specific ID, select 'Certificate' or 'UserName'
|
||||
# auth_method = "Anonymous"
|
||||
#
|
||||
## Username. Required for auth_method = "UserName"
|
||||
# username = ""
|
||||
#
|
||||
## Password. Required for auth_method = "UserName"
|
||||
# password = ""
|
||||
#
|
||||
## Node ID configuration
|
||||
## 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
|
||||
## Example:
|
||||
## {name="ProductUri", namespace="0", identifier_type="i", identifier="2262", data_type="string", description="http://open62541.org"}
|
||||
nodes = [
|
||||
{name="", namespace="", identifier_type="", identifier="", data_type="", description=""},
|
||||
{name="", namespace="", identifier_type="", identifier="", data_type="", description=""},
|
||||
]
|
||||
```
|
||||
|
||||
### Example Node Configuration
|
||||
|
|
|
|||
|
|
@ -11,38 +11,39 @@ import (
|
|||
"github.com/gopcua/opcua"
|
||||
"github.com/gopcua/opcua/ua"
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"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"`
|
||||
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
|
||||
NodeIDerror []error
|
||||
state ConnectionState
|
||||
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"`
|
||||
ConnectTimeout config.Duration `toml:"connect_timeout"`
|
||||
RequestTimeout config.Duration `toml:"request_timeout"`
|
||||
NodeList []OPCTag `toml:"nodes"`
|
||||
|
||||
Nodes []string `toml:"-"`
|
||||
NodeData []OPCData `toml:"-"`
|
||||
NodeIDs []*ua.NodeID `toml:"-"`
|
||||
NodeIDerror []error `toml:"-"`
|
||||
state ConnectionState
|
||||
|
||||
// status
|
||||
ReadSuccess int
|
||||
ReadError int
|
||||
NumberOfTags int
|
||||
ReadSuccess int `toml:"-"`
|
||||
ReadError int `toml:"-"`
|
||||
NumberOfTags int `toml:"-"`
|
||||
|
||||
// internal values
|
||||
client *opcua.Client
|
||||
req *ua.ReadRequest
|
||||
ctx context.Context
|
||||
opts []opcua.Option
|
||||
}
|
||||
|
||||
|
|
@ -80,67 +81,57 @@ const (
|
|||
|
||||
const description = `Retrieve data from OPCUA 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
|
||||
time_interval = "5s"
|
||||
#
|
||||
# # 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. 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"
|
||||
#
|
||||
# # Required for auth_method = "UserName"
|
||||
# # password = "mypassword"
|
||||
#
|
||||
# ## 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"},
|
||||
]
|
||||
|
||||
## 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."},
|
||||
|
||||
[[inputs.opcua]]
|
||||
## Device name
|
||||
# name = "localhost"
|
||||
#
|
||||
## OPC UA Endpoint URL
|
||||
# endpoint = "opc.tcp://localhost:4840"
|
||||
#
|
||||
## Maximum time allowed to establish a connect to the endpoint.
|
||||
# connect_timeout = "10s"
|
||||
#
|
||||
## Maximum time allowed for a request over the estabilished connection.
|
||||
# request_timeout = "5s"
|
||||
#
|
||||
## Security policy, one of "None", "Basic128Rsa15", "Basic256",
|
||||
## "Basic256Sha256", or "auto"
|
||||
# security_policy = "auto"
|
||||
#
|
||||
## Security mode, one of "None", "Sign", "SignAndEncrypt", or "auto"
|
||||
# security_mode = "auto"
|
||||
#
|
||||
## Path to cert.pem. Required when security mode or policy isn't "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 when security mode or policy isn't "None".
|
||||
## If key path is not supplied, self-signed cert and key will be generated.
|
||||
# private_key = "/etc/telegraf/key.pem"
|
||||
#
|
||||
## Authentication Method, one of "Certificate", "UserName", or "Anonymous". To
|
||||
## authenticate using a specific ID, select 'Certificate' or 'UserName'
|
||||
# auth_method = "Anonymous"
|
||||
#
|
||||
## Username. Required for auth_method = "UserName"
|
||||
# username = ""
|
||||
#
|
||||
## Password. Required for auth_method = "UserName"
|
||||
# password = ""
|
||||
#
|
||||
## Node ID configuration
|
||||
## 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
|
||||
## Example:
|
||||
## {name="ProductUri", namespace="0", identifier_type="i", identifier="2262", data_type="string", description="http://open62541.org"}
|
||||
nodes = [
|
||||
{name="", namespace="", identifier_type="", identifier="", data_type="", description=""},
|
||||
{name="", namespace="", identifier_type="", identifier="", data_type="", description=""},
|
||||
]
|
||||
`
|
||||
|
||||
// Description will appear directly above the plugin definition in the config file
|
||||
|
|
@ -157,8 +148,6 @@ func (o *OpcUA) SampleConfig() string {
|
|||
func (o *OpcUA) Init() error {
|
||||
o.state = Disconnected
|
||||
|
||||
o.ctx = context.Background()
|
||||
|
||||
err := o.validateEndpoint()
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -177,13 +166,12 @@ func (o *OpcUA) Init() error {
|
|||
}
|
||||
|
||||
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")
|
||||
return fmt.Errorf("endpoint url is empty")
|
||||
}
|
||||
|
||||
_, err := url.Parse(o.Endpoint)
|
||||
|
|
@ -191,15 +179,6 @@ func (o *OpcUA) validateEndpoint() error {
|
|||
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":
|
||||
|
|
@ -294,7 +273,9 @@ func Connect(o *OpcUA) error {
|
|||
}
|
||||
|
||||
o.client = opcua.NewClient(o.Endpoint, o.opts...)
|
||||
if err := o.client.Connect(o.ctx); err != nil {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(o.ConnectTimeout))
|
||||
defer cancel()
|
||||
if err := o.client.Connect(ctx); err != nil {
|
||||
return fmt.Errorf("Error in Client Connection: %s", err)
|
||||
}
|
||||
|
||||
|
|
@ -336,7 +317,7 @@ func (o *OpcUA) setupOptions() error {
|
|||
}
|
||||
}
|
||||
|
||||
o.opts = generateClientOpts(endpoints, o.Certificate, o.PrivateKey, 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, time.Duration(o.RequestTimeout))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -427,9 +408,17 @@ func (o *OpcUA) Gather(acc telegraf.Accumulator) error {
|
|||
|
||||
// Add this plugin to telegraf
|
||||
func init() {
|
||||
inputs.Add("opcua_client", func() telegraf.Input {
|
||||
inputs.Add("opcua", func() telegraf.Input {
|
||||
return &OpcUA{
|
||||
AuthMethod: "Anonymous",
|
||||
Name: "localhost",
|
||||
Endpoint: "opc.tcp://localhost:4840",
|
||||
SecurityPolicy: "auto",
|
||||
SecurityMode: "auto",
|
||||
RequestTimeout: config.Duration(5 * time.Second),
|
||||
ConnectTimeout: config.Duration(10 * time.Second),
|
||||
Certificate: "/etc/telegraf/cert.pem",
|
||||
PrivateKey: "/etc/telegraf/key.pem",
|
||||
AuthMethod: "Anonymous",
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ import (
|
|||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type OPCTags struct {
|
||||
|
|
@ -16,6 +20,10 @@ type OPCTags struct {
|
|||
}
|
||||
|
||||
func TestClient1(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
var testopctags = []OPCTags{
|
||||
{"ProductName", "0", "i", "2261", "string", "open62541 OPC UA Server"},
|
||||
{"ProductUri", "0", "i", "2262", "string", "http://open62541.org"},
|
||||
|
|
@ -27,8 +35,9 @@ func TestClient1(t *testing.T) {
|
|||
|
||||
o.Name = "testing"
|
||||
o.Endpoint = "opc.tcp://opcua.rocks:4840"
|
||||
o.Interval = "10ms"
|
||||
o.TimeOut = 30
|
||||
o.AuthMethod = "Anonymous"
|
||||
o.ConnectTimeout = config.Duration(10 * time.Second)
|
||||
o.RequestTimeout = config.Duration(1 * time.Second)
|
||||
o.SecurityPolicy = "None"
|
||||
o.SecurityMode = "None"
|
||||
for _, tags := range testopctags {
|
||||
|
|
@ -40,7 +49,7 @@ func TestClient1(t *testing.T) {
|
|||
}
|
||||
err = Connect(&o)
|
||||
if err != nil {
|
||||
t.Logf("Connect Error: %s", err)
|
||||
t.Fatalf("Connect Error: %s", err)
|
||||
}
|
||||
|
||||
for i, v := range o.NodeData {
|
||||
|
|
@ -65,3 +74,37 @@ func MapOPCTag(tags OPCTags) (out OPCTag) {
|
|||
out.DataType = tags.DataType
|
||||
return out
|
||||
}
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
toml := `
|
||||
[[inputs.opcua]]
|
||||
name = "localhost"
|
||||
endpoint = "opc.tcp://localhost:4840"
|
||||
connect_timeout = "10s"
|
||||
request_timeout = "5s"
|
||||
security_policy = "auto"
|
||||
security_mode = "auto"
|
||||
certificate = "/etc/telegraf/cert.pem"
|
||||
private_key = "/etc/telegraf/key.pem"
|
||||
auth_method = "Anonymous"
|
||||
username = ""
|
||||
password = ""
|
||||
nodes = [
|
||||
{name="name", namespace="", identifier_type="", identifier="", data_type="", description=""},
|
||||
{name="name2", namespace="", identifier_type="", identifier="", data_type="", description=""},
|
||||
]
|
||||
`
|
||||
|
||||
c := config.NewConfig()
|
||||
err := c.LoadConfigData([]byte(toml))
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, c.Inputs, 1)
|
||||
|
||||
o, ok := c.Inputs[0].Input.(*OpcUA)
|
||||
require.True(t, ok)
|
||||
|
||||
require.Len(t, o.NodeList, 2)
|
||||
require.Equal(t, o.NodeList[0].Name, "name")
|
||||
require.Equal(t, o.NodeList[1].Name, "name2")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ func pemBlockForKey(priv interface{}) *pem.Block {
|
|||
|
||||
// OPT FUNCTIONS
|
||||
|
||||
func generateClientOpts(endpoints []*ua.EndpointDescription, certFile, keyFile, policy, mode, auth, username, password string) []opcua.Option {
|
||||
func generateClientOpts(endpoints []*ua.EndpointDescription, certFile, keyFile, policy, mode, auth, username, password string, requestTimeout time.Duration) []opcua.Option {
|
||||
opts := []opcua.Option{}
|
||||
appuri := "urn:telegraf:gopcua:client"
|
||||
appname := "Telegraf"
|
||||
|
|
@ -156,6 +156,8 @@ func generateClientOpts(endpoints []*ua.EndpointDescription, certFile, keyFile,
|
|||
opts = append(opts, opcua.ApplicationURI(appuri))
|
||||
opts = append(opts, opcua.ApplicationName(appname))
|
||||
|
||||
opts = append(opts, opcua.RequestTimeout(requestTimeout))
|
||||
|
||||
if certFile == "" && keyFile == "" {
|
||||
if policy != "None" || mode != "None" {
|
||||
certFile, keyFile = generateCert(appuri, 2048, certFile, keyFile, (365 * 24 * time.Hour))
|
||||
|
|
|
|||
Loading…
Reference in New Issue