aboutsummaryrefslogtreecommitdiffstats
path: root/lib/common_test
diff options
context:
space:
mode:
Diffstat (limited to 'lib/common_test')
-rw-r--r--lib/common_test/doc/src/ct_master_chapter.xml2
-rw-r--r--lib/common_test/doc/src/notes.xml48
-rw-r--r--lib/common_test/src/Makefile3
-rw-r--r--lib/common_test/src/ct_property_test.erl24
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE.erl31
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl187
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl147
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/netconfc_test_lib.erl166
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/netconfc_test_lib.hrl14
9 files changed, 418 insertions, 204 deletions
diff --git a/lib/common_test/doc/src/ct_master_chapter.xml b/lib/common_test/doc/src/ct_master_chapter.xml
index 37a0805055..adfe79e41a 100644
--- a/lib/common_test/doc/src/ct_master_chapter.xml
+++ b/lib/common_test/doc/src/ct_master_chapter.xml
@@ -198,7 +198,7 @@
<section>
<title>Automatic startup of test target nodes</title>
<marker id="ct_slave"></marker>
- <p>Is is possible to automatically start, and perform initial actions, on
+ <p>It is possible to automatically start, and perform initial actions, on
test target nodes by using the test specification term <c>init</c>.</p>
<p>Currently, two sub-terms are supported, <c>node_start</c> and <c>eval</c>.</p>
<p>Example:</p>
diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml
index b53ba32e6c..f4ce5369f7 100644
--- a/lib/common_test/doc/src/notes.xml
+++ b/lib/common_test/doc/src/notes.xml
@@ -32,6 +32,54 @@
<file>notes.xml</file>
</header>
+<section><title>Common_Test 1.8.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Ticket OTP-11971 introduced a runtime dependency towards
+ test_server-3.7.1, since the interface between
+ test_server and common_test was changed. Erroneously, the
+ common_test.app file was not updated according to this.
+ This has now been corrected.</p>
+ <p>
+ Own Id: OTP-12037</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Warning: this is experimental and may disappear or change
+ without previous warning.</p>
+ <p>
+ Experimental support for running Quickcheck and PropEr
+ tests from common_test suites is added to common_test.
+ See the reference manual for the new module
+ <c>ct_property_testing</c>.</p>
+ <p>
+ Experimental property tests are added under
+ <c>lib/{inet,ssh}/test/property_test</c>. They can be run
+ directly or from the commont_test suites
+ <c>inet/ftp_property_test_SUITE.erl</c> and
+ <c>ssh/test/ssh_property_test_SUITE.erl</c>.</p>
+ <p>
+ See the code in the <c>test</c> directories and the man
+ page for details.</p>
+ <p>
+ (Thanks to Tuncer Ayaz for a patch adding Triq)</p>
+ <p>
+ Own Id: OTP-12119</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Common_Test 1.8.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/common_test/src/Makefile b/lib/common_test/src/Makefile
index 4600c0ad78..8d74546880 100644
--- a/lib/common_test/src/Makefile
+++ b/lib/common_test/src/Makefile
@@ -74,7 +74,8 @@ MODULES= \
ct_netconfc \
ct_conn_log_h \
cth_conn_log \
- ct_groups
+ ct_groups \
+ ct_property_test
TARGET_MODULES= $(MODULES:%=$(EBIN)/%)
BEAM_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
diff --git a/lib/common_test/src/ct_property_test.erl b/lib/common_test/src/ct_property_test.erl
index e401fef669..52acda5388 100644
--- a/lib/common_test/src/ct_property_test.erl
+++ b/lib/common_test/src/ct_property_test.erl
@@ -59,8 +59,10 @@
%%% '''
%%%
%%% <warning>
+%%% <p>
%%% This is experimental code which may be changed or removed
-%%% anytime without any warning.
+%%% anytime without any warning.
+%%% </p>
%%% </warning>
%%%
%%% @end
@@ -78,7 +80,8 @@
%%%
%%% @doc Initializes Config for property testing.
%%%
-%%% <p>The function investigates if support is available for either Quickcheck or PropEr.
+%%% <p>The function investigates if support is available for either Quickcheck, PropEr,
+%%% or Triq.
%%% The options <c>{property_dir,AbsPath}</c> and
%%% <c>{property_test_tool,Tool}</c> is set in the Config returned.</p>
%%% <p>The function is intended to be called in the init_per_suite in the test suite.</p>
@@ -86,7 +89,7 @@
%%% @end
init_per_suite(Config) ->
- case which_module_exists([eqc,proper]) of
+ case which_module_exists([eqc,proper,triq]) of
{ok,ToolModule} ->
ct:pal("Found property tester ~p",[ToolModule]),
Path = property_tests_path("property_test", Config),
@@ -114,7 +117,8 @@ init_per_suite(Config) ->
quickcheck(Property, Config) ->
Tool = proplists:get_value(property_test_tool,Config),
- mk_ct_return( Tool:quickcheck(Property) ).
+ F = function_name(quickcheck, Tool),
+ mk_ct_return( Tool:F(Property), Tool ).
%%%================================================================
@@ -123,10 +127,10 @@ quickcheck(Property, Config) ->
%%%
%%% Make return values back to the calling Common Test suite
-mk_ct_return(true) ->
+mk_ct_return(true, _Tool) ->
true;
-mk_ct_return(Other) ->
- try lists:last(hd(eqc:counterexample()))
+mk_ct_return(Other, Tool) ->
+ try lists:last(hd(Tool:counterexample()))
of
{set,{var,_},{call,M,F,Args}} ->
{fail, io_lib:format("~p:~p/~p returned bad result",[M,F,length(Args)])}
@@ -174,5 +178,9 @@ compile_tests(Path, ToolModule) ->
macro_def(eqc) -> [{d, 'EQC'}];
-macro_def(proper) -> [{d, 'PROPER'}].
+macro_def(proper) -> [{d, 'PROPER'}];
+macro_def(triq) -> [{d, 'TRIQ'}].
+
+function_name(quickcheck, triq) -> check;
+function_name(F, _) -> F.
diff --git a/lib/common_test/test/ct_netconfc_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE.erl
index c89a4cdabe..2959f77087 100644
--- a/lib/common_test/test/ct_netconfc_SUITE.erl
+++ b/lib/common_test/test/ct_netconfc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -63,7 +63,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[
- default
+ netconfc1_SUITE,
+ netconfc_remote_SUITE
].
%%--------------------------------------------------------------------
@@ -72,14 +73,21 @@ all() ->
%%%-----------------------------------------------------------------
%%%
-default(Config) when is_list(Config) ->
+netconfc1_SUITE(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
Suite = filename:join(DataDir, "netconfc1_SUITE"),
CfgFile = filename:join(DataDir, "netconfc1.cfg"),
{Opts,ERPid} = setup([{suite,Suite},{config,CfgFile},
- {label,default}], Config),
+ {label,netconfc1_SUITE}], Config),
- ok = execute(default, Opts, ERPid, Config).
+ ok = execute(netconfc1_SUITE, Opts, ERPid, Config).
+
+netconfc_remote_SUITE(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "netconfc_remote_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{label,netconfc_remote_SUITE}], Config),
+
+ ok = execute(netconfc_remote_SUITE, Opts, ERPid, Config).
%%%-----------------------------------------------------------------
@@ -112,16 +120,15 @@ reformat(Events, EH) ->
%%%-----------------------------------------------------------------
%%% TEST EVENTS
%%%-----------------------------------------------------------------
-events_to_check(default,Config) ->
- {module,_} = code:load_abs(filename:join(?config(data_dir,Config),
- netconfc1_SUITE)),
- TCs = netconfc1_SUITE:all(),
- code:purge(netconfc1_SUITE),
- code:delete(netconfc1_SUITE),
+events_to_check(Suite,Config) ->
+ {module,_} = code:load_abs(filename:join(?config(data_dir,Config),Suite)),
+ TCs = Suite:all(),
+ code:purge(Suite),
+ code:delete(Suite),
OneTest =
[{?eh,start_logging,{'DEF','RUNDIR'}}] ++
- [{?eh,tc_done,{netconfc1_SUITE,TC,ok}} || TC <- TCs] ++
+ [{?eh,tc_done,{Suite,TC,ok}} || TC <- TCs] ++
[{?eh,stop_logging,[]}],
%% 2 tests (ct:run_test + script_start) is default
diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl
index 2bcfeeec0c..f2adeb9065 100644
--- a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl
@@ -1,7 +1,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -30,25 +30,10 @@
-module(netconfc1_SUITE).
-include_lib("common_test/include/ct.hrl").
-include_lib("common_test/src/ct_netconfc.hrl").
--include_lib("public_key/include/public_key.hrl").
+-include("netconfc_test_lib.hrl").
-compile(export_all).
-%% Default timetrap timeout (set in init_per_testcase).
--define(default_timeout, ?t:minutes(1)).
-
--define(NS,ns).
--define(LOCALHOST, "127.0.0.1").
--define(SSH_PORT, 2060).
-
--define(DEFAULT_SSH_OPTS,[{ssh,?LOCALHOST},
- {port,?SSH_PORT},
- {user,"xxx"},
- {password,"xxx"}]).
--define(DEFAULT_SSH_OPTS(Dir), ?DEFAULT_SSH_OPTS++[{user_dir,Dir}]).
-
--define(ok,ok).
-
suite() ->
[{ct_hooks, [{cth_conn_log,
[{ct_netconfc,[{log_type,html}, %will be overwritten by config
@@ -136,8 +121,8 @@ end_per_testcase(_Case, Config) ->
init_per_suite(Config) ->
case catch {crypto:start(), ssh:start()} of
{ok, ok} ->
- {ok, _} = get_id_keys(Config),
- make_dsa_files(Config),
+ {ok, _} = netconfc_test_lib:get_id_keys(Config),
+ netconfc_test_lib:make_dsa_files(Config),
Server = ?NS:start(?config(data_dir,Config)),
[{server,Server}|Config];
_ ->
@@ -148,7 +133,7 @@ end_per_suite(Config) ->
?NS:stop(?config(server,Config)),
ssh:stop(),
crypto:stop(),
- remove_id_keys(Config),
+ netconfc_test_lib:remove_id_keys(Config),
Config.
hello(Config) ->
@@ -1001,165 +986,3 @@ pad(I) when I<10 ->
"0"++integer_to_list(I);
pad(I) ->
integer_to_list(I).
-
-
-%%%-----------------------------------------------------------------
-%%% BEGIN SSH key management
-%% copy private keys to given dir from ~/.ssh
-get_id_keys(Config) ->
- DstDir = ?config(priv_dir, Config),
- SrcDir = filename:join(os:getenv("HOME"), ".ssh"),
- RsaOk = copyfile(SrcDir, DstDir, "id_rsa"),
- DsaOk = copyfile(SrcDir, DstDir, "id_dsa"),
- case {RsaOk, DsaOk} of
- {{ok, _}, {ok, _}} -> {ok, both};
- {{ok, _}, _} -> {ok, rsa};
- {_, {ok, _}} -> {ok, dsa};
- {Error, _} -> Error
- end.
-
-%% Remove later on. Use make_dsa_files instead.
-remove_id_keys(Config) ->
- Dir = ?config(priv_dir, Config),
- file:delete(filename:join(Dir, "id_rsa")),
- file:delete(filename:join(Dir, "id_dsa")).
-
-
-make_dsa_files(Config) ->
- make_dsa_files(Config, rfc4716_public_key).
-make_dsa_files(Config, Type) ->
- {DSA, EncodedKey} = gen_dsa(128, 20),
- PKey = DSA#'DSAPrivateKey'.y,
- P = DSA#'DSAPrivateKey'.p,
- Q = DSA#'DSAPrivateKey'.q,
- G = DSA#'DSAPrivateKey'.g,
- Dss = #'Dss-Parms'{p=P, q=Q, g=G},
- {ok, Hostname} = inet:gethostname(),
- {ok, {A, B, C, D}} = inet:getaddr(Hostname, inet),
- IP = lists:concat([A, ".", B, ".", C, ".", D]),
- Attributes = [], % Could be [{comment,"user@" ++ Hostname}],
- HostNames = [{hostnames,[IP, IP]}],
- PublicKey = [{{PKey, Dss}, Attributes}],
- KnownHosts = [{{PKey, Dss}, HostNames}],
-
- KnownHostsEnc = public_key:ssh_encode(KnownHosts, known_hosts),
- KnownHosts = public_key:ssh_decode(KnownHostsEnc, known_hosts),
-
- PublicKeyEnc = public_key:ssh_encode(PublicKey, Type),
-
- SystemTmpDir = ?config(data_dir, Config),
- filelib:ensure_dir(SystemTmpDir),
- file:make_dir(SystemTmpDir),
-
- DSAFile = filename:join(SystemTmpDir, "ssh_host_dsa_key.pub"),
- file:delete(DSAFile),
-
- DSAPrivateFile = filename:join(SystemTmpDir, "ssh_host_dsa_key"),
- file:delete(DSAPrivateFile),
-
- KHFile = filename:join(SystemTmpDir, "known_hosts"),
- file:delete(KHFile),
-
- PemBin = public_key:pem_encode([EncodedKey]),
-
- file:write_file(DSAFile, PublicKeyEnc),
- file:write_file(KHFile, KnownHostsEnc),
- file:write_file(DSAPrivateFile, PemBin),
- ok.
-
-
-%%--------------------------------------------------------------------
-%% @doc Creates a dsa key (OBS: for testing only)
-%% the sizes are in bytes
-%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()}
-%% @end
-%%--------------------------------------------------------------------
-gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) ->
- Key = gen_dsa2(LSize, NSize),
- {Key, encode_key(Key)}.
-
-encode_key(Key = #'DSAPrivateKey'{}) ->
- Der = public_key:der_encode('DSAPrivateKey', Key),
- {'DSAPrivateKey', Der, not_encrypted}.
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% DSA key generation (OBS: for testing only)
-%% See http://en.wikipedia.org/wiki/Digital_Signature_Algorithm
-%% and the fips_186-3.pdf
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-gen_dsa2(LSize, NSize) ->
- Q = prime(NSize), %% Choose N-bit prime Q
- X0 = prime(LSize),
- P0 = prime((LSize div 2) +1),
-
- %% Choose L-bit prime modulus P such that p-1 is a multiple of q.
- case dsa_search(X0 div (2*Q*P0), P0, Q, 1000) of
- error ->
- gen_dsa2(LSize, NSize);
- P ->
- G = crypto:mod_pow(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q.
- %% such that This may be done by setting g = h^(p-1)/q mod p, commonly h=2 is used.
-
- X = prime(20), %% Choose x by some random method, where 0 < x < q.
- Y = crypto:mod_pow(G, X, P), %% Calculate y = g^x mod p.
-
- #'DSAPrivateKey'{version=0, p = P, q = Q,
- g = crypto:bytes_to_integer(G), y = crypto:bytes_to_integer(Y), x = X}
- end.
-
-%% See fips_186-3.pdf
-dsa_search(T, P0, Q, Iter) when Iter > 0 ->
- P = 2*T*Q*P0 + 1,
- case is_prime(P, 50) of
- true -> P;
- false -> dsa_search(T+1, P0, Q, Iter-1)
- end;
-dsa_search(_,_,_,_) ->
- error.
-
-
-%%%%%%% Crypto Math %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-prime(ByteSize) ->
- Rand = odd_rand(ByteSize),
- prime_odd(Rand, 0).
-
-prime_odd(Rand, N) ->
- case is_prime(Rand, 50) of
- true ->
- Rand;
- false ->
- prime_odd(Rand+2, N+1)
- end.
-
-%% see http://en.wikipedia.org/wiki/Fermat_primality_test
-is_prime(_, 0) -> true;
-is_prime(Candidate, Test) ->
- CoPrime = odd_rand(10000, Candidate),
- Result = crypto:mod_pow(CoPrime, Candidate, Candidate) ,
- is_prime(CoPrime, crypto:bytes_to_integer(Result), Candidate, Test).
-
-is_prime(CoPrime, CoPrime, Candidate, Test) ->
- is_prime(Candidate, Test-1);
-is_prime(_,_,_,_) ->
- false.
-
-odd_rand(Size) ->
- Min = 1 bsl (Size*8-1),
- Max = (1 bsl (Size*8))-1,
- odd_rand(Min, Max).
-
-odd_rand(Min,Max) ->
- Rand = crypto:rand_uniform(Min,Max),
- case Rand rem 2 of
- 0 ->
- Rand + 1;
- _ ->
- Rand
- end.
-
-copyfile(SrcDir, DstDir, Fn) ->
- file:copy(filename:join(SrcDir, Fn),
- filename:join(DstDir, Fn)).
-
-%%% END SSH key management
-%%%-----------------------------------------------------------------
diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl
new file mode 100644
index 0000000000..7a44d148dd
--- /dev/null
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl
@@ -0,0 +1,147 @@
+%%--------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2014. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%----------------------------------------------------------------------
+-module(netconfc_remote_SUITE).
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/src/ct_netconfc.hrl").
+-include("netconfc_test_lib.hrl").
+
+-compile(export_all).
+
+suite() ->
+ [{ct_hooks, [{cth_conn_log,[{ct_netconfc,[{log_type,html}]}]}]}].
+
+all() ->
+ case os:find_executable("ssh") of
+ false ->
+ {skip, "SSH not installed on host"};
+ _ ->
+ [remote_crash
+ ]
+ end.
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+init_per_testcase(Case, Config) ->
+ stop_node(Case),
+ Dog = test_server:timetrap(?default_timeout),
+ [{watchdog, Dog}|Config].
+
+end_per_testcase(Case, Config) ->
+ stop_node(Case),
+ Dog=?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+stop_node(Case) ->
+ {ok,Host} = inet:gethostname(),
+ Node = list_to_atom("nc_" ++ atom_to_list(Case)++ "@" ++ Host),
+ rpc:call(Node,erlang,halt,[]).
+
+
+init_per_suite(Config) ->
+ case {crypto:start(),ssh:start()} of
+ {ok,ok} ->
+ {ok, _} = netconfc_test_lib:get_id_keys(Config),
+ netconfc_test_lib:make_dsa_files(Config),
+ Config;
+ _ ->
+ {skip, "Crypto and/or SSH could not be started locally!"}
+ end.
+
+end_per_suite(Config) ->
+ ssh:stop(),
+ crypto:stop(),
+ netconfc_test_lib:remove_id_keys(Config),
+ Config.
+
+%% This test case is related to seq12645
+%% Running the netconf server in a remote node, test that the client
+%% process terminates if the remote node goes down.
+remote_crash(Config) ->
+ {ok,Node} = ct_slave:start(nc_remote_crash),
+ Pa = filename:dirname(code:which(?NS)),
+ true = rpc:call(Node,code,add_patha,[Pa]),
+
+ case {rpc:call(Node,crypto,start,[]),rpc:call(Node,ssh,start,[])} of
+ {ok,ok} ->
+ Server = rpc:call(Node,?NS,start,[?config(data_dir,Config)]),
+ remote_crash(Node,Config);
+ _ ->
+ {skip, "Crypto and/or SSH could not be started remote!"}
+ end.
+
+remote_crash(Node,Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Client} = open_success(Node,DataDir),
+
+ ns(Node,expect_reply,[{'create-subscription',[stream]},ok]),
+ ?ok = ct_netconfc:create_subscription(Client),
+
+ true = erlang:is_process_alive(Client),
+ Ref = erlang:monitor(process,Client),
+ rpc:call(Node,erlang,halt,[]), % take the node down as brutally as possible
+ receive {'DOWN',Ref,process,Client,_} ->
+ ok
+ after 10000 ->
+ ct:fail(client_still_alive)
+ end.
+
+%%%-----------------------------------------------------------------
+
+break(_Config) ->
+ test_server:break("break test case").
+
+%%%-----------------------------------------------------------------
+%% Open a netconf session which is not specified in a config file
+open_success(Node,Dir) ->
+ open_success(Node,Dir,[]).
+
+%% Open a netconf session which is not specified in a config file, and
+%% give som extra options in addition to the test defaults.
+open_success(Node,Dir,ExtraOpts) when is_list(Dir), is_list(ExtraOpts) ->
+ ns(Node,hello,[1]), % tell server to send hello with session id 1
+ ns(Node,expect,[hello]), % tell server to expect a hello message from client
+ open(Dir,ExtraOpts);
+
+%% Open a named netconf session which is not specified in a config file
+open_success(Node,KeyOrName,Dir) when is_atom(KeyOrName), is_list(Dir) ->
+ ns(Node,hello,[1]),
+ ns(Node,expect,[hello]),
+ ct_netconfc:open(KeyOrName,?DEFAULT_SSH_OPTS(Dir)).
+
+open(Dir) ->
+ open(Dir,[]).
+open(Dir,ExtraOpts) ->
+ Opts = lists:ukeymerge(1,lists:keysort(1,ExtraOpts),
+ lists:keysort(1,?DEFAULT_SSH_OPTS(Dir))),
+ ct_netconfc:open(Opts).
+
+%%%-----------------------------------------------------------------
+%%% Call server on remote node
+ns(Node,Func,Args) ->
+ rpc:call(Node,?NS,Func,Args).
+
diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_test_lib.erl b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_test_lib.erl
new file mode 100644
index 0000000000..e058bc7600
--- /dev/null
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_test_lib.erl
@@ -0,0 +1,166 @@
+-module(netconfc_test_lib).
+
+-export([get_id_keys/1, remove_id_keys/1, make_dsa_files/1]).
+-include_lib("common_test/include/ct.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
+%%%-----------------------------------------------------------------
+%%% BEGIN SSH key management
+%% copy private keys to given dir from ~/.ssh
+get_id_keys(Config) ->
+ DstDir = ?config(priv_dir, Config),
+ SrcDir = filename:join(os:getenv("HOME"), ".ssh"),
+ RsaOk = copyfile(SrcDir, DstDir, "id_rsa"),
+ DsaOk = copyfile(SrcDir, DstDir, "id_dsa"),
+ case {RsaOk, DsaOk} of
+ {{ok, _}, {ok, _}} -> {ok, both};
+ {{ok, _}, _} -> {ok, rsa};
+ {_, {ok, _}} -> {ok, dsa};
+ {Error, _} -> Error
+ end.
+
+%% Remove later on. Use make_dsa_files instead.
+remove_id_keys(Config) ->
+ Dir = ?config(priv_dir, Config),
+ file:delete(filename:join(Dir, "id_rsa")),
+ file:delete(filename:join(Dir, "id_dsa")).
+
+
+make_dsa_files(Config) ->
+ make_dsa_files(Config, rfc4716_public_key).
+make_dsa_files(Config, Type) ->
+ {DSA, EncodedKey} = gen_dsa(128, 20),
+ PKey = DSA#'DSAPrivateKey'.y,
+ P = DSA#'DSAPrivateKey'.p,
+ Q = DSA#'DSAPrivateKey'.q,
+ G = DSA#'DSAPrivateKey'.g,
+ Dss = #'Dss-Parms'{p=P, q=Q, g=G},
+ {ok, Hostname} = inet:gethostname(),
+ {ok, {A, B, C, D}} = inet:getaddr(Hostname, inet),
+ IP = lists:concat([A, ".", B, ".", C, ".", D]),
+ Attributes = [], % Could be [{comment,"user@" ++ Hostname}],
+ HostNames = [{hostnames,[IP, IP]}],
+ PublicKey = [{{PKey, Dss}, Attributes}],
+ KnownHosts = [{{PKey, Dss}, HostNames}],
+
+ KnownHostsEnc = public_key:ssh_encode(KnownHosts, known_hosts),
+ KnownHosts = public_key:ssh_decode(KnownHostsEnc, known_hosts),
+
+ PublicKeyEnc = public_key:ssh_encode(PublicKey, Type),
+
+ SystemTmpDir = ?config(data_dir, Config),
+ filelib:ensure_dir(SystemTmpDir),
+ file:make_dir(SystemTmpDir),
+
+ DSAFile = filename:join(SystemTmpDir, "ssh_host_dsa_key.pub"),
+ file:delete(DSAFile),
+
+ DSAPrivateFile = filename:join(SystemTmpDir, "ssh_host_dsa_key"),
+ file:delete(DSAPrivateFile),
+
+ KHFile = filename:join(SystemTmpDir, "known_hosts"),
+ file:delete(KHFile),
+
+ PemBin = public_key:pem_encode([EncodedKey]),
+
+ file:write_file(DSAFile, PublicKeyEnc),
+ file:write_file(KHFile, KnownHostsEnc),
+ file:write_file(DSAPrivateFile, PemBin),
+ ok.
+
+
+%%--------------------------------------------------------------------
+%% @doc Creates a dsa key (OBS: for testing only)
+%% the sizes are in bytes
+%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()}
+%% @end
+%%--------------------------------------------------------------------
+gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) ->
+ Key = gen_dsa2(LSize, NSize),
+ {Key, encode_key(Key)}.
+
+encode_key(Key = #'DSAPrivateKey'{}) ->
+ Der = public_key:der_encode('DSAPrivateKey', Key),
+ {'DSAPrivateKey', Der, not_encrypted}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% DSA key generation (OBS: for testing only)
+%% See http://en.wikipedia.org/wiki/Digital_Signature_Algorithm
+%% and the fips_186-3.pdf
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+gen_dsa2(LSize, NSize) ->
+ Q = prime(NSize), %% Choose N-bit prime Q
+ X0 = prime(LSize),
+ P0 = prime((LSize div 2) +1),
+
+ %% Choose L-bit prime modulus P such that p-1 is a multiple of q.
+ case dsa_search(X0 div (2*Q*P0), P0, Q, 1000) of
+ error ->
+ gen_dsa2(LSize, NSize);
+ P ->
+ G = crypto:mod_pow(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q.
+ %% such that This may be done by setting g = h^(p-1)/q mod p, commonly h=2 is used.
+
+ X = prime(20), %% Choose x by some random method, where 0 < x < q.
+ Y = crypto:mod_pow(G, X, P), %% Calculate y = g^x mod p.
+
+ #'DSAPrivateKey'{version=0, p = P, q = Q,
+ g = crypto:bytes_to_integer(G), y = crypto:bytes_to_integer(Y), x = X}
+ end.
+
+%% See fips_186-3.pdf
+dsa_search(T, P0, Q, Iter) when Iter > 0 ->
+ P = 2*T*Q*P0 + 1,
+ case is_prime(P, 50) of
+ true -> P;
+ false -> dsa_search(T+1, P0, Q, Iter-1)
+ end;
+dsa_search(_,_,_,_) ->
+ error.
+
+
+%%%%%%% Crypto Math %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+prime(ByteSize) ->
+ Rand = odd_rand(ByteSize),
+ prime_odd(Rand, 0).
+
+prime_odd(Rand, N) ->
+ case is_prime(Rand, 50) of
+ true ->
+ Rand;
+ false ->
+ prime_odd(Rand+2, N+1)
+ end.
+
+%% see http://en.wikipedia.org/wiki/Fermat_primality_test
+is_prime(_, 0) -> true;
+is_prime(Candidate, Test) ->
+ CoPrime = odd_rand(10000, Candidate),
+ Result = crypto:mod_pow(CoPrime, Candidate, Candidate) ,
+ is_prime(CoPrime, crypto:bytes_to_integer(Result), Candidate, Test).
+
+is_prime(CoPrime, CoPrime, Candidate, Test) ->
+ is_prime(Candidate, Test-1);
+is_prime(_,_,_,_) ->
+ false.
+
+odd_rand(Size) ->
+ Min = 1 bsl (Size*8-1),
+ Max = (1 bsl (Size*8))-1,
+ odd_rand(Min, Max).
+
+odd_rand(Min,Max) ->
+ Rand = crypto:rand_uniform(Min,Max),
+ case Rand rem 2 of
+ 0 ->
+ Rand + 1;
+ _ ->
+ Rand
+ end.
+
+copyfile(SrcDir, DstDir, Fn) ->
+ file:copy(filename:join(SrcDir, Fn),
+ filename:join(DstDir, Fn)).
+
+%%% END SSH key management
+%%%-----------------------------------------------------------------
diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_test_lib.hrl b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_test_lib.hrl
new file mode 100644
index 0000000000..dcaad5ba93
--- /dev/null
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_test_lib.hrl
@@ -0,0 +1,14 @@
+%% Default timetrap timeout (set in init_per_testcase).
+-define(default_timeout, ?t:minutes(1)).
+
+-define(NS,ns). % netconf server module
+-define(LOCALHOST, "127.0.0.1").
+-define(SSH_PORT, 2060).
+
+-define(DEFAULT_SSH_OPTS,[{ssh,?LOCALHOST},
+ {port,?SSH_PORT},
+ {user,"xxx"},
+ {password,"xxx"}]).
+-define(DEFAULT_SSH_OPTS(Dir), ?DEFAULT_SSH_OPTS++[{user_dir,Dir}]).
+
+-define(ok,ok).