Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Added onboarding token generation related functional tests for provider-client feature #9938

Open
wants to merge 28 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e40e32f
Added onboarding token generation related functional tests for provid…
amr1ta Jun 12, 2024
0ef9eaf
Updated ux pod respin testcase
amr1ta Jun 13, 2024
0aa0621
Updated squad details
amr1ta Jun 13, 2024
d7e6c6a
Addressed review comments
amr1ta Jun 14, 2024
d0b3c53
Added a testcase for creating a hosted cluster and onboarding storage…
amr1ta Aug 7, 2024
6d58c33
Updated with hci_provider_required fixture
amr1ta Aug 13, 2024
bd2436e
Updated with create and destroy hypershift cluster fixtures
amr1ta Aug 14, 2024
30cc6ed
Added steps to validate default storage-quota and create onboarding t…
amr1ta Sep 2, 2024
5d79a68
removed duplicate import
amr1ta Sep 4, 2024
8ae52c7
Updated test
amr1ta Sep 4, 2024
439c6c9
Updated test
amr1ta Sep 4, 2024
e3dfd06
added sleep to let the dashboard load successfully and increaded the …
amr1ta Sep 4, 2024
100e447
added fixture for login and close browser
amr1ta Sep 4, 2024
8fa7208
Updated 'local-cluster_dropdown_item' locator to 'local-cluster_drop…
amr1ta Sep 4, 2024
23d7d25
Updated few locators of generate onboarding token page
amr1ta Sep 4, 2024
2f6b735
updated navigate_to_all_clusters method
amr1ta Sep 13, 2024
c8453e9
Added ignore leftover tag
amr1ta Sep 24, 2024
5bf77aa
Added retry in case of ResourceNotFoundError to collect_pod_container…
amr1ta Oct 7, 2024
4b3d20e
Imported locally to resolve circular import issue
amr1ta Oct 10, 2024
db8b80d
Added parameter to accept storage_quota from config.env
amr1ta Oct 10, 2024
8d820c0
Updated onboardin token generation from ui
amr1ta Oct 21, 2024
7fc2699
Updated onboardin token generation from ui
amr1ta Oct 23, 2024
b318ba4
Added setup method
amr1ta Oct 24, 2024
e6201a5
Updated test
amr1ta Nov 19, 2024
8a0394c
Added 4.18 locators
amr1ta Nov 19, 2024
9fa038a
Updated odf subscription code for hcp clusters and added storage_quot…
amr1ta Dec 4, 2024
4984cb5
Updated onboarding of custom stotage quota test
amr1ta Dec 5, 2024
dcce051
Updated onboarding of custom stotage quota test
amr1ta Dec 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions ocs_ci/deployment/hosted_cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,7 @@ def storage_client_exists(self):
)

@kubeconfig_exists_decorator
def create_storage_client(self):
def create_storage_client(self, onboarding_token=None):
"""
Create storage client

Expand All @@ -843,7 +843,7 @@ def create_storage_client(self):
return True

@retry((CommandFailed, TimeoutError), tries=3, delay=30, backoff=1)
def _apply_storage_client_cr():
def _apply_storage_client_cr(onboarding_key=onboarding_token):
"""
Internal function to apply storage client CR
Returns:
Expand All @@ -856,7 +856,8 @@ def _apply_storage_client_cr():
"storageProviderEndpoint"
] = self.get_provider_address()

onboarding_key = self.get_onboarding_key()
if not onboarding_key:
onboarding_key = self.get_onboarding_key()

if not len(onboarding_key):
return False
Expand Down Expand Up @@ -948,7 +949,7 @@ def get_onboarding_key(self):
logger.error("ticketgen.sh failed to generate Onboarding token")
return token

def get_onboarding_key_ui(self):
def get_onboarding_key_ui(self, storage_quota=None):
"""
Get onboarding key from UI

Expand All @@ -958,7 +959,9 @@ def get_onboarding_key_ui(self):
from ocs_ci.ocs.ui.page_objects.page_navigator import PageNavigator

storage_clients = PageNavigator().nav_to_storageclients_page()
onboarding_key = storage_clients.generate_client_onboarding_ticket()
onboarding_key = storage_clients.generate_client_onboarding_ticket_ui(
storage_quota=storage_quota
)

return onboarding_key

Expand Down Expand Up @@ -1144,6 +1147,8 @@ def create_subscription(self):
)
if "latest" in hosted_odf_version:
hosted_odf_version = hosted_odf_version.split("-")[-1]
elif len(hosted_odf_version) > 4:
hosted_odf_version = hosted_odf_version[:4]

subscription_data["spec"]["channel"] = f"stable-{str(hosted_odf_version)}"

Expand Down
12 changes: 7 additions & 5 deletions ocs_ci/ocs/resources/storage_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ def odf_installation_on_client(

def create_storage_client(
self,
storage_provider_endpoint=None,
onboarding_token=None,
storage_provider_endpoint,
onboarding_token,
):
"""
This method creates storage clients
Expand All @@ -144,7 +144,9 @@ def create_storage_client(

# Pull storage-client yaml data
log.info("Pulling storageclient CR data from yaml")
storage_client_data = templating.load_yaml(constants.STORAGE_CLIENT_YAML)
storage_client_data = templating.load_yaml(
constants.PROVIDER_MODE_STORAGE_CLIENT
)
resource_name = storage_client_data["metadata"]["name"]
log.info(f"the resource name: {resource_name}")

Expand All @@ -164,7 +166,7 @@ def create_storage_client(
] = storage_provider_endpoint

# Set onboarding token
log.info("Updating storage provider endpoint details: %s", onboarding_token)
log.info("Updating storage client onboarding token: %s", onboarding_token)
storage_client_data["spec"]["onboardingTicket"] = onboarding_token
storage_client_data_yaml = tempfile.NamedTemporaryFile(
mode="w+", prefix="storage_client", delete=False
Expand Down Expand Up @@ -475,7 +477,7 @@ def create_native_storage_client(
from ocs_ci.ocs.ui.page_objects.page_navigator import PageNavigator

storage_clients = PageNavigator().nav_to_storageclients_page()
onboarding_token = storage_clients.generate_client_onboarding_ticket()
onboarding_token = storage_clients.generate_client_onboarding_ticket_ui()

# Create ODF subscription for storage-client
self.odf_installation_on_client()
Expand Down
24 changes: 16 additions & 8 deletions ocs_ci/ocs/ui/base_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,9 @@ def __init__(self):
locators, self.ocp_version, "add_capacity"
)
self.topology_loc = self.deep_get(locators, self.ocp_version, "topology")
self.storage_clients_loc = self.deep_get(locators, self.ocp_version, "storage")
self.storage_clients_loc = self.deep_get(
locators, self.ocp_version, "storage_clients"
)
self.alerting_loc = self.deep_get(locators, self.ocp_version, "alerting")

def __repr__(self):
Expand Down Expand Up @@ -988,6 +990,8 @@ def login_ui(console_url=None, username=None, password=None):
)

if hci_platform_conf:
logger.info("Adding some sleep time so the page loads successfuly")
time.sleep(60)
dashboard_url = console_url + "/dashboards"
# proceed to local-cluster page if not already there. The rule is always to start from the local-cluster page
# when the hci platform is confirmed and proceed to the client if needed from within the test
Expand Down Expand Up @@ -1083,7 +1087,7 @@ def navigate_to_local_cluster(**kwargs):
if "timeout" in kwargs:
timeout = kwargs["timeout"]
else:
timeout = 30
timeout = 60

all_clusters_dropdown = acm_page_loc["all-clusters_dropdown"]
try:
Expand Down Expand Up @@ -1116,12 +1120,16 @@ def navigate_to_all_clusters(**kwargs):
timeout = 30

local_clusters_dropdown = acm_page["local-cluster_dropdown"]
find_element = wait_for_element_to_be_visible(acm_page["click-local-cluster"], 60)
try:
acm_dropdown = wait_for_element_to_be_visible(local_clusters_dropdown, timeout)
acm_dropdown.click()
all_clusters_item = wait_for_element_to_be_visible(
acm_page["all-clusters_dropdown_item"]
)
all_clusters_item.click()
if not not find_element:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

due to implementation of wait_for_element_to_be_visible, it will fail if element not found.
The if not not find_element will not work.

Also "not" has been used 2 times

def wait_for_element_to_be_visible(locator, timeout=30):
    """
    Wait for element to be visible. Use when Web element is not have to be clickable (icons, disabled btns, etc.)
    Method does not fail when Web element not found

    Args:
         locator (tuple): (GUI element needs to operate on (str), type (By)).
         timeout (int): Looks for a web element until timeout (sec) occurs

    Returns:
        selenium.webdriver.remote.webelement.WebElement: Visible web element.
    """
    wait = WebDriverWait(SeleniumDriver(), timeout)
    try:
        web_element = wait.until(
            ec.visibility_of_element_located((locator[1], locator[0]))
        )
    except TimeoutException:
        take_screenshot()
        copy_dom()
        raise
    return web_element

acm_dropdown = wait_for_element_to_be_visible(
local_clusters_dropdown, timeout
)
acm_dropdown.click()
all_clusters_item = wait_for_element_to_be_visible(
acm_page["all-clusters_dropdown_item"]
)
all_clusters_item.click()
except TimeoutException:
wait_for_element_to_be_visible(acm_page["all-clusters_dropdown"])
23 changes: 22 additions & 1 deletion ocs_ci/ocs/ui/page_objects/storage_clients.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import logging

from ocs_ci.ocs.ui.base_ui import take_screenshot, copy_dom, BaseUI
from ocs_ci.utility import version
from ocs_ci.ocs.ui.validation_ui import ValidationUI

logger = logging.getLogger(__name__)

Expand All @@ -12,15 +14,34 @@ class StorageClients(BaseUI):

def __init__(self):
super().__init__()
self.ocs_version = version.get_semantic_ocs_version_from_config()

def generate_client_onboarding_ticket(self):
def generate_client_onboarding_ticket_ui(self, storage_quota=None):
"""
Generate a client onboarding ticket

Returns:
str: onboarding_key
"""
logger.info("Generating onboarding token from ui")
self.do_click(self.storage_clients_loc["generate_client_onboarding_ticket"])
ValidationUI().verify_storage_clients_page()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you merged it before, but is it possible to move the method verify_storage_clients_page to this class? It really belongs to StorageClients, so you would not create ValidationUI instance here but use self.
That will save us some time because ValidationUI constructor loads all the parents up to BaseUI and checks the PageNavigator presence

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better not to run verification of storage clients page by default each time we generate a token in the UI.

if storage_quota and self.ocs_version >= version.VERSION_4_17:
self.do_click(
self.validation_loc["storage_quota_custom"],
enable_screenshot=True,
)
self.do_clear(self.validation_loc["allocate_quota_value"])
self.do_send_keys(
locator=self.validation_loc["allocate_quota_value"], text=storage_quota
)
self.do_click(
self.validation_loc["quota_unit_dropdown"], enable_screenshot=True
)
self.do_click(
self.storage_clients_loc["generate_token"], enable_screenshot=True
)

onboarding_key = self.get_element_text(
self.storage_clients_loc["onboarding_key"]
)
Expand Down
31 changes: 24 additions & 7 deletions ocs_ci/ocs/ui/validation_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import warnings

import pandas as pd
from selenium.common.exceptions import TimeoutException
from ocs_ci.framework import config
from ocs_ci.framework.logger_helper import log_step
from ocs_ci.ocs.cluster import (
Expand All @@ -21,7 +22,7 @@
from ocs_ci.ocs.resources.storage_cluster import StorageCluster
from ocs_ci.utility import version
from ocs_ci.utility.utils import TimeoutSampler
from selenium.common.exceptions import TimeoutException
from ocs_ci.ocs.ui.base_ui import wait_for_element_to_be_visible

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -656,20 +657,37 @@ def verify_storage_clients_page(self):
"""
Verify storage clients page in UI

Returns:
StorageClients: Storage Clients page object

"""
self.refresh_web_console()
storage_client_obj = self.nav_to_storageclients_page()
self.nav_to_storageclients_page()
strings_storage_clients_tab = ["Storage clients", "Name"]
self.verify_page_contain_strings(
strings_on_page=strings_storage_clients_tab, page_name="storage clients"
)
self.do_click(
self.validation_loc["generate_client_onboarding_token_button"],
self.storage_clients_loc["generate_client_onboarding_ticket"],
enable_screenshot=True,
)
# Starting from ODF 4.17 custom storage amount select option is available
if self.ocs_version_semantic >= version.VERSION_4_17:
select_storage_quota_window = [
"Add storage capacity for the client cluster to consume from the provider cluster."
"Storage quota: Unlimited",
"Unlimited",
"Custom",
]
self.verify_page_contain_strings(
strings_on_page=select_storage_quota_window,
page_name="client_onboarding_token_page",
)
# Check default storage quota selected as 'Unlimited'
assert wait_for_element_to_be_visible(
self.validation_loc["storage_quota_unlimited"]
).is_selected(), "Default value unlimited quota is not selected"

# Take screenshot
self.take_screenshot()
Copy link
Contributor

@DanielOsypenko DanielOsypenko Sep 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pls use description param, this way we will identify screenshot by name in the UI logs dir
something like
self.take_screenshot("verify_storage_clients_page")


strings_object_service_tab = [
"Client onboarding token",
"How to use this token",
Expand All @@ -678,7 +696,6 @@ def verify_storage_clients_page(self):
strings_on_page=strings_object_service_tab,
page_name="client_onboarding_token_page",
)
return storage_client_obj

def calculate_est_days_and_average_manually(self):
"""
Expand Down
35 changes: 27 additions & 8 deletions ocs_ci/ocs/ui/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -661,14 +661,19 @@

storage_clients = {
"generate_client_onboarding_ticket": (
"//button[normalize-space()='Generate client onboarding token']",
"//button[text()='Generate client onboarding token']",
By.XPATH,
),
"client_onboarding_token": (
"onboarding_key": (
"//div[@class='odf-onboarding-modal__text-area']",
By.XPATH,
),
"copy_to_clipboard": ("//button[text()='Copy to clipboard']", By.XPATH),
"close_token_modal": ("//button[@aria-label='Close']", By.XPATH),
"generate_token": (
"//button[text()='Generate token']",
By.XPATH,
),
}

page_nav = {
Expand Down Expand Up @@ -731,6 +736,13 @@
"storageclients_page": ("Storage Clients", By.LINK_TEXT),
}

page_nav_4_17 = {
"storage_quota_selection_page_for_onboarding_client": (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you use it somewhere? Are you sure you'll get to storage quota selection page by clicking "Object Storage"?

"Object Storage",
By.LINK_TEXT,
),
}

acm_page_nav = {
"Home": ("//button[text()='Home']", By.XPATH),
"Welcome_page": ("Welcome", By.LINK_TEXT),
Expand Down Expand Up @@ -1765,12 +1777,16 @@
"/ancestor::div[2]//div[@class='ceph-raw-card-legend__text']",
By.XPATH,
),
"generate_client_onboarding_token_button": (
"//button[text()='Generate client onboarding token']",
}

validation_4_17 = {
"storage_quota_unlimited": ("storage-quota-unlimited", By.ID),
"storage_quota_custom": ("storage-quota-custom", By.ID),
"allocate_quota_value": (
"//input[@type='number']",
By.XPATH,
),
"copy to clipboard": ("//button[text()='Copy to clipboard']", By.XPATH),
"onboarding_token": ("//*[@class='odf-onboarding-modal__text-area']", By.XPATH),
"quota_unit_dropdown": ("pf-dropdown-toggle-id-2", By.ID),
}

validation_4_17 = {
Expand Down Expand Up @@ -1983,7 +1999,7 @@
locators = {
"4.18": {
"login": {**login, **login_4_11, **login_4_14},
"page": {**page_nav, **page_nav_4_10, **page_nav_4_14},
"page": {**page_nav, **page_nav_4_10, **page_nav_4_14, **page_nav_4_17},
"generic": generic_locators,
"add_capacity": {**add_capacity, **add_capacity_4_11, **add_capacity_4_12},
"deployment": {
Expand Down Expand Up @@ -2032,10 +2048,11 @@
"topology": topology,
"mcg_stores": mcg_stores,
"alerting": alerting,
"storage_clients": storage_clients,
},
"4.17": {
"login": {**login, **login_4_11, **login_4_14},
"page": {**page_nav, **page_nav_4_10, **page_nav_4_14},
"page": {**page_nav, **page_nav_4_10, **page_nav_4_14, **page_nav_4_17},
"generic": generic_locators,
"add_capacity": {**add_capacity, **add_capacity_4_11, **add_capacity_4_12},
"deployment": {
Expand Down Expand Up @@ -2084,6 +2101,7 @@
"topology": topology,
"mcg_stores": mcg_stores,
"alerting": alerting,
"storage_clients": storage_clients,
},
"4.16": {
"login": {**login, **login_4_11, **login_4_14},
Expand Down Expand Up @@ -2134,6 +2152,7 @@
"topology": topology,
"mcg_stores": mcg_stores,
"alerting": alerting,
"storage_clients": storage_clients,
},
"4.15": {
"login": {**login, **login_4_11, **login_4_14},
Expand Down
12 changes: 11 additions & 1 deletion ocs_ci/ocs/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@
get_submariner_operator_version,
get_volsync_operator_version,
)

from ocs_ci.ocs.exceptions import (
ResourceNotFoundError,
)

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -1809,6 +1811,7 @@ def collect_pod_container_rpm_package(dir_name):
"""
# Import pod here to avoid circular dependency issue
from ocs_ci.ocs.resources import pod
from ocs_ci.ocs.resources.pod import wait_for_pods_to_be_running

timestamp = time.time()
cluster_namespace = ocsci_config.ENV_DATA["cluster_namespace"]
Expand All @@ -1826,6 +1829,13 @@ def collect_pod_container_rpm_package(dir_name):
ocp_obj = OCP(namespace=cluster_namespace)
for pod_obj in pods:
pod_object = pod_obj.get()
# avoid resource not found error when pod got respinned inbetween
retry(ResourceNotFoundError, tries=2, delay=2, backoff=2)(
wait_for_pods_to_be_running
)(
pods=pod.get_all_pods(namespace=cluster_namespace),
raise_pod_not_found_error=True,
)
pod_containers = pod_object.get("spec").get("containers")
ocp_pod_obj = OCP(kind=constants.POD, namespace=cluster_namespace)
pod_status = ocp_pod_obj.get_resource_status(pod_obj.name)
Expand Down
Loading
Loading