Skip to content
This repository has been archived by the owner on Apr 7, 2022. It is now read-only.

Commit

Permalink
[1LP][RFR]tests for system schedules (#6861)
Browse files Browse the repository at this point in the history
* New schedules tests

* select row fix

* more tests

* lint fixes

* use server time

* mark provider, small fix

* time fixes for schedule time changes

* added bz ids for test
  • Loading branch information
Ruslana Babyuk authored and mshriver committed Apr 27, 2018
1 parent 2a4d2e5 commit c76b828
Show file tree
Hide file tree
Showing 2 changed files with 246 additions and 18 deletions.
74 changes: 56 additions & 18 deletions cfme/configure/configuration/system_schedules.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
Calendar)
from widgetastic_patternfly import Input, BootstrapSelect, Button
from widgetastic.widget import View
from widgetastic.exceptions import NoSuchElementException

from cfme.base.ui import ConfigurationView
from cfme.modeling.base import BaseCollection, BaseEntity
Expand Down Expand Up @@ -54,11 +55,11 @@ class ScheduleAddEditEntities(View):
items_analysis = View.nested(ItemsAnalysisEntities)
database_backup = View.nested(DatabaseBackupEntities)
# Timer
run_timer = BootstrapSelect("timer_typ")
run_type = BootstrapSelect("timer_typ")
time_zone = BootstrapSelect("time_zone")
starting_date = Calendar("start_date")
start_date = Calendar("start_date")
start_hour = BootstrapSelect("start_hour")
start_min = BootstrapSelect("start_min")
start_minute = BootstrapSelect("start_min")
# Buttons
cancel_button = Button("Cancel")

Expand Down Expand Up @@ -176,7 +177,7 @@ class SystemSchedule(BaseEntity, Updateable, Pretty):
time_zone = attr.ib(default=None)
start_date = attr.ib(default=None)
start_hour = attr.ib(default=None)
start_min = attr.ib(default=None)
start_minute = attr.ib(default=None)
# Analysis
filter_level1 = attr.ib(default=None)
filter_level2 = attr.ib(default=None)
Expand All @@ -197,20 +198,42 @@ def update(self, updates, reset=False, cancel=False):
cancel: Whether to click on the cancel button to interrupt the editation.
"""
form_mapping = {
'name': updates.get('name'),
'description': updates.get('description'),
'active': updates.get('active'),
'action_type': updates.get('action_type'),
'run_type': updates.get('run_type'),
'run_every': updates.get('run_every'),
'time_zone': updates.get('time_zone'),
'start_date': updates.get('start_date'),
'start_hour': updates.get('start_hour'),
'start_minute': updates.get('start_minute'),
'database_backup': {
'depot_name': updates.get('depot_name'),
'backup_type': updates.get('backup_type'),
'uri': updates.get('uri'),
},
'samba_protocol': {
'samba_username': updates.get('samba_username'),
'samba_password': updates.get('samba_password'),
'samba_password_verify': updates.get('samba_password'),
},
'items_analysis': {
'filter_level1': updates.get('filter_level1'),
'filter_level2': updates.get('filter_level2'),
}
}
view = navigate_to(self, 'Edit')
updated = view.fill(updates)
updated = view.fill(form_mapping)
if reset:
view.reset_button.click()
flash_message = 'All changes have been reset'
if cancel:
view.cancel_button.click()
flash_message = 'Edit of Schedule "{}" was cancelled by the user'.format(self.name)
elif updated:
elif updated and not cancel and not reset:
view.save_button.click()
view = self.create_view(ScheduleDetailsView, override=updates)
name = updates.get('name') if updates.get('name') else self.name
flash_message = 'Schedule "{}" was saved'.format(name)
view.flash.assert_message(flash_message)
view.flash.assert_no_error()

def delete(self, cancel=False):
""" Delete the schedule represented by this object.
Expand All @@ -224,7 +247,7 @@ def delete(self, cancel=False):
view.toolbar.configuration.item_select('Delete this Schedule from the Database',
handle_alert=(not cancel))
view = self.create_view(ScheduleAllView)
view.flash.assert_message('Schedule "{}": Delete successful'.format(self.description))
view.flash.assert_no_error()

def enable(self):
""" Enable the schedule via table checkbox and Configuration menu. """
Expand All @@ -239,7 +262,7 @@ def disable(self):
def select(self):
""" Select the checkbox for current schedule """
view = navigate_to(self.parent, 'All')
row = view.table.row(name=self.name)
row = view.paginator.find_row_on_pages(view.table, name=self.name)
row[0].check()
return view

Expand All @@ -249,14 +272,29 @@ def last_run_date(self):
row = view.table.row(name=self.name)
return row['Last Run Time'].read()

@property
def next_run_date(self):
view = navigate_to(self.parent, 'All')
row = view.table.row(name=self.name)
return row['Next Run Time'].read()

@property
def exists(self):
view = navigate_to(self.parent, 'All')
try:
view.paginator.find_row_on_pages(view.table, name=self.name)
return True
except NoSuchElementException:
return False


@attr.s
class SystemSchedulesCollection(BaseCollection):
""" Configure/Configuration/Region/Schedules collection functionality """
ENTITY = SystemSchedule

def create(self, name, description, active=True, action_type=None, run_type=None,
run_every=None, time_zone=None, start_date=None, start_hour=None, start_min=None,
run_every=None, time_zone=None, start_date=None, start_hour=None, start_minute=None,
filter_level1=None, filter_level2=None, backup_type=None, depot_name=None, uri=None,
samba_username=None, samba_password=None, cancel=False):
""" Create a new schedule from the informations stored in the object.
Expand All @@ -274,7 +312,7 @@ def create(self, name, description, active=True, action_type=None, run_type=None
'time_zone': time_zone,
'start_date': start_date,
'start_hour': start_hour,
'start_min': start_min
'start_minute': start_minute
}
if action_type == 'Database Backup':
details.update({
Expand Down Expand Up @@ -307,14 +345,14 @@ def create(self, name, description, active=True, action_type=None, run_type=None
view.add_button.click()
view = self.create_view(ScheduleAllView)
view.flash.assert_message('Schedule "{}" was saved'.format(name))
shedule = self.instantiate(name, description, active=active, action_type=action_type,
schedule = self.instantiate(name, description, active=active, action_type=action_type,
run_type=run_type, run_every=run_type, time_zone=time_zone,
start_date=start_date, start_hour=start_hour,
start_min=start_min, filter_level1=filter_level1,
start_minute=start_minute, filter_level1=filter_level1,
filter_level2=filter_level2, backup_type=backup_type,
depot_name=depot_name, uri=uri, samba_username=samba_username,
samba_password=samba_password)
return shedule
return schedule


@navigator.register(SystemSchedulesCollection, 'All')
Expand Down
190 changes: 190 additions & 0 deletions cfme/tests/configure/test_schedule_operations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import fauxfactory
import pytest
import pytz
from dateutil import parser, relativedelta

from cfme.base.ui import BaseLoggedInPage
from cfme.infrastructure.provider.virtualcenter import VMwareProvider
from cfme.markers.env_markers.provider import ONE
from cfme.utils.appliance.implementations.ui import navigate_to
from cfme.utils.wait import wait_for
from cfme.utils.hosts import setup_host_creds
from cfme.utils.update import update

pytestmark = [
pytest.mark.provider([VMwareProvider], required_fields=['hosts'], selector=ONE, scope='module'),
pytest.mark.usefixtures("setup_provider")
]

run_types = (
['Once', None, None],
['Hourly', 'hours', 1],
['Daily', 'days', 1],
['Weekly', 'weeks', 1],
['Monthly', 'months', 1]
)


@pytest.yield_fixture(scope='module')
def host_with_credentials(appliance, provider):
""" Add credentials to hosts """
host = provider.hosts[0]
setup_host_creds(provider, host.name)
yield host
setup_host_creds(provider, host.name, remove_creds=True)


@pytest.fixture
def current_server_time(appliance):
current_time = parser.parse(appliance.ssh_client.run_command('date').output)
tz_list = appliance.ssh_client.run_command("timedatectl | grep 'Time zone'") \
.output.strip().split(' ')

tz_name = tz_list[2]
tz_num = tz_list[-1][:-1]
date = current_time.replace(tzinfo=pytz.timezone(tz_name))
return date, tz_num


def round_min(value, base=5):
return (0 if int(base * round(float(value) / base)) == 60
else int(base * round(float(value) / base)))


def test_schedule_crud(appliance, current_server_time):
current_time, _ = current_server_time
start_date = current_time + relativedelta.relativedelta(days=2)
schedule = appliance.collections.system_schedules.create(
name=fauxfactory.gen_alphanumeric(),
description=fauxfactory.gen_alphanumeric(),
start_date=start_date
)

view = appliance.browser.create_view(BaseLoggedInPage)
view.flash.assert_message('Schedule "{}" was saved'.format(schedule.name))
# test for bz 1569127
start_date_updated = start_date - relativedelta.relativedelta(days=1)
updates = {
'name': fauxfactory.gen_alphanumeric(),
'description': fauxfactory.gen_alphanumeric(),
}
schedule.update(updates, cancel=True)
view.flash.assert_message(
'Edit of Schedule "{}" was cancelled by the user'.format(schedule.name))
schedule.update(updates, reset=True)
view.flash.assert_message('All changes have been reset')
with update(schedule):
schedule.name = fauxfactory.gen_alphanumeric()
schedule.start_date = start_date_updated
view.flash.assert_message('Schedule "{}" was saved'.format(schedule.name))
schedule.delete(cancel=True)
schedule.delete()
view.flash.assert_message('Schedule "{}": Delete successful'.format(schedule.description))


def test_schedule_analysis_in_the_past(appliance, current_server_time, request):
current_time, _ = current_server_time
past_time = current_time - relativedelta.relativedelta(minutes=5)
if round_min(past_time.minute) == 0:
past_time = past_time + relativedelta.relativedelta(hours=1)
past_time_minute = '0'
else:
past_time_minute = str(round_min(past_time.minute))
schedule = appliance.collections.system_schedules.create(
name=fauxfactory.gen_alphanumeric(),
description=fauxfactory.gen_alphanumeric(),
start_hour=str(past_time.hour),
start_minute=past_time_minute
)
request.addfinalizer(schedule.delete)
view = appliance.browser.create_view(BaseLoggedInPage)
assert (
"Warning: This 'Run Once' timer is in the past and will never"
" run as currently configured" in [message.text for message in view.flash.messages]
)


def test_create_multiple_schedules_in_one_timezone(appliance, request):
schedule_list = []
request.addfinalizer(lambda: map(lambda item: item.delete(), schedule_list))

for i in range(6):
schedule = appliance.collections.system_schedules.create(
name=fauxfactory.gen_alphanumeric(),
description=fauxfactory.gen_alphanumeric(),
time_zone='(GMT-04:00) Atlantic Time (Canada)'
)
view = appliance.browser.create_view(BaseLoggedInPage)
view.flash.assert_message('Schedule "{}" was saved'.format(schedule.name))
schedule_list.append(schedule)


def test_inactive_schedule(appliance, current_server_time):
current_time, _ = current_server_time
start_date = current_time + relativedelta.relativedelta(minutes=5)

schedule = appliance.collections.system_schedules.create(
name=fauxfactory.gen_alphanumeric(),
description=fauxfactory.gen_alphanumeric(),
start_date=start_date,
start_hour=str(start_date.hour),
start_minute=str(round_min(start_date.minute)),

)
assert schedule.next_run_date
schedule.disable()
assert not schedule.next_run_date


@pytest.mark.parametrize('run_types', run_types, ids=[type[0] for type in run_types])
def test_schedule_timer(appliance, run_types, host_with_credentials, request, current_server_time):

run_time, time_diff, time_num = run_types
current_time, tz_num = current_server_time
start_date = current_time + relativedelta.relativedelta(minutes=5)
view = navigate_to(appliance.collections.system_schedules, 'Add')
available_list = view.time_zone.all_options
# bz is here 1559904
for tz in available_list:
if '{}:00'.format(tz_num[0:3]) in tz.text and 'Atlantic Time (Canada)' not in tz.text:
tz_select = tz.text
break
if round_min(start_date.minute) == 0:
start_date = start_date + relativedelta.relativedelta(minutes=60 - start_date.minute)
start_date_minute = str(start_date.minute)
else:
start_date_minute = str(round_min(start_date.minute))
schedule = appliance.collections.system_schedules.create(
name=fauxfactory.gen_alphanumeric(),
description=fauxfactory.gen_alphanumeric(),
action_type='Host Analysis',
filter_level1='A single Host',
filter_level2=host_with_credentials.name,
run_type=run_time,
start_date=start_date,
time_zone=tz_select,
start_hour=str(start_date.hour),
start_minute=start_date_minute,

)

@request.addfinalizer
def _finalize():
if schedule.exists:
schedule.delete()

wait_for(lambda: schedule.last_run_date != '',
delay=60, timeout="10m", fail_func=appliance.server.browser.refresh,
message="Scheduled task didn't run in first time")

if time_diff:
next_date = parser.parse(schedule.next_run_date)
up = {time_diff: time_num}
next_run_date = start_date + relativedelta.relativedelta(minutes=-5, **up)
appliance.ssh_client.run_command("date {}".format(next_run_date.strftime('%m%d%H%M%Y')))

wait_for(
lambda: next_date.strftime('%m%d%H') == parser.parse(
schedule.last_run_date).strftime('%m%d%H'),
delay=60, timeout="10m", fail_func=appliance.server.browser.refresh,
message="Scheduled task didn't run in appropriate time set")

0 comments on commit c76b828

Please sign in to comment.