From 70f849148dbe38c2a284c0466f2af540b06e07c0 Mon Sep 17 00:00:00 2001 From: Vadim Rovachev Date: Wed, 13 Nov 2013 19:35:40 +0400 Subject: [PATCH] Added EDP tests Add EDP tests to savanna dashboard tests and fixed some flows Implements blueprint add-edp-tests-to-ui Implements blueprint neutron-for-ui-tests Change-Id: I1d0329423e201c8c39e51875f20e5a6da1eae608 --- savannadashboard/tests/README.rst | 60 ++- savannadashboard/tests/base.py | 404 ++++++++++++++++-- .../cluster_template_tests/negative_tests.py | 2 +- .../test_create_cluster_template.py | 2 +- .../tests/cluster_tests/test_clusters.py | 113 ++++- .../tests/configs/config.conf.sample | 13 +- savannadashboard/tests/configs/config.py | 37 +- .../negative_tests.py | 4 +- .../test_create_node_group_template.py | 4 +- savannadashboard/tests/resources/README.rst | 82 ++++ savannadashboard/tests/resources/edp-job.jar | Bin 0 -> 15641 bytes savannadashboard/tests/resources/edp-job.pig | 3 + savannadashboard/tests/resources/edp-lib.jar | Bin 0 -> 3745 bytes test-requirements.txt | 1 + tox.ini | 1 - 15 files changed, 657 insertions(+), 69 deletions(-) create mode 100644 savannadashboard/tests/resources/README.rst create mode 100644 savannadashboard/tests/resources/edp-job.jar create mode 100644 savannadashboard/tests/resources/edp-job.pig create mode 100644 savannadashboard/tests/resources/edp-lib.jar diff --git a/savannadashboard/tests/README.rst b/savannadashboard/tests/README.rst index 7aa791e8..fa839646 100644 --- a/savannadashboard/tests/README.rst +++ b/savannadashboard/tests/README.rst @@ -1,22 +1,54 @@ -UI tests for Savanna dashboard +Savanna Dashboard Selenium Tests ===================================== -How to run + +Main goal of Selenium Tests ---------- -Create config file for selenium tests - `/savannadashboard/tests/configs/config.conf`. -You can take a look at the sample config file - `/savannadashboard/tests/configs/config.conf.sample`. -All values used in `/savannadashboard/tests/configs/config.py` file are -defaults, so, if they are applicable for your environment then you can skip -config file creation. +Selenium tests for Savanna Dashboard are designed to check the quality of plug-in Savanna Dashboard for the Horizon. -Install virtual framebuffer X server for X Version 11 (Xvfb): -sudo apt-get -y install xvfb -Install Firefox: -sudo add-apt-repository ppa:ubuntu-mozilla-security/ppa -sudo apt-get update -sudo apt-get install firefox libstdc++5 +How to run UI tests: +---------- -To run ui tests you should use the corresponding tox env: `tox -e tests`. +It's assumed that the savanna and horizon are already installed and running. +Information about installation and start of savanna and horizon can be found on the savanna site + http://docs.openstack.org/developer/savanna/#user-guide + in tabs Savanna Installation Guide and Savanna UI Installation Guide. + +1. Go to savanna dashboard path. +2. Create config file for selenium tests - `savannadashboard/tests/configs/config.py`. + You can take a look at the sample config file - `savannadashboard/tests/configs/config.py.sample`. + All values used in `savannadashboard/tests/configs/parameters.py` file are + defaults, so, if they are applicable for your environment then you can skip + config file creation. + +3. Install virtual framebuffer X server for X Version 11 (Xvfb): + sudo apt-get -y install xvfb + +4. Install Firefox: + sudo add-apt-repository ppa:ubuntu-mozilla-security/ppa + sudo apt-get update + sudo apt-get install firefox libstdc++5 + +5. To run ui tests you should use the corresponding tox env: `tox -e tests`. + If need to run only one test module, use: + + tox -e tests -- -a tags='' + + may be equal 'cluster', 'cluster_template', 'image_registry', 'node_group_template', 'image_registry', 'vanilla', 'hdp' + It's full list of actual modules. + + +Coverage: +---------- + +-Clusters +-Cluster templates +-Node group templates +-Image registry +-Data sources +-Job binaries +-Jobs +-Job executions \ No newline at end of file diff --git a/savannadashboard/tests/base.py b/savannadashboard/tests/base.py index 7024ec7e..94b69ca9 100644 --- a/savannadashboard/tests/base.py +++ b/savannadashboard/tests/base.py @@ -13,16 +13,26 @@ # See the License for the specific language governing permissions and # limitations under the License. +import logging +import os +import time +import traceback + import nose.plugins.attrib +import selenium.common.exceptions as selenim_except from selenium import webdriver import selenium.webdriver.common.by as by +from swiftclient import client as swift_client import testtools -import time import unittest2 import savannadashboard.tests.configs.config as cfg +logger = logging.getLogger('swiftclient') +logger.setLevel(logging.WARNING) + + def attr(*args, **kwargs): def decorator(f): if 'type' in kwargs and isinstance(kwargs['type'], str): @@ -39,11 +49,22 @@ class UITestCase(unittest2.TestCase): @classmethod def setUpClass(cls): - cls.driver = webdriver.Firefox() - cls.driver.get(cfg.common.base_url + "/") - cls.find_clear_send(by.By.ID, "id_username", cfg.common.user) - cls.find_clear_send(by.By.ID, "id_password", cfg.common.password) - cls.driver.find_element_by_xpath("//button[@type='submit']").click() + try: + cls.ifFail = False + cls.driver = webdriver.Firefox() + cls.driver.get(cfg.common.base_url + "/") + cls.find_clear_send(by.By.ID, "id_username", cfg.common.user) + cls.find_clear_send(by.By.ID, "id_password", cfg.common.password) + cls.driver.find_element_by_xpath( + "//button[@type='submit']").click() + except Exception: + traceback.print_exc() + cls.ifFail = True + pass + + def setUp(self): + if self.ifFail: + self.fail("setUpClass method is fail") def image_registry(self, image_name, user_name=None, description=None, tags_to_add=None, tags_to_remove=None, positive=True, @@ -65,10 +86,12 @@ class UITestCase(unittest2.TestCase): close_window, message, 'Edit') def create_node_group_template( - self, name, list_processes, plugin, flavor="m1.tiny", params=None, + self, name, list_processes, plugin, flavor=None, params=None, storage={'type': 'Ephemeral Drive'}, description=None, positive=True, message=None, close_window=True): driver = self.driver + if not flavor: + flavor = cfg.common.flavor driver.get(cfg.common.base_url + "/savanna/nodegroup_templates/") self.await_element(by.By.ID, "nodegroup_templates__action_create") driver.find_element_by_id("nodegroup_templates__action_create").click() @@ -84,13 +107,17 @@ class UITestCase(unittest2.TestCase): storage['volume_per_node']) self.find_clear_send(by.By.ID, "id_volumes_size", storage['volume_size']) + if cfg.common.floationg_ip_pool: + self.driver.find_element_by_xpath( + "//*[@id='id_floating_ip_pool']/option[text()='%s']" + % cfg.common.floationg_ip_pool).click() processes = [] for process in list_processes: number_pr = self.search_id_processes(process, plugin) driver.find_element_by_id( - "id_processes_%d" % number_pr).click() + "id_processes_%s" % str(number_pr)).click() processes.append(driver.find_element_by_id( - "id_processes_%d" % number_pr). + "id_processes_%s" % str(number_pr)). find_element_by_xpath('..').text) if params: self.config_helper(params) @@ -119,7 +146,7 @@ class UITestCase(unittest2.TestCase): if anti_affinity_groups: for group in anti_affinity_groups: driver.find_element_by_id( - "id_anti_affinity_%d" % self.search_id_processes( + "id_anti_affinity_%s" % self.search_id_processes( group, plugin)).click() driver.find_element_by_link_text("Node Groups").click() number_to_add = 0 @@ -147,7 +174,7 @@ class UITestCase(unittest2.TestCase): else: self.error_helper(message) - def create_cluster(self, name, cluster_template, keypair, plugin, + def create_cluster(self, name, cluster_template, plugin, keypair=None, close_window=True, description=None, positive=True, await_run=True, message=None): driver = self.driver @@ -162,8 +189,14 @@ class UITestCase(unittest2.TestCase): driver.find_element_by_xpath("//select[@id='id_image']/option" "[text()='%s']" % plugin.base_image).click() + if not keypair: + keypair = cfg.common.keypair driver.find_element_by_xpath("//select[@id='id_keypair']" "/option[text()='%s']" % keypair).click() + if cfg.common.neutron_management_network: + driver.find_element_by_xpath( + "//select[@id='id_neutron_management_network']/option[text()=" + "'%s']" % cfg.common.neutron_management_network).click() driver.find_element_by_xpath("//input[@value='Create']").click() if not message: message = 'Success: Created Cluster %s' % name @@ -174,49 +207,325 @@ class UITestCase(unittest2.TestCase): if await_run: self.await_cluster_active(name) - def delete_node_group_templates(self, names, undelete_names=None): + def create_data_source(self, name, url, close_window=True, + description=None, positive=True, message=None): + + driver = self.driver + driver.get(cfg.common.base_url + "/savanna/data_sources/") + self.await_element(by.By.ID, "data_sources__action_create data source") + driver.find_element_by_id( + "data_sources__action_create data source").click() + + self.await_element(by.By.ID, "id_data_source_name") + + self.find_clear_send(by.By.ID, "id_data_source_name", name) + self.find_clear_send(by.By.ID, "id_data_source_url", url) + self.find_clear_send(by.By.ID, "id_data_source_credential_user", + cfg.common.user) + self.find_clear_send(by.By.ID, "id_data_source_credential_pass", + cfg.common.password) + if description: + self.find_clear_send(by.By.ID, "id_data_source_description", + description) + + driver.find_element_by_xpath("//input[@value='Create']").click() + + if not message: + message = 'Success: Data source created' + if close_window: + self.check_create_object(name, positive, message) + else: + self.error_helper(message) + + def create_job_binary(self, name, parameters_of_storage, description=None, + positive=True, message=None, close_window=True): + + driver = self.driver + storage_type = parameters_of_storage['storage_type'] + driver.get(cfg.common.base_url + "/savanna/job_binaries/") + self.await_element(by.By.ID, "job_binaries__action_create job binary") + driver.find_element_by_id( + "job_binaries__action_create job binary").click() + + self.await_element(by.By.ID, "id_job_binary_name") + + self.find_clear_send(by.By.ID, "id_job_binary_name", name) + driver.find_element_by_xpath("//select[@id='id_job_binary_type']/optio" + "n[text()='%s']" % storage_type).click() + + if storage_type == 'Swift internal': + self.find_clear_send(by.By.ID, "id_job_binary_url", + parameters_of_storage['url']) + self.find_clear_send(by.By.ID, "id_job_binary_username", + cfg.common.user) + self.find_clear_send(by.By.ID, "id_job_binary_password", + cfg.common.password) + + elif storage_type == 'Savanna internal database': + savanna_binary = parameters_of_storage['Savanna binary'] + driver.find_element_by_xpath( + "//select[@id='id_job_binary_savanna_internal']/option[text()" + "='%s']" % savanna_binary).click() + if savanna_binary == '*Upload a new file': + file = '%s/tests/resources/%s' % ( + os.getcwd(), parameters_of_storage['filename']) + driver.find_element_by_id('id_job_binary_file').send_keys(file) + + elif savanna_binary == '*Create a script': + self.find_clear_send(by.By.ID, "id_job_binary_script_name", + parameters_of_storage['script_name']) + self.find_clear_send(by.By.ID, "id_job_binary_script", + parameters_of_storage['script_text']) + + if description: + self.find_clear_send(by.By.ID, "id_job_binary_description", + description) + + driver.find_element_by_xpath("//input[@value='Create']").click() + + if not message: + message = 'Success: Successfully created job binary' + if close_window: + self.check_create_object(name, positive, message) + else: + self.error_helper(message) + + def create_job(self, name, job_type, main=None, libs=None, + close_window=True, description=None, positive=True, + message=None): + + driver = self.driver + driver.get(cfg.common.base_url + "/savanna/jobs/") + self.await_element(by.By.ID, "jobs__action_create job") + driver.find_element_by_id("jobs__action_create job").click() + + self.await_element(by.By.ID, "id_job_name") + self.find_clear_send(by.By.ID, "id_job_name", name) + driver.find_element_by_xpath( + "//select[@id='id_job_type']/option[text()='%s']" + % job_type).click() + if main: + driver.find_element_by_xpath( + "//select[@id='id_main_binary']/option[text()='%s']" + % main).click() + if description: + self.find_clear_send(by.By.ID, "id_job_description", description) + if libs: + driver.find_element_by_link_text('Libs').click() + self.await_element(by.By.ID, "id_lib_binaries") + for lib in libs: + driver.find_element_by_xpath( + "//select[@id='id_lib_binaries']/option[text()='%s']" + % lib).click() + driver.find_element_by_id('add_lib_button').click() + + driver.find_element_by_xpath("//input[@value='Create']").click() + + if not message: + message = 'Success: Job created' + if close_window: + self.check_create_object(name, positive, message) + else: + self.error_helper(message) + + def launch_job_on_existing_cluster(self, name, input, output, cluster, + configure=None, positive=True, + message=None, close_window=True, + await_launch=True): + + driver = self.driver + driver.get(cfg.common.base_url + "/savanna/jobs/") + self.await_element(by.By.ID, "jobs__action_create job") + + action_column = driver.find_element_by_link_text( + name).find_element_by_xpath('../../td[4]') + action_column.find_element_by_link_text('More').click() + action_column.find_element_by_link_text( + 'Launch On Existing Cluster').click() + + self.await_element(by.By.ID, "id_job_input") + driver.find_element_by_xpath( + "//select[@id='id_job_input']/option[text()='%s']" % input).click() + driver.find_element_by_xpath( + "//select[@id='id_job_output']/option[text()='%s']" % + output).click() + driver.find_element_by_xpath( + "//select[@id='id_cluster']/option[text()='%s']" % cluster).click() + + if configure: + driver.find_element_by_link_text('Configure').click() + for config_part, values in configure.items(): + config_number = 1 + for config, value in values.items(): + driver.find_element_by_id( + config_part).find_element_by_link_text('Add').click() + driver.find_element_by_xpath( + '//*[@id="%s"]/table/tbody/tr[%i]/td[1]/input' % ( + config_part, config_number)).send_keys(config) + driver.find_element_by_xpath( + '//*[@id="%s"]/table/tbody/tr[%i]/td[2]/input' % ( + config_part, config_number)).send_keys(value) + config_number += 1 + + driver.find_element_by_xpath("//input[@value='Launch']").click() + + if not message: + message = 'Success: Job launched' + if close_window: + self.check_create_object(name, positive, message, + check_create_element=False) + if await_launch: + self.await_launch_job() + + else: + self.error_helper(message) + + def delete_node_group_templates(self, names, undelete_names=None, + finally_delete=False): url = "/savanna/nodegroup_templates/" delete_button_id = 'nodegroup_templates__action_' \ 'delete_nodegroup_template' - self.delete_and_validate(url, delete_button_id, names, undelete_names) + self.delete_and_validate(url, delete_button_id, names, undelete_names, + finally_delete) - def delete_cluster_templates(self, names, undelete_names=None): + def delete_cluster_templates(self, names, undelete_names=None, + finally_delete=False): url = "/savanna/cluster_templates/" delete_button_id = "cluster_templates__action_delete_cluster_template" - self.delete_and_validate(url, delete_button_id, names, undelete_names) + self.delete_and_validate(url, delete_button_id, names, undelete_names, + finally_delete) - def delete_clusters(self, names, undelete_names=None): + def delete_clusters(self, names, undelete_names=None, + finally_delete=False): url = "/savanna/" delete_button_id = "clusters__action_delete" msg = "Success: Deleted Cluster" self.delete_and_validate(url, delete_button_id, names, undelete_names, - succes_msg=msg) + finally_delete, succes_msg=msg) - def unregister_images(self, names, undelete_names=[]): + def delete_data_sources(self, names, undelete_names=None, + finally_delete=False): + url = "/savanna/data_sources/" + delete_button_id = "data_sources__action_delete" + msg = "Success: Deleted Data source" + err_msg = 'Error: Unable to delete data source' + info_msg = 'Info: Deleted Data source' + self.delete_and_validate(url, delete_button_id, names, undelete_names, + finally_delete, msg, err_msg, info_msg) + + def delete_job_binaries(self, names, undelete_names=None, + finally_delete=False): + + url = "/savanna/job_binaries/" + delete_button_id = "job_binaries__action_delete" + + msg = "Success: Deleted Job binary" + err_msg = 'Error: Unable to delete job binary' + info_msg = 'Info: Deleted Job binary' + + if not undelete_names and len(names) > 1: + msg = "Success: Deleted Job binarie" + + if undelete_names and len(names)-len(undelete_names) > 1: + info_msg = 'Info: Deleted Job binarie' + + if undelete_names and len(undelete_names) > 1: + err_msg = 'Error: Unable to delete job binarie' + + self.delete_and_validate(url, delete_button_id, names, undelete_names, + finally_delete, msg, err_msg, info_msg) + + def delete_jobs(self, names, undelete_names=None, finally_delete=False): + url = "/savanna/jobs/" + delete_button_id = "jobs__action_delete" + msg = "Success: Deleted Job" + err_msg = 'Error: Unable to delete job' + info_msg = 'Info: Deleted Job' + self.delete_and_validate(url, delete_button_id, names, undelete_names, + finally_delete, msg, err_msg, info_msg) + + def delete_all_job_executions(self): + + driver = self.driver + driver.get(cfg.common.base_url + "/savanna/job_executions/") + + delete_button_id = 'job_executions__action_delete' + + self.await_element(by.By.ID, delete_button_id) + + if self.does_element_present(by.By.CLASS_NAME, 'multi_select_column'): + + if not driver.find_element_by_xpath( + '//*[@class=\'multi_select_column\']/input').is_selected(): + + driver.find_element_by_class_name( + 'multi_select_column').click() + + driver.find_element_by_id(delete_button_id).click() + self.await_element(by.By.LINK_TEXT, 'Delete Job executions') + driver.find_element_by_link_text('Delete Job executions').click() + self.await_element(by.By.CLASS_NAME, "alert-success") + message = 'Success: Deleted Job execution' + actual_message = self.find_alert_message( + "alert-success", first_character=2, + last_character=len(message)+2) + self.assertEqual(actual_message, message) + + def unregister_images(self, names, undelete_names=[], + finally_delete=False): url = '/savanna/image_registry/' delete_button_id = "image_registry__action_Unregister" msg = "Success: Unregistered Image" self.delete_and_validate(url, delete_button_id, names, undelete_names, - succes_msg=msg) + finally_delete, succes_msg=msg,) #-------------------------helpers_methods-------------------------------------- + @staticmethod + def connect_to_swift(): + return swift_client.Connection( + authurl=cfg.common.keystone_url, + user=cfg.common.user, + key=cfg.common.password, + tenant_name=cfg.common.tenant, + auth_version=2 + ) + + @staticmethod + def delete_swift_container(swift, container): + + objects = [obj['name'] for obj in swift.get_container(container)[1]] + for obj in objects: + + swift.delete_object(container, obj) + + swift.delete_container(container) + @classmethod def find_clear_send(cls, by_find, find_element, send): cls.driver.find_element(by=by_find, value=find_element).clear() cls.driver.find_element(by=by_find, value=find_element).send_keys(send) def delete_and_validate(self, url, delete_button_id, names, undelete_names, + finally_delete, succes_msg='Success: Deleted Template', error_msg='Error: Unable to delete template', info_msg='Info: Deleted Template'): driver = self.driver + driver.refresh() driver.get(cfg.common.base_url + url) self.await_element(by.By.ID, delete_button_id) for name in names: # choose checkbox for this element - driver.find_element_by_link_text("%s" % name).\ - find_element_by_xpath("../../td[1]/input").click() + try: + driver.find_element_by_link_text("%s" % name).\ + find_element_by_xpath("../../td[1]/input").click() + except selenim_except.NoSuchElementException as e: + if finally_delete: + pass + else: + print ('element with name %s not found for delete' % name) + raise e # click deletebutton driver.find_element_by_id(delete_button_id).click() # wait window to confirm the deletion @@ -326,7 +635,7 @@ class UITestCase(unittest2.TestCase): else: #Add existing tags in the list list_for_check_tags = driver.\ - find_element(by=by.By.LINK_TEXT, value='latest-ci-image').\ + find_element(by=by.By.LINK_TEXT, value=image_name).\ find_element_by_xpath('../../td[3]').text.split('\n') # Click "Edit Tags" driver.find_element(by=by.By.LINK_TEXT, value=image_name).\ @@ -385,7 +694,7 @@ class UITestCase(unittest2.TestCase): def choose_plugin_name(self, plugin_name, hadoop_version, name, description, id_name): self.await_element(by.By.XPATH, "//*[@id='modal_wrapper']" - "/div/form/div[4]/input") + "/div/form/div[3]/input") self.driver.find_element_by_xpath( "//select[@id='id_plugin_name']/option[text()='%s']" % plugin_name).click() @@ -403,13 +712,13 @@ class UITestCase(unittest2.TestCase): if description: self.find_clear_send(by.By.ID, "id_description", description) - def check_alert(self, alert, message, list_obj, deleted=True): + def check_alert(self, alert, expected_message, list_obj, deleted=True): self.await_element(by.By.CLASS_NAME, alert) - if self.find_alert_message(alert, first_character=2, - last_character=len(message)+2) != message: - self.fail("%s != %s" % (alert, message)) + actual_message = self.find_alert_message( + alert, first_character=2, last_character=len(expected_message)+2) + self.assertEqual(actual_message, expected_message) not_expected_objs = list(set(self.find_alert_message( - alert, first_character=len(message)+2).split( + alert, first_character=len(expected_message)+2).split( ", ")).symmetric_difference(set(list_obj))) if not_expected_objs: self.fail("have deleted objects: %s" % not_expected_objs) @@ -457,7 +766,7 @@ class UITestCase(unittest2.TestCase): self.fail(message) def check_create_object(self, name, positive, expected_message, - check_columns=None): + check_columns=None, check_create_element=True): driver = self.driver expected_alert = "alert-error" unexpected_alert = "alert-success" @@ -471,13 +780,13 @@ class UITestCase(unittest2.TestCase): fail_mesg = self.driver.find_element( by=by.By.CLASS_NAME, value=unexpected_alert).text[2:] self.fail("Result of creation %s is not expected: %s != %s" - % (name, expected_alert, fail_mesg)) + % (name, expected_message, fail_mesg)) time.sleep(1) else: self.fail("alert check:%s time out" % expected_alert) actual_message = self.driver.find_element( by=by.By.CLASS_NAME, value=expected_alert).text[2:] - if positive: + if check_create_element and positive: self.assertEqual(expected_message, str(actual_message)) if not self.does_element_present(by.By.LINK_TEXT, name): self.fail("object with name:%s not found" % name) @@ -501,15 +810,48 @@ class UITestCase(unittest2.TestCase): find_element_by_xpath("../../td[3]").text i = 1 while str(status) != 'Active': + if i > cfg.common.cluster_creation_timeout * 6: self.fail( 'cluster is not getting status \'Active\', ' 'passed %d minutes' % cfg.common.cluster_creation_timeout) + + if str(status) == 'Error': + self.fail('Cluster state == \'Error\'.') + status = driver.find_element_by_link_text("selenium-cl").\ find_element_by_xpath("../../td[3]").text time.sleep(10) i += 1 + def await_launch_job(self): + driver = self.driver + driver.get(cfg.common.base_url + "/savanna/job_executions/") + self.await_element(by.By.ID, 'job_executions') + + job_id = driver.find_element_by_id( + 'job_executions').find_elements_by_class_name( + 'ajax-update')[-1].get_attribute('id') + + status = driver.find_element_by_xpath( + '//*[@id="%s"]/td[3]' % job_id).text + timeout = cfg.common.job_launch_timeout * 60 + + while str(status) != 'SUCCEEDED': + + if timeout <= 0: + self.fail( + 'Job did not return to \'SUCCEEDED\' status within ' + '%d minute(s).' % cfg.common.job_launch_timeout) + + if status == 'KILLED': + self.fail('Job status == \'KILLED\'.') + + status = driver.find_element_by_xpath( + '//*[@id="%s"]/td[3]' % job_id).text + time.sleep(10) + timeout -= 10 + @classmethod def tearDownClass(cls): cls.driver.quit() diff --git a/savannadashboard/tests/cluster_template_tests/negative_tests.py b/savannadashboard/tests/cluster_template_tests/negative_tests.py index 2be1e023..4ed55b63 100644 --- a/savannadashboard/tests/cluster_template_tests/negative_tests.py +++ b/savannadashboard/tests/cluster_template_tests/negative_tests.py @@ -21,7 +21,7 @@ import savannadashboard.tests.configs.config as cfg class UINegativeCreateClusterTemplateTest(base.UITestCase): - @base.attr(tags='cluster_template') + @base.attr(tags=['cluster_template', 'vanilla']) @testtools.skip @testtools.skipIf(cfg.vanilla.skip_plugin_tests, 'tests for vanilla plugin skipped') diff --git a/savannadashboard/tests/cluster_template_tests/test_create_cluster_template.py b/savannadashboard/tests/cluster_template_tests/test_create_cluster_template.py index 2d54a554..39fb6448 100644 --- a/savannadashboard/tests/cluster_template_tests/test_create_cluster_template.py +++ b/savannadashboard/tests/cluster_template_tests/test_create_cluster_template.py @@ -52,7 +52,7 @@ class UICreateClusterTemplate(base.UITestCase): self.delete_node_group_templates(["selenium-master", "selenium-worker"]) - @base.attr('cluster_template', 'hdp') + @base.attr(tags=['cluster_template', 'hdp']) @testtools.skipIf(cfg.hdp.skip_plugin_tests, 'tests for hdp plugin skipped') def test_create_cluster_template_for_hdp(self): diff --git a/savannadashboard/tests/cluster_tests/test_clusters.py b/savannadashboard/tests/cluster_tests/test_clusters.py index da1b0aa6..71ef932e 100644 --- a/savannadashboard/tests/cluster_tests/test_clusters.py +++ b/savannadashboard/tests/cluster_tests/test_clusters.py @@ -13,7 +13,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +import random +import string import testtools +import traceback from savannadashboard.tests import base import savannadashboard.tests.configs.config as cfg @@ -21,12 +24,20 @@ import savannadashboard.tests.configs.config as cfg class UICreateCluster(base.UITestCase): - @base.attr('cluster', 'vanilla', speed='slow') + @base.attr(tags=['cluster', 'vanilla'], speed='slow') @testtools.skipIf(cfg.vanilla.skip_plugin_tests, 'tests for vanilla plugin skipped') def test_create_vanilla_cluster(self): + try: - self.create_node_group_template('selenium-master', ["NN", "JT"], + processes = ["NN", "JT"] + await_run = False + + if not cfg.vanilla.skip_edp_test: + processes = ["NN", "JT", "OZ"] + await_run = True + + self.create_node_group_template('selenium-master', processes, cfg.vanilla, storage={'type': 'Cinder Volume', "volume_per_node": 1, @@ -41,7 +52,7 @@ class UICreateCluster(base.UITestCase): cfg.vanilla) self.create_cluster_template("selenium-cl-tmpl", {'selenium-master': 1, - 'selenium-worker': 2}, cfg.vanilla, + 'selenium-worker': 1}, cfg.vanilla, anti_affinity_groups=["NN", "DN", "TT"]) self.create_cluster_template("selenium-cl-tmpl2", @@ -50,8 +61,10 @@ class UICreateCluster(base.UITestCase): cfg.vanilla, anti_affinity_groups= ["NN", "DN", "TT", "JT"]) - self.create_cluster('selenium-cl', 'selenium-cl-tmpl', 'vrovachev', - cfg.vanilla, await_run=False) + self.create_cluster('selenium-cl', 'selenium-cl-tmpl', + cfg.vanilla, await_run=await_run) + if not cfg.vanilla.skip_edp_test: + self.edp_helper() self.delete_node_group_templates(["selenium-master", "selenium-worker", "selenium-del1", @@ -68,8 +81,90 @@ class UICreateCluster(base.UITestCase): undelete_names=[ "selenium-master", "selenium-worker"]) + + except Exception as e: + traceback.print_exc() + raise e finally: - self.delete_clusters(['selenium-cl']) - self.delete_cluster_templates(['selenium-cl-tmpl']) - self.delete_node_group_templates(["selenium-master", - "selenium-worker"]) + try: + self.delete_clusters(['selenium-cl'], finally_delete=True) + except Exception: + pass + + try: + self.delete_cluster_templates(['selenium-cl-tmpl', + 'selenium-cl-tmpl2'], + finally_delete=True) + except Exception: + pass + try: + self.delete_node_group_templates(["selenium-master", + "selenium-worker", + "selenium-del1", + "selenium-del2"], + finally_delete=True) + except Exception: + pass + + def edp_helper(self): + + try: + + swift = self.connect_to_swift() + swift.put_container('selenium-container') + swift.put_object( + 'selenium-container', 'input', ''.join(random.choice( + ':' + ' ' + '\n' + string.ascii_lowercase) + for x in range(10000))) + + self.create_data_source( + 'input', 'selenium-container.savanna/input') + self.create_data_source( + 'output', 'selenium-container.savanna/output') + + parameters_of_storage = { + 'storage_type': 'Savanna internal database', + 'Savanna binary': '*Upload a new file', + 'filename': 'edp-lib.jar'} + + self.create_job_binary('edp-lib.jar', parameters_of_storage) + + parameters_of_storage = { + 'storage_type': 'Savanna internal database', + 'Savanna binary': '*Create a script', + 'script_name': 'edp-job.pig', + 'script_text': open('tests/resources/edp-job.pig').read()} + + self.create_job_binary('edp-job.pig', parameters_of_storage) + + self.create_job( + 'selenium-job', 'Pig', 'edp-job.pig', ['edp-lib.jar']) + self.launch_job_on_existing_cluster( + 'selenium-job', 'input', 'output', 'selenium-cl') + + except Exception as e: + raise e + + finally: + try: + self.delete_swift_container(swift, 'selenium-container') + except Exception: + pass + try: + self.delete_all_job_executions() + except Exception: + pass + try: + self.delete_jobs(['selenium-job'], finally_delete=True) + except Exception: + pass + try: + self.delete_job_binaries(['edp-lib.jar', 'edp-job.pig'], + finally_delete=True) + except Exception: + pass + try: + self.delete_data_sources(['input', 'output'], + finally_delete=True) + except Exception: + pass diff --git a/savannadashboard/tests/configs/config.conf.sample b/savannadashboard/tests/configs/config.conf.sample index 28873dc5..78a4d707 100644 --- a/savannadashboard/tests/configs/config.conf.sample +++ b/savannadashboard/tests/configs/config.conf.sample @@ -2,10 +2,19 @@ base_url = "http://127.0.0.1:8080" user = "admin" password = "admin" +keypair = 'jenkins' +tenant = 'admin' +flavor = 'm1.minniemouse' +# uncomment this parameters if quantum in OpenStack +# neutron_management_network = '' +# floationg_ip_pool = '' +keystone_url = 'http://127.0.0.1:5000/v2.0' # in minutes cluster_creation_timeout = 10 # in seconds await_element = 10 +# in minutes +job_launch_timeout = 5 image_name_for_register = 'image_name' image_name_for_edit = 'image_name' [vanilla] @@ -13,10 +22,10 @@ skip_plugin_tests = False plugin_name = "Vanilla Apache Hadoop" plugin_overview_name = "vanilla" hadoop_version = "1.2.1" -processes = {"NN": 0, "DN": 1, "SNN": 2, "OZ": 3, "TT": 4, "JT": 5} +processes = NN: 0, DN: 1, SNN: 2, OZ: 3, TT: 4, JT: 5 base_image = "image_name" [hdp] skip_plugin_tests = False plugin_name = "Hortonworks Data Platform" -hadoop_version = "1.3.0" +hadoop_version = "1.3.2" base_image = "image_name" \ No newline at end of file diff --git a/savannadashboard/tests/configs/config.py b/savannadashboard/tests/configs/config.py index 16856359..e0c484bc 100644 --- a/savannadashboard/tests/configs/config.py +++ b/savannadashboard/tests/configs/config.py @@ -28,18 +28,42 @@ CommonGroup = [ cfg.StrOpt('password', default='pass', help="password for keystone user"), + cfg.StrOpt('keypair', + default='public-jenkins', + help='keypair for create cluster'), + cfg.StrOpt('tenant', + default='admin', + help='keystone tenant'), + cfg.StrOpt('flavor', + default='m1.minniemouse', + help='OpenStack flavor name for image.'), + cfg.StrOpt('neutron_management_network', + default=None, + help='Private network for quantum.' + 'Must be specified in create cluster tab'), + cfg.StrOpt('floationg_ip_pool', + default=None, + help='Public network for quantum.' + 'Must be specified in create nodegroup template tab'), + cfg.StrOpt('keystone_url', + default='http://127.0.0.1:5000/v2.0', + help='url for keystone authentication'), cfg.IntOpt('cluster_creation_timeout', default=10, help="cluster timeout in minutes"), cfg.IntOpt('await_element', - default=10, + default=15, help="await each web element in seconds"), cfg.StrOpt('image_name_for_register', default='fedora_19', help='Image name for register to Savanna'), cfg.StrOpt('image_name_for_edit', default='latest-ci-image', - help='Image name for edit in image registry in Savanna') + help='Image name for edit in image registry in Savanna'), + cfg.IntOpt('job_launch_timeout', + default=5, + help='Timeout for job launch (in minutes); ' + 'minimal value is 1.'), ] vanilla_group = cfg.OptGroup(name='vanilla', title="vanilla configs") @@ -51,6 +75,7 @@ VanillaGroup = [ If this variable is True then tests for vanilla will be skipped """), + cfg.BoolOpt('skip_edp_test', default=True), cfg.StrOpt('plugin_name', default='Vanilla Apache Hadoop', help="plugin title, default: Vanilla Apache Hadoop"), @@ -60,12 +85,12 @@ VanillaGroup = [ cfg.StrOpt('hadoop_version', default='1.2.1', help="hadoop version for plugin"), - cfg.ListOpt('processes', + cfg.DictOpt('processes', default={"NN": 0, "DN": 1, "SNN": 2, "OZ": 3, "TT": 4, "JT": 5}, help='numbers of processes for vanilla in savannabashboard'), cfg.StrOpt('base_image', - default='latest-ci-image', + default='ubuntu_savanna_latest', help="image name for start vanilla cluster") ] @@ -85,7 +110,7 @@ HdpGroup = [ default='hdp', help="plugin name in overview"), cfg.StrOpt('hadoop_version', - default='1.3.0', + default='1.3.2', help="hadoop version for plugin"), cfg.ListOpt('processes', default= @@ -95,7 +120,7 @@ HdpGroup = [ "NAGIOS_SERVER": 11}, help='numbers of processes for hdp in savannabashboard'), cfg.StrOpt('base_image', - default='latest-ci-image', + default='ib-centos-6-4-64-hdp-13', help="image name for start hdp cluster") ] diff --git a/savannadashboard/tests/node_group_template_tests/negative_tests.py b/savannadashboard/tests/node_group_template_tests/negative_tests.py index cb90f134..2847f24e 100644 --- a/savannadashboard/tests/node_group_template_tests/negative_tests.py +++ b/savannadashboard/tests/node_group_template_tests/negative_tests.py @@ -21,7 +21,7 @@ import savannadashboard.tests.configs.config as cfg class UINegativeCreateNodeGroupTemplate(base.UITestCase): - @base.attr(tags='node_group_template') + @base.attr(tags=['node_group_template', 'vanilla']) @testtools.skipIf(cfg.vanilla.skip_plugin_tests, 'tests for vanilla plugin skipped') def test_create_vanilla_node_group_template_with_wrong_parameters(self): @@ -38,7 +38,7 @@ class UINegativeCreateNodeGroupTemplate(base.UITestCase): 'MapReduce Parameters:io.sort.mb:' 'Enter a whole number.') - @base.attr(tags='node_group_template') + @base.attr(tags=['node_group_template', 'vanilla']) @testtools.skipIf(cfg.vanilla.skip_plugin_tests, 'tests for vanilla plugin skipped') def test_create_vanilla_node_group_template_with_missing_parameters(self): diff --git a/savannadashboard/tests/node_group_template_tests/test_create_node_group_template.py b/savannadashboard/tests/node_group_template_tests/test_create_node_group_template.py index dade4644..15df2544 100644 --- a/savannadashboard/tests/node_group_template_tests/test_create_node_group_template.py +++ b/savannadashboard/tests/node_group_template_tests/test_create_node_group_template.py @@ -21,7 +21,7 @@ import savannadashboard.tests.configs.config as cfg class UICreateNodeGroupTemplate(base.UITestCase): - @base.attr('node_group_template', 'vanilla') + @base.attr(tags=['node_group_template', 'vanilla']) @testtools.skipIf(cfg.vanilla.skip_plugin_tests, 'tests for vanilla plugin skipped') def test_create_node_group_template_vanilla(self): @@ -44,7 +44,7 @@ class UICreateNodeGroupTemplate(base.UITestCase): message=msg) self.delete_node_group_templates(['selenium-vanilla']) - @base.attr('node_group_template', 'hdp') + @base.attr(tags=['node_group_template', 'hdp']) @testtools.skipIf(cfg.hdp.skip_plugin_tests, 'tests for hdp plugin skipped') def test_create_node_group_template_hdp(self): diff --git a/savannadashboard/tests/resources/README.rst b/savannadashboard/tests/resources/README.rst new file mode 100644 index 00000000..c12b21f4 --- /dev/null +++ b/savannadashboard/tests/resources/README.rst @@ -0,0 +1,82 @@ +README for resources +===================================== + +Resources in this directory using for check EDP for Savanna. +For this purpose the cluster with oozie by process is created. +With these resources created and launched jobs. +Success of performance of jobs is checked. + +Pig job +------------------------------------- + +Description. +------------ + +Resources 'edp-job.pig' and 'edp-lib.jar' used for create oozie job +that deletes all symbols in line after ":". + +Example: +-------- + +input file: +""" +qweqwe +qweqweqwe: +qweqweqwe:qwe +:ertertert +asd +""" + +output file: +""" +qweqwe +qweqweqwe +asd +""" + +Sources. +-------- + +Link for 'edp-job.pig': +https://github.com/apache/oozie/blob/branch-4.0/examples/src/main/apps/pig/id.pig + +Source for 'edp-lib.jar': +https://github.com/apache/oozie/blob/branch-4.0/examples/src/main/java/org/apache/oozie/example/DateList.java + + +Jar job +------------------------------------- + +Description. +------------ + +Resource 'edp-job.jar' used for create oozie job +which counts the characters in file and displays values ​​at end of each line. + +Example: +-------- + +input file: +""" +qweqwe +qweqweqwe: +qweqweqwe:qwe +:ertertert +asd +""" + +output file: +""" +qweqwe 6 +qweqweqwe: 16 +qweqweqwe:qwe 29 +:ertertert 39 +asd 42 +""" + +Sources. +-------- + +Sources for 'edp-job.jar': +https://github.com/apache/oozie/blob/branch-4.0/examples/src/main/java/org/apache/oozie/example/SampleMapper.java +https://github.com/apache/oozie/blob/branch-4.0/examples/src/main/java/org/apache/oozie/example/SampleReducer.java \ No newline at end of file diff --git a/savannadashboard/tests/resources/edp-job.jar b/savannadashboard/tests/resources/edp-job.jar new file mode 100644 index 0000000000000000000000000000000000000000..04388a958325568618c807f4e5d764f303a765f1 GIT binary patch literal 15641 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 XB*9*b5`lo;zJAVM?~s4QPeA_<2*LpM literal 0 HcmV?d00001 diff --git a/savannadashboard/tests/resources/edp-job.pig b/savannadashboard/tests/resources/edp-job.pig new file mode 100644 index 00000000..4141906e --- /dev/null +++ b/savannadashboard/tests/resources/edp-job.pig @@ -0,0 +1,3 @@ +A = load '$INPUT' using PigStorage(':') as (fruit: chararray); +B = foreach A generate com.hadoopbook.pig.Trim(fruit); +store B into '$OUTPUT' USING PigStorage(); diff --git a/savannadashboard/tests/resources/edp-lib.jar b/savannadashboard/tests/resources/edp-lib.jar new file mode 100644 index 0000000000000000000000000000000000000000..2eba7023ec8e6cf6ec5a0df4f32a8c976f94e946 GIT binary patch literal 3745 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.6 mock>=1.0 nose openstack.nose_plugin>=0.7 +python-swiftclient>=1.5 pylint==0.25.2 unittest2 selenium diff --git a/tox.ini b/tox.ini index c19064ee..14ffaa7a 100644 --- a/tox.ini +++ b/tox.ini @@ -15,7 +15,6 @@ setenv = deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt -commands = nosetests {posargs} [testenv:tests] sitepackages = False