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 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d457768
Added onboarding token generation related functional tests for provid…
amr1ta Jun 12, 2024
6fe28fd
Updated ux pod respin testcase
amr1ta Jun 13, 2024
bede2dd
Updated squad details
amr1ta Jun 13, 2024
a2f2cac
Addressed review comments
amr1ta Jun 14, 2024
021a287
Added a testcase for creating a hosted cluster and onboarding storage…
amr1ta Aug 7, 2024
16e8644
Updated with hci_provider_required fixture
amr1ta Aug 13, 2024
33d0b36
Updated with create and destroy hypershift cluster fixtures
amr1ta Aug 14, 2024
acb877a
Added steps to validate default storage-quota and create onboarding t…
amr1ta Sep 2, 2024
23a2576
Corrected a typo
amr1ta Sep 3, 2024
a4d1846
Corrected a typo
amr1ta Sep 3, 2024
0e63039
removed duplicate import
amr1ta Sep 4, 2024
6cdad7f
Updated test
amr1ta Sep 4, 2024
c243c1f
Updated test
amr1ta Sep 4, 2024
2b0d5f9
added sleep to let the dashboard load successfully and increaded the …
amr1ta Sep 4, 2024
b9a9883
added fixture for login and close browser
amr1ta Sep 4, 2024
88dd7ff
Updated 'local-cluster_dropdown_item' locator to 'local-cluster_drop…
amr1ta Sep 4, 2024
ddd6b7c
Updated few locators of generate onboarding token page
amr1ta Sep 4, 2024
42f27a4
updated navigate_to_all_clusters method
amr1ta Sep 13, 2024
6bf355d
Added ignore leftover tag
amr1ta Sep 24, 2024
b9bb5f6
Added retry in case of ResourceNotFoundError to collect_pod_container…
amr1ta Oct 7, 2024
6de6253
Imported locally to resolve circular import issue
amr1ta Oct 10, 2024
8b6e710
Added parameter to accept storage_quota from config.env
amr1ta Oct 10, 2024
3f17617
Updated onboardin token generation from ui
amr1ta Oct 21, 2024
ea98930
Updated onboardin token generation from ui
amr1ta Oct 23, 2024
a9ac0cb
Added setup method
amr1ta Oct 24, 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
13 changes: 8 additions & 5 deletions ocs_ci/deployment/hosted_cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -822,7 +822,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 @@ -837,7 +837,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 @@ -850,7 +850,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 @@ -942,7 +943,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 @@ -952,7 +953,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
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

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
29 changes: 23 additions & 6 deletions ocs_ci/ocs/ui/validation_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from ocs_ci.ocs import constants
from ocs_ci.ocs.resources.storage_cluster import StorageCluster
from ocs_ci.framework.logger_helper import log_step
from ocs_ci.ocs.ui.base_ui import wait_for_element_to_be_visible


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -654,20 +655,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 @@ -676,4 +694,3 @@ def verify_storage_clients_page(self):
strings_on_page=strings_object_service_tab,
page_name="client_onboarding_token_page",
)
return storage_client_obj
33 changes: 26 additions & 7 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": (
"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),
}

topology = {
Expand Down Expand Up @@ -1972,7 +1988,7 @@
locators = {
"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 @@ -2013,13 +2029,15 @@
**validation_4_12,
**validation_4_13,
**validation_4_14,
**validation_4_17,
},
"block_pool": {**block_pool, **block_pool_4_12, **block_pool_4_13},
"storageclass": {**storageclass, **storageclass_4_9},
"bucketclass": bucketclass,
"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 @@ -2070,6 +2088,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 @@ -46,7 +46,9 @@
get_submariner_operator_version,
get_volsync_operator_version,
)

from ocs_ci.ocs.exceptions import (
ResourceNotFoundError,
)

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -1761,6 +1763,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 @@ -1778,6 +1781,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