diff --git a/docs/source/advanced/restapi/restapi_usage/restapi_usage.rst b/docs/source/advanced/restapi/restapi_usage/restapi_usage.rst index 09c65095c..53490eb13 100644 --- a/docs/source/advanced/restapi/restapi_usage/restapi_usage.rst +++ b/docs/source/advanced/restapi/restapi_usage/restapi_usage.rst @@ -58,7 +58,15 @@ Then in the subsequent REST API access, the token can be used to replace the use curl -X GET -k -H X-Auth-Token:5cabd675-bc2e-4318-b1d6-831fd1f32f97 'https:///xcatws/? -The validity of token is 24 hours. If an old token has expired, you will get a 'Authentication failure' error. Then you need reacquire a token with your account. +The default validity of a token is 1 day. This default can be changed by the setting of `tokenexpiredays` attribute in `site` table. :: + + chdef -t site clustersite tokenexpiredays= + +To make tokens valid forever use "never". :: + + chdef -t site clustersite tokenexpiredays=never + +If an old token has expired, you will get a 'Authentication failure' error. You will need to reacquire a token for your account. The Common Parameters for Resource URI diff --git a/docs/source/guides/admin-guides/references/man1/xcattest.1.rst b/docs/source/guides/admin-guides/references/man1/xcattest.1.rst index 5b07c71cc..53a022bdf 100644 --- a/docs/source/guides/admin-guides/references/man1/xcattest.1.rst +++ b/docs/source/guides/admin-guides/references/man1/xcattest.1.rst @@ -229,9 +229,10 @@ For example, to run rscan command against the hardware control point of compute .. code-block:: perl rscan __GETNODEATTR($$CN, hcp)__ -z - 3. B To get the value of column where keyname == key in specified table. +3. \ **GETTABLEVALUE(keyname, key, colname, table)**\ To get the value of column where keyname == key in specified table. + ***** FILES diff --git a/docs/source/guides/admin-guides/references/man5/policy.5.rst b/docs/source/guides/admin-guides/references/man5/policy.5.rst index e9b53ba0c..7b41a78db 100644 --- a/docs/source/guides/admin-guides/references/man5/policy.5.rst +++ b/docs/source/guides/admin-guides/references/man5/policy.5.rst @@ -80,7 +80,7 @@ policy Attributes: \ **rule**\ - Specifies how this rule should be applied. Valid values are: allow, accept, trusted. Allow or accept will allow the user to run the commands. Any other value will deny the user access to the commands. Trusted means that once this client has been authenticated via the certificate, all other information that is sent (e.g. the username) is believed without question. This authorization should only be given to the xcatd on the management node at this time. + Specifies how this rule should be applied. Valid values are: allow, trusted. Allow will allow the user to run the commands. Any other value will deny the user access to the commands. Trusted means that once this client has been authenticated via the certificate, all other information that is sent (e.g. the username) is believed without question. This authorization should only be given to the xcatd on the management node at this time. diff --git a/docs/source/guides/admin-guides/references/man5/site.5.rst b/docs/source/guides/admin-guides/references/man5/site.5.rst index d4ea2ab7e..ed5180374 100644 --- a/docs/source/guides/admin-guides/references/man5/site.5.rst +++ b/docs/source/guides/admin-guides/references/man5/site.5.rst @@ -429,6 +429,8 @@ site Attributes: -------------------- XCAT DAEMON ATTRIBUTES -------------------- + tokenexpiredays: Number of days before REST API token will expire. The default is 1. + use 'never' if you want your token to never expire. useflowcontrol: (yes/1 or no/0). If yes, the postscript processing on each node contacts xcatd on the MN/SN using a lightweight UDP packet to wait until xcatd is ready to handle the requests associated with diff --git a/docs/source/guides/admin-guides/references/man5/token.5.rst b/docs/source/guides/admin-guides/references/man5/token.5.rst index f70bf9eaf..5089aa16b 100644 --- a/docs/source/guides/admin-guides/references/man5/token.5.rst +++ b/docs/source/guides/admin-guides/references/man5/token.5.rst @@ -19,7 +19,7 @@ SYNOPSIS ******** -\ **token Attributes:**\ \ *tokenid*\ , \ *username*\ , \ *expire*\ , \ *comments*\ , \ *disable*\ +\ **token Attributes:**\ \ *tokenid*\ , \ *username*\ , \ *expire*\ , \ *created*\ , \ *access*\ , \ *comments*\ , \ *disable*\ *********** @@ -54,6 +54,18 @@ token Attributes: +\ **created**\ + + Creation time for this token. + + + +\ **access**\ + + Last access time for this token. + + + \ **comments**\ Any user-provided notes. diff --git a/docs/source/guides/admin-guides/references/man7/policy.7.rst b/docs/source/guides/admin-guides/references/man7/policy.7.rst index 5c9709647..b920c052f 100644 --- a/docs/source/guides/admin-guides/references/man7/policy.7.rst +++ b/docs/source/guides/admin-guides/references/man7/policy.7.rst @@ -77,7 +77,7 @@ policy Attributes: \ **rule**\ (policy.rule) - Specifies how this rule should be applied. Valid values are: allow, accept, trusted. Allow or accept will allow the user to run the commands. Any other value will deny the user access to the commands. Trusted means that once this client has been authenticated via the certificate, all other information that is sent (e.g. the username) is believed without question. This authorization should only be given to the xcatd on the management node at this time. + Specifies how this rule should be applied. Valid values are: allow, trusted. Allow will allow the user to run the commands. Any other value will deny the user access to the commands. Trusted means that once this client has been authenticated via the certificate, all other information that is sent (e.g. the username) is believed without question. This authorization should only be given to the xcatd on the management node at this time. diff --git a/perl-xCAT/xCAT/Schema.pm b/perl-xCAT/xCAT/Schema.pm index d06d4c780..e16dba65b 100755 --- a/perl-xCAT/xCAT/Schema.pm +++ b/perl-xCAT/xCAT/Schema.pm @@ -890,7 +890,7 @@ passed as argument rather than by table value', noderange => 'The Noderange that this rule applies to. Default is "*" (all nodes). Not supported with the *def commands.', parameters => 'A regular expression that matches the command parameters (everything except the noderange) that this rule applies to. Default is "*" (all parameters). Not supported with the *def commands.', time => 'Time ranges that this command may be executed in. This is not supported.', - rule => 'Specifies how this rule should be applied. Valid values are: allow, accept, trusted. Allow or accept will allow the user to run the commands. Any other value will deny the user access to the commands. Trusted means that once this client has been authenticated via the certificate, all other information that is sent (e.g. the username) is believed without question. This authorization should only be given to the xcatd on the management node at this time.', + rule => 'Specifies how this rule should be applied. Valid values are: allow, trusted. Allow will allow the user to run the commands. Any other value will deny the user access to the commands. Trusted means that once this client has been authenticated via the certificate, all other information that is sent (e.g. the username) is believed without question. This authorization should only be given to the xcatd on the management node at this time.', comments => 'Any user-written notes.', disable => "Set to 'yes' or '1' to comment out this row.", }, @@ -1285,6 +1285,8 @@ passed as argument rather than by table value', " --------------------\n" . "XCAT DAEMON ATTRIBUTES\n" . " --------------------\n" . +" tokenexpiredays: Number of days before REST API token will expire. The default is 1.\n" . +" use 'never' if you want your token to never expire.\n" . " useflowcontrol: (yes/1 or no/0). If yes, the postscript processing on each node\n" . " contacts xcatd on the MN/SN using a lightweight UDP packet to wait\n" . " until xcatd is ready to handle the requests associated with\n" . @@ -1802,13 +1804,15 @@ zvmivp => { }, }, token => { - cols => [qw(tokenid username expire comments disable)], + cols => [qw(tokenid username expire created access comments disable)], keys => [qw(tokenid)], table_desc => 'The token of users for authentication.', descriptions => { tokenid => 'It is a UUID as an unified identify for the user.', username => 'The user name.', expire => 'The expire time for this token.', + created => 'Creation time for this token.', + access => 'Last access time for this token.', comments => 'Any user-provided notes.', disable => "Set to 'yes' or '1' to comment out this row.", }, diff --git a/perl-xCAT/xCAT/Utils.pm b/perl-xCAT/xCAT/Utils.pm index cc03a05b5..4130b3397 100644 --- a/perl-xCAT/xCAT/Utils.pm +++ b/perl-xCAT/xCAT/Utils.pm @@ -3894,6 +3894,42 @@ sub gettimezone #-------------------------------------------------------------------------------- +=head3 time2string + Return passed in time (in DateTime format) as a string in YYYY/MM/DD HH:MM:SS format + Arguments: + Unix DateTime as returned by time() for example + Optional Separator character for date, default is "/" + Returns: + String in YYYY/MM/DD HH:MM:SS format + Globals: + none + Error: + None + Example: + my $time_string = xCAT::Utils->time2string($time,"-"); + Comments: + none +=cut + +#-------------------------------------------------------------------------------- +sub time2string +{ + my $unixtime = shift; + my $date_separator; + if ($unixtime =~ /xCAT::Utils/) + { + $unixtime = shift; + $date_separator = shift // "/"; # Optional date separator, if not specified, default to "/" + } + my $time_separator = ":"; + + my ($sec, $min, $hour, $mday, $mon, $year) = localtime($unixtime); + $year += 1900; + $mon += 1; + return $year . $date_separator . $mon . $date_separator . $mday . " " . $hour . $time_separator . $min . $time_separator . $sec; +} +#-------------------------------------------------------------------------------- + =head3 specialservicemgr some special services cannot be processed in sysVinit, upstart and systemd framework, should be process here... Arguments: diff --git a/xCAT-server/lib/perl/xCAT/xcatd.pm b/xCAT-server/lib/perl/xCAT/xcatd.pm index 48cb9c85a..e36e690e1 100644 --- a/xCAT-server/lib/perl/xCAT/xcatd.pm +++ b/xCAT-server/lib/perl/xCAT/xcatd.pm @@ -7,11 +7,14 @@ BEGIN $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : -d '/opt/xcat' ? '/opt/xcat' : '/usr'; } use strict; +use Date::Parse; use xCAT::Table; +use xCAT::TableUtils; use xCAT::MsgUtils; use Data::Dumper; use xCAT::NodeRange; use xCAT::Utils; +use Scalar::Util qw/looks_like_number/; #-------------------------------------------------------------------------------- @@ -351,36 +354,66 @@ sub validate { return 0; } -my $tokentimeout = 86400; # one day +my $one_day = 86400; # one day in seconds +my $days = 1; # default days for token expiration +my $never_label = "never"; -# this subroutine search the token table -# 1. find the existed token entry for the user and reset the expire time -# 1.1. if not find existed token, create a new one and add it to token table -# 2. clean up the expired token +# this subroutine creates a new token in token table +# 1. If old style unix DateTime format token found in the token table +# if expired -> remove it +# if not expired -> replace unix DateTime expiration with new human readable format +# 2. create a new token and add it to token table # # this subroutine is called after the account has been authorized sub gettoken { my $class = shift; my $req = shift; + my $current_time = time(); my $user = $req->{gettoken}->[0]->{username}->[0]; my $tokentb = xCAT::Table->new('token'); unless ($tokentb) { return undef; } my $tokens = $tokentb->getAllEntries; + + # Search for "old" style tokens containing unix DateTime format expiration date foreach my $token (@{$tokens}) { - #clean the expired token - if ($token->{'expire'} < time()) { - $tokentb->delEntries({ 'tokenid' => $token->{tokenid} }); + if ($token->{'expire'} and looks_like_number($token->{'expire'})) { + # Expiration field contains only digits -> this is a old style token with unix DateTime format + + if ($token->{'expire'} and ($token->{'expire'} < $current_time)) { + # Clean expired token with old unix DateTime format + $tokentb->delEntries({ tokenid => $token->{tokenid} }); + } else { + # Change non-expired old style token to new human readable format + $tokentb->setAttribs({ tokenid => $token->{tokenid}, username => $token->{'username'} }, {expire => xCAT::Utils->time2string($token->{'expire'}, "-")}); + } } } - # create a new token for this request + # create a new token id my $uuid = xCAT::Utils->genUUID(); - my $expiretime = time() + $tokentimeout; - $tokentb->setAttribs({ tokenid => $uuid, username => $user }, { expire => $expiretime }); + # extract site table setting for number of days before token expires + my $token_days = xCAT::TableUtils->get_site_attribute("tokenexpiredays"); + my $expiretime = $current_time + $one_day; # default is one day + my $expire_time_string = xCAT::Utils->time2string($expiretime, "-"); + if ($token_days and (uc($token_days) eq uc($never_label))) { + # Tokens never expire + $expiretime = $never_label; + $expire_time_string = $never_label; + } + elsif ($token_days and $token_days > 0) { + # Use number of days from site table + $days = $token_days; + $expiretime = $current_time + $one_day * $days; + $expire_time_string = xCAT::Utils->time2string($expiretime, "-"); + } + my $access_time_string = xCAT::Utils->time2string($current_time, "-"); + # create a new token and set its expiration and creation time + $tokentb->setAttribs({ tokenid => $uuid, username => $user }, + { expire => $expire_time_string, created => $access_time_string }); $tokentb->close(); return ($uuid, $expiretime); @@ -391,6 +424,7 @@ sub verifytoken { my $class = shift; my $req = shift; + my $current_time = time(); my $tokenid = $req->{tokens}->[0]->{tokenid}->[0]; my $tokentb = xCAT::Table->new('token'); unless ($tokentb) { @@ -398,16 +432,32 @@ sub verifytoken { } my $token = $tokentb->getAttribs({ 'tokenid' => $tokenid }, ('username', 'expire')); if (defined($token) && defined($token->{'username'}) && defined($token->{'expire'})) { - my $expiretime = time() + $tokentimeout; - if ($token->{'expire'} < time()) { - $tokentb->delEntries({ 'tokenid' => $token->{tokenid} }); - return undef; + + if ($token->{'expire'} and looks_like_number($token->{'expire'})) { + # Expiration field contains only digits -> this is a old style token with unix DateTime format + if ($token->{'expire'} and $token->{'expire'} < $current_time) { + # Clean expired token with old unix DateTime format + $tokentb->delEntries({ 'tokenid' => $token->{tokenid} }); + return undef; + } else { + # Change non-expired old style token to new human readable format + $tokentb->setAttribs({ tokenid => $tokenid, username => $token->{'username'} }, + {access => xCAT::Utils->time2string($current_time, "-"), + expire => xCAT::Utils->time2string($token->{'expire'}, "-")}); + return $token->{'username'}; + } } else { - return $token->{'username'}; + if ($token->{'expire'} and ($token->{'expire'} ne "never") and str2time($token->{'expire'}) < $current_time) { + # Expired new style token + return undef; + } else { + # Not expired new style token - update current access time + $tokentb->setAttribs({ tokenid => $tokenid, username => $token->{'username'} }, {access => xCAT::Utils->time2string($current_time, "-")}); + return $token->{'username'}; + } } } else { + # Token entry was not found return undef; } -} - 1; diff --git a/xCAT-server/sbin/xcatd b/xCAT-server/sbin/xcatd index 6c717bebe..16c896718 100755 --- a/xCAT-server/sbin/xcatd +++ b/xCAT-server/sbin/xcatd @@ -2826,11 +2826,15 @@ sub service_connection { if ($peername) { # for a valid account, get a token + my $htime; my ($tokenid, $exptime) = xCAT::xcatd->gettoken($req); - my ($sec, $min, $hour, $mday, $mon, $year) = localtime($exptime); - $year += 1900; - $mon += 1; - my $htime = "$year-$mon-$mday $hour:$min:$sec"; + if ($exptime eq "never") { + # If token expiration time was set to "never", return that to the user. + $htime = $exptime; + } else { + # Token expiration is a unix DateTime, convert to readable string + $htime = xCAT::Utils->time2string($exptime, "-"); + } $resp = { data => [ { token => [ { id => $tokenid, expire => $htime } ] } ] }; } else { $resp = { error => ["Authentication failure"], errorcode => [1] }; diff --git a/xCAT-test/autotest/testcase/restapi/node/case0 b/xCAT-test/autotest/testcase/restapi/node/case0 deleted file mode 100644 index a1308afe4..000000000 --- a/xCAT-test/autotest/testcase/restapi/node/case0 +++ /dev/null @@ -1,242 +0,0 @@ -start:node_post -description: node_post -cmd:restapitest -m POST -r /nodes/node1 -d '{"groups":"all","mgt":"dfm","netboot":"yaboot"}' -check:rc==201 -end - -start:node_post2 -description: node_post2 -cmd:restapitest -m POST -r /nodes/node1 -d '{"groups":"all","mgt":"dfm","netboot":"yaboot"}' -check:rc==403 -cmdcheck:restapitest -o '{"errorcode":"1"}' -O == -end - - -start:node_put -description: node_put -cmd:restapitest -m PUT -r /nodes/node1 -d '{"mgt":"hmc","netboot":"xnba"}' -check:rc==200 -end - - - -start:nodes_get -description: nodes_get -cmd:restapitest -m GET -r /nodes -check:rc==200 -cmdcheck:restapitest -o '["node1"]' -O == -end - -start:node_get -description: node_get -cmd:restapitest -m GET -r /nodes/node1 -check:rc==200 -cmdcheck:restapitest -o '{"node1":{"netboot":"xnba"}}' -O == -end - -start:node_delete -description: node_delete -cmd:restapitest -m DELETE -r /nodes/node1 -check:rc==200 -end - -start:nodes_get2 -description: nodes_get2 -cmd:restapitest -m GET -r /nodes -check:rc==200 -cmdcheck:restapitest -o '["node1"]' -O != -end - -start:node_get2 -description: node_get2 -cmd:restapitest -m GET -r /nodes/node1 -check:rc==403 -cmdcheck:restapitest -o '{"errorcode":"1"}' -O == -end - -start:node_post3_for_get_test -description: node_post3_for_get_test -cmd:restapitest -m POST -r /nodes/node1 -d '{"groups":"all","mgt":"dfm","netboot":"yaboot"}' -check:rc==201 -end - -start:node_attr_get -description: node_get2 -cmd:restapitest -m GET -r /nodes/node1/attrs/mgt,groups,netboot -check:rc==200 -cmdcheck:restapitest -o '{"node1":{"netboot":"yaboot"}}' -O == -end - -start:node_makehosts -description: node_makehosts -cmd:restapitest -m POST -r /nodes/node1/host -check:rc==201 -end - -start:node_makedns -description: node_makehosts -cmd:restapitest -m POST -r /nodes/node1/dns -check:rc==201 -end - - -start:node_delete_dns -description: node_delete_dns -cmd:restapitest -m DELETE -r /nodes/node1/dns -check:rc==200 -end - -start:node_put -description: node_put -cmd:restapitest -m PUT -r /nodes/node1 -d '{"mac":"00:1a:64:54:14:80"}' -check:rc==200 -end - -start:node_makedhcp -description: node_makedhcp -cmd:restapitest -m POST -r /nodes/node1/dhcp -check:rc==201 -end - -start:node_delete_dhcp -description: node_delete_dhcp -cmd:restapitest -m DELETE -r /nodes/node1/dhcp -check:rc==200 -end - -start:node_state -description: node_state -cmd:restapitest -m GET -r /nodes/node1/nodestat -check:rc==200 -cmdcheck:restapitest -o '{"node1":{"nodestat":"ANY"}}' -O == -end - - -#start:node_post4_for_scan_test -#description: node_post4_for_scan_test -#cmd:restapitest -m POST -r /nodes/e108m6hmc02 -d '{"groups":"all,hmc","mgt":"hmc","hwtype":"hmc","mtm":"7042CR4","serial":"1050FBB","nodetype":"ppc"}' -#check:rc==201 -#end - -start:node_scan -description: node_scan -cmd:restapitest -m GET -r /nodes/__GETNODEATTR($$CN,hcp)__ -check:rc==200 -cmdcheck:restapitest -o '{"__GETNODEATTR($$CN,hcp)__":"ANY"}' -O == -end - - -start:node_power_get -description: node_power_get -cmd:restapitest -m GET -r /nodes/$$CN/power -check:rc==200 -cmdcheck:restapitest -o '{"$$CN":{"power":"ANY"}}' -O == -end - -start:node_power_put -description: node_power_reset -cmd:restapitest -m PUT -r /nodes/$$CN/power -d '{"action":"reset"}' -check:rc==200 -end - -#start:node_energy_put -#description: node_energy_put -#cmd:restapitest -m PUT -r /nodes/Vc68m5sn01/energy -d '{"cappingstatus":"on"}' -#check:rc==200 -#end - -#start:node_energy_get -#description: node_energy_get -#cmd:restapitest -m GET -r /nodes/Vc68m5sn01/energy -#check:rc==200 -#cmdcheck:restapitest -o '{"Vc68m5sn01":{"cappingmin":"on"}}' -O == -#end - -#start:node_energy_get_attr -#description: node_energy_get_attr -#cmd:restapitest -m GET -r /nodes/Vc68m5sn01/energy/cappingmaxmin,cappingstatus -#check:rc==200 -#cmdcheck:restapitest -o '{"Vc68m5sn01":{"cappingmin":"ANY"}}' -O == -#end - -#start:node_get_attr -#description: node_get_attr -#cmd:restapitest -m GET -r /nodes/Vc68m5sn01/sp/community -#check:rc==200 -#cmdcheck:restapitest -o '{"Vc68m5sn01":{"SP SNMP Community":"public"}}' -O == -#end - -#start:node_put_attr -#description: node_put_attr -#cmd:restapitest -m PUT -r /nodes/Vc68m5sn01/sp/community -d '{"value":"mycommunity"}'' -#check:rc==200 -#end - -#start:node_put_nextboot -#description: node_put_nextboot -#cmd:restapitest -m PUT -r /nodes/$$CN/nextboot -d '{"order":"net"}' -#check:rc==201 -#end - -#start:node_get_nextboot -#description: node_get_nextboot -#cmd:restapitest -m GET -r /nodes/Vc68m5sn01/nextboot -#check:rc==200 -#cmdcheck:restapitest -o '{"Vc68m5sn01":{"nextboot":"net"}}' -O == -#end - -start:node_put_bootstate -description: node_put_bootstate -cmd:restapitest -m PUT -r /nodes/Vc68m5sn01/bootstate -d '{"osimage":"rhels6.4-x86_64-install-compute"}' -check:rc==201 -end - -start:node_get_bootstate -description: node_get_bootstate -cmd:restapitest -m GET -r /nodes/$$CN/bootstate -check:rc==200 -cmdcheck:restapitest -o '{"$$CN":{"bootstat":"ANY"}}' -O == -end - -start:node_get_vitals -description: node_get_vitals -cmd:restapitest -m GET -r /nodes/Vc68m5sn01/vitals -check:rc==200 -cmdcheck:restapitest -o '{"Vc68m5sn01":{"SysBrd Fault":"0"}}' -O == -end - -start:node_get_vitals_attr -description: node_get_vitals_attr -cmd:restapitest -m GET -r /nodes/$$CN/vitals/all -check:rc==200 -cmdcheck:restapitest -o '{"$$CN":{"System Temperature":"ANY"}}' -O == -end - -start:node_get_inventory -description: node_get_inventory -cmd:restapitest -m GET -r /nodes/Vc68m5sn01/inventory -check:rc==200 -cmdcheck:restapitest -o '{"Vc68m5sn01":{"Power Supply 2 Board FRU Number":"94Y8105"}}' -O == -end - -start:node_get_inventory_attr -description: node_get_inventory_attr -cmd:restapitest -m GET -r /nodes/Vc68m5sn01/inventory/model -check:rc==200 -cmdcheck:restapitest -o '{"Vc68m5sn01":{"System Description":"System x3650 M4"}}' -O == -end - -#start:node_get_eventlog -#description: node_get_eventlog -#cmd:restapitest -m GET -r /nodes/Vc68m5sn01/eventlog -#check:rc==200 -#cmdcheck:restapitest -o '{"Vc68m5sn01":{"eventlog":"ANY"}}' -O == -#end - -start:node_post_nodecopy -description: node_post_nodecopy -cmd:restapitest -m POST -r /nodes/$$CN/nodecopy -d '{"src":["/etc/hosts","/etc/resolv.conf"],"target":"/tmp"}' -check:rc==201 -end - - diff --git a/xCAT-test/autotest/testcase/restapi/node/cases0 b/xCAT-test/autotest/testcase/restapi/node/cases0 index a1308afe4..da8f29146 100644 --- a/xCAT-test/autotest/testcase/restapi/node/cases0 +++ b/xCAT-test/autotest/testcase/restapi/node/cases0 @@ -1,99 +1,82 @@ -start:node_post -description: node_post -cmd:restapitest -m POST -r /nodes/node1 -d '{"groups":"all","mgt":"dfm","netboot":"yaboot"}' +start:create_node_rest +description: create a node with REST API +cmd:restapitest -m POST -r /nodes/restnode -d '{"groups":"all","mgt":"dfm","netboot":"yaboot"}' check:rc==201 end -start:node_post2 -description: node_post2 -cmd:restapitest -m POST -r /nodes/node1 -d '{"groups":"all","mgt":"dfm","netboot":"yaboot"}' -check:rc==403 -cmdcheck:restapitest -o '{"errorcode":"1"}' -O == +start:create_node_rest2 +description: create a node with REST API failure +cmd:restapitest -m POST -r /nodes/restnode -d '{"groups":"all","mgt":"dfm","netboot":"yaboot"}' +check:rc==147 +check:output=~'errorcode' => '1' end - -start:node_put -description: node_put -cmd:restapitest -m PUT -r /nodes/node1 -d '{"mgt":"hmc","netboot":"xnba"}' -check:rc==200 -end - - - -start:nodes_get -description: nodes_get +start:get_nodes_rest +description: get all nodes with REST API cmd:restapitest -m GET -r /nodes check:rc==200 -cmdcheck:restapitest -o '["node1"]' -O == +check:output=~'restnode' end -start:node_get -description: node_get -cmd:restapitest -m GET -r /nodes/node1 +start:get_node_rest +description: get single node with REST API +cmd:restapitest -m GET -r /nodes/restnode check:rc==200 -cmdcheck:restapitest -o '{"node1":{"netboot":"xnba"}}' -O == +check:output=~'restnode' end -start:node_delete -description: node_delete -cmd:restapitest -m DELETE -r /nodes/node1 +start:node_delete_rest +description: delete node with REST API +cmd:restapitest -m DELETE -r /nodes/restnode check:rc==200 end -start:nodes_get2 -description: nodes_get2 +start:get_nodes_rest2 +description: get all nodes with REST API cmd:restapitest -m GET -r /nodes check:rc==200 -cmdcheck:restapitest -o '["node1"]' -O != +check:output!~'restnode' end -start:node_get2 -description: node_get2 -cmd:restapitest -m GET -r /nodes/node1 -check:rc==403 -cmdcheck:restapitest -o '{"errorcode":"1"}' -O == +start:get_node_rest2 +description: get single node with REST API +cmd:restapitest -m GET -r /nodes/restnode +check:rc==147 +check:output=~'errorcode' => '1' end -start:node_post3_for_get_test -description: node_post3_for_get_test -cmd:restapitest -m POST -r /nodes/node1 -d '{"groups":"all","mgt":"dfm","netboot":"yaboot"}' +start:create_node_token_rest +description: create a node with REST API using authentication token +cmd:restapitest -m POST -r /nodes/restnode -d '{"groups":"all","mgt":"dfm","netboot":"yaboot"}' -t check:rc==201 end -start:node_attr_get -description: node_get2 -cmd:restapitest -m GET -r /nodes/node1/attrs/mgt,groups,netboot -check:rc==200 -cmdcheck:restapitest -o '{"node1":{"netboot":"yaboot"}}' -O == -end - -start:node_makehosts -description: node_makehosts -cmd:restapitest -m POST -r /nodes/node1/host +start:node_makehosts_rest +description: makehosts for node with REST API +cmd:restapitest -m POST -r /nodes/restnode/host check:rc==201 end -start:node_makedns -description: node_makehosts -cmd:restapitest -m POST -r /nodes/node1/dns +start:node_makedns_rest +description: makehosts for node with REST API +cmd:restapitest -m POST -r /nodes/restnode/dns check:rc==201 end - -start:node_delete_dns -description: node_delete_dns -cmd:restapitest -m DELETE -r /nodes/node1/dns +start:node_delete_dns_rest +description: delete dns for node with REST API +cmd:restapitest -m DELETE -r /nodes/restnode/dns check:rc==200 end -start:node_put -description: node_put -cmd:restapitest -m PUT -r /nodes/node1 -d '{"mac":"00:1a:64:54:14:80"}' +start:node_change_attr_rest +description: change node attribute with REST API +cmd:restapitest -m PUT -r /nodes/restnode -d '{"mac":"00:1a:64:54:14:80"}' check:rc==200 end -start:node_makedhcp -description: node_makedhcp +start:node_makedhcp_rest +description: makedhcp for node with REST API cmd:restapitest -m POST -r /nodes/node1/dhcp check:rc==201 end @@ -104,139 +87,18 @@ cmd:restapitest -m DELETE -r /nodes/node1/dhcp check:rc==200 end -start:node_state -description: node_state -cmd:restapitest -m GET -r /nodes/node1/nodestat +start:node_state_rest +description: get node state with REST API +cmd:restapitest -m GET -r /nodes/restnode/nodestat check:rc==200 -cmdcheck:restapitest -o '{"node1":{"nodestat":"ANY"}}' -O == +check:output=~'restnode' +check:output=~'nodestat' end - -#start:node_post4_for_scan_test -#description: node_post4_for_scan_test -#cmd:restapitest -m POST -r /nodes/e108m6hmc02 -d '{"groups":"all,hmc","mgt":"hmc","hwtype":"hmc","mtm":"7042CR4","serial":"1050FBB","nodetype":"ppc"}' -#check:rc==201 -#end - -start:node_scan -description: node_scan -cmd:restapitest -m GET -r /nodes/__GETNODEATTR($$CN,hcp)__ +start:node_state_token_rest +description: get node state with REST API using authentication token +cmd:restapitest -m GET -r /nodes/restnode/nodestat -t check:rc==200 -cmdcheck:restapitest -o '{"__GETNODEATTR($$CN,hcp)__":"ANY"}' -O == +check:output=~'restnode' +check:output=~'nodestat' end - - -start:node_power_get -description: node_power_get -cmd:restapitest -m GET -r /nodes/$$CN/power -check:rc==200 -cmdcheck:restapitest -o '{"$$CN":{"power":"ANY"}}' -O == -end - -start:node_power_put -description: node_power_reset -cmd:restapitest -m PUT -r /nodes/$$CN/power -d '{"action":"reset"}' -check:rc==200 -end - -#start:node_energy_put -#description: node_energy_put -#cmd:restapitest -m PUT -r /nodes/Vc68m5sn01/energy -d '{"cappingstatus":"on"}' -#check:rc==200 -#end - -#start:node_energy_get -#description: node_energy_get -#cmd:restapitest -m GET -r /nodes/Vc68m5sn01/energy -#check:rc==200 -#cmdcheck:restapitest -o '{"Vc68m5sn01":{"cappingmin":"on"}}' -O == -#end - -#start:node_energy_get_attr -#description: node_energy_get_attr -#cmd:restapitest -m GET -r /nodes/Vc68m5sn01/energy/cappingmaxmin,cappingstatus -#check:rc==200 -#cmdcheck:restapitest -o '{"Vc68m5sn01":{"cappingmin":"ANY"}}' -O == -#end - -#start:node_get_attr -#description: node_get_attr -#cmd:restapitest -m GET -r /nodes/Vc68m5sn01/sp/community -#check:rc==200 -#cmdcheck:restapitest -o '{"Vc68m5sn01":{"SP SNMP Community":"public"}}' -O == -#end - -#start:node_put_attr -#description: node_put_attr -#cmd:restapitest -m PUT -r /nodes/Vc68m5sn01/sp/community -d '{"value":"mycommunity"}'' -#check:rc==200 -#end - -#start:node_put_nextboot -#description: node_put_nextboot -#cmd:restapitest -m PUT -r /nodes/$$CN/nextboot -d '{"order":"net"}' -#check:rc==201 -#end - -#start:node_get_nextboot -#description: node_get_nextboot -#cmd:restapitest -m GET -r /nodes/Vc68m5sn01/nextboot -#check:rc==200 -#cmdcheck:restapitest -o '{"Vc68m5sn01":{"nextboot":"net"}}' -O == -#end - -start:node_put_bootstate -description: node_put_bootstate -cmd:restapitest -m PUT -r /nodes/Vc68m5sn01/bootstate -d '{"osimage":"rhels6.4-x86_64-install-compute"}' -check:rc==201 -end - -start:node_get_bootstate -description: node_get_bootstate -cmd:restapitest -m GET -r /nodes/$$CN/bootstate -check:rc==200 -cmdcheck:restapitest -o '{"$$CN":{"bootstat":"ANY"}}' -O == -end - -start:node_get_vitals -description: node_get_vitals -cmd:restapitest -m GET -r /nodes/Vc68m5sn01/vitals -check:rc==200 -cmdcheck:restapitest -o '{"Vc68m5sn01":{"SysBrd Fault":"0"}}' -O == -end - -start:node_get_vitals_attr -description: node_get_vitals_attr -cmd:restapitest -m GET -r /nodes/$$CN/vitals/all -check:rc==200 -cmdcheck:restapitest -o '{"$$CN":{"System Temperature":"ANY"}}' -O == -end - -start:node_get_inventory -description: node_get_inventory -cmd:restapitest -m GET -r /nodes/Vc68m5sn01/inventory -check:rc==200 -cmdcheck:restapitest -o '{"Vc68m5sn01":{"Power Supply 2 Board FRU Number":"94Y8105"}}' -O == -end - -start:node_get_inventory_attr -description: node_get_inventory_attr -cmd:restapitest -m GET -r /nodes/Vc68m5sn01/inventory/model -check:rc==200 -cmdcheck:restapitest -o '{"Vc68m5sn01":{"System Description":"System x3650 M4"}}' -O == -end - -#start:node_get_eventlog -#description: node_get_eventlog -#cmd:restapitest -m GET -r /nodes/Vc68m5sn01/eventlog -#check:rc==200 -#cmdcheck:restapitest -o '{"Vc68m5sn01":{"eventlog":"ANY"}}' -O == -#end - -start:node_post_nodecopy -description: node_post_nodecopy -cmd:restapitest -m POST -r /nodes/$$CN/nodecopy -d '{"src":["/etc/hosts","/etc/resolv.conf"],"target":"/tmp"}' -check:rc==201 -end - - diff --git a/xCAT-test/pods/man1/xcattest.1.pod b/xCAT-test/pods/man1/xcattest.1.pod index 5dba9d902..f21804684 100644 --- a/xCAT-test/pods/man1/xcattest.1.pod +++ b/xCAT-test/pods/man1/xcattest.1.pod @@ -151,6 +151,7 @@ The xCAT-test testing framework provides some inline functions. The inline funct For example, to run rscan command against the hardware control point of compute node specified in the configuration file: rscan __GETNODEATTR($$CN, hcp)__ -z + 3. B To get the value of column where keyname == key in specified table. =head1 FILES diff --git a/xCAT-test/restapitest b/xCAT-test/restapitest index 83ba63c38..f5db2e79c 100755 --- a/xCAT-test/restapitest +++ b/xCAT-test/restapitest @@ -4,14 +4,14 @@ # Flags are used for test input: # -m method. Should be GET, POST, PUT, DELETE # -r resource -# -t token +# -t # -h host # -u user # -p passwd # -P port (BC) # -d data # -c cert -# -n hostname +# --debug # Flags are used for check output: # -o expected output # -O logical operator @@ -36,6 +36,7 @@ use strict; my $help; my $method; my $token; +my $usetoken; my $resource; my $host; my $user; @@ -43,7 +44,6 @@ my $passwd; my $port; my $data; my $cert; -my $hostname; my $output; my $loperator; my $debug; @@ -54,7 +54,7 @@ my $outputfile = "/tmp/testrestapiresult"; if ( !GetOptions("h|?" => \$help, "m=s" => \$method, - "t=s" => \$token, + "t" => \$usetoken, # use generated token instead of the user/pw "r=s" => \$resource, "h=s" => \$host, "u=s" => \$user, @@ -62,7 +62,6 @@ if ( "P=s" => \$port, "d=s" => \$data, "c=s" => \$cert, - "n=s" => \$hostname, "o=s" => \$output, "O=s" => \$loperator, "debug" => \$debug, @@ -131,52 +130,46 @@ while () { } } -# get token -my $defaultserver = $confhash{DefaultServer}; -my $defaultuser = $confhash{DefaultUser1}; -my $DefaultPasswd = $confhash{DefaultPasswd1}; -my $gettoken = `curl -X POST -k 'https://$defaultserver/xcatws/tokens?userName=$defaultuser&password=$DefaultPasswd' -H Content-Type:application/json --data '{"userName":"$defaultuser","password":"$DefaultPasswd"}' 2>/dev/null`; -my $reshash = parse_json($gettoken); -my $token1 = $$reshash{token}{id}; - -# get hostname -unless ($hostname) { - $hostname = `hostname`; - chomp($hostname); -} -unless ($defaultserver) { +# get hostname or default to local host if not specified +unless ($host) { $host = "127.0.0.1"; -} else { - $host = $defaultserver; } -# keey default test result for save -my $res = run_restapi($method, $resource, $data, "", $port, "$host", "$defaultuser", "$DefaultPasswd"); +unless ($user) { + $user = $confhash{DefaultUser1}; +} +unless ($passwd) { + $passwd = $confhash{DefaultPasswd1}; +} +my $DefaultPasswd = $confhash{DefaultPasswd1}; + +if ($usetoken) { +# get token + my $gettoken = `curl -X POST -k 'https://$host/xcatws/tokens' -H Content-Type:application/json --data '{"userName":"$user","userPW":"$passwd"}' 2>/dev/null`; + my $reshash = parse_json($gettoken); + $token = $$reshash{token}{id}; +} + +# debug and log info +log_debug(3, "User $user. \n"); +log_debug(3, "Password $passwd. \n"); +log_debug(3, "Host $host. \n"); +log_debug(3, "Got token $token. \n"); +log_debug(3, "get path of ca $confhash{Cert} \n"); + +my $res = run_restapi($method, $resource, $data, "", $port, "$host", "$user", "$passwd", "$token"); $defaulthash = parse_json($res); $defaulthttpresult = check_errcode(); -# debug and log info -log_debug(3, "get token $token1. \n"); -log_debug(3, "get first default user $confhash{DefaultUser1} \n"); -log_debug(3, "get first default user's passwd is $confhash{DefaultPasswd1} \n"); -log_debug(3, "get second default user $confhash{DefaultUser2} \n"); -log_debug(3, "get second default user's passwd is $confhash{DefaultPasswd2} \n"); -log_debug(3, "get path of ca $confhash{Cert} \n"); -log_debug(3, "get server $defaultserver from configuration file and it is $host\n"); -log_debug(3, "get hostname $hostname.\n"); -log_debug(3, "default result is $res. \n"); -log_debug(3, "default resulthash is: \n"); -log_debug(3, $defaulthash); -log_debug(3, "default errcode is $defaulthttpresult \n"); -log_debug(3, "**************begin to run more restapi test, stop when post***************"); - +print Dumper $defaulthash; +exit $defaulthttpresult; #################################################### # Begin to run test cases #################################################### my @users = ($confhash{DefaultUser1}, $confhash{DefaultUser2}, $user); my @passwds = ($confhash{DefaultPasswd1}, $confhash{DefaultPasswd2}, $passwd); -my @tokens = ("", $token1, $token); +my @tokens = ("", $token, $token); my @certs = ("", $confhash{Cert}, $cert); my $i = 0; unless ($method eq "POST") { # Should not post sevral times @@ -235,7 +228,7 @@ sub usage print " testrestapi [-?|-h]\n"; print " testrestapi [-m method] [-r resource] [-t tocken]\n"; print " [-h host] [-P port][-u user] [-p passwd]\n"; - print " [-d data] [-c cert] [-n hostname]\n"; + print " [-d data] [-c cert] \n"; print " [-o expect_output] [-O logical_operator] \n"; print " [--debug]\n"; print "\n"; @@ -287,18 +280,14 @@ sub run_restapi if ($t) { $cmd .= " -H X-Auth-Token:$t "; } - if ($t or $c) { - $cmd .= " 'https://$hostname"; - } else { - $cmd .= " 'https://$h"; - } + $cmd .= " 'https://$h"; if ($p) { $cmd .= ":$p"; } $cmd .= "/xcatws"; $cmd .= "$r?"; unless ($t) { - $cmd .= "userName=$u&password=$a'"; + $cmd .= "userName=$u&userPW=$a'"; } else { $cmd .= "'"; } @@ -331,7 +320,7 @@ sub parse_json # {"networks":[{"mgtifname":"eth1","mask":"255.255.255.0"},{"mgtifname":"eth1","mask":"255.255.255.0"}]} if ($input =~ /^\[(.*)\]$/s) { my $content = $1; - log_debug(2, "[:] content is $content \n"); + # log_debug(2, "[:] content is $content \n"); parse_json($content); } @@ -344,7 +333,7 @@ sub parse_json # record result foreach my $t (@contents) { - log_debug(2, ":{}, content is $t \n"); + #log_debug(2, ":{}, content is $t \n"); my $re = parse_json($t); push @reval, $re; } @@ -366,7 +355,7 @@ sub parse_json # record result foreach my $t (@contents) { - log_debug(2, "{},{}, content is $t \n"); + #log_debug(2, "{},{}, content is $t \n"); my $re = parse_json($t); push @reval, $re; } @@ -386,7 +375,7 @@ sub parse_json # {"clustersite":{"domain":"cluster.com","master":"192.168.1.15"}} elsif ($input =~ /^\s*{(.*)}\s*$/s) { my $content = $1; - log_debug(2, "{} content is $content \n"); + #log_debug(2, "{} content is $content \n"); parse_json($content); } elsif ($input =~ /],\"\S+\":/) { @@ -396,7 +385,7 @@ sub parse_json # record result foreach my $t (@contents) { - log_debug(2, "],:, content is $t \n"); + #log_debug(2, "],:, content is $t \n"); my $re = parse_json($t); push @reval, $re; } @@ -420,7 +409,7 @@ sub parse_json # record result foreach my $t (@contents) { - log_debug(2, ", content is $t \n"); + #log_debug(2, ", content is $t \n"); my $re = parse_json($t); push @reval, $re; } @@ -446,7 +435,7 @@ sub parse_json if ($value =~ /{/) { # "clustersite":{"domain":"cluster.com","master":"192.168.1.15"} - log_debug(2, "{ content is $value \n"); + #log_debug(2, "{ content is $value \n"); $hash{$key} = parse_json($value, $key); return \%hash; } else { @@ -464,7 +453,7 @@ sub parse_json else { if ($input =~ /^\[(.*)\]/s) { my $content = $1; - log_debug(2, "[] content is $content \n"); + #log_debug(2, "[] content is $content \n"); my @all = split /,/, $content; foreach my $n (@all) { $n =~ /\"(.*)\"/;