Documentation
¶
Index ¶
- func AcquireNetconfLock(opMutex *sync.Mutex, reuseConnection bool, isWrite bool) bool
- func AppendFromXPath(body netconf.Body, xPath string, value any) netconf.Body
- func CleanupRedundantRemoveOperations(body netconf.Body) netconf.Body
- func CloseNetconfConnection(ctx context.Context, client *netconf.Client, reuseConnection bool)
- func Commit(ctx context.Context, client *netconf.Client) error
- func Contains(s []string, str string) bool
- func ConvertXPathToRestconfPath(xPath string) string
- func EditConfig(ctx context.Context, client *netconf.Client, body string, commit bool) error
- func FormatNetconfError(err error) string
- func GetFromXPath(res xmldot.Result, xPath string) xmldot.Result
- func GetInt64List(result []gjson.Result) types.List
- func GetInt64ListXML(result []xmldot.Result) types.List
- func GetInt64Set(result []gjson.Result) types.Set
- func GetInt64SetXML(result []xmldot.Result) types.Set
- func GetStringList(result []gjson.Result) types.List
- func GetStringListXML(result []xmldot.Result) types.List
- func GetStringSet(result []gjson.Result) types.Set
- func GetStringSetXML(result []xmldot.Result) types.Set
- func GetValueSlice(result []gjson.Result) []attr.Value
- func GetXpathFilter(xPath string) netconf.Filter
- func IsFlagImporting(ctx context.Context, req resource.ReadRequest) (bool, diag.Diagnostics)
- func IsGetConfigResponseEmpty(res *netconf.Res) bool
- func IsListPath(xPath string) bool
- func LastElement(path string) string
- func Must[T any](v T, err error) T
- func RemoveEmptyStrings(s []string) []string
- func RemoveFromXPath(body netconf.Body, xPath string) netconf.Body
- func SaveConfig(ctx context.Context, client *netconf.Client) error
- func SaveConfigRestconf(client *restconf.Client) error
- func SetFlagImporting(ctx context.Context, importing bool, sk SetKeyer, respDiags *diag.Diagnostics)
- func SetFromXPath(body netconf.Body, xPath string, value any) netconf.Body
- func SetRawFromXPath(body netconf.Body, xPath string, value string) netconf.Body
- type AttributeDescription
- func (d *AttributeDescription) AddDefaultValueDescription(defaultValue string) *AttributeDescription
- func (d *AttributeDescription) AddFloatRangeDescription(min, max float64) *AttributeDescription
- func (d *AttributeDescription) AddIntegerRangeDescription(min, max int64) *AttributeDescription
- func (d *AttributeDescription) AddStringEnumDescription(values ...string) *AttributeDescription
- type KeyValue
- type SetKeyer
- type TflogAdapter
- func (t *TflogAdapter) Debug(ctx context.Context, msg string, keysAndValues ...any)
- func (t *TflogAdapter) Error(ctx context.Context, msg string, keysAndValues ...any)
- func (t *TflogAdapter) Info(ctx context.Context, msg string, keysAndValues ...any)
- func (t *TflogAdapter) Warn(ctx context.Context, msg string, keysAndValues ...any)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AcquireNetconfLock ¶ added in v0.10.0
AcquireNetconfLock acquires the appropriate lock for a NETCONF operation.
Lock strategy based on reuseConnection and operation type: - When reuseConnection=false: Lock for ALL operations (serializes everything including Close) - When reuseConnection=true && isWrite=true: Lock for WRITE operations (Lock/EditConfig/Commit sequence) - When reuseConnection=true && isWrite=false: NO lock for READ operations (concurrent reads allowed)
This prevents issues: - When reuse disabled: Prevents concurrent Close() attempts on same connection - When reuse enabled: Serializes write sequences but allows concurrent reads
Returns true if lock was acquired, false if not acquired.
Usage:
locked := helpers.AcquireNetconfLock(opMutex, reuseConnection, isWrite)
if locked {
defer opMutex.Unlock()
}
defer helpers.CloseNetconfConnection(ctx, client, reuseConnection)
func AppendFromXPath ¶ added in v0.10.0
AppendFromXPath creates all elements in an XPath and appends a value to a list by using the ".-1" syntax. This is useful for adding multiple items to a list without keys. The function automatically appends ".-1" to the final element in the path.
Example:
body := netconf.Body{}
body = AppendFromXPath(body, "native/route-map/rule/match/ip/address", "10")
body = AppendFromXPath(body, "native/route-map/rule/match/ip/address", "20")
// Result: <native><route-map><rule><match><ip>
// <address>10</address>
// <address>20</address>
// </ip></match></rule></route-map></native>
Note: This function is designed for simple list items without keys. For lists with keys, use SetFromXPath with predicates instead.
func CleanupRedundantRemoveOperations ¶ added in v0.10.0
func CleanupRedundantRemoveOperations(body netconf.Body) netconf.Body
CleanupRedundantRemoveOperations removes redundant operation="remove" attributes from child elements when a parent element already has operation="remove". This is called after the full NETCONF payload has been built to sanitize the XML before sending to the device.
Example:
Input: <trap operation="remove"><severity operation="remove"></severity></trap> Output: <trap operation="remove"></trap>
This prevents NETCONF errors like '"" is not a valid value' for empty child elements.
func CloseNetconfConnection ¶ added in v0.10.0
CloseNetconfConnection safely closes a NETCONF connection if reuse is disabled.
IMPORTANT: This must be called with the operation mutex still held (deferred after AcquireNetconfLock) to prevent concurrent close attempts.
Usage:
defer helpers.CloseNetconfConnection(ctx, device.NetconfClient, device.ReuseConnection)
func ConvertXPathToRestconfPath ¶ added in v0.10.0
ConvertXPathToRestconfPath converts an XPath-style path to RESTCONF-style path. It retains placeholders (%v, %s, %d) and namespace prefixes. Values are URL-encoded as required by RESTCONF (except placeholders).
XPath uses: element[key=value] or element[%v=value] RESTCONF uses: element=value (URL-encoded)
Examples:
- "interface[name=GigabitEthernet1]" → "interface=GigabitEthernet1"
- "interface[%v=GigabitEthernet1]" → "interface=%v"
- "interface[%v=%v]" → "interface=%v"
- "vrf[name=VRF1][af-name=ipv4]" → "vrf=VRF1,ipv4"
- "Cisco-IOS-XE-native:native/vrf[%v=%v]" → "Cisco-IOS-XE-native:native/vrf=%v"
- "native/interface[name='GigabitEthernet1/0/1']" → "native/interface=GigabitEthernet1%2F0%2F1"
- "route-map[name='test map']" → "route-map=test+map"
func EditConfig ¶ added in v0.10.0
EditConfig edits the configuration on the device If the server supports the candidate capability, it will edit the configuration in the candidate datastore and commit it to the running datastore if commit is true. If the server does not support the candidate capability, it will edit the configuration in the running datastore.
IMPORTANT: When connection reuse is enabled, callers MUST serialize calls to EditConfig using an application-level mutex that also covers ManageNetconfConnection(). This prevents concurrent goroutines from attempting to acquire NETCONF datastore locks simultaneously on the same session.
Parameters:
- ctx: context.Context
- client: *netconf.Client
- body: string
- commit: bool
func FormatNetconfError ¶ added in v0.10.0
FormatNetconfError extracts detailed error information from a NETCONF error
func GetFromXPath ¶ added in v0.10.0
GetFromXPath converts an XPath expression to a xmldot path and retrieves the result. Uses manual filtering to correctly handle both single and multiple elements with predicates. Supports the same XPath formats as SetFromXPath:
- Single: /interface[name='GigabitEthernet1']
- Multiple predicates: /interface[name='GigabitEthernet1'][vrf='VRF1']
- Combined with 'and': /interface[name='GigabitEthernet1' and vrf='VRF1']
- Values with slashes: /interface[name='GigabitEthernet1/0/1']
- Nested paths: /native/interface[name='Gi1']/ip/address
Example: /native/interface[name='Gi1']/ip/address Processes path segments, validates predicates, and constructs the final xmldot path
func GetInt64List ¶
GetInt64List converts a slice of gjson.Result to a Terraform types.List of int64.
func GetInt64ListXML ¶ added in v0.10.0
GetInt64ListXML converts a slice of xmldot.Result to a Terraform types.List of int64.
func GetInt64Set ¶ added in v0.8.0
GetInt64Set converts a slice of gjson.Result to a Terraform types.Set of int64.
func GetInt64SetXML ¶ added in v0.10.0
GetInt64SetXML converts a slice of xmldot.Result to a Terraform types.Set of int64.
func GetStringList ¶
GetStringList converts a slice of gjson.Result to a Terraform types.List of strings.
func GetStringListXML ¶ added in v0.10.0
GetStringListXML converts a slice of xmldot.Result to a Terraform types.List of strings.
func GetStringSet ¶ added in v0.8.0
GetStringSet converts a slice of gjson.Result to a Terraform types.Set of strings.
func GetStringSetXML ¶ added in v0.10.0
GetStringSetXML converts a slice of xmldot.Result to a Terraform types.Set of strings.
func GetValueSlice ¶
GetValueSlice converts a slice of gjson.Result to a slice of Terraform attr.Value.
func GetXpathFilter ¶ added in v0.10.0
func GetXpathFilter(xPath string) netconf.Filter
GetXpathFilter creates a NETCONF XPath filter with namespace prefixes removed. It processes the XPath expression to strip namespace prefixes from both element names and predicate key names, preserving the path structure.
Supports the same XPath formats as SetFromXPath and GetFromXPath:
- Paths with namespace prefixes: /Cisco-IOS-XE-native:native/interface
- Single predicates: /native/interface[name='GigabitEthernet1']
- Multiple predicates: /native/interface[name='Gi1'][vrf='VRF1']
- Nested paths: /native/interface[name='Gi1']/ip/address
- Values with slashes: /native/interface[name='GigabitEthernet1/0/1']
- Predicates with namespace prefixes: /Cisco-IOS-XE-native:interface[Cisco-IOS-XE-native:name='Gi1']
Example transformations:
Input: "/Cisco-IOS-XE-native:native/aaa"
Output: netconf.Filter{Type: "xpath", Content: "/native/aaa"}
Input: "/Cisco-IOS-XE-native:native/interface[Cisco-IOS-XE-native:name='Gi1']/Cisco-IOS-XE-native:ip"
Output: netconf.Filter{Type: "xpath", Content: "/native/interface[name='Gi1']/ip"}
Input: "/native/interface[name='GigabitEthernet1/0/1']"
Output: netconf.Filter{Type: "xpath", Content: "/native/interface[name='GigabitEthernet1/0/1']"}
func IsFlagImporting ¶ added in v0.5.8
func IsFlagImporting(ctx context.Context, req resource.ReadRequest) (bool, diag.Diagnostics)
func IsGetConfigResponseEmpty ¶ added in v0.10.1
func IsGetConfigResponseEmpty(res *netconf.Res) bool
IsGetConfigResponseEmpty checks if a GetConfig response has an empty <data> element. Returns true if the response contains <data></data> with no child elements, indicating that the requested configuration does not exist on the device.
This is useful for determining if a resource exists before attempting to parse its attributes, particularly during Read operations or import.
IMPORTANT: This should typically be combined with IsListPath() to only treat empty responses as "not found" for list items:
if helpers.IsGetConfigResponseEmpty(&res) && helpers.IsListPath(state.getXPath()) {
// List item does not exist
resp.State.RemoveResource(ctx)
return
}
Parameters:
- res: The NETCONF response from GetConfig operation
Returns:
- true if the data element is empty (no child elements)
- false if the data element contains configuration
Example usage:
res, err := device.NetconfClient.GetConfig(ctx, "running", filter)
if err != nil {
return err
}
if helpers.IsGetConfigResponseEmpty(&res) && helpers.IsListPath(state.getXPath()) {
// Resource does not exist (list item)
resp.State.RemoveResource(ctx)
return
}
// Parse the configuration
state.fromBodyXML(ctx, res.Res)
func IsListPath ¶ added in v0.10.1
IsListPath checks if an XPath represents a list item (ends with a predicate). List items have predicates like [name='value'] or [name=value] at the end of their XPath, while containers/singletons don't end with predicates.
This is useful for determining if an empty GetConfig response should be interpreted as "resource not found" (for list items) vs other semantics.
Parameters:
- xPath: The XPath to check
Returns:
- true if the path ends with a predicate (is a list item)
- false if the path does not end with a predicate (is a container/singleton)
Examples:
- "/native/interface/Vlan[name=10]" → true (ends with predicate)
- "/native/interface/GigabitEthernet[name='1/0/1']" → true (ends with predicate)
- "/native/router/bgp[id=65000]/neighbor" → false (has predicate but doesn't end with one)
- "/native/clock" → false (container)
- "/native/hostname" → false (singleton)
func LastElement ¶
LastElement returns the last element of a YANG path with its namespace prefix. Example: "Cisco-IOS-XE-native:native/interface/GigabitEthernet=1" -> "Cisco-IOS-XE-native:GigabitEthernet"
func Must ¶ added in v0.5.8
Must panics if err is not nil, otherwise returns the value v. Useful for must-succeed operations in initialization code.
func RemoveEmptyStrings ¶ added in v0.7.0
RemoveEmptyStrings filters out empty strings from a slice.
func RemoveFromXPath ¶ added in v0.10.0
func RemoveFromXPath(body netconf.Body, xPath string) netconf.Body
RemoveFromXPath creates all elements in an XPath with an operation="remove" attribute on the last element for NETCONF delete operations. Supports the same XPath formats as SetFromXPath.
Example: /native/interface[name='Gi1']/ip/address Creates: <native><interface><name>Gi1</name><ip><address operation="remove"/></ip></interface></native>
func SaveConfig ¶ added in v0.10.0
SaveConfig saves the running configuration to startup configuration. This is equivalent to 'copy running-config startup-config' in the CLI. Uses the cisco-ia:save-config RPC operation.
func SaveConfigRestconf ¶ added in v0.10.1
func SaveConfigRestconf(client *restconf.Client) error
SaveConfigRestconf saves the running configuration to startup configuration via RESTCONF. This is equivalent to 'copy running-config startup-config' in the CLI. Uses the cisco-ia:save-config RPC operation via RESTCONF POST.
Parameters:
- client: The RESTCONF client to use for the operation
Returns:
- error if the save operation fails, nil otherwise
Example usage:
if err := helpers.SaveConfigRestconf(device.RestconfClient); err != nil {
return fmt.Errorf("failed to save config: %s", err)
}
func SetFlagImporting ¶ added in v0.5.8
func SetFlagImporting(ctx context.Context, importing bool, sk SetKeyer, respDiags *diag.Diagnostics)
SetFlagImporting checks the respDiags and if they are error-free it sets the `importing` as a private flag inside SetKeyer. It appends its own results to respDiags.
The caller must include in respDiags the result of state modification in the first place, to ensure consistency. The SetKeyer is something like resp.Private.
func SetFromXPath ¶ added in v0.10.0
SetFromXPath creates all elements in an XPath, including keys and namespaces, and optionally sets a value at the final path location. Supports single and composite keys:
- Single: /interface[name='GigabitEthernet1']
- Multiple predicates: /interface[name='GigabitEthernet1'][vrf='VRF1']
- Combined with 'and': /interface[name='GigabitEthernet1' and vrf='VRF1']
- Values with slashes: /interface[name='GigabitEthernet1/0/1']
If value is nil or empty string, only the structure is created without setting a value. If value is non-empty, it's set at the final path location.
Multi-Root Support: SetFromXPath properly handles multiple root-level sibling elements (e.g., both <deny> and <permit> at the same level). The underlying xmldot library automatically detects when different root paths are used and creates sibling elements instead of nesting them.
Example (multi-root XML):
body := netconf.Body{}
body = SetFromXPath(body, "sequence", "10")
body = SetFromXPath(body, "deny/std-ace/prefix", "10.0.0.0")
body = SetFromXPath(body, "permit/std-ace/prefix", "192.168.0.0")
// Result: <sequence>10</sequence><deny>...</deny><permit>...</permit>
func SetRawFromXPath ¶ added in v0.10.0
SetRawFromXPath creates all elements in an XPath, including keys and namespace declarations, then inserts raw XML content at the final path location. This is useful when you have pre-formatted XML that needs to be inserted as child elements.
The value parameter should contain raw XML content (child elements, attributes, etc.) that will be parsed and inserted at the target path. The content is wrapped in the final element tag specified by the xPath.
Multi-root Support: When called multiple times with the same path, this function appends the new XML content as an additional sibling element, creating a multi-root XML fragment at the parent level. The underlying xmldot library automatically handles multi-root fragments, making this safe for creating multiple sibling elements (e.g., multiple <interface> elements in a list).
Example (single call):
xPath: /Cisco-IOS-XE-native:native/interface[name='Gi1']
value: "<description>Management</description><shutdown/>"
Result: <native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native">
<interface>
<name>Gi1</name>
<description>Management</description>
<shutdown/>
</interface>
</native>
Example (multiple calls - multi-root):
First call: SetRawFromXPath(body, "/native/interface", "<name>Gi1</name>")
Second call: SetRawFromXPath(body, "/native/interface", "<name>Gi2</name>")
Result: <native>
<interface><name>Gi1</name></interface>
<interface><name>Gi2</name></interface>
</native>
Unlike SetFromXPath, this function:
- Adds xmlns declarations for namespace prefixes in the path (via buildXPathStructure)
- Inserts the value as raw XML (parsed as child elements) rather than as text content
- Uses body.SetRaw() instead of body.Set() for XML insertion
- Supports appending multiple elements at the same path (multi-root fragments)
Types ¶
type AttributeDescription ¶
type AttributeDescription struct {
String string
}
func NewAttributeDescription ¶
func NewAttributeDescription(s string) *AttributeDescription
func (*AttributeDescription) AddDefaultValueDescription ¶
func (d *AttributeDescription) AddDefaultValueDescription(defaultValue string) *AttributeDescription
func (*AttributeDescription) AddFloatRangeDescription ¶
func (d *AttributeDescription) AddFloatRangeDescription(min, max float64) *AttributeDescription
func (*AttributeDescription) AddIntegerRangeDescription ¶
func (d *AttributeDescription) AddIntegerRangeDescription(min, max int64) *AttributeDescription
func (*AttributeDescription) AddStringEnumDescription ¶
func (d *AttributeDescription) AddStringEnumDescription(values ...string) *AttributeDescription
type SetKeyer ¶ added in v0.5.8
SetKeyer is something like ReadResponse.Private or ImportStateResponse.Private.
type TflogAdapter ¶ added in v0.10.0
type TflogAdapter struct {
// contains filtered or unexported fields
}
TflogAdapter adapts go-netconf's Logger interface to Terraform's tflog package.
This adapter bridges the gap between go-netconf's logging interface and Terraform's context-based logging. It leverages go-netconf's context-aware Logger interface, which passes context as the first parameter to all logging methods.
The adapter automatically creates the "netconf" subsystem on first use, eliminating the need for manual subsystem creation in provider code.
The adapter stores a device identifier (host or device name) that is included in all log messages, making it easy to correlate logs when operating on multiple devices in parallel.
Thread-safety: This adapter is safe for concurrent use. Context is passed per log call via the Logger interface, and the device identifier is immutable after creation.
Usage in provider initialization:
logger := helpers.NewTflogAdapter("192.168.1.1")
client, err := netconf.NewClient(host,
netconf.Username(username),
netconf.Password(password),
netconf.WithLogger(logger),
)
Usage in resource operations:
// Context is automatically propagated through go-netconf's Logger interface _, err := device.NetconfClient.GetConfig(ctx, "running", filter) // Logs will automatically include device identifier and use the correct context and netconf subsystem
func NewTflogAdapter ¶ added in v0.10.0
func NewTflogAdapter(deviceID string) *TflogAdapter
NewTflogAdapter creates a new Terraform logging adapter with device identification.
The adapter automatically receives context from go-netconf's Logger interface on each logging call, ensuring proper context propagation without manual management.
The deviceID parameter should be a unique identifier for the device (e.g., hostname, IP address, or device name from configuration). This identifier is included in all log messages to enable correlation when operating on multiple devices in parallel.
Parameters:
- deviceID: Unique identifier for the device (e.g., "192.168.1.1" or "core-switch-1")
Returns:
- *TflogAdapter: A new adapter configured for the specified device
func (*TflogAdapter) Debug ¶ added in v0.10.0
func (t *TflogAdapter) Debug(ctx context.Context, msg string, keysAndValues ...any)
Debug logs a debug message with structured key-value pairs to tflog.SubsystemDebug.
Debug logs are typically used for detailed operational information useful for troubleshooting and development.
Context is provided by go-netconf's Logger interface, ensuring automatic propagation of trace IDs, request IDs, and deadlines.
Logs are written to the "netconf" subsystem for proper organization and filtering. The subsystem is automatically created if it doesn't exist.
The device identifier is automatically included in the log fields for correlation.
func (*TflogAdapter) Error ¶ added in v0.10.0
func (t *TflogAdapter) Error(ctx context.Context, msg string, keysAndValues ...any)
Error logs an error message with structured key-value pairs to tflog.SubsystemError.
Errors indicate serious problems that prevent successful operation.
Context is provided by go-netconf's Logger interface, ensuring automatic propagation of trace IDs, request IDs, and deadlines.
Logs are written to the "netconf" subsystem for proper organization and filtering. The subsystem is automatically created if it doesn't exist.
The device identifier is automatically included in the log fields for correlation.
func (*TflogAdapter) Info ¶ added in v0.10.0
func (t *TflogAdapter) Info(ctx context.Context, msg string, keysAndValues ...any)
Info logs an informational message with structured key-value pairs to tflog.SubsystemInfo.
Info logs represent normal operational messages that highlight progress or state changes.
Context is provided by go-netconf's Logger interface, ensuring automatic propagation of trace IDs, request IDs, and deadlines.
Logs are written to the "netconf" subsystem for proper organization and filtering. The subsystem is automatically created if it doesn't exist.
The device identifier is automatically included in the log fields for correlation.
func (*TflogAdapter) Warn ¶ added in v0.10.0
func (t *TflogAdapter) Warn(ctx context.Context, msg string, keysAndValues ...any)
Warn logs a warning message with structured key-value pairs to tflog.SubsystemWarn.
Warnings indicate potentially harmful situations that don't prevent operation but should be addressed.
Context is provided by go-netconf's Logger interface, ensuring automatic propagation of trace IDs, request IDs, and deadlines.
Logs are written to the "netconf" subsystem for proper organization and filtering. The subsystem is automatically created if it doesn't exist.
The device identifier is automatically included in the log fields for correlation.