Fix issue #2187: multiple admins; root not UID 1

Along with the root user there may be other users with
ADMINISTRATOR privilege, and there is no guarantee that root will be
assigned user ID 1. But the test cases are hard-coded to expect root at
UID 1 and to have no other UIDs with ADMINISTRATOR privilege.
This can result in several test failures.

New keyword 'Determine Root User Id' dynamically determines the user ID
for root. New keyword 'Find Free User Id' finds a free user ID, avoiding
conflicts with all other enabled user IDs.

Signed-off-by: Gene Ratzlaff <generatz@gmail.com>
Change-Id: Iba7fe3afa11b6dd328e8536c2fbd136f3ee756f0
diff --git a/ipmi/test_ipmi_user.robot b/ipmi/test_ipmi_user.robot
index 7cccf29..979724e 100644
--- a/ipmi/test_ipmi_user.robot
+++ b/ipmi/test_ipmi_user.robot
@@ -27,6 +27,9 @@
 &{password_values}      16=0penBmc10penBmc2  17=0penBmc10penBmc2B
               ...       20=0penBmc10penBmc2Bmc3  21=0penBmc10penBmc2Bmc34
               ...       7=0penBmc  8=0penBmc0
+${expected_max_ids}     15
+${root_pattern}         ^.*\\sroot\\s.*ADMINISTRATOR.*$
+${empty_name_pattern}   ^User Name\\s.*\\s:\\s$
 
 # User defined count.
 ${USER_LOOP_COUNT}      20
@@ -35,22 +38,25 @@
 
 Verify IPMI User Summary
     [Documentation]  Verify IPMI maximum supported IPMI user ID and
-    ...  enabled user form user summary
+    ...  enabled user from user summary.
     [Tags]  Verify_IPMI_User_Summary
-    [Teardown]  Run Keywords  FFDC On Test Case Fail  AND  Delete Created User  ${random_userid}
+    [Teardown]  Run Keywords  FFDC On Test Case Fail  AND
+    ...  Delete Created User  ${random_userid}
 
-    # Create random user
+    ${initial_user_count}  ${maximum_ids}=  Get Enabled User Count
+
     ${random_userid}  ${random_username}=  Create Random IPMI User
+    Wait And Confirm New User Entry  ${random_username}
     Set Test Variable  ${random_userid}
     Run IPMI Standard Command  user enable ${random_userid}
 
-    ${output}=  Run IPMI Standard Command  user summary ${CHANNEL_NUMBER}
-    # TODO: Verification of enabled IPMI user needs to be done.
-    # https://github.com/openbmc/openbmc-test-automation/issues/2189
+    # Verify number of currently enabled users.
+    ${current_user_count}  ${maximum_ids}=  Get Enabled User Count
+    ${calculated_count}=  Evaluate  ${initial_user_count} + 1
+    Should Be Equal As Integers  ${current_user_count}   ${calculated_count}
 
-    ${maximum_ids}=  Get Lines Containing String  ${output}  Maximum IDs
-
-    Should Contain  ${maximum_ids}  15
+    # Verify maximum user count IPMI local user can have.
+    Should Be Equal As Integers  ${maximum_ids}  ${expected_max_ids}
 
 
 Verify IPMI User List
@@ -95,10 +101,10 @@
 
 Verify IPMI User Creation With Invalid Name
     [Documentation]  Verify error while creating IPMI user with invalid
-    ...  name(e.g. user name with special characters).
+    ...  name (e.g. user name with special characters).
     [Tags]  Verify_IPMI_User_Creation_With_Invalid_Name
 
-    ${random_userid}=  Evaluate  random.randint(2, 15)  modules=random
+    ${random_userid}=  Find Free User Id
     ${msg}=  Run Keyword And Expect Error  *  Run IPMI Standard Command
     ...  user set name ${random_userid} ${invalid_username}
     Should Contain  ${msg}  Invalid data
@@ -120,6 +126,7 @@
       Should Contain Any  ${msg}  User ID is limited to range  Parameter out of range
     END
 
+
 Verify Setting IPMI User With Invalid Password
     [Documentation]  Verify error while setting IPMI user with invalid
     ...  password.
@@ -139,6 +146,7 @@
 
     Should Contain  ${msg}  Set User Password command failed
 
+
 Verify Setting IPMI Root User With New Name
     [Documentation]  Verify error while setting IPMI root user with new
     ...  name.
@@ -199,14 +207,14 @@
     [Documentation]  Verify error while creating two IPMI user with same name.
     [Tags]  Verify_IPMI_User_Creation_With_Same_Name
     [Teardown]  Run Keywords  FFDC On Test Case Fail  AND
-    ...  Delete Created User  2
+    ...  Delete Created User  ${random_userid}
 
-    ${random_username}=  Generate Random String  8  [LETTERS]
-    IPMI Create User  2  ${random_username}
+    ${random_userid}  ${random_username}=  Create Random IPMI User
 
     # Set same username for another IPMI user.
+    ${rand_userid_two}=  Find Free User Id
     ${msg}=  Run Keyword And Expect Error  *  Run IPMI Standard Command
-    ...  user set name 3 ${random_username}
+    ...  user set name ${rand_userid_two} ${random_username}
     Should Contain  ${msg}  Invalid data field in request
 
 
@@ -276,7 +284,7 @@
 
 
 Test IPMI No Access Privilege Level
-    [Documentation]  Verify IPMI user with no access privilege can not run only any level command.
+    [Documentation]  Verify IPMI user with no access privilege can not run command at any level.
     [Tags]  Test_IPMI_No_Access_Privilege_Level
     [Template]  Test IPMI User Privilege
     [Teardown]  Run Keywords  FFDC On Test Case Fail  AND
@@ -517,7 +525,7 @@
     [Documentation]  Create IPMI user with random username and userid and return those fields.
 
     ${random_username}=  Generate Random String  8  [LETTERS]
-    ${random_userid}=  Evaluate  random.randint(2, 15)  modules=random
+    ${random_userid}=  Find Free User Id
     IPMI Create User  ${random_userid}  ${random_username}
     [Return]  ${random_userid}  ${random_username}
 
@@ -673,24 +681,118 @@
 
 
 Suite Setup Execution
-    [Documentation]  Check for the enabled user.
+    [Documentation]  Make sure the enabled user count is below maximum,
+    ...  and prepares administrative user list suite variables.
+
+    Check Enabled User Count
+    Determine Root User Id
+
+
+Check Enabled User Count
+    [Documentation]  Ensure that there are available user IDs.
 
     # Check for the enabled user count
     ${resp}=  Run IPMI Standard Command  user summary ${CHANNEL_NUMBER}
     ${enabled_user_count}=
     ...  Get Lines Containing String  ${resp}  Enabled User Count
 
-    Should not contain  ${enabled_user_count}  15
+    Should not contain  ${enabled_user_count}  ${expected_max_ids}
     ...  msg=IPMI have reached maximum user count
 
 
+Determine Root User Id
+    [Documentation]  Determines the user ID of the root user.
+
+    ${resp}=  Wait Until Keyword Succeeds  15 sec  1 sec  Run IPMI Standard Command
+    ...  user list
+    @{lines}=  Split To Lines  ${resp}
+
+    ${root_userid}=  Set Variable  ${-1}
+    ${line_count}=  Get Length  ${lines}
+    FOR  ${id_index}  IN RANGE  1  ${line_count}
+        ${line}=  Get From List  ${lines}  ${id_index}
+        ${root_found}=  Get Lines Matching Regexp  ${line}  ${root_pattern}
+        IF  '${root_found}' != '${EMPTY}'
+            ${root_userid}=  Set Variable  ${id_index}
+            BREAK
+        END
+    END
+    Set Suite Variable  ${root_userid}
+
+    Log To Console  The root user ID is ${root_userid}.
+    Run Keyword If  ${root_userid} < ${1}  Fail  msg= Did not identify root user ID.
+
+
 Wait And Confirm New Username And Password
     [Documentation]  Wait in loop trying to to confirm Username And Password.
     [Arguments]  ${username}  ${password}
 
+    # Description of argument(s):
+    # username         The user name (e.g. "root", "robert", etc.).
+    # password         The user password (e.g. "0penBmc", "0penBmc1", etc.).
+
     # Give time for previous command to complete.
     Sleep  5s
 
     # Looping verify that root user is able to run IPMI command using new password.
     Wait Until Keyword Succeeds  15 sec  5 sec  Verify IPMI Username And Password
     ...  ${username}  ${password}
+
+
+Get Enabled User Count
+    [Documentation]  Return as integers: current number of enabled users and
+    ...  Maximum number of Ids.
+
+    # Isolate 'Enabled User Count' value and convert to integer
+    ${resp}=  Wait Until Keyword Succeeds  15 sec  1 sec  Run IPMI Standard Command
+    ...  user summary ${CHANNEL_NUMBER}
+    ${user_count_line}=  Get Lines Containing String  ${resp}  Enabled User Count
+    ${count}=  Fetch From Right  ${user_count_line}  \:
+    ${user_count}=  Convert To Integer  ${count}
+
+    # Isolate 'Maximum IDs' value and convert to integer
+    ${maximum_ids}=  Get Lines Containing String  ${resp}  Maximum IDs
+    ${max_ids}=  Fetch From Right  ${maximum_ids}  \:
+    ${int_maximum_ids_count}=  Convert To Integer  ${max_ids}
+
+    [Return]  ${user_count}  ${int_maximum_ids_count}
+
+
+Wait And Confirm New User Entry
+    [Documentation]  Wait in loop until new user appears with given username.
+    [Arguments]  ${username}
+
+    # Description of argument(s):
+    # username         The user name (e.g. "root", "robert", etc.).
+
+    Wait Until Keyword Succeeds  45 sec  1 sec  Verify IPMI Username Visible
+    ...  ${username}
+
+
+Verify IPMI Username Visible
+    [Documentation]  Confirm that username is present in user list.
+    [Arguments]  ${username}
+
+    # Description of argument(s):
+    # username         The user name (e.g. "root", "robert", etc.).
+
+    ${resp}=  Run IPMI Standard Command  user list
+    Should Contain  ${resp}  ${username}
+
+
+Find Free User Id
+    [Documentation]  Find a user ID that is not being used.
+
+    Check Enabled User Count
+    FOR    ${num}    IN RANGE    300
+        ${random_userid}=  Evaluate  random.randint(1, ${expected_max_ids})  modules=random
+        ${access}=  Run IPMI Standard Command  channel getaccess 1 ${random_userid}
+
+        ${name_line}=  Get Lines Containing String  ${access}  User Name
+        Log To Console  For ID ${random_userid}: ${name_line}
+        ${is_empty}=  Run Keyword And Return Status
+        ...  Should Match Regexp  ${name_line}  ${empty_name_pattern}
+
+        Exit For Loop If  ${is_empty} == ${True}
+    END
+    [Return]  ${random_userid}