Resource API transports
A transport connects providers to remote resources, such as a device, cloud infrastructure, or a REST API.
The transport class contains the code for managing connections and processing information to and from the remote resource. The transport schema, similar to a resource type, describes the structure of the data that is passed for it to make a connection.
Transport implementation methods
When you are writing a transport class to manage remote resources, use the following methods as appropriate:
-
initialize(context, connection_info)
-
The
connection_info
contains a hash that matches the schema. After you run the initialize method, the provider assumes that you have defined your transport in such as way as to ensure that it’s ready for processing requests. The transport will report connection errors by throwing an exception, for example, if the network is unreachable or the credentials are rejected. In some cases, for example when the target is a REST API, no processing happens during initialization.
-
-
verify(context)
-
Use this method to check whether the transport can connect to the remote target. If the connection fails, the transport will raise an exception.
-
-
facts(context)
-
Use this method to access the target and the facts hash which contains a subset of default facts from Facter, and more specific facts appropriate for the target.
-
-
close(context)
-
Use this method to close the connection. Calling this method releases the transport so that you can't use it any more and frees up cache and operating system resources, such as open connections. For implementation quality, the library will ignore exceptions that are thrown.
-
context
is the primary way to signal
changes, successes, and failures to the runtime environment. For more information,
see Runtime environment.An example of a transport class:
# lib/puppet/transport/device_type.rb module Puppet::Transport # The main connection class to a Device endpoint class DeviceType def initialize(context, connection_info) # Add additional validation for connection_info # and pre-load a connection if it is useful for your target end def verify(context) # Test that transport can talk to the remote target end def facts(context) # Access target, return a Facter facts hash end def close(context) # Close connection, free up resources end end end
An example of a corresponding schema:
# lib/puppet/transport/device_type.rb Puppet::ResourceAPI.register_transport( name: 'device_type', # points at class Puppet::Transport::DeviceType desc: 'Connects to a device_type', connection_info: { host: { type: 'String', desc: 'The host to connect to.', }, user: { type: 'String', desc: 'The user.', }, password: { type: 'String', sensitive: true, desc: 'The password to connect.', }, enable_password: { type: 'String', sensitive: true, desc: 'The password escalate to enable access.', }, port: { type: 'Integer', desc: 'The port to connect to.', }, }, )
If the following attributes apply to your target, use these names for consistency across transports:
-
uri
: use to specify which URL to connect to. -
host
: use to specify an IP or address to connect to. -
protocol
: use to specify which protocol the transport uses, for examplehttp
,https
,ssh
ortcp
. -
user
: the user you want the transport to connect as. -
port
: the port you want the transport to connect to.
Do not use the following keywords in when writing the connection_info
:
-
name
-
path
-
query
-
run-on
-
remote-transport
-
remote-*
-
implementations
To ensure that the data the schema passes to the implementation is handled securely, set
password attributes to sensitive: true
. Attributes marked
with the sensitive
flag allow a user interface based on this
schema to make appropriate presentation choices, such as obscuring the password field.
Values that you’ve marked sensitive are passed to the transport wrapped in the type Puppet::Pops::Types::PSensitiveType::Sensitive
. This keeps the
value from being logged or saved inadvertently while it is being transmitted between
components. To access the sensitive value within the transport, use the unwrap
method, for example, connection_info[:password].unwrap
.
Errors and retry handling in transport implementation
The Resource API does not put many constraints on when and how a transport can fail. The remote resource you are connecting to will have it's own device specific connection and error handling capabilities. Be aware of the following issues that may arise:
-
Your initial connection might fail. To retry making the connection, verify whether you have network problems or whether there has been a service restart of the target that you are trying to connect to. As part of the retry logic, the transport avoids passing these issues to other parts of your system and waits up to 30 seconds for a single target to recover. When you execute a retry, the transport logs transient problems at the
notice
level. -
After you make your connection and have run the initialize method, the transport might apply deeper validation to the passed connection information — like mutual exclusivity of optional values, for example,
password
orkey
— and throw anArgumentError
. The transport then tries to establish a connection to the remote target. If this fails due to unrecoverable errors, it throws another exception. -
The
verify
andfacts
methods, likeinitialize
, throw exceptions only when unrecoverable errors are encountered, or when the retry logic times out.
Port your existing device code to transports
If you have old code that uses Device, port it by updating your code with the following replacements as appropriate:
-
Move the device class to
Puppet::Transport
-
Change
Util::NetworkDevice::NAME::Device
toResourceApi::Transport::NAME
-
Change the initialization to accept and process a
connection_info
hash. -
When accessing the
connection_info
in your new transport, change all string keys to symbols, for example,name
to:name
. -
Add
context
as the first argument to yourinitialize
andfacts
method. -
Change
puppet/util/network_device/NAME/device
topuppet/transport/NAME
-
Replace calls to Puppet logging with calls to the context logger
context.device
, but you won't be able
to make use of the functionality in Bolt
plans.After porting your code, you will have a transport class and a shim Device
class that connects your transport in a way that Puppet understands. Specify the transport name in
the super
call to make the
connection:
# lib/puppet/type/nx9k_vlan.rb Puppet::ResourceApi.register_type( name: 'nx9k_vlan', features: [ 'remote_resource' ], # ... ) # lib/puppet/util/network_device/nexus/device.rb require 'puppet/resource_api/transport/wrapper' # force registering the transport require 'puppet/transport/schema/nexus' module Puppet::Util::NetworkDevice::Nexus class Device < Puppet::ResourceApi::Transport::Wrapper def initialize(url_or_config, _options = {}) super('nexus', url_or_config) end end end