From 78db53b1dae7befdcdd6f89d6354f42f77adabcd Mon Sep 17 00:00:00 2001 From: Hayk Hovsepyan Date: Thu, 15 Apr 2021 17:05:48 +0200 Subject: [PATCH] Service/Application/Workload details page refactoring: Initial commit. More commits will come later. --- kiali_qe/components/__init__.py | 538 +++++++--------------- kiali_qe/entities/__init__.py | 2 +- kiali_qe/entities/istio_config.py | 2 +- kiali_qe/entities/overview.py | 2 +- kiali_qe/entities/service.py | 72 +-- kiali_qe/entities/workload.py | 64 +-- kiali_qe/rest/kiali_api.py | 97 +--- kiali_qe/rest/openshift_api.py | 30 +- kiali_qe/tests/__init__.py | 69 ++- kiali_qe/tests/test_istio_objects_crud.py | 10 +- kiali_qe/tests/test_routing_wizard.py | 3 +- 11 files changed, 273 insertions(+), 616 deletions(-) diff --git a/kiali_qe/components/__init__.py b/kiali_qe/components/__init__.py index c4bc2204..529d4247 100644 --- a/kiali_qe/components/__init__.py +++ b/kiali_qe/components/__init__.py @@ -33,7 +33,6 @@ ServiceDetails, VirtualServiceOverview, DestinationRuleOverview, - SourceWorkload, VirtualServiceGateway, ServiceHealth, IstioConfigRow, @@ -56,7 +55,6 @@ ApplicationHealth ) from kiali_qe.entities.overview import Overview -from kiali_qe.utils.date import parse_from_rest from time import sleep from kiali_qe.utils.log import logger from wait_for import wait_for @@ -1581,9 +1579,9 @@ def _cb_action(self, filter_name, action, value=None, skipOpen=False): self.open() try: _cb = Checkbox(locator=self.ITEM.format(filter_name), parent=self) - if action is 'fill': + if action == 'fill': _cb.fill(value) - elif action is 'read': + elif action == 'read': return _cb.read() finally: if not skipOpen: @@ -2035,21 +2033,23 @@ def back_to_info(self): self.browser.click(tab) wait_to_spinner_disappear(self.browser) - def _get_date_tooltip(self, element): - date_text = '' + def _get_tooltip_items(self, element): + result = [] try: - self.browser.move_to_element(locator='.//span', parent=element) + self.browser.move_to_element(locator='.//svg/path', parent=element) sleep(1.5) - date_text = self.browser.element( + _texts = self.browser.element( locator=(POPOVER), - parent='/').text + parent='/').text.split('\n') + for _text in _texts: + result.append(_text.split(' ')[1].strip()) except (NoSuchElementException, StaleElementReferenceException): # skip errors caused by browser delays, this health will be ignored pass finally: self.browser.send_keys_to_focused_element(Keys.ESCAPE) sleep(0.5) - return date_text + return result def click_more_labels(self, parent): try: @@ -2132,14 +2132,15 @@ class ListViewAbstract(ViewAbstract): ITEMS = '//tr[contains(@role, "row")]' ITEM_COL = './/td' ITEM_TEXT = './/*[contains(@class, "virtualitem_definition_link")]' - DETAILS_ROOT = ('.//section[@id="pf-tab-section-0-basic-tabs"]' - '/div[contains(@class, "pf-l-grid")]') + DETAILS_ROOT = ('//section[@id="pf-tab-section-0-basic-tabs"]' + '//div[contains(@class, "pf-l-grid")]') ISTIO_PROPERTIES = ('.//*[contains(@class, "pf-l-stack__item")]' '/h6[text()="{}"]/..') - NETWORK_PROPERTIES = ('.//*[contains(@class, "pf-l-stack__item")]' - '//h6[text()="{}"]/..') - PROPERTY_SECTIONS = ('.//*[contains(@class, "pf-l-stack__item")]' - '//span[text()="{}"]/../..') + NETWORK_PROPERTIES = ('.//span[text()="{}"]/..') + PROPERTY_SECTIONS = ('.//span[text()="{}"]/../..') + GRAPH_ROOT = (DETAILS_ROOT + + '//article[@id="MiniGraphCard"]') + NAME_PROPERTY = '//div[contains(@class, "pf-c-card__header")]//h5' NAME = 'Name' PODS = 'Pods' SERVICES = 'Services' @@ -2147,6 +2148,7 @@ class ListViewAbstract(ViewAbstract): IP = 'IP' SERVICE_IP = 'Service IP' PORTS = 'Ports' + ENDPOINTS = 'Endpoints' CREATED_AT = 'Created at' RESOURCE_VERSION = 'Resource Version' INBOUND_METRICS = 'Inbound Metrics' @@ -2159,8 +2161,7 @@ class ListViewAbstract(ViewAbstract): CONFIG = 'strong[normalize-space(text()="{}:")]/..//'.format(CONFIG_TEXT) CONFIG_TABS_PARENT = './/ul[contains(@class, "pf-c-tabs__list")]' CONFIG_TAB_OVERVIEW = './/button[@id="pf-tab-0-basic-tabs"]' - GRAPH_OVERVIEW_MENU = '//div[contains(@class, "pf-c-card__head")]//'\ - 'h3[text()="Graph Overview"]/../..//button' + GRAPH_OVERVIEW_MENU = GRAPH_ROOT + '//button' def __init__(self, parent, locator=None, logger=None): Widget.__init__(self, parent, logger=logger) @@ -2185,29 +2186,31 @@ def _item_namespace(self, cell): def _get_service_endpoints(self, element): result = [] + # TODO tooltip pod value _endpoints = self.browser.elements( - locator='.//div[@id="endpoints"]//span', parent=element) + locator='.//div//span', parent=element) for endpoint in _endpoints: result.append(endpoint.text) return result def _get_details_health(self): - _health_sublocator = '/../..//div[@id="health"]' + _health_parent = self.browser.element(locator=self.NAME_PROPERTY, + parent=self.DETAILS_ROOT) _healthy = len(self.browser.elements( - parent=self.DETAILS_ROOT, - locator='.//*[contains(@class, "icon-healthy")]' + _health_sublocator)) > 0 + parent=_health_parent, + locator='.//*[contains(@class, "icon-healthy")]')) > 0 _not_healthy = len(self.browser.elements( - parent=self.DETAILS_ROOT, - locator='.//*[contains(@class, "icon-failure")]' + _health_sublocator)) > 0 + parent=_health_parent, + locator='.//*[contains(@class, "icon-failure")]')) > 0 _degraded = len(self.browser.elements( - parent=self.DETAILS_ROOT, - locator='.//*[contains(@class, "icon-degraded")]' + _health_sublocator)) > 0 + parent=_health_parent, + locator='.//*[contains(@class, "icon-degraded")]')) > 0 _idle = len(self.browser.elements( - parent=self.DETAILS_ROOT, - locator='.//*[contains(@class, "icon-idle")]' + _health_sublocator)) > 0 + parent=_health_parent, + locator='.//*[contains(@class, "icon-idle")]')) > 0 _not_available = len(self.browser.elements( - parent=self.DETAILS_ROOT, - locator='.//*[contains(@class, "icon-na")]' + _health_sublocator)) > 0 + parent=_health_parent, + locator='.//*[contains(@class, "icon-na")]')) > 0 _health = None if _healthy: _health = HealthType.HEALTHY @@ -2479,7 +2482,7 @@ def _get_details_labels(self): pass _labels = self.browser.elements( parent=self.DETAILS_ROOT, - locator=('//div[@id="labels"]//*[contains(@class, "label-pair")]')) + locator=('//div//*[contains(@class, "label-pair")]')) if _labels: for _label in _labels: _label_key = self.browser.element( @@ -2755,9 +2758,9 @@ def get_details(self, load_only=False): return _breadcrumb self.back_to_info() - _table_view_workloads = TableViewAppWorkloads(self.parent, self.locator, self.logger) + _card_view_workloads = CardViewAppWorkloads(self.parent, self.locator, self.logger) - _table_view_services = TableViewAppServices(self.parent, self.locator, self.logger) + _card_view_services = CardViewServices(self.parent, self.locator, self.logger) _traffic_tab = TrafficView(parent=self.parent, locator=self.locator, logger=self.logger) @@ -2772,8 +2775,8 @@ def get_details(self, load_only=False): istio_sidecar=self._details_sidecar_text(), health=self._get_details_health(), application_status=self._get_application_details_health(), - workloads=_table_view_workloads.all_items, - services=_table_view_services.all_items, + workloads=_card_view_workloads.all_items, + services=_card_view_services.all_items, traffic_tab=_traffic_tab, inbound_metrics=_inbound_metrics, outbound_metrics=_outbound_metrics, @@ -2806,39 +2809,40 @@ def items(self): class ListViewWorkloads(ListViewAbstract): SIDECAR_INJECTION_TEXT = 'Istio Sidecar Inject Annotation' + DETAILS_ROOT = (ListViewAbstract.DETAILS_ROOT + + '//article[@id="WorkloadDescriptionCard"]') + CONFIGS_ROOT = (ListViewAbstract.DETAILS_ROOT + + '//article[@id="IstioConfigCard"]') - def _details_sidecar_injection_text(self): + def _details_missing_sidecar(self): """ - Return the value Istio Sidecar Inject Annotation in workload details, - empty string if does not exist + Return if is missing Istio Sidecar icon in workload details, """ - return self.browser.text_or_default( + return len(self.browser.elements( parent=self.DETAILS_ROOT, - locator=self.ISTIO_PROPERTIES.format(self.SIDECAR_INJECTION_TEXT), - default='').replace(self.SIDECAR_INJECTION_TEXT, '').strip() + locator='.//*[contains(@d, "M78")]')) > 0 def get_details(self, load_only=False): if load_only: return BreadCrumb(self.parent) self.back_to_info() _name = self.browser.text( - locator=self.ISTIO_PROPERTIES.format(self.NAME), - parent=self.DETAILS_ROOT).replace(self.NAME, '').strip() - _type = self.browser.text(locator=self.ISTIO_PROPERTIES.format(self.TYPE), - parent=self.DETAILS_ROOT).replace(self.TYPE, '').strip() - _created_at_ui = self.browser.text(locator=self.ISTIO_PROPERTIES.format(self.CREATED_AT), - parent=self.DETAILS_ROOT).replace( - self.CREATED_AT, '').strip() - _created_at = self._get_date_tooltip(self.browser.element( - locator=self.ISTIO_PROPERTIES.format(self.CREATED_AT), + locator=self.NAME_PROPERTY, + parent=self.DETAILS_ROOT).replace('W', '').strip() + + ''' TODO tooltip items + _tooltip_items = self._get_tooltip_items(self.browser.element( + locator=self.NAME_PROPERTY, parent=self.DETAILS_ROOT)) - _resource_version = self.browser.text( - locator=self.ISTIO_PROPERTIES.format(self.RESOURCE_VERSION), - parent=self.DETAILS_ROOT).replace(self.RESOURCE_VERSION, '').strip() + _type = _tooltip_items[0] + _created_at = _tooltip_items[1] + _resource_version = _tooltip_items[2]''' - _table_view_pods = TableViewWorkloadPods(self.parent, self.locator, self.logger) + _card_view_pods = CardViewWorkloadPods(self.parent, self.locator, self.logger) - _table_view_services = TableViewServices(self.parent, self.locator, self.logger) + _card_view_services = CardViewServices(self.parent, self.locator, self.logger) + + _card_view_istio_config = CardViewIstioConfig(self.parent, self.locator, self.logger) _traffic_tab = TrafficView(parent=self.parent, locator=self.locator, logger=self.logger) @@ -2852,21 +2856,17 @@ def get_details(self, load_only=False): _traces_tab = TracesView(parent=self.parent, locator=self.locator, logger=self.logger) return WorkloadDetails(name=str(_name), - workload_type=_type, - sidecar_injection=self._details_sidecar_injection_text(), - created_at_ui=_created_at_ui, - created_at=parse_from_rest(_created_at), - resource_version=_resource_version, + workload_type=None, + missing_sidecar=self._details_missing_sidecar(), + created_at=None, + resource_version=None, istio_sidecar=self._details_sidecar_text(), health=self._get_details_health(), workload_status=self._get_workload_details_health(_name), icon=self._get_additional_details_icon(), - pods_number=_table_view_pods.number, - services_number=_table_view_services.number, - pods=_table_view_pods.all_items, - services=_table_view_services.all_items, - istio_configs_number=self.table_view_istio_config.number, - istio_configs=self.table_view_istio_config.all_items, + pods=_card_view_pods.all_items, + services=_card_view_services.all_items, + istio_configs=_card_view_istio_config.all_items, labels=self._get_details_labels(), traffic_tab=_traffic_tab, logs_tab=_logs_tab, @@ -2900,44 +2900,44 @@ def items(self): _items.append(_workload) return _items - @property - def table_view_istio_config(self): - return TableViewWorkloadIstioConfig(self.parent, self.locator, self.logger) - class ListViewServices(ListViewAbstract): + DETAILS_ROOT = (ListViewAbstract.DETAILS_ROOT + + '//article[@id="ServiceDescriptionCard"]') + NETWORK_ROOT = (ListViewAbstract.DETAILS_ROOT + + '//article[@id="ServiceNetworkCard"]') + def get_details(self, load_only=False): if load_only: return BreadCrumb(self.parent) self.back_to_info() - self.browser.click('.//button[contains(text(), "Network")]', parent=self) _type = self.browser.text(locator=self.NETWORK_PROPERTIES.format(self.TYPE), - parent=self.DETAILS_ROOT).replace(self.TYPE, '').strip() + parent=self.NETWORK_ROOT).replace(self.TYPE, '').strip() _ip = self.browser.text(locator=self.NETWORK_PROPERTIES.format(self.SERVICE_IP), - parent=self.DETAILS_ROOT).replace(self.SERVICE_IP, '').strip() + parent=self.NETWORK_ROOT).replace(self.SERVICE_IP, '').strip() _ports = self.browser.text( - locator=self.PROPERTY_SECTIONS.format(self.PORTS), - parent=self.DETAILS_ROOT).replace(self.PORTS, '').strip() - _endpoints = self._get_service_endpoints(self.DETAILS_ROOT) + locator=self.NETWORK_PROPERTIES.format(self.PORTS), + parent=self.NETWORK_ROOT).replace(self.PORTS, '').strip() + _endpoints = self._get_service_endpoints( + self.browser.element(locator=self.NETWORK_PROPERTIES.format(self.ENDPOINTS), + parent=self.NETWORK_ROOT)) - self.browser.click('.//button[contains(text(), "Properties")]', parent=self) _name = self.browser.text( - locator=self.NETWORK_PROPERTIES.format(self.NAME), - parent=self.DETAILS_ROOT).replace(self.NAME, '').replace( - self.MISSING_SIDECAR_TEXT, '').strip() - _created_at_ui = self.browser.text( - locator=self.NETWORK_PROPERTIES.format(self.CREATED_AT), - parent=self.DETAILS_ROOT).replace(self.CREATED_AT, '').strip() + locator=self.NAME_PROPERTY, + parent=self.DETAILS_ROOT).replace('S', '').strip() + ''' TODO tooltip values _created_at = self._get_date_tooltip(self.browser.element( locator=self.NETWORK_PROPERTIES.format(self.CREATED_AT), parent=self.DETAILS_ROOT)) _resource_version = self.browser.text( locator=self.NETWORK_PROPERTIES.format(self.RESOURCE_VERSION), - parent=self.DETAILS_ROOT).replace(self.RESOURCE_VERSION, '').strip() + parent=self.DETAILS_ROOT).replace(self.RESOURCE_VERSION, '').strip()''' - _table_view_wl = TableViewWorkloads(self.parent, self.locator, self.logger) + _card_view_wl = CardViewWorkloads(self.parent, self.locator, self.logger) + + _card_view_apps = CardViewApplications(self.parent, self.locator, self.logger) _traffic_tab = TrafficView(parent=self.parent, locator=self.locator, logger=self.logger) @@ -2946,10 +2946,9 @@ def get_details(self, load_only=False): _traces_tab = TracesView(parent=self.parent, locator=self.locator, logger=self.logger) return ServiceDetails(name=_name, - created_at=parse_from_rest(_created_at), - created_at_ui=_created_at_ui, + created_at=None, service_type=_type, - resource_version=_resource_version, + resource_version=None, ip=_ip, ports=str(_ports.replace('\n', ' ')), endpoints=_endpoints, @@ -2958,10 +2957,9 @@ def get_details(self, load_only=False): istio_sidecar=self._details_sidecar_text(), labels=self._get_details_labels(), selectors=self._get_details_selectors(), - workloads_number=_table_view_wl.number, - istio_configs_number=self.table_view_istio_config.number, - istio_configs=self.table_view_istio_config.all_items, - workloads=_table_view_wl.all_items, + istio_configs=self.card_view_istio_config.all_items, + workloads=_card_view_wl.all_items, + applictions=_card_view_apps.all_items, traffic_tab=_traffic_tab, inbound_metrics=_inbound_metrics, traces_tab=_traces_tab) @@ -2994,8 +2992,8 @@ def items(self): return _items @property - def table_view_istio_config(self): - return TableViewIstioConfig(self.parent, self.locator, self.logger) + def card_view_istio_config(self): + return CardViewIstioConfig(self.parent, self.locator, self.logger) class ListViewIstioConfig(ListViewAbstract): @@ -3009,7 +3007,7 @@ class ListViewIstioConfig(ListViewAbstract): UPDATE_HANDLER_BUTTON = './/button[text()="Save"]' def __init__(self, parent, locator=None, logger=None): - TableViewAbstract.__init__(self, parent, locator=self.CONFIG_DETAILS_ROOT, logger=logger) + ListViewAbstract.__init__(self, parent, locator=self.CONFIG_DETAILS_ROOT, logger=logger) self._handler_name = TextInput(parent=self, locator=self.CONFIG_INPUT.format('name')) @@ -3044,7 +3042,8 @@ def items(self): return _items -class TableViewAbstract(ViewAbstract): +class CardViewAbstract(ViewAbstract): + SECTION_ID = 'pf-tab-section-0-basic-tabs' SERVICE_DETAILS_ROOT = './/section[contains(@class, "pf-c-page__main-section")]/div' OVERVIEW_DETAILS_ROOT = './/div[contains(@class, "pf-l-grid")]' OVERVIEW_HEADER = SERVICE_DETAILS_ROOT + \ @@ -3056,8 +3055,6 @@ class TableViewAbstract(ViewAbstract): SERVICES_TAB = '//*[contains(@class, "pf-c-tabs__item")]//button[contains(text(), "{}")]' ROOT = '//[contains(@class, "tab-pane") and contains(@class, "active") and \ contains(@class, "in")]' - ROWS = ('//section[@id="{}"]//table[contains(@class, "table")]' - '//tbody//tr') COLUMN = './/td' ROW_BY_NAME = \ '//div[@id="{}"]//table[contains(@class, "table")]//tbody//tr//a[text()="{}"]/../..' @@ -3110,196 +3107,66 @@ def _get_item_status(self, element): locator='.//*[contains(@style, "warning")]')) > 0 return get_validation(_valid, _not_valid, _warning) + def _get_config_type(self, element): + _type = self.browser.text(locator='.//span//span', parent=element) + if _type == 'DR': + return IstioConfigObjectType.DESTINATION_RULE.text + elif _type == 'VS': + return IstioConfigObjectType.VIRTUAL_SERVICE.text + else: + return IstioConfigObjectType.PEER_AUTHENTICATION.text + @property def all_items(self): return self.items -class TableViewAppWorkloads(TableViewAbstract): - ROWS = ('//div[contains(@class, "pf-c-data-list__item-content")]' - '//h3[contains(text(), "Workloads")]/..' - '/ul[contains(@class, "pf-c-list")]/li') - COLUMN = './/a' +class CardViewAppWorkloads(CardViewAbstract): + WORKLOADS_TEXT = 'Workloads' + ROWS = '//h5[contains(text(), "{}")]/../ul/li'.format(WORKLOADS_TEXT) @property def items(self): - _items = [] for el in self.browser.elements(locator=self.ROWS, - parent=self.ROOT): - _value = self.browser.element( - locator=self.COLUMN, parent=el).text + parent=ListViewAbstract.DETAILS_ROOT): + _name = el.text.replace('W', '').strip() + # TODO status # create Workload instance _workload = AppWorkload( - name=_value, - istio_sidecar=self._item_sidecar_icon(el)) - # append this item to the final list - _items.append(_workload) - return _items - - -class TableViewAppServices(TableViewAbstract): - ROWS = ('//div[contains(@class, "pf-c-data-list__item-content")]' - '//h3[contains(text(), "Services")]/..' - '/ul[contains(@class, "pf-c-list")]/li/a') - - @property - def items(self): - - _items = [] - for el in self.browser.elements(locator=self.ROWS, - parent=self.ROOT): - _items.append(el.text) - return _items - - -class TableViewWorkloads(TableViewAbstract): - WLD_TEXT = 'Workloads' - - def open(self): - tab = self.browser.element(locator=self.SERVICES_TAB.format(self.WLD_TEXT), - parent=self.SERVICE_DETAILS_ROOT) - try: - self.browser.click(tab) - finally: - self.browser.click(tab) - wait_to_spinner_disappear(self.browser) - self.browser.wait_for_element(locator='//section[@id="pf-tab-section-0-service-tabs"]', - parent=self.ROOT) - - @property - def number(self): - _wl_text = self.browser.text(locator=self.SERVICES_TAB.format(self.WLD_TEXT), - parent=self.SERVICE_DETAILS_ROOT) - return int(re.search(r'\d+', _wl_text).group()) - - @property - def items(self): - self.open() - - _items = [] - for el in self.browser.elements(locator=self.ROWS.format( - 'pf-tab-section-0-service-tabs'), - parent=self.ROOT): - _columns = list(self.browser.elements(locator=self.COLUMN, parent=el)) - - _name = _columns[0].text.strip() - _type = _columns[1].text.strip() - _created_at_ui = _columns[3].text.strip() - _created_at = self._get_date_tooltip(_columns[3]) - _resource_version = _columns[4].text.strip() - - # workload object creation - _workload = WorkloadDetails( name=_name, - workload_type=_type, - labels=self._get_labels(_columns[2]), - created_at=parse_from_rest(_created_at), - created_at_ui=_created_at_ui, - resource_version=_resource_version) - # append this item to the final list - _items.append(_workload) - return _items - - -class TableViewSourceWorkloads(TableViewAbstract): - WLD_TEXT = 'Source Workloads' - ROWS = '//div[contains(@class, "card-pf")]\ - //div[contains(@class, "row-cards-pf")]\ - //div[contains(@class, "card-pf-body")]' - DEST = './/div[contains(@class, "progress-description")]' - COLUMN = './/li' - - def open(self): - tab = self.browser.element(locator=self.SERVICES_TAB.format(self.WLD_TEXT), - parent=self.SERVICE_DETAILS_ROOT) - try: - self.browser.click(tab) - finally: - self.browser.click(tab) - wait_to_spinner_disappear(self.browser) - self.browser.wait_for_element(locator=self.ROWS, parent=self.ROOT) - - @property - def number(self): - _wl_text = self.browser.text(locator=self.SERVICES_TAB.format(self.WLD_TEXT), - parent=self.SERVICE_DETAILS_ROOT) - return int(re.search(r'\d+', _wl_text).group()) - - @property - def items(self): - self.open() - - _items = [] - for el in self.browser.elements(locator=self.ROWS.format( - 'service-tabs-pane-sources'), - parent=self.ROOT): - _to = self.browser.element(locator=self.DEST, parent=el).text.replace('To:', '').strip() - _columns = list(self.browser.elements(locator=self.COLUMN, parent=el)) - _workloads = [] - for _column in _columns: - _workloads.append(_column.text.strip()) - # source workload object creation - _workload = SourceWorkload( - to=_to, - workloads=_workloads) + istio_sidecar=self._item_sidecar_icon(el)) # append this item to the final list _items.append(_workload) return _items -class TableViewIstioConfig(TableViewAbstract): +class CardViewIstioConfig(CardViewAbstract): CONFIG_TEXT = 'Istio Config' GATEWAYS = '//div[@id="gateways"]//ul[contains(@class, "details")]//li' - TAB_ID = 'pf-tab-section-1-service-tabs' - ROWS = '//section[@id="{}"]//table[contains(@class, "table")]'\ - '//tbody//tr' - ROW = '//section[@id="{}"]//table[contains(@class, "table")]'\ - '//tbody//tr//td//a[text()="{}"]/../..//td[text()="{}"]/..' + SECTION_ROOT = (ListViewAbstract.DETAILS_ROOT + + '//article[@id="IstioConfigCard"]') + ROWS = SECTION_ROOT+'//table//tbody//tr' + ROW = SECTION_ROOT+'//table'\ + '//tbody//tr//td//a[text()="{}"]/..//span[text()="{}"]/../../..' SUBSETS_ROW = '//div[@id="subsets"]//table//tbody//tr' - def open(self): - wait_to_spinner_disappear(self.browser) - tab = self.browser.element(locator=self.SERVICES_TAB.format(self.CONFIG_TEXT), - parent=self.SERVICE_DETAILS_ROOT) - try: - self.browser.click(tab) - finally: - self.browser.click(tab) - wait_to_spinner_disappear(self.browser) - self.browser.wait_for_element(locator='//section[@id="{}"]'.format(self.TAB_ID), - parent=self.ROOT) - - @property - def number(self): - _vs_text = self.browser.text(locator=self.SERVICES_TAB.format(self.CONFIG_TEXT), - parent=self.SERVICE_DETAILS_ROOT) - return int(re.search(r'\d+', _vs_text).group()) - @property def items(self): - self.open() _items = [] - for el in self.browser.elements(locator=self.ROWS.format(self.TAB_ID), - parent=self.ROOT): + for el in self.browser.elements(self.ROWS): _columns = list(self.browser.elements(locator=self.COLUMN, parent=el)) if len(_columns) < 2: # empty row continue - _name = _columns[1].text.strip() - _type = _columns[2].text.strip() - _created_at_ui = _columns[3].text.strip() - _created_at = self._get_date_tooltip(_columns[3]) - _resource_version = _columns[4].text.strip() + _name = re.sub('^[A-Z]{2}', '', _columns[0].text.strip()) + _type = self._get_config_type(_columns[0]) # create IstioConfigRow instance _istio_config = IstioConfigRow( - status=self._get_item_status(_columns[0]), + status=self._get_item_status(_columns[1]), name=_name, - type=_type, - created_at=parse_from_rest(_created_at), - created_at_ui=_created_at_ui, - resource_version=_resource_version) + type=_type) # append this item to the final list _items.append(_istio_config) return _items @@ -3313,15 +3180,10 @@ def get_overview(self, name, config_type): return self._get_peerauth_overview(name) def _get_vs_overview(self, name): - self.open() - - _row = self.browser.element(locator=self.ROW.format( - self.TAB_ID, name, - IstioConfigObjectType.VIRTUAL_SERVICE.text), - parent=self.ROOT) + _row = self.browser.element(self.ROW.format(name, 'VS')) _columns = list(self.browser.elements(locator=self.COLUMN, parent=_row)) - self.browser.click('.//a', parent=_columns[1]) + self.browser.click('./..//a', parent=_columns[1]) wait_to_spinner_disappear(self.browser) _hosts = get_texts_of_elements(self.browser.elements( @@ -3355,15 +3217,10 @@ def _get_vs_overview(self, name): validation_references=_validation_references) def _get_dr_overview(self, name): - self.open() - - _row = self.browser.element(locator=self.ROW.format( - self.TAB_ID, name, - IstioConfigObjectType.DESTINATION_RULE.text), - parent=self.ROOT) + _row = self.browser.element(self.ROW.format(name, 'DR')) _columns = list(self.browser.elements(locator=self.COLUMN, parent=_row)) - self.browser.click('.//a', parent=_columns[1]) + self.browser.click('./..//a', parent=_columns[1]) wait_to_spinner_disappear(self.browser) _host = self.browser.text( @@ -3393,13 +3250,10 @@ def _get_dr_overview(self, name): def _get_peerauth_overview(self, name): self.open() - _row = self.browser.element(locator=self.ROW.format( - self.TAB_ID, name, - IstioConfigObjectType.PEER_AUTHENTICATION.text), - parent=self.ROOT) + _row = self.browser.element(self.ROW.format(name, 'PA')) _columns = list(self.browser.elements(locator=self.COLUMN, parent=_row)) - self.browser.click('.//a', parent=_columns[1]) + self.browser.click('./..//a', parent=_columns[1]) wait_to_spinner_disappear(self.browser) _text = self.browser.text(locator=self.CONFIG_TEXT_LOCATOR, @@ -3411,118 +3265,70 @@ def _get_peerauth_overview(self, name): return IstioConfigDetails(name=name, text=_text) -class TableViewWorkloadIstioConfig(TableViewIstioConfig): - TAB_ID = 'pf-tab-section-2-service-tabs' - - -class TableViewWorkloadPods(TableViewAbstract): - POD_TEXT = 'Pods' - SECTION_ID = 'pf-tab-section-0-service-tabs' - PODS_SECTION_ROOT = '//section[@id="{}"]'.format(SECTION_ID) - TABLE_ROOT = '//table[contains(@class, "pf-c-table")]' - - def open(self): - tab = self.browser.element(locator=self.SERVICES_TAB.format(self.POD_TEXT), - parent=self.SERVICE_DETAILS_ROOT) - try: - self.browser.click(tab) - finally: - self.browser.click(tab) - wait_to_spinner_disappear(self.browser) - self.browser.wait_for_element(locator=self.TABLE_ROOT, parent=self.PODS_SECTION_ROOT) - - @property - def number(self): - _vs_text = self.browser.text(locator=self.SERVICES_TAB.format(self.POD_TEXT), - parent=self.SERVICE_DETAILS_ROOT) - return int(re.search(r'\d+', _vs_text).group()) +class CardViewWorkloadPods(CardViewAbstract): + SECTION_ROOT = (ListViewAbstract.DETAILS_ROOT + + '//article[@id="WorkloadPodsCard"]') + ROWS = '//table//tbody//tr' @property def items(self): - self.open() - self.click_more_labels(parent=self.ROOT) _items = [] - for el in self.browser.elements(locator=self.ROWS.format( - self.SECTION_ID), - parent=self.ROOT): + for el in self.browser.elements(self.SECTION_ROOT+self.ROWS): _columns = list(self.browser.elements(locator=self.COLUMN, parent=el)) if len(_columns) < 2: # empty row continue - _name = _columns[1].text.strip() - _created_at_ui = _columns[2].text.strip() - _created_at = self._get_date_tooltip(_columns[2]) - _created_by = _columns[3].text.strip() - _istio_init_containers = _columns[5].text.strip() - _istio_containers = _columns[6].text.strip() - _phase = _columns[7].text.strip() - + _name = _columns[0].text.strip() + '''TODO pod tooltip values''' _items.append(WorkloadPod( - name=str(_name), - created_at=parse_from_rest(_created_at), - created_at_ui=_created_at_ui, - created_by=_created_by, - labels=self._get_labels(_columns[4]), - istio_init_containers=_istio_init_containers, - istio_containers=_istio_containers, - status=self._get_item_health(el), - phase=_phase)) + name=str(_name).replace('P', '').strip(), + status=self._get_item_health(el))) return _items -class TableViewServices(TableViewAbstract): - SERVICES_TEXT = 'Services' - SECTION_ID = 'pf-tab-section-1-service-tabs' - PODS_SECTION_ROOT = '//section[@id="{}"]'.format(SECTION_ID) - TABLE_ROOT = '//table[contains(@class, "pf-c-table")]' - - def open(self): - tab = self.browser.element(locator=self.SERVICES_TAB.format(self.SERVICES_TEXT), - parent=self.SERVICE_DETAILS_ROOT) - try: - self.browser.click(tab) - finally: - self.browser.click(tab) - wait_to_spinner_disappear(self.browser) - self.browser.wait_for_element(locator=self.TABLE_ROOT, parent=self.PODS_SECTION_ROOT) +class CardViewApplications(CardViewAbstract): + APPLICATIONS_TEXT = 'Applications' + ROWS = '//h5[contains(text(), "{}")]/../ul/li'.format(APPLICATIONS_TEXT) @property - def number(self): - _vs_text = self.browser.text(locator=self.SERVICES_TAB.format(self.SERVICES_TEXT), - parent=self.SERVICE_DETAILS_ROOT) - return int(re.search(r'\d+', _vs_text).group()) + def items(self): + _items = [] + for el in self.browser.elements(locator=self.ROWS, + parent=ListViewAbstract.DETAILS_ROOT): + _name = el.text.replace('A', '').strip() + + _items.append(_name) + return _items + + +class CardViewWorkloads(CardViewAbstract): + WORKLOADS_TEXT = 'Workloads' + ROWS = '//h5[contains(text(), "{}")]/../div//ul//li'.format(WORKLOADS_TEXT) @property def items(self): - self.open() - self.click_more_labels(parent=self.ROOT) + _items = [] + for el in self.browser.elements(locator=self.ROWS, + parent=ListViewAbstract.DETAILS_ROOT): + _name = el.text.replace('W', '').strip() + + _items.append(_name) + return _items + +class CardViewServices(CardViewAbstract): + SERVICES_TEXT = 'Services' + ROWS = '//h5[contains(text(), "{}")]/../ul/li'.format(SERVICES_TEXT) + + @property + def items(self): _items = [] - for el in self.browser.elements(locator=self.ROWS.format( - self.SECTION_ID), - parent=self.ROOT): - _columns = list(self.browser.elements(locator=self.COLUMN, parent=el)) - if len(_columns) < 2: - # empty row - continue - _name = _columns[0].text.strip() - _created_at_ui = _columns[1].text.strip() - _created_at = self._get_date_tooltip(_columns[1]) - _type = _columns[2].text.strip() - _resource_version = _columns[4].text.strip() - _ip = _columns[5].text.strip() - _ports = _columns[6].text.strip() - - _items.append(ServiceDetails( - name=_name, - created_at=parse_from_rest(_created_at), - created_at_ui=_created_at_ui, - service_type=str(_type), - labels=self._get_labels(_columns[3]), - resource_version=str(_resource_version), - ip=str(_ip), - ports=str(_ports.replace('\n', ' ')))) + for el in self.browser.elements(locator=self.ROWS, + parent=ListViewAbstract.DETAILS_ROOT): + _name = el.text.replace('S', '').strip() + + _items.append(_name) return _items diff --git a/kiali_qe/entities/__init__.py b/kiali_qe/entities/__init__.py index 5594d8c4..ff4f01c8 100644 --- a/kiali_qe/entities/__init__.py +++ b/kiali_qe/entities/__init__.py @@ -170,7 +170,7 @@ def __str__(self): self.validation, self.link) def __repr__(self): - return "{}({}, {}, {}, {})".format( + return "{}({}, {})".format( type(self).__name__, repr(self.validation), repr(self.link)) def is_equal(self, other): diff --git a/kiali_qe/entities/istio_config.py b/kiali_qe/entities/istio_config.py index cbc11fa3..d3d6ac36 100644 --- a/kiali_qe/entities/istio_config.py +++ b/kiali_qe/entities/istio_config.py @@ -58,7 +58,7 @@ def __str__(self): self.name, self.text, self.validation) def __repr__(self): - return "{}({}, {})".format( + return "{}({}, {}, {})".format( type(self).__name__, repr(self.name), repr(self.text), repr(self.validation)) def __eq__(self, other): diff --git a/kiali_qe/entities/overview.py b/kiali_qe/entities/overview.py index 6051e43a..1bf01e63 100644 --- a/kiali_qe/entities/overview.py +++ b/kiali_qe/entities/overview.py @@ -29,7 +29,7 @@ def __str__(self): self.tls_type) def __repr__(self): - return "{}({}, {}, {}, {}, {}, {}, {}, {})".format( + return "{}({}, {}, {}, {}, {}, {}, {}, {}, {})".format( type(self).__name__, repr(self.overview_type), repr(self.namespace), repr(self.items), repr(self.healthy), repr(self.unhealthy), repr(self.degraded), repr(self.na), repr(self.idle), diff --git a/kiali_qe/entities/service.py b/kiali_qe/entities/service.py index 18cda16d..170a863d 100644 --- a/kiali_qe/entities/service.py +++ b/kiali_qe/entities/service.py @@ -127,7 +127,7 @@ class ServiceDetails(EntityBase): health: health status """ - def __init__(self, name, created_at, created_at_ui, service_type, + def __init__(self, name, created_at, service_type, resource_version, ip, ports, labels={}, selectors={}, istio_sidecar=False, health=None, service_status=None, @@ -139,7 +139,6 @@ def __init__(self, name, created_at, created_at_ui, service_type, self.istio_sidecar = istio_sidecar self.health = health self.created_at = created_at - self.created_at_ui = created_at_ui self.service_type = service_type self.resource_version = resource_version self.ip = ip @@ -199,11 +198,10 @@ def is_equal(self, other, advanced_check=True): return False if self.created_at and other.created_at and self.created_at != other.created_at: return False - if self.created_at_ui != other.created_at_ui: + if self.service_type and other.service_type and self.service_type != other.service_type: return False - if self.service_type != other.service_type: - return False - if self.resource_version != other.resource_version: + if self.resource_version and other.resource_version and \ + self.resource_version != other.resource_version: return False if self.ip != other.ip: return False @@ -242,14 +240,13 @@ class VirtualService(EntityBase): resource_version: resource version """ - def __init__(self, status, name, created_at, created_at_ui, + def __init__(self, status, name, created_at, resource_version, protocol_route=None, hosts=[], weights=[], gateways=[]): if name is None: raise KeyError("'name' should not be 'None'") self.name = name self.created_at = created_at - self.created_at_ui = created_at_ui self.resource_version = resource_version self.status = status self.protocol_route = protocol_route @@ -264,7 +261,7 @@ def __str__(self): self.hosts, self.weights) def __repr__(self): - return "{}({}, {}, {}, {}, {})".format( + return "{}({}, {}, {}, {}, {}, {})".format( type(self).__name__, repr(self.name), repr(self.status), @@ -285,9 +282,8 @@ def is_equal(self, other, advanced_check=True): return False if self.created_at and other.created_at and self.created_at != other.created_at: return False - if self.created_at_ui != other.created_at_ui: - return False - if self.resource_version != other.resource_version: + if self.resource_version and other.resource_version and \ + self.resource_version != other.resource_version: return False # advanced check if not advanced_check: @@ -373,7 +369,7 @@ def __str__(self): self.text, self.link) def __repr__(self): - return "{}({}, {}, {})".format( + return "{}({}, {})".format( type(self).__name__, repr(self.text), repr(self.link)) @@ -476,10 +472,9 @@ def __init__(self, status, name, host, subsets): self.status = status def __str__(self): - return 'name:{}, status:{}, host:{}, subsets:{}, '\ - 'created_at:{}, resource_version:{}'.format( - self.name, self.status, self.host, - self.subsets) + return 'name:{}, status:{}, host:{}, subsets:{}'.format( + self.name, self.status, self.host, + self.subsets) def __repr__(self): return "{}({}, {}, {}, {})".format( @@ -576,7 +571,7 @@ class DestinationRule(EntityBase): """ def __init__(self, status, name, host, traffic_policy, subsets, - created_at, created_at_ui, resource_version): + created_at, resource_version): if name is None: raise KeyError("'name' should not be 'None'") self.name = name @@ -584,7 +579,6 @@ def __init__(self, status, name, host, traffic_policy, subsets, self.traffic_policy = traffic_policy self.subsets = subsets self.created_at = created_at - self.created_at_ui = created_at_ui self.resource_version = resource_version self.status = status @@ -596,7 +590,7 @@ def __str__(self): self.created_at, self.resource_version) def __repr__(self): - return "{}({}, {}, {}, {}, {})".format( + return "{}({}, {}, {}, {}, {}, {}, {})".format( type(self).__name__, repr(self.name), repr(self.status), repr(self.host), @@ -620,9 +614,8 @@ def is_equal(self, other, advanced_check=True): return False if self.created_at and other.created_at and self.created_at != other.created_at: return False - if self.created_at_ui != other.created_at_ui: - return False - if self.resource_version != other.resource_version: + if self.resource_version and other.resource_version and \ + self.resource_version != other.resource_version: return False if self.traffic_policy != other.traffic_policy: return False @@ -643,39 +636,26 @@ class IstioConfigRow(EntityBase): Args: status: the validation status of config name: name of the config - type: the config type - created_at: creation datetime - resource_version: resource version """ - def __init__(self, status, name, type, - created_at, created_at_ui, resource_version): + def __init__(self, status, name, type): if name is None: raise KeyError("'name' should not be 'None'") self.name = name - self.type = type - self.created_at = created_at - self.created_at_ui = created_at_ui - self.resource_version = resource_version self.status = status + self.type = type def __str__(self): - return 'name:{}, status:{}, type:{}, '\ - 'created_at:{}, resource_version:{}'.format( - self.name, self.status, self.type, - self.created_at, self.resource_version) + return 'name:{}, status:{}, type: {}'.format( + self.name, self.status, self.type) def __repr__(self): - return "{}({}, {}, {}, {}, {})".format( + return "{}({}, {}, {})".format( type(self).__name__, - repr(self.name), repr(self.status), - repr(self.host), - repr(self.traffic_policy), repr(self.subsets), - repr(self.created_at), repr(self.resource_version)) + repr(self.name), repr(self.status), repr(self.type)) def __hash__(self): - return (hash(self.name) ^ hash(self.type) ^ hash(self.created_at) - ^ hash(self.resource_version)) + return (hash(self.name) ^ hash(self.status) ^ hash(self.type)) def __eq__(self, other): return self.is_equal(other, advanced_check=True) @@ -688,12 +668,6 @@ def is_equal(self, other, advanced_check=True): return False if self.type != other.type: return False - if self.created_at and other.created_at and self.created_at != other.created_at: - return False - if self.created_at_ui != other.created_at_ui: - return False - if self.resource_version != other.resource_version: - return False # advanced check if not advanced_check: return True diff --git a/kiali_qe/entities/workload.py b/kiali_qe/entities/workload.py index cc378d1d..edd02891 100644 --- a/kiali_qe/entities/workload.py +++ b/kiali_qe/entities/workload.py @@ -63,20 +63,19 @@ def is_equal(self, other, advanced_check=True): class WorkloadDetails(EntityBase): - def __init__(self, name, workload_type, created_at, created_at_ui, resource_version, - istio_sidecar=False, sidecar_injection=None, + def __init__(self, name, workload_type, created_at, resource_version, + istio_sidecar=False, missing_sidecar=False, health=None, workload_status=None, icon=None, **kwargs): if name is None: raise KeyError("'name' should not be 'None'") self.name = name self.workload_type = workload_type self.istio_sidecar = istio_sidecar - self.sidecar_injection = sidecar_injection + self.missing_sidecar = missing_sidecar self.health = health self.workload_status = workload_status self.icon = icon self.created_at = created_at - self.created_at_ui = created_at_ui self.resource_version = resource_version self.labels = kwargs['labels']\ if 'labels' in kwargs else {} @@ -86,10 +85,6 @@ def __init__(self, name, workload_type, created_at, created_at_ui, resource_vers if 'availableReplicas' in kwargs else None self.unavailableReplicas = kwargs['unavailableReplicas']\ if 'unavailableReplicas' in kwargs else None - self.pods_number = kwargs['pods_number']\ - if 'pods_number' in kwargs else None - self.services_number = kwargs['services_number']\ - if 'services_number' in kwargs else None self.services = kwargs['services']\ if 'services' in kwargs else None self.traffic_tab = kwargs['traffic_tab']\ @@ -102,8 +97,6 @@ def __init__(self, name, workload_type, created_at, created_at_ui, resource_vers if 'inbound_metrics' in kwargs else None self.outbound_metrics = kwargs['outbound_metrics']\ if 'outbound_metrics' in kwargs else None - self.istio_configs_number = kwargs['istio_configs_number']\ - if 'istio_configs_number' in kwargs else None self.istio_configs = kwargs['istio_configs']\ if 'istio_configs' in kwargs else None self.traces_tab = kwargs['traces_tab']\ @@ -138,13 +131,12 @@ def is_equal(self, other, advanced_check=True): return False if self.name != other.name: return False - if self.workload_type != other.workload_type: + if self.workload_type and other.workload_type and self.workload_type != other.workload_type: return False if self.created_at and other.created_at and self.created_at != other.created_at: return False - if self.created_at_ui != other.created_at_ui: - return False - if self.resource_version != other.resource_version: + if self.resource_version and other.resource_version and \ + self.resource_version != other.resource_version: return False if self.labels != other.labels: return False @@ -164,41 +156,25 @@ def is_equal(self, other, advanced_check=True): class WorkloadPod(EntityBase): - def __init__(self, name, created_at, created_at_ui, created_by, labels={}, - istio_init_containers=None, istio_containers=None, status=None, - phase=None, podIP=None): + def __init__(self, name, status=None, podIP=None): self.name = name - self.created_at = created_at - self.created_by = created_by - self.created_at_ui = created_at_ui - self.labels = labels - self.istio_init_containers = istio_init_containers - self.istio_containers = istio_containers self.status = status - self.phase = phase self.podIP = podIP def __str__(self): - return 'name:{}, created_at:{}, created_by:{}, labels: {}\ - istio_init_containers:{}, istio_containers:{}\ - status:{}, phase:{}'.format( - self.name, self.created_at, self.created_by, self.labels, - self.istio_init_containers, self.istio_containers, - self.status, self.phase) + return 'name:{}, status:{}'.format( + self.name, self.status) def __repr__(self): - return "{}({}, {}, {}, {}, {}, {}, {}, {}, {})".format( + return "{}({}, {}".format( type(self).__name__, repr(self.name), - repr(self.created_at), repr(self.created_by), - repr(self.labels), - repr(self.istio_init_containers), repr(self.istio_containers), - repr(self.status), repr(self.phase)) + repr(self.status)) def __eq__(self, other): return self.is_equal(other, advanced_check=True) def __hash__(self): - return (hash(self.name) ^ hash(self.created_at) ^ hash(self.created_by)) + return (hash(self.name) ^ hash(self.status)) def is_equal(self, other, advanced_check=True): # basic check @@ -206,20 +182,6 @@ def is_equal(self, other, advanced_check=True): return False if self.name != other.name: return False - if self.created_at and other.created_at and self.created_at != other.created_at: - return False - if self.created_at_ui != other.created_at_ui: - return False - if self.created_by != other.created_by: - return False - if self.labels != other.labels: - return False - if self.istio_init_containers != other.istio_init_containers: - return False - if self.istio_containers != other.istio_containers: - return False - if self.phase != other.phase: - return False # advanced check if advanced_check: if self.status != other.status: @@ -238,7 +200,7 @@ def __str__(self): self.workload_status, self.requests) def __repr__(self): - return "{}({}, {}, {})".format( + return "{}({}, {})".format( type(self).__name__, repr(self.workload_status), repr(self.requests)) diff --git a/kiali_qe/rest/kiali_api.py b/kiali_qe/rest/kiali_api.py index dcabc886..8acfb047 100644 --- a/kiali_qe/rest/kiali_api.py +++ b/kiali_qe/rest/kiali_api.py @@ -1,7 +1,5 @@ import json -from itertools import groupby - from selenium.common.exceptions import NoSuchElementException from kiali.client import KialiClient from kiali_qe.components.enums import ( @@ -40,7 +38,7 @@ ) from kiali_qe.entities.overview import Overview from kiali_qe.utils import to_linear_string, dict_to_params -from kiali_qe.utils.date import parse_from_rest, from_rest_to_ui +from kiali_qe.utils.date import from_rest_to_ui from kiali_qe.utils.log import logger @@ -459,8 +457,7 @@ def service_details(self, namespace, service_name): name=_wl_data['name'], workload_type=_wl_data['type'], labels=self.get_labels(_wl_data), - created_at=parse_from_rest(_wl_data['createdAt']), - created_at_ui=from_rest_to_ui(_wl_data['createdAt']), + created_at=from_rest_to_ui(_wl_data['createdAt']), resource_version=_wl_data['resourceVersion'])) source_workloads = [] # TODO better way to find Traffic @@ -506,8 +503,7 @@ def service_details(self, namespace, service_name): virtual_services.append(VirtualService( status=_validation, name=_vs_data['metadata']['name'], - created_at=parse_from_rest(_vs_data['metadata']['creationTimestamp']), - created_at_ui=from_rest_to_ui(_vs_data['metadata']['creationTimestamp']), + created_at=from_rest_to_ui(_vs_data['metadata']['creationTimestamp']), resource_version=_vs_data['metadata']['resourceVersion'], protocol_route=_protocol_route, hosts=_vs_data['spec']['hosts'], @@ -547,8 +543,7 @@ def service_details(self, namespace, service_name): host=_dr_data['spec']['host'], traffic_policy=_traffic_policy if _traffic_policy else '', subsets=_dr_subsets, - created_at=parse_from_rest(_dr_data['metadata']['creationTimestamp']), - created_at_ui=from_rest_to_ui(_dr_data['metadata']['creationTimestamp']), + created_at=from_rest_to_ui(_dr_data['metadata']['creationTimestamp']), resource_version=_dr_data['metadata']['resourceVersion'])) # It also requires IstioConfig type of objects in several testcases @@ -560,9 +555,9 @@ def service_details(self, namespace, service_name): _ports = '' for _port in _service_data['service']['ports']: - _ports += '{}{} ({}) '.format(_port['protocol'], - ' ' + _port['name'] if _port['name'] != '' else '', - _port['port']) + _ports += '{}{}/{} '.format(_port['name'] + ' ' if _port['name'] != '' else '', + _port['port'], + _port['protocol']) endpoints = [] if _service_data['endpoints']: for _endpoint in _service_data['endpoints'][0]['addresses']: @@ -579,9 +574,7 @@ def service_details(self, namespace, service_name): _service = ServiceDetails( name=_service_data['service']['name'], istio_sidecar=_service_rest.istio_sidecar, - created_at=parse_from_rest( - _service_data['service']['createdAt']), - created_at_ui=from_rest_to_ui( + created_at=from_rest_to_ui( _service_data['service']['createdAt']), resource_version=_service_data['service']['resourceVersion'], service_type=_service_data['service']['type'], @@ -619,22 +612,7 @@ def workload_details(self, namespace, workload_name, workload_type): _services = [] if _workload_data['services']: for _ws_data in _workload_data['services']: - _ports = '' - for _port in _ws_data['ports']: - _ports += '{}{} ({}) '.format(_port['protocol'], - ' ' + _port['name'] - if _port['name'] != '' else '', - _port['port']) - _services.append(ServiceDetails( - name=_ws_data['name'], - created_at=parse_from_rest(_ws_data['createdAt']), - created_at_ui=from_rest_to_ui(_ws_data['createdAt']), - service_type=_ws_data['type'], - ip=_ws_data['ip'], - ports=_ports.strip(), - labels=self.get_labels(_ws_data), - selectors=self.get_selectors(_ws_data), - resource_version=_ws_data['resourceVersion'])) + _services.append(_ws_data['name']) _destination_services = [] # TODO find a better way to take Traffic if 'destinationServices' in _workload_data: @@ -646,6 +624,7 @@ def workload_details(self, namespace, workload_name, workload_type): _all_pods = [] if _workload_data['pods']: for _pod_data in _workload_data['pods']: + ''' TODO read from tooltip in UI and then activate this code _istio_init_containers = '' _istio_containers = '' if _pod_data['istioContainers']: @@ -653,54 +632,12 @@ def workload_details(self, namespace, workload_name, workload_type): if _pod_data['istioInitContainers']: _istio_init_containers = _pod_data['istioInitContainers'][0]['image'] _created_by = '{} ({})'.format(_pod_data['createdBy'][0]['name'], - _pod_data['createdBy'][0]['kind']) + _pod_data['createdBy'][0]['kind'])''' _pod = WorkloadPod( name=str(_pod_data['name']), - created_at=parse_from_rest(_pod_data['createdAt']), - created_at_ui=from_rest_to_ui(_pod_data['createdAt']), - created_by=_created_by, - labels=self.get_labels(_pod_data), - istio_init_containers=str(_istio_init_containers), - istio_containers=str(_istio_containers), - status=self.get_pod_status(_workload_data['istioSidecar'], _pod_data), - phase=_pod_data['status']) + status=self.get_pod_status(_workload_data['istioSidecar'], _pod_data)) _all_pods.append(_pod) - def get_created_by(nodeid): - return nodeid.created_by - - _pods = [] - # group by created_by fielts, as it is shown grouped in UI - for _created_by, _grouped_pods in groupby(_all_pods, key=get_created_by): - _workload_pods = [] - for _grouped_pod in _grouped_pods: - _workload_pods.append(_grouped_pod) - if len(_workload_pods) > 1: - _pod = WorkloadPod( - name='{}... ({} replicas)'.format(_pod.name[:-5], len(_workload_pods)), - created_at='{} and {}'.format( - _pod.created_at, _workload_pods[len(_workload_pods)-1].created_at), - created_at_ui=_pod.created_at_ui, - created_by=_created_by, - labels=_workload_pods[0].labels, - istio_init_containers=_workload_pods[0].istio_init_containers, - istio_containers=_workload_pods[0].istio_containers, - status=_workload_pods[0].status, - phase=_workload_pods[0].phase) - _pods.append(_pod) - elif len(_workload_pods) == 1: - _pod = WorkloadPod( - name=_workload_pods[0].name, - created_at=_workload_pods[0].created_at, - created_at_ui=_workload_pods[0].created_at_ui, - created_by=_created_by, - labels=_workload_pods[0].labels, - istio_init_containers=_workload_pods[0].istio_init_containers, - istio_containers=_workload_pods[0].istio_containers, - status=_workload_pods[0].status, - phase=_workload_pods[0].phase) - _pods.append(_pod) - _workload_health = self.get_workload_health( namespace=namespace, workload_name=_workload_data['name']) @@ -714,20 +651,16 @@ def get_created_by(nodeid): name=_workload_data['name'], istio_sidecar=_workload_rest.istio_sidecar, workload_type=_workload_data['type'], - created_at=parse_from_rest(_workload_data['createdAt']), - created_at_ui=from_rest_to_ui(_workload_data['createdAt']), + created_at=from_rest_to_ui(_workload_data['createdAt']), resource_version=_workload_data['resourceVersion'], health=_workload_health.is_healthy() if _workload_health else None, workload_status=_workload_health, icon=self.get_icon_type(_workload_data), labels=_labels, - pods_number=len(_pods), - services_number=len(_services), traffic=_destination_services, - pods=_pods, + pods=_all_pods, services=_services, - istio_configs=_config_list, - istio_configs_number=len(_config_list)) + istio_configs=_config_list) return _workload def application_details(self, namespace, application_name): diff --git a/kiali_qe/rest/openshift_api.py b/kiali_qe/rest/openshift_api.py index e70b2ebb..d18656a2 100644 --- a/kiali_qe/rest/openshift_api.py +++ b/kiali_qe/rest/openshift_api.py @@ -23,7 +23,7 @@ ApplicationHealth ) from kiali_qe.utils import dict_contains, to_linear_string -from kiali_qe.utils.date import parse_from_rest, from_rest_to_ui +from kiali_qe.utils.date import from_rest_to_ui from kiali_qe.utils.log import logger @@ -485,17 +485,6 @@ def get_workload_pods(self, namespace, workload_name): if self._get_workload_name(_item.metadata) == workload_name: _filtered_items.append(WorkloadPod( name=_item.metadata.name, - created_at=parse_from_rest( - _item.metadata.creationTimestamp), - created_at_ui=from_rest_to_ui( - _item.metadata.creationTimestamp), - created_by='{} ({})'.format( - _item.metadata.ownerReferences[0].name, - _item.metadata.ownerReferences[0].kind), - labels=self._get_labels(_item), - istio_init_containers=self._get_initcontainer_image(_item), - istio_containers=self._get_initcontainer_image(_item), - phase=_item.status.phase, podIP=_item.status.podIP)) return _filtered_items @@ -545,19 +534,16 @@ def service_details(self, namespace, service_name, skip_workloads=True): _response = self._service.get(namespace=namespace, name=service_name) _ports = '' for _port in _response.spec.ports: - _ports += '{}{} ({}) '.format(_port['protocol'], - ' ' + _port['name'] - if _port['name'] and _port['name'] != '' - else '', - _port['port']) + _ports += '{}{}/{} '.format(_port['name'] + ' ' if _port['name'] and _port['name'] != '' + else '', + _port['port'], + _port['protocol']) _labels = dict(_response.metadata.labels if _response.metadata.labels else {}) _service = ServiceDetails( namespace=_response.metadata.namespace, name=_response.metadata.name, istio_sidecar=None, - created_at=parse_from_rest( - _response.metadata.creationTimestamp), - created_at_ui=from_rest_to_ui( + created_at=from_rest_to_ui( _response.metadata.creationTimestamp), resource_version=_response.metadata.resourceVersion, service_type=_response.spec.type, @@ -660,9 +646,7 @@ def workload_details(self, namespace, workload_name, workload_type): _workload = WorkloadDetails( workload_type=_response.kind, name=_response.metadata.name, - created_at=parse_from_rest( - _response.metadata.creationTimestamp), - created_at_ui=from_rest_to_ui( + created_at=from_rest_to_ui( _response.metadata.creationTimestamp), resource_version=_response.metadata.resourceVersion, istio_sidecar=None, diff --git a/kiali_qe/tests/__init__.py b/kiali_qe/tests/__init__.py index 4be88fa9..79a1c917 100644 --- a/kiali_qe/tests/__init__.py +++ b/kiali_qe/tests/__init__.py @@ -529,8 +529,6 @@ def assert_graph_overview(self, name, namespace): self.browser.execute_script("history.back();") def assert_istio_configs(self, object_ui, object_rest, object_oc, namespace): - assert object_ui.istio_configs_number == len(object_ui.istio_configs), \ - 'Config tab\'s number should be equal to items' assert len(object_rest.istio_configs) == len(object_ui.istio_configs), \ 'UI configs should be equal to REST configs items' assert len(object_rest.istio_configs) == len(object_oc.istio_configs), \ @@ -557,7 +555,7 @@ def assert_istio_configs(self, object_ui, object_rest, object_oc, namespace): if not found: assert found, 'Config {} not found in OC {}'.format(istio_config_ui, istio_config_oc) - config_overview_ui = self.page.content.table_view_istio_config.get_overview( + config_overview_ui = self.page.content.card_view_istio_config.get_overview( istio_config_ui.name, istio_config_ui.type) config_details_oc = self.openshift_client.istio_config_details( @@ -945,12 +943,12 @@ def _prepare_load_details_page(self, name, namespace): self.apply_filters(filters=[ {'name': ApplicationsPageFilter.APP_NAME.text, 'value': name}]) - def load_details_page(self, name, namespace, force_refresh, load_only=False): + def load_details_page(self, name, namespace, force_refresh=False, load_only=False): logger.debug('Loading details page for application: {}'.format(name)) if not self.is_in_details_page(name, namespace): self._prepare_load_details_page(name, namespace) self.open(name, namespace, force_refresh) - self.browser.wait_for_element(locator='//*[contains(text(), "Overall Health")]') + self.browser.wait_for_element(locator='//*[contains(., "Application")]') return self.page.content.get_details(load_only) def assert_random_details(self, namespaces=[], filters=[], force_refresh=False): @@ -997,7 +995,7 @@ def assert_details(self, name, namespace, check_metrics=False, force_refresh=Fal advanced_check=True), \ 'Application UI {} not equal to REST {}'\ .format(application_details_ui, application_details_rest) - + '''TODO read health tooltip values if application_details_ui.application_status: assert application_details_ui.application_status.is_healthy() == \ application_details_ui.health, \ @@ -1013,7 +1011,7 @@ def assert_details(self, name, namespace, check_metrics=False, force_refresh=Fal .format( application_details_ui.application_status.deployment_statuses, application_details_oc.application_status.deployment_statuses, - application_details_ui.name) + application_details_ui.name)''' for workload_ui in application_details_ui.workloads: found = False for workload_rest in application_details_rest.workloads: @@ -1197,12 +1195,12 @@ def _prepare_load_details_page(self, name, namespace): self.apply_filters(filters=[ {'name': WorkloadsPageFilter.WORKLOAD_NAME.text, 'value': name}]) - def load_details_page(self, name, namespace, force_refresh, load_only=False): + def load_details_page(self, name, namespace, force_refresh=False, load_only=False): logger.debug('Loading details page for workload: {}'.format(name)) if not self.is_in_details_page(name, namespace): self._prepare_load_details_page(name, namespace) self.open(name, namespace, force_refresh) - self.browser.wait_for_element(locator='//*[contains(., "Workload Properties")]') + self.browser.wait_for_element(locator='//*[contains(., "Workload")]') return self.page.content.get_details(load_only) def assert_random_details(self, namespaces=[], filters=[], @@ -1233,8 +1231,6 @@ def assert_details(self, name, namespace, workload_type, check_metrics=False, workload_details_ui = self.load_details_page(name, namespace, force_refresh) assert workload_details_ui assert name == workload_details_ui.name - assert workload_type == workload_details_ui.workload_type, \ - '{} and {} are not equal'.format(workload_type, workload_details_ui.workload_type) # get workload detals from rest workload_details_rest = self.kiali_client.workload_details( namespace=namespace, @@ -1258,10 +1254,6 @@ def assert_details(self, name, namespace, workload_type, check_metrics=False, advanced_check=False), \ 'Workload UI {} not equal to OC {}'\ .format(workload_details_ui, workload_details_oc) - if workload_details_ui.pods_number != workload_details_rest.pods_number: - return False - if workload_details_ui.services_number != workload_details_rest.services_number: - return False if workload_details_ui.workload_status: assert workload_details_ui.workload_status.is_healthy() == \ workload_details_ui.health, \ @@ -1293,8 +1285,7 @@ def assert_details(self, name, namespace, workload_type, check_metrics=False, for service_ui in workload_details_ui.services: found = False for service_rest in workload_details_rest.services: - if service_ui.is_equal(service_rest, - advanced_check=True): + if service_ui == service_rest: found = True break if not found: @@ -1477,21 +1468,21 @@ def test_disable_enable_delete_auto_injection(self, name, namespace): if self.page.actions.is_disable_auto_injection_visible(): self.page.actions.select(OverviewInjectionLinks.DISABLE_AUTO_INJECTION.text) self.page.page_refresh() - assert 'false' == self.page.content._details_sidecar_injection_text() + assert self.page.content._details_missing_sidecar() assert self.page.actions.is_enable_auto_injection_visible() assert self.page.actions.is_remove_auto_injection_visible() assert not self.page.actions.is_disable_auto_injection_visible() elif self.page.actions.is_remove_auto_injection_visible(): self.page.actions.select(OverviewInjectionLinks.REMOVE_AUTO_INJECTION.text) self.page.page_refresh() - assert not self.page.content._details_sidecar_injection_text() + assert self.page.content._details_missing_sidecar() assert self.page.actions.is_enable_auto_injection_visible() assert not self.page.actions.is_remove_auto_injection_visible() assert not self.page.actions.is_disable_auto_injection_visible() elif self.page.actions.is_enable_auto_injection_visible(): self.page.actions.select(OverviewInjectionLinks.ENABLE_AUTO_INJECTION.text) self.page.page_refresh() - assert 'true' == self.page.content._details_sidecar_injection_text() + assert self.page.content._details_missing_sidecar() assert not self.page.actions.is_enable_auto_injection_visible() assert self.page.actions.is_remove_auto_injection_visible() assert self.page.actions.is_disable_auto_injection_visible() @@ -1516,13 +1507,12 @@ def _prepare_load_details_page(self, name, namespace): self.apply_filters(filters=[ {'name': ServicesPageFilter.SERVICE_NAME.text, 'value': name}]) - def load_details_page(self, name, namespace, force_refresh, load_only=False): + def load_details_page(self, name, namespace, force_refresh=False, load_only=False): logger.debug('Loading details page for service: {}'.format(name)) if not self.is_in_details_page(name, namespace): self._prepare_load_details_page(name, namespace) self.open(name, namespace, force_refresh) - self.browser.wait_for_element(locator='//*[contains(text(), "Overall Health")]', - parent='//*[@id="health"]') + self.browser.wait_for_element(locator='//*[contains(., "Service")]') return self.page.content.get_details(load_only) def assert_random_details(self, namespaces=[], filters=[], force_refresh=False): @@ -1571,14 +1561,14 @@ def assert_details(self, name, namespace, check_metrics=False, advanced_check=False), \ 'Service UI {} not equal to OC {}'\ .format(service_details_ui, service_details_oc) - assert service_details_ui.workloads_number\ + assert len(service_details_ui.workloads)\ == len(service_details_rest.workloads) - assert service_details_ui.istio_configs_number\ + assert len(service_details_ui.istio_configs)\ == len(service_details_rest.istio_configs) - assert service_details_ui.workloads_number\ - == len(service_details_rest.workloads) - assert service_details_ui.istio_configs_number\ - == len(service_details_ui.istio_configs) + assert len(service_details_ui.workloads)\ + == len(service_details_oc.workloads) + assert len(service_details_ui.istio_configs)\ + == len(service_details_oc.istio_configs) if service_details_ui.service_status: assert service_details_ui.service_status.is_healthy() == \ @@ -1592,7 +1582,7 @@ def assert_details(self, name, namespace, check_metrics=False, for workload_ui in service_details_ui.workloads: found = False for workload_rest in service_details_rest.workloads: - if workload_ui.is_equal(workload_rest, advanced_check=True): + if workload_ui == workload_rest.name: found = True break if not found: @@ -1600,7 +1590,7 @@ def assert_details(self, name, namespace, check_metrics=False, workload_rest) found = False for workload_oc in service_details_oc.workloads: - if workload_ui.is_equal(workload_oc, advanced_check=True): + if workload_ui == workload_oc.name: found = True break if not found: @@ -2027,7 +2017,7 @@ def __init__(self, kiali_client, openshift_client, browser): openshift_client=openshift_client, page=IstioConfigPage(browser)) self.browser = browser - def _prepare_load_details_page(self, name, namespace): + def _prepare_load_details_page(self, name, namespace, object_type=None): # load the page first self.page.load(force_load=True) # apply namespace @@ -2035,11 +2025,15 @@ def _prepare_load_details_page(self, name, namespace): # apply filters self.apply_filters(filters=[ {'name': IstioConfigPageFilter.ISTIO_NAME.text, 'value': name}]) + if object_type: + self.apply_filters(filters=[ + {'name': IstioConfigPageFilter.ISTIO_TYPE.text, 'value': object_type}]) - def load_details_page(self, name, namespace, force_refresh, load_only=False): + def load_details_page(self, name, namespace, object_type=None, + force_refresh=False, load_only=False): logger.debug('Loading details page for istio config: {}'.format(name)) if not self.is_in_details_page(name, namespace): - self._prepare_load_details_page(name, namespace) + self._prepare_load_details_page(name, namespace, object_type) wait_to_spinner_disappear(self.browser) self.open(name, namespace, force_refresh) self.browser.wait_for_element(locator='//button[contains(., "YAML")]', @@ -2126,7 +2120,8 @@ def assert_details(self, name, object_type, logger.debug('Asserting details for: {}, in namespace: {}'.format(name, namespace)) # load config details page - config_details_ui = self.load_details_page(name, namespace, force_refresh=False) + config_details_ui = self.load_details_page(name, namespace, object_type, + force_refresh=False) assert config_details_ui assert name == config_details_ui.name assert config_details_ui.text @@ -2355,9 +2350,9 @@ def test_requestauth_create(self, name, namespaces, expected_created=True, for _key, _value in jwt_rules.items(): assert '\"{}\": \"{}\"'.format(_key, _value) in config_details_rest.text - def delete_istio_config(self, name, namespace=None): + def delete_istio_config(self, name, object_type, namespace=None): logger.debug('Deleting istio config: {}, from namespace: {}'.format(name, namespace)) - self.load_details_page(name, namespace, force_refresh=False, load_only=True) + self.load_details_page(name, namespace, object_type, force_refresh=False, load_only=True) # TODO: wait for all notification boxes to disappear, those are blocking the button time.sleep(10) self.page.actions.select('Delete') diff --git a/kiali_qe/tests/test_istio_objects_crud.py b/kiali_qe/tests/test_istio_objects_crud.py index 0269b162..64d45677 100644 --- a/kiali_qe/tests/test_istio_objects_crud.py +++ b/kiali_qe/tests/test_istio_objects_crud.py @@ -611,9 +611,10 @@ def _istio_config_list(kiali_client, name, namespace=BOOKINFO_1): return kiali_client.istio_config_list(namespaces=[namespace], config_names=[name]) -def _ui_istio_config_delete(tests, name, namespace=BOOKINFO_1): +def _ui_istio_config_delete(tests, name, object_type, namespace=BOOKINFO_1): tests.delete_istio_config(name=name, - namespace=namespace) + namespace=namespace, + object_type=object_type) def _create_dest_rule_vs(openshift_client, destination_rule_conf, namespace=BOOKINFO_1): @@ -719,7 +720,8 @@ def _istio_config_test(kiali_client, openshift_client, browser, config_dict, namespace) if delete_istio_config: - _ui_istio_config_delete(tests, config_dict.metadata.name, namespace) + _ui_istio_config_delete(tests, config_dict.metadata.name, + kind, namespace) assert len(_istio_config_list(kiali_client, config_dict.metadata.name, namespace)) == 0 finally: if delete_istio_config: @@ -753,7 +755,7 @@ def _vs_gateway_link_test(kiali_client, openshift_client, browser, config_dict, tests = IstioConfigPageTest( kiali_client=kiali_client, openshift_client=openshift_client, browser=browser) - tests.load_details_page(vs_name, namespace, force_refresh=False, load_only=True) + tests.load_details_page(vs_name, namespace, kind, force_refresh=False, load_only=True) tests.click_on_gateway(config_dict.metadata.name, namespace) diff --git a/kiali_qe/tests/test_routing_wizard.py b/kiali_qe/tests/test_routing_wizard.py index 0427716a..e3f74abf 100644 --- a/kiali_qe/tests/test_routing_wizard.py +++ b/kiali_qe/tests/test_routing_wizard.py @@ -95,7 +95,8 @@ def test_routing_keep_advanced_settings(kiali_client, openshift_client, browser, namespace=namespace, object_type=IstioConfigObjectType.VIRTUAL_SERVICE.text, object_name=service_details.virtual_services[0].name) - assert '\"gateways\": [\"bookinfo/bookinfo-gateway\", \"mesh\"]' in vs_details.text + assert 'bookinfo/bookinfo-gateway' in vs_details.text + assert 'mesh' in vs_details.text assert len(service_details.destination_rules) == 1 dr_details = kiali_client.istio_config_details( namespace=namespace,