diff --git a/ods_ci/tasks/Resources/Files/dsc_template.yml b/ods_ci/tasks/Resources/Files/dsc_template.yml index 19e0f94a5..36fb88d70 100644 --- a/ods_ci/tasks/Resources/Files/dsc_template.yml +++ b/ods_ci/tasks/Resources/Files/dsc_template.yml @@ -11,11 +11,14 @@ spec: datasciencepipelines: managementState: kserve: + defaultDeploymentMode: Serverless managementState: modelmeshserving: managementState: ray: managementState: + kueue: + managementState: trustyai: managementState: workbenches: diff --git a/ods_ci/tasks/Resources/RHODS_OLM/install/oc_install.robot b/ods_ci/tasks/Resources/RHODS_OLM/install/oc_install.robot index f33f4f9b4..a7b37ca71 100644 --- a/ods_ci/tasks/Resources/RHODS_OLM/install/oc_install.robot +++ b/ods_ci/tasks/Resources/RHODS_OLM/install/oc_install.robot @@ -8,7 +8,7 @@ Resource ../../../../tests/Resources/Page/OCPDashboard/UserManagement/Groups.r *** Variables *** ${DSC_NAME} = default-dsc -@{COMPONENT_LIST} = dashboard datasciencepipelines kserve modelmeshserving workbenches codeflare ray trustyai # robocop: disable +@{COMPONENT_LIST} = dashboard datasciencepipelines kserve modelmeshserving workbenches codeflare ray trustyai kueue # robocop: disable ${SERVERLESS_OP_NAME}= serverless-operator ${SERVERLESS_SUB_NAME}= serverless-operator ${SERVERLESS_NS}= openshift-serverless diff --git a/ods_ci/tests/Resources/Common.robot b/ods_ci/tests/Resources/Common.robot index bf368641e..0cbedf64b 100644 --- a/ods_ci/tests/Resources/Common.robot +++ b/ods_ci/tests/Resources/Common.robot @@ -105,6 +105,28 @@ CSS Property Value Should Be Run Keyword And Continue On Failure Should Be Equal ${actual_value} ${exp_value} END +Get All Text Under Element + [Documentation] Returns a list of all text content under an element tree, including trailing spaces + # This is usefull since Get Text ignores trailing spaces and sibling elements. + # The returned list can be evaluated with keyword: Should Contain ${text_list} Text Data + [Arguments] ${parent_element} + ${elements}= Get WebElements ${parent_element} + ${text_list}= Create List + FOR ${element} IN @{elements} + ${text}= Get Element Attribute ${element} textContent + Append To List ${text_list} ${text} + END + RETURN ${text_list} + +Get All Strings That Contain + [Documentation] Returns new list of strings, for each item in ${list_of_strings} that contains ${substring_to_search} + [Arguments] ${list_of_strings} ${substring_to_search} + ${matched_list}= Create List + FOR ${str} IN @{list_of_strings} + IF "${substring_to_search}" in "${str}" Append To List ${matched_list} ${str} + END + RETURN ${matched_list} + Page Should Contain A String In List [Documentation] Verifies that page contains at least one of the strings in text_list [Arguments] ${text_list} diff --git a/ods_ci/tests/Resources/Page/ODH/ODHDashboard/ODHDashboard.robot b/ods_ci/tests/Resources/Page/ODH/ODHDashboard/ODHDashboard.robot index 0a44452ae..95144a381 100644 --- a/ods_ci/tests/Resources/Page/ODH/ODHDashboard/ODHDashboard.robot +++ b/ods_ci/tests/Resources/Page/ODH/ODHDashboard/ODHDashboard.robot @@ -32,6 +32,7 @@ ${IMAGE_XP_OLD}= ${HEADER_XP}/img[contains(@class, 'odh-card__header-brand')] ${APPS_DICT_PATH_LATEST}= ods_ci/tests/Resources/Files/AppsInfoDictionary_latest.json ${SIDEBAR_TEXT_CONTAINER_XP}= //div[contains(@class,'odh-markdown-view')] ${SUCCESS_MSG_XP}= //div[@class='pf-v5-c-alert pf-m-success'] +${PAGE_TITLE_XP}= //*[@data-testid="app-page-title" and text()="Cluster settings"] ${USAGE_DATA_COLLECTION_XP}= //*[@id="usage-data-checkbox"] ${CUSTOM_IMAGE_SOFTWARE_TABLE}= //caption[contains(., "the advertised software")]/../tbody ${CUSTOM_IMAGE_PACKAGE_TABLE}= //caption[contains(., "the advertised packages")]/../tbody @@ -72,11 +73,11 @@ Login To RHODS Dashboard ${expected_text_list}= Create List Log in with Data Science Projects Wait Until Page Contains A String In List ${expected_text_list} - ${oauth_prompt_visible} = Is OpenShift OAuth Login Prompt Visible + ${oauth_prompt_visible}= Is OpenShift OAuth Login Prompt Visible IF ${oauth_prompt_visible} Click Button Log in with OpenShift - ${login-required} = Is OpenShift Login Visible + ${login-required}= Is OpenShift Login Visible IF ${login-required} Login To Openshift ${ocp_user_name} ${ocp_user_pw} ${ocp_user_auth_type} - ${authorize_service_account} = Is rhods-dashboard Service Account Authorization Required + ${authorize_service_account}= Is rhods-dashboard Service Account Authorization Required IF ${authorize_service_account} Authorize rhods-dashboard service account Logout From RHODS Dashboard @@ -107,7 +108,8 @@ Wait for RHODS Dashboard to Load ... timeout=75s END IF ${wait_for_cards} == ${TRUE} - Wait Until Cards Are Loaded + Wait Until Keyword Succeeds 3 times 5 seconds + ... Wait Until Cards Are Loaded END Wait Until RHODS Dashboard ${dashboard_app} Is Visible @@ -153,7 +155,7 @@ Verify Service Is Enabled Verify Service Is Not Enabled [Documentation] Verify the service is not present in Applications > Enabled [Arguments] ${app_name} - ${app_is_enabled} = Run Keyword And Return Status Verify Service Is Enabled ${app_name} + ${app_is_enabled}= Run Keyword And Return Status Verify Service Is Enabled ${app_name} Should Be True not ${app_is_enabled} msg=${app_name} should not be enabled in ODS Dashboard Verify Service Is Available In The Explore Page @@ -256,8 +258,10 @@ Load Expected Data Of RHODS Explore Section Wait Until Cards Are Loaded [Documentation] Waits until the Application cards are displayed in the page - Run Keyword And Continue On Failure Wait Until Page Contains Element - ... xpath:${CARDS_XP} timeout=15s error="This might be caused by bug RHOAIENG-404" + ${status}= Run Keyword and Return Status Wait Until Page Contains Element + ... xpath:${CARDS_XP} timeout=10s + IF not ${status} Reload Page + Should Be True ${status} msg=This might be caused by bug RHOAIENG-404 Get App ID From Card [Arguments] ${card_locator} @@ -508,7 +512,7 @@ Verify Cluster Settings Is Available Page Should Contain Settings Menu.Navigate To Page Settings Cluster settings Capture Page Screenshot - Wait Until Page Contains Update global settings for all users timeout=30 + Wait Until Page Contains Element ${PAGE_TITLE_XP} timeout=30 Wait Until Page Contains Element ${USAGE_DATA_COLLECTION_XP} timeout=30 Verify Cluster Settings Is Not Available @@ -624,7 +628,7 @@ Delete Custom Image ... Needs an additional check on removed ImageStream [Arguments] ${image_name} Click Button xpath://td[@data-label="Name"]/div/div/div[.="${image_name} "]/../../../../td[last()]//button - ${image_name_id} = Replace String ${image_name} ${SPACE} - + ${image_name_id}= Replace String ${image_name} ${SPACE} - Click Element xpath://td[@data-label="Name"]/div/div/div[.="${image_name} "]/../../../../td[last()]//button/..//button[@id="custom-${image_name_id}-delete-button"] # robocop: disable Handle Deletion Confirmation Modal ${image_name} notebook image @@ -638,7 +642,8 @@ Open Edit Menu For Custom Image Expand Custom Image Details [Documentation] Expands a custom image's row in the dashboard UI [Arguments] ${image_name} - ${is_expanded} = Run Keyword And Return Status Page Should Contain Element xpath://td[.="${image_name}"]/../td[1]/button[@aria-expanded="true"] + ${is_expanded}= Run Keyword And Return Status + ... Page Should Contain Element xpath://td[.="${image_name}"]/../td[1]/button[@aria-expanded="true"] IF ${is_expanded}==False Click Button xpath://td[.="${image_name}"]/../td[1]//button END @@ -646,7 +651,8 @@ Expand Custom Image Details Collapse Custom Image Details [Documentation] Collapses a custom image's row in the dashboard UI [Arguments] ${image_name} - ${is_expanded} = Run Keyword And Return Status Page Should Contain Element xpath://td[.="${image_name}"]/../td[1]/button[@aria-expanded="true"] + ${is_expanded}= Run Keyword And Return Status + ... Page Should Contain Element xpath://td[.="${image_name}"]/../td[1]/button[@aria-expanded="true"] IF ${is_expanded}==True Click Button xpath://td[.="${image_name}"]/../td[1]//button END @@ -655,10 +661,10 @@ Verify Custom Image Description [Documentation] Verifies that the description shown in the dashboard UI ... matches the given one [Arguments] ${image_name} ${expected_description} - ${exists} = Run Keyword And Return Status Page Should Contain Element + ${exists}= Run Keyword And Return Status Page Should Contain Element ... xpath://td[@data-label="Name"]/div/div/div[.="${image_name} "]/../../../../td[@data-label="Description" and .="${expected_description}"] # robocop: disable IF ${exists}==False - ${desc} = Get Text xpath://td[@data-label="Name"]/div/div/div[.="${image_name} "]/../../../../td[@data-label="Description"] + ${desc}= Get Text xpath://td[@data-label="Name"]/div/div/div[.="${image_name} "]/../../../../td[@data-label="Description"] Log Description for ${image_name} does not match ${expected_description} - Actual description is ${desc} FAIL END @@ -670,7 +676,7 @@ Verify Custom Image Is Listed [Arguments] ${image_name} # whitespace after ${image_name} in the xpath is important! Sleep 2s #wait for page to finish loading - ${exists} = Run Keyword And Return Status Page Should Contain Element xpath://td[@data-label="Name"]/div/div/div[.="${image_name} "] # robocop: disable + ${exists}= Run Keyword And Return Status Page Should Contain Element xpath://td[@data-label="Name"]/div/div/div[.="${image_name} "] # robocop: disable IF ${exists}==False Log ${image_name} not visible in page FAIL @@ -681,10 +687,10 @@ Verify Custom Image Provider [Documentation] Verifies that the user listed for an image in the dahsboard ... UI matches the given one [Arguments] ${image_name} ${expected_user} - ${exists} = Run Keyword And Return Status Page Should Contain Element + ${exists}= Run Keyword And Return Status Page Should Contain Element ... xpath://td[@data-label="Name"]/div/div/div[.="${image_name} "]/../../../../td[@data-label="Provider" and .="${expected_user}"] # robocop: disable IF ${exists}==False - ${user} = Get Text xpath://td[@data-label="Name"]/div/div/div[.="${image_name} "]/../../../../td[@data-label="Provider"] # robocop: disable + ${user}= Get Text xpath://td[@data-label="Name"]/div/div/div[.="${image_name} "]/../../../../td[@data-label="Provider"] # robocop: disable Log User for ${image_name} does not match ${expected_user} - Actual user is ${user} FAIL END @@ -693,7 +699,7 @@ Verify Custom Image Provider Enable Custom Image [Documentation] Enables a custom image (i.e. displayed in JH) [WIP] [Arguments] ${image_name} - ${is_enabled} = # Need to find a check + ${is_enabled}= # Need to find a check IF ${is_enabled}==False Click Element xpath://td[@data-label="Name"]/div/div/div[.="${image_name} "]/../../../..//input END @@ -701,7 +707,7 @@ Enable Custom Image Disable Custom Image [Documentation] Disables a custom image (i.e. not displayed in JH) [WIP] [Arguments] ${image_name} - ${is_enabled} = # Need to find a check + ${is_enabled}= # Need to find a check IF ${is_enabled}==True Click Element xpath://td[@data-label="Name"]/div/div/div[.="${image_name} "]/../../../..//input END @@ -736,7 +742,7 @@ Clear Dashboard Notifications [Documentation] Clears Notifications present in RHODS dashboard Click Element xpath=//*[contains(@class,'notification-badge')] Sleep 2s reason=To avoid Element Not Interactable Exception - ${notification_count} = Get Element Count class:odh-dashboard__notification-drawer__item-remove + ${notification_count}= Get Element Count class:odh-dashboard__notification-drawer__item-remove FOR ${index} IN RANGE ${notification_count} Click Element xpath=//*[contains(@class,"odh-dashboard__notification-drawer__item-remove")] END @@ -755,9 +761,9 @@ Get Dashboard Pods Names Get Dashboard Pod Logs [Documentation] Fetches the logs from one dashboard pod [Arguments] ${pod_name} - ${pod_logs}= Oc Get Pod Logs name=${pod_name} namespace=${APPLICATIONS_NAMESPACE} container=rhods-dashboard - ${pod_logs_lines}= Split String string=${pod_logs} separator=\n - ${n_lines}= Get Length ${pod_logs_lines} + ${pod_logs}= Oc Get Pod Logs name=${pod_name} namespace=${APPLICATIONS_NAMESPACE} container=rhods-dashboard + ${pod_logs_lines}= Split String string=${pod_logs} separator=\n + ${n_lines}= Get Length ${pod_logs_lines} Log ${pod_logs_lines}[${n_lines-3}:] IF "${pod_logs_lines}[${n_lines-1}]" == "${EMPTY}" Remove From List ${pod_logs_lines} ${n_lines-1} @@ -768,8 +774,10 @@ Get Dashboard Pod Logs Get ConfigMaps For RHODS Groups Configuration [Documentation] Returns a dictionary containing "rhods-group-config" and "groups-config" ... ConfigMaps - ${rgc_status} ${rgc_yaml}= Run Keyword And Ignore Error OpenShiftLibrary.Oc Get kind=ConfigMap name=${RHODS_GROUPS_CONFIG_CM} namespace=${APPLICATIONS_NAMESPACE} - ${gc_status} ${gc_yaml}= Run Keyword And Ignore Error OpenShiftLibrary.Oc Get kind=ConfigMap name=${GROUPS_CONFIG_CM} namespace=${APPLICATIONS_NAMESPACE} + ${rgc_status} ${rgc_yaml}= Run Keyword And Ignore Error OpenShiftLibrary.Oc Get + ... kind=ConfigMap name=${RHODS_GROUPS_CONFIG_CM} namespace=${APPLICATIONS_NAMESPACE} + ${gc_status} ${gc_yaml}= Run Keyword And Ignore Error OpenShiftLibrary.Oc Get + ... kind=ConfigMap name=${GROUPS_CONFIG_CM} namespace=${APPLICATIONS_NAMESPACE} IF $rgc_status == 'FAIL' ${rgc_yaml}= Create List ${EMPTY} END @@ -782,7 +790,7 @@ Get ConfigMaps For RHODS Groups Configuration Get Links From Switcher [Documentation] Returns the OpenShift Console and OpenShift Cluster Manager Link - ${list_of_links} = Create List + ${list_of_links}= Create List ${link_elements}= Get WebElements //a[@class="pf-m-external pf-v5-c-app-launcher__menu-item" and not(starts-with(@href, '#'))] FOR ${ext_link} IN @{link_elements} ${href}= Get Element Attribute ${ext_link} href @@ -798,16 +806,15 @@ Maybe Wait For Dashboard Loading Spinner Page [Documentation] Detecs the loading symbol (spinner) and wait for it to disappear. ... If the spinner does not appear, the keyword ignores the error. [Arguments] ${timeout-pre}=3s ${timeout}=5s - ${do not wait for spinner}= Get Variable Value ${ODH_DASHBOARD_DO_NOT_WAIT_FOR_SPINNER_PAGE} # defaults to None if undefined IF ${do not wait for spinner} == ${true} RETURN END - + ${spinner_ball}= Set Variable xpath=//span[@class="pf-v5-c-spinner__tail-ball"] Run Keyword And Ignore Error Run Keywords - ... Wait Until Page Contains Element xpath=//span[@class="pf-v5-c-spinner__tail-ball"] timeout=${timeout-pre} + ... Wait Until Page Contains Element ${spinner_ball} timeout=${timeout-pre} ... AND - ... Wait Until Page Does Not Contain Element xpath=//span[@class="pf-v5-c-spinner__tail-ball"] timeout=${timeout} + ... Wait Until Page Does Not Contain Element ${spinner_ball} timeout=${timeout} Reload RHODS Dashboard Page [Documentation] Reload the web page and wait for RHODS Dashboard @@ -822,7 +829,7 @@ Handle Deletion Confirmation Modal [Arguments] ${item_title} ${item_type} ${press_cancel}=${FALSE} ${additional_msg}=${NONE} # Once fixed https://issues.redhat.com/browse/RHODS-9730 change the button xpath to # xpath=//button[text()="Delete ${item_type}"] - ${delete_btn_xp} Set Variable xpath=//button[contains(text(), 'Delete')] + ${delete_btn_xp}= Set Variable xpath=//button[contains(text(), 'Delete')] Wait Until Generic Modal Appears Run Keyword And Warn On Failure Page Should Contain Delete ${item_type}? Run Keyword And Warn On Failure Page Should Contain This action cannot be undone. diff --git a/ods_ci/tests/Resources/Page/ODH/ODHDashboard/ODHDataScienceProject/Projects.resource b/ods_ci/tests/Resources/Page/ODH/ODHDashboard/ODHDataScienceProject/Projects.resource index a44cf548d..f4d114f36 100644 --- a/ods_ci/tests/Resources/Page/ODH/ODHDashboard/ODHDataScienceProject/Projects.resource +++ b/ods_ci/tests/Resources/Page/ODH/ODHDashboard/ODHDataScienceProject/Projects.resource @@ -30,7 +30,7 @@ Is Data Science Projects Page Open [Documentation] Checks if Data Science Projects home page is open. Returns TRUE or FALSE Close Generic Modal If Present ${page_open}= Run Keyword And Return Status Page Should Contain Element ${DS_PROJECT_XP} - [Return] ${page_open} + RETURN ${page_open} Open Data Science Project Details Page [Documentation] Verifies submenu Settings > "Data Science Projects" is visible @@ -57,7 +57,7 @@ Delete Data Science Project ODHDashboard.Click Action From Actions Menu item_title=${project_title} item_type=project action=Delete Handle Deletion Confirmation Modal item_title=${project_title} item_type=project ... press_cancel=${press_cancel} - ... additional_msg=It will destroy all workbenches, storages, data connections and other resources in ${project_title} + ... additional_msg=It will destroy all workbenches, storages, data connections and other resources in ${project_title} # robocop: disable Wait Until Data Science Project Is Deleted project_title=${project_title} Wait Until Data Science Project Is Deleted @@ -78,14 +78,14 @@ Is Data Science Project Details Page Open [Arguments] ${project_title} ${page_open}= Run Keyword And Return Status ... SeleniumLibrary.Page Should Contain Element xpath=//h1[contains(text(),"${project_title}")] - [Return] ${page_open} + RETURN ${page_open} Wait Until Project Is Open [Documentation] Waits until a DS Project Details page is laoded [Arguments] ${project_title} ${timeout-pre-spinner}=3s ${timeout-spinner}=5s - Wait Until Page Contains Element xpath=//h1[contains(text(),"${project_title}")] timeout=30 Maybe Wait For Dashboard Loading Spinner Page ... timeout-pre=${timeout-pre-spinner} timeout=${timeout-spinner} + Wait Until Page Contains Element xpath=//h1[contains(text(),"${project_title}")] timeout=30s Project Should Be Listed [Documentation] Checks a Project is available in DS Project home page @@ -107,7 +107,8 @@ Project's Owner Should Be [Documentation] Checks if the owner of a DS project is displayed and corresponds to the expected one [Arguments] ${project_title} ${expected_username} Run Keyword And Continue On Failure - ... Page Should Contain Element xpath=//a[text()="${project_title}"]/ancestor::td[@data-label='Name']//small[text()="${expected_username}"] + ... Page Should Contain Element + ... xpath=//a[text()="${project_title}"]/ancestor::td[@data-label='Name']//small[text()="${expected_username}"] Create Data Science Project [Documentation] Creates a DS Project with the given ${title}, ${description} and ${resource_name}. @@ -161,7 +162,7 @@ Get Openshift Namespace From Data Science Project [Arguments] ${project_title} ${rc} ${k8s_name}= Run And Return Rc And Output oc get projects -o json | jq '.items[] | select((.metadata.annotations."openshift.io/display-name" != null) and (.metadata.labels."opendatahub.io/dashboard"=="true") and (.metadata.annotations."openshift.io/display-name"=="${project_title}")) | .metadata.name' # robocop: disable ${k8s_name}= Replace String ${k8s_name} " ${EMPTY} - [Return] ${k8s_name} + RETURN ${k8s_name} Delete Data Science Projects From CLI [Documentation] Deletes the Openshift Projects using OpenshiftLibrary. @@ -188,7 +189,7 @@ Delete Data Science Project From CLI END END - +#robocop: disable: line-too-long Workbench Status Should Be From Projects Home Page [Documentation] Checks the workbench status is the expected one, from the DS Project home page [Arguments] ${workbench_title} ${status} ${project_title} @@ -223,6 +224,7 @@ Start Workbench From Projects Home Page Wait Until Workbench Is Started From Projects Home Page workbench_title=${workbench_title} ... project_title=${project_title} +#robocop: disable: line-too-long Stop Workbench From Projects Home Page [Documentation] Triggers the workbench "stop" process from DS Projects home page. ... It needs ${workbench_title} and ${project_title} at least. If ${namespace} and/or @@ -290,14 +292,8 @@ Workbench Launch Link Should Be Disabled Get All Displayed Projects [Documentation] Gets all the DS projects visible in the DS Projects home page - ${projects_names}= Create List - ${elements}= Get WebElements xpath=//td[@data-label="Name"]//a - FOR ${element} IN @{elements} - # Get exact project display name (including trailing spaces) - ${name}= Get Element Attribute ${element} textContent - Append To List ${projects_names} ${name} - END - [Return] ${projects_names} + ${projects_names}= Get All Text Under Element xpath=//td[@data-label="Name"]//a + RETURN ${projects_names} Number Of Displayed Projects Should Be [Documentation] Checks the number the DS projects visible in the DS Projects home page is expected @@ -319,7 +315,6 @@ Launch Data Science Project Main Page ... browser_alias=${browser_alias} Open Data Science Projects Home Page - Remove Current Page Projects From All Projects [Documentation] Remove list of currently displayed Data Science projects from list of all projects [Arguments] ${all_projects} ${curr_page_projects} @@ -409,7 +404,8 @@ Create Data Science Project If Not Exists [Documentation] If the given ${project_title} DS Project does not exist, it creates one. ... Useful as test setup. [Arguments] ${project_title} ${username} ${description}=${EMPTY} - ${rc} ${resource_name}= Run And Return Rc And Output oc get projects -o json | jq '.items[] | select((.metadata.annotations."openshift.io/display-name" != null) and (.metadata.labels."opendatahub.io/dashboard"=="true") and (.metadata.annotations."openshift.io/display-name"=="${project_title}")) | .metadata.name' | tr -d '"' # robocop: disable + ${rc} ${resource_name}= Run And Return Rc And Output + ... oc get projects -o json | jq '.items[] | select((.metadata.annotations."openshift.io/display-name" != null) and (.metadata.labels."opendatahub.io/dashboard"=="true") and (.metadata.annotations."openshift.io/display-name"=="${project_title}")) | .metadata.name' | tr -d '"' # robocop: disable IF "${resource_name}" == "${EMPTY}" Log msg=There is no DS Projects with Diplay Name equal to ${project_title}. Creating it now. Launch Data Science Project Main Page username=${username} @@ -424,4 +420,4 @@ Clean Project From Workbench Resources ... ${pvc_title}=${workbench_title} Delete Workbench From CLI workbench_title=${workbench_title} ... project_title=${project_title} - Delete PVC From CLI pvc_title=${pvc_title} project_title=${project_title} + Delete PVC In Project From CLI pvc_title=${pvc_title} project_title=${project_title} # robocop: disable diff --git a/ods_ci/tests/Resources/Page/ODH/ODHDashboard/ODHDataScienceProject/Storages.resource b/ods_ci/tests/Resources/Page/ODH/ODHDashboard/ODHDataScienceProject/Storages.resource index ca6a5f78e..a91d77d6c 100644 --- a/ods_ci/tests/Resources/Page/ODH/ODHDashboard/ODHDataScienceProject/Storages.resource +++ b/ods_ci/tests/Resources/Page/ODH/ODHDashboard/ODHDataScienceProject/Storages.resource @@ -12,12 +12,13 @@ ${STORAGE_DESCR_INPUT_XP}= xpath=//textarea[@name="create-new-storage-de ${STORAGE_SIZE_INPUT_XP}= xpath=//input[@aria-label="Input"] ${STORAGE_SIZE_PLUS_BTN_XP}= xpath=//div/button[@aria-label="Plus"] ${STORAGE_MOUNT_DIR_INPUT_XP}= xpath=//input[@aria-label="mount-path-folder-value"] -${STORAGE_WORKBENCH_SELECTOR_XP}= xpath=//div[contains(@class,"modal")]//div[contains(@class,"pf-c-select")]/ul/li +${STORAGE_WORKBENCH_SELECTOR_XP}= xpath=//div[contains(@class,"modal")]//ul/li ${STORAGE_ADD_BTN_1_XP}= ${STORAGE_SECTION_XP}//button[.="Add cluster storage"] -${STORAGE_ADD_BTN_2_XP}= xpath=//footer//button[.="Add storage"] +${STORAGE_SUBMIT_BTN_XP}= xpath=//footer//button[@data-testid="modal-submit-button"] *** Keywords *** +#robocop: disable: line-too-long Storage Should Be Listed [Documentation] Checks storage is listed in DS Project details page [Arguments] ${name} ${description} ${type} ${connected_workbench} @@ -47,17 +48,17 @@ Storage Size Should Be Run Keyword And Continue On Failure Wait Until Page Contains Element ${STORAGE_SECTION_XP} Click Element ${STORAGE_SECTION_XP}//tr[td//*/div[text()="${name}"]]//button[@aria-label="Details"] Wait Until Element Is Visible ${STORAGE_SECTION_XP}//tr[contains(@class, "-table__expandable-row pf-m-expanded")] - ${rc} ${pvc_status_phase}= Run And Return Rc And Output ... oc get pvc -n ${namespace} -o jsonpath='{.items[?(@.metadata.annotations.openshift\\.io/display-name=="${name}")].status.phase}' # robocop: disable Should Be Equal As Integers ${rc} 0 An error occurred during the check of PVC ${name} .status.phase value! + ${storage_size_xp}= Set Variable + ... ${STORAGE_SECTION_XP}//tr[td//*/div[text()="${name}"]]/following-sibling::*//td[contains(@data-label,"Size")]//div[strong[text()="Size"]] # robocop: disable IF "${pvc_status_phase}" == "Pending" # PVC hasn't been bound yet - only simple text with max storage should be shown - ${storage_size_el}= Set Variable - ... ${STORAGE_SECTION_XP}//tr[@class="pf-c-table__expandable-row pf-m-expanded"]/td/div[strong[text()="Size"]]/div # robocop: disable - Wait Until Page Contains Element ${storage_size_el} timeout=20s - ${displayed_size}= Get Text ${storage_size_el} - Run Keyword And Continue On Failure Should Be Equal As Strings ${displayed_size} Max ${size}Gi + Wait Until Page Contains Element ${storage_size_xp} timeout=2m + ${displayed_sizes}= Get All Text Under Element ${storage_size_xp}//* + ${matched_sizes}= Get All Strings That Contain ${displayed_sizes} Max ${size}Gi + Run Keyword And Continue On Failure Should Not Be Empty ${matched_sizes} ELSE IF "${pvc_status_phase}" == "Bound" # PVC is now or had been bound already sometime in past - there is: # - a number stating current usage @@ -65,16 +66,15 @@ Storage Size Should Be # - and finally an acutal storage size number # Note: it may take some time before UI updates the storage # info WRT cluster usage, look like it does so every 60s - ${bound_storage_size_el}= Set Variable - ... ${STORAGE_SECTION_XP}//tr[contains(@class, "-table__expandable-row pf-m-expanded")]/td/div[strong[text()="Size"]]/div/div[3]/div # robocop: disable - Wait Until Page Contains Element ${bound_storage_size_el} timeout=80s - ${displayed_size}= Get Text ${bound_storage_size_el} - Run Keyword And Continue On Failure Should Be Equal As Strings ${displayed_size} ${size}Gi + Wait Until Page Contains Element ${storage_size_xp} timeout=2m + ${displayed_sizes}= Get All Text Under Element ${storage_size_xp}//* + ${matched_sizes}= Get All Strings That Contain ${displayed_sizes} ${size}Gi + Run Keyword And Continue On Failure Should Not Be Empty ${matched_sizes} ${rc} ${oc_object_size}= Run And Return Rc And Output ... oc get pvc -n ${namespace} -o jsonpath='{.items[?(@.metadata.annotations.openshift\\.io/display-name=="${name}")].status.capacity.storage}' # robocop: disable Should Be Equal As Integers ${rc} 0 ... An error occurred during the check of PVC ${name} '.status.capacity.storage' value! - Run Keyword And Continue On Failure Should Be Equal As Strings ${oc_object_size} ${size}Gi + Run Keyword And Continue On Failure Should Contain ${oc_object_size} ${size}Gi ELSE ${error_msg}= Catenate The PVC is in a '${pvc_status_phase}' state which we don't expect for this ... scenario. We expect either 'Pending' or 'Bound' states here. Please, either complete the @@ -82,23 +82,34 @@ Storage Size Should Be Fail ${error_msg} RETURN 1 END - # This check is common for both "Pending" and "Bound" states of PVC. + # This check is common for both "Pending" and "Bound" states of PVC, and it verifies storage size directly via OC CLI ${rc} ${oc_object_size}= Run And Return Rc And Output - ... oc get pvc -n ${namespace} -o jsonpath='{.items[?(@.metadata.annotations.openshift\\.io/display-name=="${name}")].spec.resources.requests.storage}' # robocop: disable + ... oc get pvc -n ${namespace} -o jsonpath="{.items[?(@.metadata.annotations['openshift\\.io/display-name']=='${name}')].spec.resources.requests.storage}" # robocop: disable Should Be Equal As Integers ${rc} 0 ... An error occurred during the check of PVC ${name} '.spec.resources.requests.storage' value! Run Keyword And Continue On Failure Should Be Equal As Strings ${oc_object_size} ${size}Gi Create PersistentVolume Storage [Documentation] Create a PersistenVolume storage in DS Project details page - [Arguments] ${project_title} ${name} ${description} ${size} ${connected_workbench}=${NONE} ${press_cancel}=${FALSE} - Click Button ${STORAGE_ADD_BTN_1_XP} + [Arguments] ${project_title} ${name} ${description} ${size} ${connected_workbench}=${NONE} + ... ${press_cancel}=${FALSE} ${existing_storage}=${FALSE} + IF ${existing_storage} + ${storage_row_xpath}= Set Variable xpath://tr[starts-with(., '${name} ') and contains(., 'Persistent storage')] + ${existing_storage}= Run Keyword And Return Status + ... Wait Until Page Contains Element ${storage_row_xpath} + END + IF "${existing_storage}" == "${TRUE}" + Log Storage '${name}' already exists, editing storage console=True + ODHDashboard.Click Action From Actions Menu item_title=${name} action=Edit + ELSE + Click Button ${STORAGE_ADD_BTN_1_XP} + END Fill In New PV Data ${name} ${description} ${size} ${connected_workbench} IF ${press_cancel} == ${TRUE} Click Button ${GENERIC_CANCEL_BTN_XP} ELSE - Wait Until Element Is Enabled ${STORAGE_ADD_BTN_2_XP} - Click Button ${STORAGE_ADD_BTN_2_XP} + Wait Until Element Is Enabled ${STORAGE_SUBMIT_BTN_XP} + Click Button ${STORAGE_SUBMIT_BTN_XP} END Wait Until Generic Modal Disappears Wait Until Project Is Open project_title=${project_title} @@ -106,13 +117,13 @@ Create PersistentVolume Storage Fill In New PV Data [Documentation] Compiles the modal for creating a new PersistenVolume storage in DS Project details page [Arguments] ${name} ${description} ${size} ${connected_workbench}=${NONE} - ${is_storage_modal}= Run Keyword And Return Status Page Should Contain Element ${STORAGE_ADD_BTN_2_XP} + ${is_storage_modal}= Run Keyword And Return Status Page Should Contain Element ${STORAGE_SUBMIT_BTN_XP} Wait Until Page Contains Element ${STORAGE_NAME_INPUT_XP} - IF ${is_storage_modal} == ${TRUE} Run Keyword And Continue On Failure Element Should Be Disabled ${STORAGE_ADD_BTN_2_XP} + IF ${is_storage_modal} Run Keyword And Continue On Failure Element Should Be Disabled ${STORAGE_SUBMIT_BTN_XP} Clear Element Text ${STORAGE_NAME_INPUT_XP} Input Text ${STORAGE_NAME_INPUT_XP} ${name} Wait Until Page Contains Element ${STORAGE_DESCR_INPUT_XP} - IF ${is_storage_modal} == ${TRUE} Run Keyword And Continue On Failure Element Should Be Enabled ${STORAGE_ADD_BTN_2_XP} + IF ${is_storage_modal} Run Keyword And Continue On Failure Element Should Be Enabled ${STORAGE_SUBMIT_BTN_XP} Input Text ${STORAGE_DESCR_INPUT_XP} ${description} Clear Element Text ${STORAGE_SIZE_INPUT_XP} IF ${size} > 1 @@ -121,13 +132,15 @@ Fill In New PV Data END END IF "${connected_workbench}" == "${NONE}" - Log msg=you are not connecting any workbenchs to ${name} PV + Log msg=Creating PV Storage '${name}' without a Workbench console=True ELSE - Run Keyword And Continue On Failure Element Should Be Enabled xpath=//div[contains(@class,"modal")]//div[contains(@class,"pf-c-select")] + Log msg=Creating PV Storage '${name}' for Workbenches: @{connected_workbench} console=True + Run Keyword And Continue On Failure Element Should Be Enabled + ... xpath=//div[contains(@class,"modal")]//input[contains(@placeholder,"Select a workbench to connect")] FOR ${workbench_title} IN @{connected_workbench} - ${mount_dir}= Set Variable ${connected_workbench}[${workbench_title}] + ${mount_dir}= Generate Mount Name ${workbench_title} Set Connection Between PV And Workbench ${workbench_title} ${mount_dir} - Run Keyword And Continue On Failure Element Should Be Enabled ${STORAGE_ADD_BTN_2_XP} + Run Keyword And Continue On Failure Element Should Be Enabled ${STORAGE_SUBMIT_BTN_XP} END END @@ -139,7 +152,7 @@ Set Connection Between PV And Workbench Wait Until Page Contains Element ${STORAGE_WORKBENCH_SELECTOR_XP}/button[text()="${workbench_title}"] Click Element ${STORAGE_WORKBENCH_SELECTOR_XP}/button[text()="${workbench_title}"] Wait Until Page Contains Element ${STORAGE_MOUNT_DIR_INPUT_XP} - Run Keyword And Continue On Failure Element Should Be Disabled ${STORAGE_ADD_BTN_2_XP} + Run Keyword And Continue On Failure Element Should Be Disabled ${STORAGE_SUBMIT_BTN_XP} Input Text ${STORAGE_MOUNT_DIR_INPUT_XP} ${mount_dir} Delete Storage @@ -151,13 +164,35 @@ Delete Storage Get Openshift PVC From Storage [Documentation] Retrieves the PVC resource name from Openshift given the Displayed Name in DS Project details page [Arguments] ${name} ${namespace} - ${rc} ${pvc_name}= Run And Return Rc And Output oc get pvc -n ${namespace} -o jsonpath='{.items[?(@.metadata.annotations.openshift\\.io/display-name=="${name}")].metadata.name}' - RETURN ${rc} ${pvc_name} + ${result}= Run Process + ... oc get pvc -n ${namespace} -o jsonpath\='{.items[?(@.metadata.annotations.openshift\\.io/display-name\=\="${name}")].metadata.name}' + ... shell=yes + RETURN ${result.rc} ${result.stdout} -Delete PVC From CLI - [Documentation] Deletes a given PVC from CLI +Delete PVC In Project From CLI + [Documentation] Deletes a single PVC in a project from CLI [Arguments] ${pvc_title} ${project_title} ${ns_name}= Get Openshift Namespace From Data Science Project project_title=${project_title} ${_} ${cr_name}= Get Openshift PVC From Storage ... name=${pvc_title} namespace=${ns_name} - Oc Delete kind=PersistentVolumeClaim name=${cr_name} namespace=${ns_name} + IF "${cr_name}" == "${EMPTY}" + Log msg=There is probably no PVC with Display Name equal to ${cr_name} + ... level=WARN + ELSE + Oc Delete kind=PersistentVolumeClaim name=${cr_name} namespace=${ns_name} + END + +Delete All PVC In Project From CLI + [Documentation] Deletes All PVCs in a project from CLI + [Arguments] ${project_title} + ${ns_name}= Get Openshift Namespace From Data Science Project project_title=${project_title} + ${result}= Run Process + ... oc delete pvc -n ${ns_name} --all shell=yes + Should Be True ${result.rc} == 0 msg=${result.stderr} + +Generate Mount Name + [Documentation] Generate a supported mount directory name (lower case and dashes only) from string '${str}' + [Arguments] ${str} + ${str}= Replace String Using Regexp string=${str} pattern=[^a-zA-Z] replace_with=- + ${str}= Convert To Lower Case ${str.strip('-')} + RETURN ${str} diff --git a/ods_ci/tests/Resources/Page/ODH/ODHDashboard/ODHDataScienceProject/Workbenches.resource b/ods_ci/tests/Resources/Page/ODH/ODHDashboard/ODHDataScienceProject/Workbenches.resource index b05bab58b..52b9d93ce 100644 --- a/ods_ci/tests/Resources/Page/ODH/ODHDashboard/ODHDataScienceProject/Workbenches.resource +++ b/ods_ci/tests/Resources/Page/ODH/ODHDashboard/ODHDataScienceProject/Workbenches.resource @@ -98,6 +98,7 @@ Create Workbench Wait Until Generic Modal Disappears timeout=1 minute Wait Until Project Is Open project_title=${prj_title} +#robocop: disable: line-too-long Add Environment Variables In Workbench [Documentation] Adds the environment variables through the workbench creation page [Arguments] ${env_variables} @@ -155,12 +156,12 @@ Add Existing Data Connection In Workbench # Use the `Jump to section` again to move the expanded form into view Click Element //a[@href="#data-connections"] Click Element ${WORKBENCH_EXISTING_CONNECTION_RAD_XP} - Select Existing Data Connection data_connection=${data_connection} + Select Existing Data Connection In Workbench data_connection=${data_connection} -Select Existing Data Connection +Select Existing Data Connection In Workbench [Documentation] Selects the existing data connection in the workbench creation page from a dropdown list [Arguments] ${data_connection} - Click Element ${ENV_DATA_CONNECTION_SECTION_XP}//div[contains(@class,"pf-c-select")]/button + Click Element ${ENV_DATA_CONNECTION_SECTION_XP}//input[contains(@placeholder,"Select a data connection")] Wait Until Page Contains Element xpath=//ul[@role="listbox"]/li/button[text()="${data_connection}"] Click Element xpath=//ul[@role="listbox"]/li/button[text()="${data_connection}"] @@ -252,10 +253,10 @@ Select Workbench Container Size Workbench Should Be Listed [Documentation] Checks a workbench is listed in the DS Project details page - [Arguments] ${workbench_title} + [Arguments] ${workbench_title} ${timeout}=5s Run keyword And Continue On Failure ... Wait Until Page Contains Element - ... ${WORKBENCH_SECTION_XP}//td[@data-label="Name"]/*[div[text()="${workbench_title}"]] + ... ${WORKBENCH_SECTION_XP}//td[@data-label="Name"]/*[div[text()="${workbench_title}"]] timeout=${timeout} Workbench With Description Should Be Listed [Documentation] Checks a workbench with particular name and description is listed in the DS Project details page @@ -281,8 +282,6 @@ Workbench Status Should Be Status Error Icon Should Appear [Documentation] Checks if the error icon appears on the given workbench [Arguments] ${workbench_title} - Reload Page - Wait Until Project Is Open project_title=${PRJ_TITLE} Page Should Contain Element ... ${WORKBENCH_SECTION_XP}//tr[td[@data-label="Name"]/*[div[text()="${workbench_title}"]]]/td[@data-label="Status"]//p//${ERROR_ICON_XP} # robocop: disable Mouse Over ${WORKBENCH_SECTION_XP}//tr[td[@data-label="Name"]/*[div[text()="${workbench_title}"]]]/td[@data-label="Status"]//p//${ERROR_ICON_XP} # robocop: disable @@ -356,8 +355,11 @@ Launch And Access Workbench Open Workbench [Documentation] Clicks on "open" link for a given workbench [Arguments] ${workbench_title} - Click Link ${WORKBENCH_SECTION_XP}//tr[td[@data-label="Name"]/*[div[text()="${workbench_title}"]]]/td//a[text()="Open"] - Switch Window NEW + ${open_workbench_link}= Set Variable + ... ${WORKBENCH_SECTION_XP}//tr[td[@data-label="Name"]/*[div[text()="${workbench_title}"]]]/td//a[text()="Open"] + Wait Until Page Contains Element ${open_workbench_link} timeout=30s + Click Link ${open_workbench_link} + Switch Window NEW Stop Workbench [Documentation] Stops a workbench from DS Project details page @@ -422,8 +424,10 @@ Check Launched Workbench Is The Correct One Get Openshift Notebook CR From Workbench [Documentation] Retrieves name of Notebook CR corresponding to a workbench [Arguments] ${workbench_title} ${namespace} - ${rc} ${cr_name}= Run And Return Rc And Output oc get notebook -n ${namespace} -o jsonpath='{.items[?(@.metadata.annotations.openshift\\.io/display-name=="${workbench_title}")].metadata.name}' - RETURN ${rc} ${cr_name} + ${result}= Run Process + ... oc get notebook -n ${namespace} -o jsonpath\='{.items[?(@.metadata.annotations.openshift\\.io/display-name\=\="${workbench_title}")].metadata.name}' + ... shell=yes + RETURN ${result.rc} ${result.stdout} Start Workbench Should Fail [Documentation] Checks the workbench fails to start @@ -433,8 +437,7 @@ Start Workbench Should Fail IF ${failed} != ${FALSE} Fail msg=Workbench is expected to fail starting... Run Keyword And Continue On Failure Wait Until Keyword Succeeds 30 times 2s ... Status Error Icon Should Appear workbench_title=${workbench_title} - Reload Page - Workbench Should Be Listed ${workbench_title} + Workbench Should Be Listed ${workbench_title} timeout=30s Wait Until Keyword Succeeds 20 times 2s ... Status Error Icon Should Appear workbench_title=${workbench_title} @@ -548,19 +551,18 @@ Delete Workbench From CLI ${_} ${cr_name}= Get Openshift Notebook CR From Workbench ... workbench_title=${workbench_title} namespace=${ns_name} IF "${cr_name}" == "${EMPTY}" - Log msg=There is probably no Workbench with Diplay Name equal to ${workbench_title} + Log msg=There is probably no Workbench with Display Name equal to ${workbench_title} ... level=WARN ELSE - Oc Delete kind=Notebook name=${cr_name} namespace=${ns_name} - WHILE ${TRUE} - ${_} ${cr_name}= Get Openshift Notebook CR From Workbench - ... workbench_title=${workbench_title} namespace=${ns_name} - IF "${cr_name}" == "${EMPTY}" BREAK + Oc Delete kind=Notebook name=${cr_name} namespace=${ns_name} + WHILE ${TRUE} + ${_} ${cr_name}= Get Openshift Notebook CR From Workbench + ... workbench_title=${workbench_title} namespace=${ns_name} + IF "${cr_name}" == "${EMPTY}" BREAK Sleep 5s reason=let's not overload the API END END - Get Workbench Pod [Documentation] Retrieves info of a workbench pod: namespace, CR resource name and pod definition [Arguments] ${workbench_title} ${project_title} diff --git a/ods_ci/tests/Tests/400__ods_dashboard/415__ods_dashboard_projects/415__ods_dashboard_projects.robot b/ods_ci/tests/Tests/400__ods_dashboard/415__ods_dashboard_projects/415__ods_dashboard_projects.robot index 6ec24d3e5..0c0c1a08d 100644 --- a/ods_ci/tests/Tests/400__ods_dashboard/415__ods_dashboard_projects/415__ods_dashboard_projects.robot +++ b/ods_ci/tests/Tests/400__ods_dashboard/415__ods_dashboard_projects/415__ods_dashboard_projects.robot @@ -10,7 +10,7 @@ Resource ../../../Resources/Page/ODH/ODHDashboard/ODHDataScienceProjec Suite Setup Project Suite Setup Suite Teardown Project Suite Teardown Test Setup Launch Data Science Project Main Page - +Test Tags Dashboard *** Variables *** ${PRJ_TITLE}= ODS-CI Common Prj @@ -108,9 +108,6 @@ Verify User Can Create A Data Science Project [Tags] Smoke Sanity ODS-1775 Tier1 [Documentation] Verifies users can create a DS project Open Data Science Projects Home Page - # Create Data Science Project title=${PRJ_TITLE} description=${PRJ_DESCRIPTION} - # ... resource_name=${PRJ_RESOURCE_NAME} - # Open Data Science Projects Home Page Log message=DS Project creation covered by the Suite Setup Project Should Be Listed project_title=${PRJ_TITLE} Project's Owner Should Be expected_username=${TEST_USER_3.USERNAME} project_title=${PRJ_TITLE} @@ -121,8 +118,8 @@ Verify User Can Create And Start A Workbench With Existent PV Storage [Documentation] Verifies users can create a workbench and connect an existent PersistenVolume ${pv_name}= Set Variable ${PV_BASENAME}-existent Open Data Science Project Details Page project_title=${PRJ_TITLE} - Create PersistentVolume Storage name=${pv_name} description=${PV_DESCRIPTION} - ... size=${PV_SIZE} connected_workbench=${NONE} project_title=${PRJ_TITLE} + Create PersistentVolume Storage name=${pv_name} description=${PV_DESCRIPTION} project_title=${PRJ_TITLE} + ... size=${PV_SIZE} connected_workbench=${NONE} existing_storage=${TRUE} Create Workbench workbench_title=${WORKBENCH_2_TITLE} workbench_description=${WORKBENCH_2_DESCRIPTION} ... prj_title=${PRJ_TITLE} image_name=${NB_IMAGE} deployment_size=Small ... storage=Persistent pv_existent=${TRUE} pv_name=${pv_name} pv_description=${NONE} pv_size=${NONE} @@ -136,23 +133,32 @@ Verify User Can Create And Start A Workbench With Existent PV Storage Verify User Can Create A PV Storage [Tags] Sanity Tier1 ODS-1819 - ... AutomationBug [Documentation] Verifies users can Create PersistentVolume Storage ... THIS MUST BE UPDATED TO CHECK WORKBENCH GETS RESTARTED LIKE FOR ODS-1825. ... MAIN GOAL OF THE TEST CASE IS COVERED BY ODS-1814 ${pv_name}= Set Variable ${PV_BASENAME}-A ${ns_name}= Get Openshift Namespace From Data Science Project project_title=${PRJ_TITLE} Open Data Science Project Details Page project_title=${PRJ_TITLE} - ${workbenches}= Create Dictionary ${WORKBENCH_TITLE}=mount-data - Create PersistentVolume Storage name=${pv_name} description=${PV_DESCRIPTION} - ... size=${PV_SIZE} connected_workbench=${NONE} press_cancel=${TRUE} - ... project_title=${PRJ_TITLE} - Create PersistentVolume Storage name=${pv_name} description=${PV_DESCRIPTION} - ... size=${PV_SIZE} connected_workbench=${workbenches} project_title=${PRJ_TITLE} + Create Workbench workbench_title=${WORKBENCH_TITLE} workbench_description=${WORKBENCH_DESCRIPTION} + ... prj_title=${PRJ_TITLE} image_name=${NB_IMAGE} deployment_size=Small + ... storage=Persistent pv_existent=${NONE} + ... pv_name=${NONE} pv_description=${NONE} pv_size=${NONE} + Workbench Should Be Listed workbench_title=${WORKBENCH_TITLE} + Wait Until Workbench Is Started workbench_title=${WORKBENCH_TITLE} + ${workbenches}= Create List ${WORKBENCH_TITLE} + Create PersistentVolume Storage name=${pv_name} description=${PV_DESCRIPTION} project_title=${PRJ_TITLE} + ... size=${PV_SIZE} connected_workbench=${NONE} press_cancel=${TRUE} + Create PersistentVolume Storage name=${pv_name} description=${PV_DESCRIPTION} project_title=${PRJ_TITLE} + ... size=${PV_SIZE} connected_workbench=${workbenches} existing_storage=${TRUE} Storage Should Be Listed name=${pv_name} description=${PV_DESCRIPTION} ... type=Persistent storage connected_workbench=${workbenches} Check Corresponding PersistentVolumeClaim Exists storage_name=${pv_name} namespace=${ns_name} Storage Size Should Be name=${pv_name} namespace=${ns_name} size=${PV_SIZE} + [Teardown] Run Keywords + ... Clean Project From Workbench Resources workbench_title=${WORKBENCH_TITLE} + ... project_title=${PRJ_TITLE} pvc_title=${pv_name} + ... AND + ... Delete All PVC In Project From CLI project_title=${PRJ_TITLE} Verify User Can Create And Start A Workbench Adding A New PV Storage [Tags] Smoke Sanity ODS-1816 Tier1 ExcludeOnDisconnected @@ -219,8 +225,6 @@ Verify User Can Create A S3 Data Connection And Connect It To Workbenches ... aws_s3_endpoint=${DC_S3_ENDPOINT} aws_region=${DC_S3_REGION} ... connected_workbench=${workbenches} Data Connection Should Be Listed name=${DC_2_S3_NAME} type=${DC_S3_TYPE} connected_workbench=${workbenches} - Run Keyword And Continue On Failure Workbench Status Should Be workbench_title=${WORKBENCH_TITLE} - ... status=${WORKBENCH_STATUS_STARTING} Run Keyword And Continue On Failure Wait Until Workbench Is Started workbench_title=${WORKBENCH_TITLE} Workbench Status Should Be workbench_title=${WORKBENCH_2_TITLE} status=${WORKBENCH_STATUS_STOPPED} [Teardown] Run Keywords @@ -258,9 +262,7 @@ Verify User Can Delete A Data Connection Verify user can create a workbench with an existing data connection [Tags] Tier1 ODS-2176 [Documentation] Verifies users can create a workbench with an existing data connection - ${data_connection_name}= Set Variable aDataConnection - Open Data Science Project Details Page project_title=${PRJ_TITLE} Create S3 Data Connection project_title=${PRJ_TITLE} ... dc_name=${data_connection_name} @@ -276,7 +278,7 @@ Verify user can create a workbench with an existing data connection # The Workbench and the Data connection appear on the project details page. Workbench Should Be Listed workbench_title=${WORKBENCH_TITLE} # The data connection has the workbench name in the "Connected workbenches" column - ${workbenches}= Create Dictionary ${WORKBENCH_TITLE}=mount-data + ${workbenches}= Create List ${WORKBENCH_TITLE} Data Connection Should Be Listed name=${data_connection_name} type=${DC_S3_TYPE} ... connected_workbench=${workbenches} @@ -310,7 +312,6 @@ Verify User Can Create Environment Variables By Uploading YAML Secret/ConfigMap ... ODS-1883 [Documentation] Verify user can set environment varibles in their workbenches by ... uploading a yaml Secret or Config Map file. - ... ProductBug: RHODS-8249 ${envs_var_secret}= Create Dictionary filepath=${ENV_SECRET_FILEPATH} ... k8s_type=Secret input_type=${UPLOAD_TYPE} ${envs_var_cm}= Create Dictionary filepath=${ENV_CM_FILEPATH} @@ -340,43 +341,40 @@ Verify User Can Create Environment Variables By Uploading YAML Secret/ConfigMap ... pvc_title=${WORKBENCH_4_TITLE}-PV Verify User Can Log Out And Return To Project From Jupyter Notebook # robocop: disable - [Tags] Sanity Tier1 ODS-1971 AutomationBug + [Tags] Sanity Tier1 ODS-1971 [Documentation] Verifies user can log out and return to the project from Jupyter notebook. ... Users have 2 options: ... 1. click "File" > "Log Out" to actually close the login session ... 2. click "File" > "Hub Control Panel" to return to project details page - ... AutomationBug: JupyterLibrary's log out keyword seems to be broken - [Setup] Run Keywords - ... Open Data Science Project Details Page project_title=${PRJ_TITLE} - ... AND - ... Create Workbench workbench_title=${WORKBENCH_TITLE} workbench_description=${WORKBENCH_DESCRIPTION} + Open Data Science Project Details Page project_title=${PRJ_TITLE} + Create Workbench workbench_title=${WORKBENCH_TITLE} workbench_description=${WORKBENCH_DESCRIPTION} ... prj_title=${PRJ_TITLE} image_name=${NB_IMAGE} deployment_size=Small ... storage=Persistent pv_name=${NONE} pv_existent=${NONE} ... pv_description=${NONE} pv_size=${NONE} ... press_cancel=${FALSE} envs=${NONE} - ... AND - ... Wait Until Workbench Is Started workbench_title=${WORKBENCH_TITLE} + Wait Until Workbench Is Started workbench_title=${WORKBENCH_TITLE} Open Workbench workbench_title=${WORKBENCH_TITLE} Run Keyword And Continue On Failure ... Log In Should Be Requested Access To Workbench username=${TEST_USER_3.USERNAME} password=${TEST_USER_3.PASSWORD} ... auth_type=${TEST_USER_3.AUTH_TYPE} Open JupyterLab Control Panel - Wait Until Project Is Open project_title=${PRJ_TITLE} + Wait Until Project Is Open project_title=${PRJ_TITLE} timeout-pre-spinner=1m timeout-spinner=2m Workbench Status Should Be workbench_title=${WORKBENCH_TITLE} ... status=${WORKBENCH_STATUS_RUNNING} Open Workbench workbench_title=${WORKBENCH_TITLE} Run Keyword And Continue On Failure ... Log In Should Not Be Requested - Wait Until JupyterLab Is Loaded + Wait Until JupyterLab Is Loaded timeout=2m Logout JupyterLab - Wait Until Project Is Open project_title=${PRJ_TITLE} + Wait Until Project Is Open project_title=${PRJ_TITLE} timeout-pre-spinner=1m timeout-spinner=2m Workbench Status Should Be workbench_title=${WORKBENCH_TITLE} ... status=${WORKBENCH_STATUS_RUNNING} Open Workbench workbench_title=${WORKBENCH_TITLE} Run Keyword And Continue On Failure ... Log In Should Be Requested - [Teardown] Stop Workbench workbench_title=${WORKBENCH_TITLE} + [Teardown] Clean Project From Workbench Resources workbench_title=${WORKBENCH_TITLE} + ... project_title=${PRJ_TITLE} Verify Event Log Is Accessible While Starting A Workbench [Tags] Tier1 Sanity @@ -431,7 +429,6 @@ Verify User Can Cancel Workbench Start From Event Log Verify Error Is Reported When Workbench Fails To Start # robocop: disable [Tags] Tier1 Sanity ... ODS-1973 - ... AutomationBug [Documentation] Verify UI informs users about workbenches failed to start. ... At the moment the test is considering only the scenario where ... the workbench fails for Insufficient resources. @@ -541,8 +538,6 @@ Verify User Can Access Only Its Owned Projects [Setup] Run Keywords ... SeleniumLibrary.Close All Browsers ... AND - ... Set Variables For User Access Test - ... AND ... Delete Data Science Project From CLI displayed_name=${PRJ_TITLE} Launch Data Science Project Main Page username=${TEST_USER_3.USERNAME} password=${TEST_USER_3.PASSWORD} Open Data Science Projects Home Page @@ -591,7 +586,9 @@ Project Suite Setup Set Library Search Order SeleniumLibrary ${to_delete}= Create List ${PRJ_TITLE} Set Suite Variable ${PROJECTS_TO_DELETE} ${to_delete} + Set Variables For User Access Test RHOSi Setup + Delete Data Science Projects From CLI ocp_projects=${PROJECTS_TO_DELETE} Launch Data Science Project Main Page Create Data Science Project title=${PRJ_TITLE} description=${PRJ_DESCRIPTION} ... resource_name=${PRJ_RESOURCE_NAME} @@ -600,7 +597,6 @@ Project Suite Teardown [Documentation] Suite teardown steps after testing DS Projects. It Deletes ... all the DS projects created by the tests and run RHOSi teardown SeleniumLibrary.Close All Browsers - # Delete All Data Science Projects From CLI Delete Data Science Projects From CLI ocp_projects=${PROJECTS_TO_DELETE} RHOSi Teardown diff --git a/ods_ci/utils/scripts/ocm/ocm.py b/ods_ci/utils/scripts/ocm/ocm.py index aa0549187..9054eba54 100644 --- a/ods_ci/utils/scripts/ocm/ocm.py +++ b/ods_ci/utils/scripts/ocm/ocm.py @@ -119,7 +119,7 @@ def ocm_describe(self, jq_filter=""): cmd += f" {jq_filter}" ret = execute_command(cmd) if ret is None or "Error: Can't retrieve cluster for key" in ret: - log.info(f"ocm describe for cluster {self.cluster_name} failed") + log.info(f"ocm describe for cluster {self.cluster_name} with id: {cluster_id} failed") return None return ret @@ -309,7 +309,7 @@ def wait_for_osd_cluster_to_be_ready(self, timeout=7200): while count <= timeout: cluster_state = self.get_osd_cluster_state() if cluster_state == "ready": - log.info("{} is in ready state".format(self.cluster_name)) + log.info(f"{self.cluster_name} is in ready state") check_flag = True break elif cluster_state == "error": @@ -343,18 +343,19 @@ def is_addon_installed(self, addon_name="managed-odh"): addon_state = self.get_addon_state(addon_name) if addon_state == "not installed": - log.info("Addon {} not installed in cluster {}".format(addon_name, self.cluster_name)) + log.info(f"Addon {addon_name} not installed in cluster {self.cluster_name}") return False - log.info("Addon {} is installed in cluster {}".format(addon_name, self.cluster_name)) + log.info(f"Addon {addon_name} is installed in cluster {self.cluster_name}") return True def get_addon_state(self, addon_name="managed-odh"): """Gets given addon's state""" - cmd = "ocm list addons --cluster {} --columns id,state | grep {} ".format(self.cluster_name, addon_name) + cluster_id = self.get_osd_cluster_id() + cmd = f"ocm list addons --cluster {cluster_id} --columns id,state | grep {addon_name} " ret = execute_command(cmd) if ret is None: - log.info("Failed to get {} addon state for cluster {}".format(addon_name, self.cluster_name)) + log.info(f"Failed to get {addon_name} addon state for cluster {self.cluster_name}") return None match = re.search(addon_name + "\s*(.*)", ret) if match is None: @@ -363,10 +364,10 @@ def get_addon_state(self, addon_name="managed-odh"): return match.group(1).strip() def check_if_machine_pool_exists(self): - """Checks if given machine pool name already - exists in cluster""" + """Checks if given machine pool name already exists in cluster""" - cmd = "/bin/ocm list machinepools --cluster {} | grep -w {}".format(self.cluster_name, self.pool_name) + cluster_id = self.get_osd_cluster_id() + cmd = f"ocm list machinepools --cluster {cluster_id} | grep -w {self.pool_name}" ret = execute_command(cmd) if not ret: return False @@ -375,24 +376,15 @@ def check_if_machine_pool_exists(self): def add_machine_pool(self): """Adds machine pool to the given cluster""" if bool(self.reuse_machine_pool) and self.check_if_machine_pool_exists(): - log.info( - "MachinePool with name {} exists in cluster " - "{}. Hence " - "reusing it".format(self.pool_name, self.cluster_name) - ) + log.info(f"MachinePool with name {self.pool_name} exists in cluster {self.cluster_name}. Hence reusing it") else: - cmd = ( - "/bin/ocm --v={} create machinepool --cluster {} " - "--instance-type {} --replicas {} " - "--taints {} " - "{}".format( - self.ocm_verbose_level, - self.cluster_name, - self.pool_instance_type, - self.pool_node_count, - self.taints, - self.pool_name, - ) + cmd = "ocm --v={} create machinepool --cluster {} --instance-type {} --replicas {} --taints {} {}".format( + self.ocm_verbose_level, + self.cluster_name, + self.pool_instance_type, + self.pool_node_count, + self.taints, + self.pool_name, ) ret = execute_command(cmd) if ret is None: @@ -441,7 +433,8 @@ def wait_for_addon_uninstallation_to_complete(self, addon_name="managed-odh", ti def list_idps(self): """Lists IDPs for the cluster""" - cmd = "ocm list idps --cluster {} --columns name".format(self.cluster_name) + cluster_id = self.get_osd_cluster_id() + cmd = f"ocm list idps --cluster {cluster_id} --columns name" ret = execute_command(cmd) if ret is None: return [] @@ -688,7 +681,7 @@ def install_managed_starburst_addon(self, license, exit_on_failure=True): # else: # self.wait_for_addon_installation_to_complete(addon_name="managed-starburst") else: - log.info("managed-api-service is already installed on {}".format(self.cluster_name)) + log.info(f"managed-api-service is already installed on {self.cluster_name}") def uninstall_managed_starburst_addon(self, exit_on_failure=True): """Uninstalls RHOAM addon""" @@ -780,7 +773,8 @@ def create_idp(self): def delete_idp(self): """Deletes Identity Provider""" - cmd = f"ocm --v={self.ocm_verbose_level} delete idp -c {self.cluster_name} {self.idp_name}" + cluster_id = self.get_osd_cluster_id() + cmd = f"ocm --v={self.ocm_verbose_level} delete idp -c {cluster_id} {self.idp_name}" ret = execute_command(cmd) if ret is None: log.info(f"Failed to delete identity provider of type {self.idp_name}") @@ -806,7 +800,8 @@ def delete_user(self, user="", group="cluster-admins"): if user == "": user = self.htpasswd_cluster_admin - cmd = f"ocm --v={self.ocm_verbose_level} delete user {user} --cluster {self.cluster_name} --group={group}" + cluster_id = self.get_osd_cluster_id() + cmd = f"ocm --v={self.ocm_verbose_level} delete user {user} --cluster {cluster_id} --group={group}" ret = execute_command(cmd) if ret is None: log.info(f"Failed to delete user {user} of group {group}") @@ -933,7 +928,7 @@ def delete_cluster(self): cmd = f"ocm --v={self.ocm_verbose_level} delete cluster {cluster_id}" ret = execute_command(cmd) if ret is None: - log.error(f"Failed to delete osd cluster {self.cluster_name}") + log.error(f"Failed to delete osd cluster '{self.cluster_name}' with id: {cluster_id}") sys.exit(1) self.wait_for_osd_cluster_to_get_deleted() @@ -945,7 +940,7 @@ def wait_for_osd_cluster_to_get_deleted(self, timeout=5400): while count <= timeout: cluster_exists = self.is_osd_cluster_exists() if not cluster_exists: - log.info(f"{self.cluster_name} is deleted") + log.info(f"Cluster '{self.cluster_name}' was deleted") check_flag = True break @@ -976,14 +971,14 @@ def wait_for_osd_cluster_to_get_hibernated(self, timeout=1800): while count <= timeout: cluster_state = self.get_osd_cluster_state() if cluster_state == "hibernating": - log.info("{} is in hibernating state".format(self.cluster_name)) + log.info(f"{self.cluster_name} is in hibernating state") check_flag = True break time.sleep(60) count += 60 if not check_flag: - log.error(f"{self.cluster_name} not in hibernating state even after 30 mins. EXITING") + log.error(f"{self.cluster_name} not in hibernating state even after {timeout / 60} minutes. EXITING") sys.exit(1) def resume_cluster(self): @@ -1007,14 +1002,14 @@ def wait_for_osd_cluster_to_get_resumed(self, timeout=3600): while count <= timeout: cluster_state = self.get_osd_cluster_state() if cluster_state == "ready": - log.info("{} is in ready state".format(self.cluster_name)) + log.info(f"{self.cluster_name} is in ready state") check_flag = True break time.sleep(60) count += 60 if not check_flag: - log.error(f"{self.cluster_name} not in ready state even after 30 mins. EXITING") + log.error(f"{self.cluster_name} not in ready state even after {timeout / 60} minutes. EXITING") sys.exit(1) def update_notification_email_address(self, addon_name, email_address, exit_on_failure=True): diff --git a/ods_ci/utils/scripts/rosa/rosa.py b/ods_ci/utils/scripts/rosa/rosa.py index 580e16f85..6a2cf7f5b 100644 --- a/ods_ci/utils/scripts/rosa/rosa.py +++ b/ods_ci/utils/scripts/rosa/rosa.py @@ -1,5 +1,6 @@ import argparse import os +import re import sys dir_path = os.path.dirname(os.path.abspath(__file__)) @@ -7,6 +8,7 @@ from awsOps import aws_configure from logger import log from rosaOps import create_account_roles, rosa_create_cluster, rosa_whoami, wait_for_osd_cluster_to_be_ready +from util import execute_command class RosaClusterManager: @@ -21,6 +23,21 @@ def __init__(self, args={}): self.rosa_version = args.get("rosa_version") self.channel_name = args.get("channel_name") + def set_rosa_version(self): + version_match = re.match(r"(\d+\.\d+)\-latest", self.rosa_version) + if version_match is None: + log.info(f"Using the rosa version given by user: {self.rosa_version}") + return + log.info(f"User provided {self.rosa_version}, trying to determine the appropriate latest version for ROSA") + version = version_match.group(1) + latest_version_cmd = ( + f"rosa list versions --channel-group {self.channel_name} | " + f"awk '{{print $1}}' | grep -w '^{re.escape(version)}*' | head -n1" + ) + latest_version = execute_command(latest_version_cmd) + self.rosa_version = latest_version.strip() + log.info(f"Using the latest rosa version: {self.rosa_version}") + def create_rosa_cluster(self): log.info( "Creating ROSA cluster with the following details:\n" @@ -33,6 +50,7 @@ def create_rosa_cluster(self): ) aws_configure(self.aws_access_key_id, self.aws_secret_access_key, self.aws_region, self.aws_profile) rosa_whoami() + self.set_rosa_version() create_account_roles() rosa_create_cluster( self.cluster_name, @@ -127,7 +145,7 @@ def main(): required=True, action="store", dest="channel_name", - help="Channel Group Stable/Candidate", + help="Channel Group stable/candidate", ) rosa_cluster_manager = RosaClusterManager()