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

Add quota to token generation #10399

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ ENV_DATA:
worker_replicas: 2
mon_type: 'hostpath'
osd_type: 'nvme'
quota: 'unrestricted'
REPORTING:
ocs_must_gather_image: "quay.io/rhceph-dev/ocs-must-gather"
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ ENV_DATA:
worker_replicas: 3
mon_type: 'hostpath'
osd_type: 'nvme'
quota: 'unrestricted'
REPORTING:
ocs_must_gather_image: "quay.io/rhceph-dev/ocs-must-gather"
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ ENV_DATA:
worker_replicas: 2
mon_type: 'hostpath'
osd_type: 'ssd'
quota: 'unrestricted'
REPORTING:
ocs_must_gather_image: "quay.io/ocs-dev/ocs-must-gather"
ocs_must_gather_latest_tag: 'latest'
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ ENV_DATA:
worker_replicas: 3
mon_type: 'hostpath'
osd_type: 'ssd'
quota: 'unrestricted'
REPORTING:
ocs_must_gather_image: "quay.io/ocs-dev/ocs-must-gather"
ocs_must_gather_latest_tag: 'latest'
27 changes: 27 additions & 0 deletions ocs_ci/framework/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,18 @@ def get_consumer_indexes_list(self):

return consumer_indexes_list

def get_consumer_with_resticted_quota_index(self):
"""
Get the consumer cluster index
of the first consumer which has quota restrictions
"""
consumer_indexes = self.get_consumer_indexes_list()
for index in consumer_indexes:
cluster = self.clusters[index]
if cluster.ENV_DATA["quota"] != "unlimited":
return index
raise ClusterNotFoundException("Didn't find any consumer with resticted quota")

def get_cluster_index_by_name(self, cluster_name):
"""
Get the cluster index by the cluster name
Expand Down Expand Up @@ -497,6 +509,21 @@ def __init__(self):
switch_index = config.cur_index
super().__init__(switch_index)

class RunWithRestrictedQuotaConsumerConfigContextIfAvailable(RunWithConfigContext):
"""
Context manager that makes sure that a given code block is executed
on a Consumer with restricted quota.
If such config is not available, then run with current config context.
"""

def __init__(self):
try:
switch_index = config.get_consumer_with_resticted_quota_index()
except ClusterNotFoundException:
logger.DEBUG("No consumer with restricted quota found")
switch_index = config.cur_index
super().__init__(switch_index)

class RunWithFirstConsumerConfigContextIfAvailable(RunWithConfigContext):
"""
Context manager that makes sure that a given code block is executed on First consumer.
Expand Down
65 changes: 65 additions & 0 deletions ocs_ci/ocs/resources/storageconsumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from ocs_ci.framework import config
from ocs_ci.ocs import constants, ocp
from ocs_ci.helpers import helpers
from ocs_ci.ocs.resources.ocs import OCS
from ocs_ci.utility.utils import exec_cmd

Expand Down Expand Up @@ -120,3 +121,67 @@ def get_heartbeat_cronjob(self):
if job["metadata"]["name"].endswith("status-reporter")
][0]
return cronjob

def fill_up_quota_percentage(self, percentage, quota=None):
"""
Create a PVC of such size that the correct percentage of quota is used

Returns:
PVC object
"""
pvc_name = f"pvc-quota-{percentage}"
if not quota:
quota = config.ENV_DATA["quota"]
quota_value = quota.split(" ")[0]
quota_units = quota.split(" ")[1]
pvc_size_int = quota_value * percentage // 100
pvc_size = f"{pvc_size_int}{quota_units}"
rbd_storageclass = helpers.default_storage_class(constants.CEPHBLOCKPOOL)
pvc_obj = helpers.create_pvc(
pvc_name=pvc_name,
sc_name=rbd_storageclass,
namespace="default",
size=pvc_size,
do_reload=False,
access_mode=constants.ACCESS_MODE_RWO,
volume_mode=constants.VOLUME_MODE_BLOCK,
)
return pvc_obj


def get_all_client_clusters():
"""
Get client cluster names of all storage consumers

Returns:
array: names of client clusters
"""
ocp_storageconsumers = ocp.OCP(
kind=constants.STORAGECONSUMER,
namespace=config.cluster_ctx.ENV_DATA["cluster_namespace"],
)
cluster_names = []
storageconsumers_data = ocp_storageconsumers.get().get("items")
for storageconsumer in storageconsumers_data:
cluster_names.append(storageconsumer["status"]["client"]["clusterName"])
return cluster_names


def get_storageconsumer_quota(cluster_name):
"""
Get the quota value from storageconsumer details
Args:
clustername(str): name of the client cluster
Returns"
str: quota value
"""
ocp_storageconsumers = ocp.OCP(
kind=constants.STORAGECONSUMER,
namespace=config.cluster_ctx.ENV_DATA["cluster_namespace"],
)
storageconsumers_data = ocp_storageconsumers.get().get("items")
for storageconsumer in storageconsumers_data:
if storageconsumer["status"]["client"]["clusterName"] == cluster_name:
if "storageQuotaInGiB" not in storageconsumer["spec"]:
return "Unlimited"
return storageconsumer["spec"]["storageQuotaInGiB"]
150 changes: 147 additions & 3 deletions ocs_ci/ocs/ui/page_objects/storage_clients.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import logging

from selenium.common.exceptions import WebDriverException
from ocs_ci.ocs.ui.base_ui import take_screenshot, copy_dom, BaseUI

logger = logging.getLogger(__name__)
Expand All @@ -13,14 +13,35 @@ class StorageClients(BaseUI):
def __init__(self):
super().__init__()

def generate_client_onboarding_ticket(self):
def generate_client_onboarding_ticket(self, quota_value=None, quota_tib=None):
"""
Generate a client onboarding ticket
Generate a client onboarding ticket.
Starting with version 4.17, client quota can be specified

Args:
quota_value (int): client's quota in GiB or TiB, unlimited if not defined
quota_tib (bool): True if quota is in TiB, False otherwise

Returns:
str: onboarding_key
"""
logger.info("Generating onboarding ticket")
self.do_click(self.storage_clients_loc["generate_client_onboarding_ticket"])
if quota_value:
logger.info("Setting client cluster quota")
self.do_click(self.storage_clients_loc["custom_quota"])
self.do_clear(
locator=self.storage_clients_loc["quota_value"],
)
self.do_send_keys(
locator=self.storage_clients_loc["quota_value"],
text=quota_value,
)
if quota_tib:
self.do_click(self.storage_clients_loc["choose_units"])
self.do_click(self.storage_clients_loc["quota_ti"])
logger.info("Confirming token generation")
self.do_click(self.storage_clients_loc["confirm_generation"])
onboarding_key = self.get_element_text(
self.storage_clients_loc["onboarding_key"]
)
Expand All @@ -41,3 +62,126 @@ def close_onboarding_token_modal(self):
Close the onboarding token modal
"""
self.do_click(self.storage_clients_loc["close_token_modal"])

def find_client_cluster_index(self, client_cluster_name):
"""
Find the index of the cluster on Storage clients page
Filtering clients by name isn't working: https://bugzilla.redhat.com/show_bug.cgi?id=2317212

Args:
client_cluster_name(str): name of the hosted cluster

Returns:
int: index of the cluster on Storage Clients page

"""
all_names = [
element.text
for element in self.get_elements(self.storage_clients_loc["cluster_name"])
]
for index in range(len(all_names)):
if client_cluster_name in all_names[index]:
logger.info(f"Storage client {client_cluster_name} has index {index}")
return index
logger.error(
f"Storage client with cluster name {client_cluster_name} not found"
)

def get_client_quota_from_ui(self, client_cluster_name):
"""
Get client's quota from Storage Client's page
Args:
client_cluster_name(str): name of the client cluster
Returns:
str: quota of the client
"""
client_index = self.find_client_cluster_index(client_cluster_name)
quota_element = self.get_elements(self.storage_clients_loc["client_quota"])[
client_index
]
return quota_element.text

def edit_quota(
self, client_cluster_name, new_value=None, new_units=False, increase_by_one=True
):
"""
Edit client's storage quota

Args:
client_cluster_name(str): name of the client cluster
new_value(int): new value of the quota
new_units(bool): True if units need to be changed, False otherwise
increase_by_one(bool): True if quota needs to be increased by 1, False otherwise

Returns:
True if quota change was successful
False otherwise
"""
client_index = self.find_client_cluster_index(client_cluster_name)
self.do_click(
self.get_elements(self.storage_clients_loc["client_kebab_menu"])[
client_index
]
)
try:
self.do_click(self.storage_clients_loc["edit_quota"])
except WebDriverException as e:
logger.info(e)
logger.info("Quota changes not possble")
return False
if increase_by_one:
self.do_click(self.storage_clients_loc["quota_increment"])
logger.info("Quota increased by 1")
else:
if not new_value:
logger.error("New quota value not provided")
return False
else:
self.clear_with_ctrl_a_del(self.storage_clients_loc["new_quota"])
self.do_send_keys(self.storage_clients_loc["new_quota"], text=new_value)
logger.info(f"Quota value changed to {new_value}")
if new_units:
self.do_click(self.storage_clients_loc["unit_change_button"])
self.do_click(self.storage_clients_loc["units_ti"])
logger.info("Quota units changed to Ti")
try:
self.do_click(self.storage_clients_loc["confirm_quota_change"])
logger.info("Quota changes saved")
return True
except WebDriverException as e:
logger.info(e)
logger.info("Quota changes could not be saved")
return False

def get_available_storage_from_quota_edit_popup(self):
"""
Get the value of available storage
from Edit quota popup

Returns:
str: available storage
"""
self.do_click(
self.get_elements(self.storage_clients_loc["client_kebab_menu"])[0]
)
av_capacity_text = self.get_element_text(
self.storage_clients_loc["available_storage"]
)
# Text is expected to be 'Available capacity (ocs-storagecluster): N TiB'
split_capacity_text = av_capacity_text.split(" ")
return f"{split_capacity_text[-2]} {split_capacity_text[-1]}"

def validate_unlimited_quota_utilization_info(self):
"""
Verify that for every client with unlimited quota
utilization column only shows "-"
"""
quota_elements = self.get_elements(self.storage_clients_loc["client_quota"])
utilization_elements = self.get_elements(
self.storage_clients_loc["quota_utilization"]
)
for i in len(quota_elements):
if quota_elements[i].text == "Unlimited":
assert (
utilization_elements[i].text == "-"
), f"Quota utilization is shown as {utilization_elements[i].text}"
25 changes: 25 additions & 0 deletions ocs_ci/ocs/ui/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -668,7 +668,32 @@
"//div[@class='odf-onboarding-modal__text-area']",
By.XPATH,
),
"quota_value": ("//input[@type='number']", By.XPATH),
"choose_units": (
"//div[contains(@class, 'request-size-input__unit')]/button",
By.XPATH,
),
"quota_ti": ("//li[@id='Ti']", By.XPATH),
"confirm_generation": ("//button[@data-test-id='confirm-action']", By.XPATH),
"close_token_modal": ("//button[@aria-label='Close']", By.XPATH),
"client_name": ("name", By.ID),
"cluster_name": ("clusterName", By.ID),
"client_quota": ("storageQuota", By.ID),
"custom_quota": ("storage-quota-custom", By.ID),
"client_kebab_menu": ("//button[@data-test='kebab-button']", By.XPATH),
"edit_quota": ("Edit Resource", By.ID),
"quota_decrement": ("button[aria-label='Decrement']", By.CSS_SELECTOR),
"quota_increment": ("button[aria-label='Increment']", By.CSS_SELECTOR),
"new_quota": ("input[type=number]", By.CSS_SELECTOR),
"unit_change_button": (
"//div[@class='pf-v5-c-dropdown request-size-input__unit']/button",
By.XPATH,
),
"units_ti": ("li[id=Ti]", By.CSS_SELECTOR),
"storage_available": ("//span[@data-test='status-text']", By.XPATH),
"quota_decreased_alert": ("//h4[@class='pf-v5-c-alert__title']", By.XPATH),
"confirm_quota_change": ("//button[@data-test-id='confirm-action']", By.XPATH),
"available_storage": (),
}

page_nav = {
Expand Down
Loading
Loading