ct_netconfc
(common_test)Netconf client module.
Netconf client module.
The Netconf client is compliant with RFC4741 and RFC4742.
For each server to test against, the following entry can be added to a configuration file:
{server_id(),options()}.
The server_id()
or an associated target_name()
(see
ct) shall then be used in calls to open/2.
If no configuration exists for a server, a session can still be opened by calling open/2 with all necessary options given in the call. The first argument to open/2 can then be any atom.
The netconf server uses the error_logger
for logging of netconf
traffic. A special purpose error handler is implemented in
ct_conn_log_h
. To use this error handler, add the cth_conn_log
hook in your test suite, e.g.
suite() -> [{ct_hooks, [{cth_conn_log, [{conn_mod(),hook_options()}]}]}].
The conn_mod()
is the name of the common_test module implementing
the connection protocol, e.g. ct_netconfc
.
The hook option log_type
specifies the type of logging:
raw
The sent and received netconf data is logged to a separate text file as is without any formatting. A link to the file is added to the test case HTML log.
pretty
The sent and received netconf data is logged to a separate text file with XML data nicely indented. A link to the file is added to the test case HTML log.
html (default)
The sent and received netconf traffic is pretty printed directly in the test case HTML log.
silent
Netconf traffic is not logged.
By default, all netconf traffic is logged in one single log
file. However, it is possible to have different connections logged
in separate files. To do this, use the hook option hosts
and
list the names of the servers/connections that will be used in the
suite. Note that the connections must be named for this to work,
i.e. they must be opened with open/2.
The hosts
option has no effect if log_type
is set to html
or
silent
.
The hook options can also be specified in a configuration file with
the configuration variable ct_conn_log
:
{ct_conn_log,[{conn_mod(),hook_options()}]}.
For example:
{ct_conn_log,[{ct_netconfc,[{log_type,pretty}, {hosts,[key_or_name()]}]}]}
Note that hook options specified in a configuration file will overwrite the hardcoded hook options in the test suite.
The following ct_hooks
statement will cause pretty printing of
netconf traffic to separate logs for the connections named
nc_server1
and nc_server2
. Any other connections will be logged
to default netconf log.
suite() -> [{ct_hooks, [{cth_conn_log, [{ct_netconfc,[{log_type,pretty}}, {hosts,[nc_server1,nc_server2]}]} ]}]}].
Connections must be opened like this:
open(nc_server1,[...]), open(nc_server2,[...]).
The following configuration file will cause raw logging of all netconf traffic into one single text file.
{ct_conn_log,[{ct_netconfc,[{log_type,raw}]}]}.
The ct_hooks
statement must look like this:
suite() -> [{ct_hooks, [{cth_conn_log, []}]}].
The same ct_hooks
statement without the configuration file would
cause HTML logging of all netconf connections into the test case
HTML log.
The netconf client is also compliant with RFC5277 NETCONF Event Notifications, which defines a mechanism for an asynchronous message notification delivery service for the netconf protocol.
Specific functions to support this are create_subscription/6 and get_event_streams/3. (The functions also exist with other arities.)
Types
client() = handle() | server_id() | target_name()
conn_mod() = ct_netconfc
error_reason() = term()
event_time() = {eventTime, xml_attributes(), [xs_datetime()]}
handle() = term()
hook_option() =
{log_type, log_type()} | {hosts, [key_or_name()]}
hook_options() = [hook_option()]
host() = inet:hostname() | inet:ip_address()
key_or_name() = server_id() | target_name()
log_type() = raw | pretty | html | silent
netconf_db() = running | startup | candidate
notification() =
{notification, xml_attributes(), notification_content()}
notification_content() = [event_time() | simple_xml()]
option() =
{ssh, host()} |
{port, inet:port_number()} |
{user, string()} |
{password, string()} |
{user_dir, string()} |
{timeout, timeout()}
options() = [option()]
server_id() = atom()
simple_xml() =
{xml_tag(), xml_attributes(), xml_content()} |
{xml_tag(), xml_content()} |
xml_tag()
stream_data() =
{description, string()} |
{replaySupport, string()} |
{replayLogCreationTime, string()} |
{replayLogAgedTime, string()}
stream_name() = string()
streams() = [{stream_name(), [stream_data()]}]
target_name() = atom()
xml_attribute_tag() = atom()
xml_attribute_value() = string()
xml_attributes() =
[{xml_attribute_tag(), xml_attribute_value()}]
xml_content() = [simple_xml() | iolist()]
xml_tag() = atom()
xpath() = {xpath, string()}
xs_datetime() = string()
Functions
action(Client, Action) -> Result
Equivalent to action(Client, Action, infinity).
action(Client, Action, Timeout) -> Result
Client = client()
Action = simple_xml()
Timeout = timeout()
Result = {ok, simple_xml()} | {error, error_reason()}
Execute an action.
close_session(Client) -> Result
Equivalent to close_session(Client, infinity).
close_session(Client, Timeout) -> Result
Client = client()
Timeout = timeout()
Result = ok | {error, error_reason()}
Request graceful termination of the session associated with the client.
When a netconf server receives a close-session
request, it
will gracefully close the session. The server will release any
locks and resources associated with the session and gracefully
close any associated connections. Any NETCONF requests received
after a close-session
request will be ignored.
copy_config(Client, Source, Target) -> Result
Equivalent to copy_config(Client, Source, Target, infinity).
copy_config(Client, Target, Source, Timeout) -> Result
Client = client()
Target = netconf_db()
Source = netconf_db()
Timeout = timeout()
Result = ok | {error, error_reason()}
Copy configuration data.
Which source and target options that can be issued depends on the
capabilities supported by the server. I.e. :candidate
and/or
:startup
are required.
create_subscription(Client) -> term()
create_subscription(Client, Timeout) -> term()
create_subscription(Client, Stream, Timeout) -> term()
create_subscription(Client, StartTime, StopTime, Timeout) -> term()
create_subscription(Client, Stream, StartTime, StopTime, Timeout) -> term()
create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout) -> Result
Client = client()
Stream = stream_name()
Filter = simple_xml()
StartTime = xs_datetime()
StopTime = xs_datetime()
Timeout = timeout()
Result = ok | {error, error_reason()}
Create a subscription for event notifications.
This function sets up a subscription for netconf event
notifications of the given stream type, matching the given
filter. The calling process will receive notifications as messages
of type notification()
.
An optional parameter that indicates which stream of events is of interest. If not present, events in the default NETCONF stream will be sent.
An optional parameter that indicates which subset of all possible events is of interest. The format of this parameter is the same as that of the filter parameter in the NETCONF protocol operations. If not present, all events not precluded by other parameters will be sent. See section 3.6 for more information on filters.
An optional parameter used to trigger the replay feature and
indicate that the replay should start at the time specified. If
StartTime
is not present, this is not a replay subscription.
It is not valid to specify start times that are later than the
current time. If the StartTime
specified is earlier than the
log can support, the replay will begin with the earliest
available notification. This parameter is of type dateTime and
compliant to [RFC3339]. Implementations must support time
zones.
An optional parameter used with the optional replay feature
to indicate the newest notifications of interest. If StopTime
is not present, the notifications will continue until the
subscription is terminated. Must be used with and be later than
StartTime
. Values of StopTime
in the future are valid. This
parameter is of type dateTime and compliant to [RFC3339].
Implementations must support time zones.
See RFC5277 for further details about the event notification mechanism.
delete_config(Client, Target) -> Result
Equivalent to delete_config(Client, Target, infinity).
delete_config(Client, Target, Timeout) -> Result
Client = client()
Target = startup | candidate
Timeout = timeout()
Result = ok | {error, error_reason()}
Delete configuration data.
The running configuration cannot be deleted and :candidate
or :startup
must be advertised by the server.
edit_config(Client, Target, Config) -> Result
Equivalent to edit_config(Client, Target, Config, infinity).
edit_config(Client, Target, Config, Timeout) -> Result
Client = client()
Target = netconf_db()
Config = simple_xml()
Timeout = timeout()
Result = ok | {error, error_reason()}
Edit configuration data.
Per default only the running target is available, unless the server
include :candidate
or :startup
in its list of
capabilities.
format_data(How, Data) -> term()
get(Client, Filter) -> Result
Equivalent to get(Client, Filter, infinity).
get(Client, Filter, Timeout) -> Result
Client = client()
Filter = simple_xml() | xpath()
Timeout = timeout()
Result = {ok, simple_xml()} | {error, error_reason()}
Get data.
This operation returns both configuration and state data from the server.
Filter type xpath
can only be used if the server supports
:xpath
.
get_capabilities(Client) -> Result
Equivalent to get_capabilities(Client, infinity).
get_capabilities(Client, Timeout) -> Result
Client = client()
Timeout = timeout()
Result = [string()] | {error, error_reason()}
Returns the server side capabilities
The following capability identifiers, defined in RFC 4741, can be returned:
"urn:ietf:params:netconf:base:1.0"
"urn:ietf:params:netconf:capability:writable-running:1.0"
"urn:ietf:params:netconf:capability:candidate:1.0"
"urn:ietf:params:netconf:capability:confirmed-commit:1.0"
"urn:ietf:params:netconf:capability:rollback-on-error:1.0"
"urn:ietf:params:netconf:capability:startup:1.0"
"urn:ietf:params:netconf:capability:url:1.0"
"urn:ietf:params:netconf:capability:xpath:1.0"
Note, additional identifiers may exist, e.g. server side namespace.
get_config(Client, Source, Filter) -> Result
Equivalent to get_config(Client, Source, Filter, infinity).
get_config(Client, Source, Filter, Timeout) -> Result
Client = client()
Source = netconf_db()
Filter = simple_xml() | xpath()
Timeout = timeout()
Result = {ok, simple_xml()} | {error, error_reason()}
Get configuration data.
To be able to access another source than running
, the server
must advertise :candidate
and/or :startup
.
Filter type xpath
can only be used if the server supports
:xpath
.
get_event_streams(Client, Timeout) -> Result
Equivalent to get_event_streams(Client, [], Timeout).
get_event_streams(Client, Streams, Timeout) -> Result
Client = client()
Streams = [stream_name()]
Timeout = timeout()
Result = {ok, streams()} | {error, error_reason()}
Send a request to get the given event streams.
Streams
is a list of stream names. The following filter will
be sent to the netconf server in a get
request:
<netconf xmlns="urn:ietf:params:xml:ns:netmod:notification"> <streams> <stream> <name>StreamName1</name> </stream> <stream> <name>StreamName2</name> </stream> ... </streams> </netconf>
If Streams
is an empty list, ALL streams will be requested
by sending the following filter:
<netconf xmlns="urn:ietf:params:xml:ns:netmod:notification"> <streams/> </netconf>
If more complex filtering is needed, a use get/2 or get/3 and specify the exact filter according to XML Schema for Event Notifications found in RFC5277.
get_session_id(Client) -> Result
Equivalent to get_session_id(Client, infinity).
get_session_id(Client, Timeout) -> Result
Client = client()
Timeout = timeout()
Result = pos_integer() | {error, error_reason()}
Returns the session id associated with the given client.
handle_msg(X1, State) -> term()
hello(Client) -> Result
Equivalent to hello(Client, infinity).
hello(Client, Timeout) -> Result
Client = handle()
Timeout = timeout()
Result = ok | {error, error_reason()}
Exchange hello
messages with the server.
Sends a hello
message to the server and waits for the return.
kill_session(Client, SessionId) -> Result
Equivalent to kill_session(Client, SessionId, infinity).
kill_session(Client, SessionId, Timeout) -> Result
Client = client()
SessionId = pos_integer()
Timeout = timeout()
Result = ok | {error, error_reason()}
Force termination of the session associated with the supplied session id.
The server side shall abort any operations currently in process, release any locks and resources associated with the session, and close any associated connections.
Only if the server is in the confirmed commit phase, the configuration will be restored to its state before entering the confirmed commit phase. Otherwise, no configuration roll back will be performed.
If the given SessionId
is equal to the current session id,
an error will be returned.
lock(Client, Target) -> Result
Equivalent to lock(Client, Target, infinity).
lock(Client, Target, Timeout) -> Result
Client = client()
Target = netconf_db()
Timeout = timeout()
Result = ok | {error, error_reason()}
Unlock configuration target.
Which target parameters that can be used depends on if
:candidate
and/or :startup
are supported by the
server. If successfull, the configuration system of the device is
not available to other clients (Netconf, CORBA, SNMP etc). Locks
are intended to be short-lived.
The operations kill_session/2 or kill_session/3 can be used to force the release of a lock owned by another Netconf session. How this is achieved by the server side is implementation specific.
only_open(Options) -> Result
Options = options()
Result = {ok, handle()} | {error, error_reason()}
Open a netconf session, but don't send hello
.
As open/1 but does not send a hello
message.
only_open(KeyOrName, ExtraOptions) -> Result
KeyOrName = key_or_name()
ExtraOptions = options()
Result = {ok, handle()} | {error, error_reason()}
Open a name netconf session, but don't send hello
.
As open/2 but does not send a hello
message.
open(Options) -> Result
Options = options()
Result = {ok, handle()} | {error, error_reason()}
Open a netconf session and exchange hello
messages.
If the server options are specified in a configuration file, or if a named client is needed for logging purposes (see Logging) use open/2 instead.
The opaque handler()
reference which is returned from this
function is required as client identifier when calling any other
function in this module.
The timeout
option (milli seconds) is used when setting up
the ssh connection and when waiting for the hello message from the
server. It is not used for any other purposes during the lifetime
of the connection.
open(KeyOrName, ExtraOptions) -> Result
KeyOrName = key_or_name()
ExtraOptions = options()
Result = {ok, handle()} | {error, error_reason()}
Open a named netconf session and exchange hello
messages.
If KeyOrName
is a configured server_id()
or a
target_name()
associated with such an ID, then the options
for this server will be fetched from the configuration file.
The ExtraOptions
argument will be added to the options found in
the configuration file. If the same options are given, the values
from the configuration file will overwrite ExtraOptions
.
If the server is not specified in a configuration file, use open/1 instead.
The opaque handle()
reference which is returned from this
function can be used as client identifier when calling any other
function in this module. However, if KeyOrName
is a
target_name()
, i.e. if the server is named via a call to
ct:require/2
or a require
statement in the test
suite, then this name may be used instead of the handle()
.
The timeout
option (milli seconds) is used when setting up
the ssh connection and when waiting for the hello message from the
server. It is not used for any other purposes during the lifetime
of the connection.
See also: ct:require/2.
unlock(Client, Target) -> Result
Equivalent to unlock(Client, Target, infinity).
unlock(Client, Target, Timeout) -> Result
Client = client()
Target = netconf_db()
Timeout = timeout()
Result = ok | {error, error_reason()}