Adding ElementSW SnapShot Schedule Module (#43974)

* Adding ElementSW SnapShot Schedule Module
pull/44648/head
Chris Archibald 6 years ago committed by John R Barker
parent d7efcc4161
commit 448db900dd

@ -0,0 +1,555 @@
#!/usr/bin/python
# (c) 2017, NetApp, Inc
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
"""Element SW Software Snapshot Schedule"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
module: na_elementsw_snapshot_schedule
short_description: NetApp Element Software Snapshot Schedules
extends_documentation_fragment:
- netapp.solidfire
version_added: '2.7'
author: NetApp Ansible Team (ng-ansibleteam@netapp.com)
description:
- Create, destroy, or update accounts on ElementSW
options:
state:
description:
- Whether the specified schedule should exist or not.
required: true
choices: ['present', 'absent']
paused:
description:
- Pause / Resume a schedule.
type: bool
recurring:
description:
- Should the schedule recur?
type: bool
schedule_type:
description:
- Schedule type for creating schedule.
choices: ['DaysOfWeekFrequency','DaysOfMonthFrequency','TimeIntervalFrequency']
time_interval_days:
description: Time interval in days.
default: 1
time_interval_hours:
description: Time interval in hours.
default: 0
time_interval_minutes:
description: Time interval in minutes.
default: 0
days_of_week_weekdays:
description: List of days of the week (Sunday to Saturday)
days_of_week_hours:
description: Time specified in hours
default: 0
days_of_week_minutes:
description: Time specified in minutes.
default: 0
days_of_month_monthdays:
description: List of days of the month (1-31)
days_of_month_hours:
description: Time specified in hours
default: 0
days_of_month_minutes:
description: Time specified in minutes.
default: 0
name:
description:
- Name for the snapshot schedule.
- It accepts either schedule_id or schedule_name
- if name is digit, it will consider as schedule_id
- If name is string, it will consider as schedule_name
snapshot_name:
description:
- Name for the created snapshots.
volumes:
description:
- Volume IDs that you want to set the snapshot schedule for.
- It accepts both volume_name and volume_id
account_id:
description:
- Account ID for the owner of this volume.
- It accepts either account_name or account_id
- if account_id is digit, it will consider as account_id
- If account_id is string, it will consider as account_name
retention:
description:
- Retention period for the snapshot.
- Format is 'HH:mm:ss'.
starting_date:
description:
- Starting date for the schedule.
- Required when C(state=present).
- "Format: C(2016-12-01T00:00:00Z)"
password:
description:
- Element SW access account password
aliases:
- pass
username:
description:
- Element SW access account user-name
aliases:
- user
'''
EXAMPLES = """
- name: Create Snapshot schedule
na_elementsw_snapshot_schedule:
hostname: "{{ elementsw_hostname }}"
username: "{{ elementsw_username }}"
password: "{{ elementsw_password }}"
state: present
name: Schedule_A
schedule_type: TimeIntervalFrequency
time_interval_days: 1
starting_date: '2016-12-01T00:00:00Z'
volumes:
- 7
- test
account_id: 1
- name: Update Snapshot schedule
na_elementsw_snapshot_schedule:
hostname: "{{ elementsw_hostname }}"
username: "{{ elementsw_username }}"
password: "{{ elementsw_password }}"
state: present
name: Schedule_A
schedule_type: TimeIntervalFrequency
time_interval_days: 1
starting_date: '2016-12-01T00:00:00Z'
volumes:
- 8
- test1
account_id: 1
- name: Delete Snapshot schedule
na_elementsw_snapshot_schedule:
hostname: "{{ elementsw_hostname }}"
username: "{{ elementsw_username }}"
password: "{{ elementsw_password }}"
state: absent
name: 6
"""
RETURN = """
schedule_id:
description: Schedule ID of the newly created schedule
returned: success
type: string
"""
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
import ansible.module_utils.netapp as netapp_utils
from ansible.module_utils.netapp_elementsw_module import NaElementSWModule
HAS_SF_SDK = netapp_utils.has_sf_sdk()
try:
from solidfire.custom.models import DaysOfWeekFrequency, Weekday, DaysOfMonthFrequency
from solidfire.common import ApiServerError
except:
HAS_SF_SDK = False
class ElementSWSnapShotSchedule(object):
"""
Contains methods to parse arguments,
derive details of ElementSW objects
and send requests to ElementSW via
the ElementSW SDK
"""
def __init__(self):
"""
Parse arguments, setup state variables,
check paramenters and ensure SDK is installed
"""
self.argument_spec = netapp_utils.ontap_sf_host_argument_spec()
self.argument_spec.update(dict(
state=dict(required=True, choices=['present', 'absent']),
name=dict(required=True, type='str'),
schedule_type=dict(required=False, choices=['DaysOfWeekFrequency', 'DaysOfMonthFrequency', 'TimeIntervalFrequency']),
time_interval_days=dict(required=False, type='int', default=1),
time_interval_hours=dict(required=False, type='int', default=0),
time_interval_minutes=dict(required=False, type='int', default=0),
days_of_week_weekdays=dict(required=False, type='list'),
days_of_week_hours=dict(required=False, type='int', default=0),
days_of_week_minutes=dict(required=False, type='int', default=0),
days_of_month_monthdays=dict(required=False, type='list'),
days_of_month_hours=dict(required=False, type='int', default=0),
days_of_month_minutes=dict(required=False, type='int', default=0),
paused=dict(required=False, type='bool'),
recurring=dict(required=False, type='bool'),
starting_date=dict(required=False, type='str'),
snapshot_name=dict(required=False, type='str'),
volumes=dict(required=False, type='list'),
account_id=dict(required=False, type='str'),
retention=dict(required=False, type='str'),
))
self.module = AnsibleModule(
argument_spec=self.argument_spec,
required_if=[
('state', 'present', ['account_id', 'volumes', 'schedule_type']),
('schedule_type', 'DaysOfMonthFrequency', ['days_of_month_monthdays']),
('schedule_type', 'DaysOfWeekFrequency', ['days_of_week_weekdays'])
],
supports_check_mode=True
)
param = self.module.params
# set up state variables
self.state = param['state']
self.name = param['name']
self.schedule_type = param['schedule_type']
self.days_of_week_weekdays = param['days_of_week_weekdays']
self.days_of_week_hours = param['days_of_week_hours']
self.days_of_week_minutes = param['days_of_week_minutes']
self.days_of_month_monthdays = param['days_of_month_monthdays']
self.days_of_month_hours = param['days_of_month_hours']
self.days_of_month_minutes = param['days_of_month_minutes']
self.time_interval_days = param['time_interval_days']
self.time_interval_hours = param['time_interval_hours']
self.time_interval_minutes = param['time_interval_minutes']
self.paused = param['paused']
self.recurring = param['recurring']
if self.schedule_type == 'DaysOfWeekFrequency':
# Create self.weekday list if self.schedule_type is days_of_week
if self.days_of_week_weekdays is not None:
# Create self.weekday list if self.schedule_type is days_of_week
self.weekdays = []
for day in self.days_of_week_weekdays:
if str(day).isdigit():
# If id specified, return appropriate day
self.weekdays.append(Weekday.from_id(int(day)))
else:
# If name specified, return appropriate day
self.weekdays.append(Weekday.from_name(day.capitalize()))
if self.state == 'present' and self.schedule_type is None:
# Mandate schedule_type for create operation
self.module.fail_json(
msg="Please provide required parameter: schedule_type")
# Mandate schedule name for delete operation
if self.state == 'absent' and self.name is None:
self.module.fail_json(
msg="Please provide required parameter: name")
self.starting_date = param['starting_date']
self.snapshot_name = param['snapshot_name']
self.volumes = param['volumes']
self.account_id = param['account_id']
self.retention = param['retention']
self.create_schedule_result = None
if HAS_SF_SDK is False:
# Create ElementSW connection
self.module.fail_json(msg="Unable to import the ElementSW Python SDK")
else:
self.sfe = netapp_utils.create_sf_connection(module=self.module)
self.elementsw_helper = NaElementSWModule(self.sfe)
def get_schedule(self):
# Checking whether schedule id is exist or not
# Return schedule details if found, None otherwise
# If exist set variable self.name
try:
schedule_list = self.sfe.list_schedules()
except ApiServerError:
return None
for schedule in schedule_list.schedules:
if str(schedule.schedule_id) == self.name:
self.name = schedule.name
return schedule
elif schedule.name == self.name:
return schedule
return None
def get_account_id(self):
# Validate account id
# Return account_id if found, None otherwise
try:
account_id = self.elementsw_helper.account_exists(self.account_id)
return account_id
except ApiServerError:
return None
def get_volume_id(self):
# Validate volume_ids
# Return volume ids if found, fail if not found
volume_ids = []
for volume in self.volumes:
volume_id = self.elementsw_helper.volume_exists(volume.strip(), self.account_id)
if volume_id:
volume_ids.append(volume_id)
else:
self.module.fail_json(msg='Specified volume %s does not exist' % volume)
return volume_ids
def get_frequency(self):
# Configuring frequency depends on self.schedule_type
frequency = None
if self.schedule_type is not None and self.schedule_type == 'DaysOfWeekFrequency':
if self.weekdays is not None:
frequency = DaysOfWeekFrequency(weekdays=self.weekdays,
hours=self.days_of_week_hours,
minutes=self.days_of_week_minutes)
elif self.schedule_type is not None and self.schedule_type == 'DaysOfMonthFrequency':
if self.days_of_month_monthdays is not None:
frequency = DaysOfMonthFrequency(monthdays=self.days_of_month_monthdays,
hours=self.days_of_month_hours,
minutes=self.days_of_month_minutes)
elif self.schedule_type is not None and self.schedule_type == 'TimeIntervalFrequency':
frequency = netapp_utils.TimeIntervalFrequency(days=self.time_interval_days,
hours=self.time_interval_hours,
minutes=self.time_interval_minutes)
return frequency
def is_same_schedule_type(self, schedule_detail):
# To check schedule type is same or not
if str(schedule_detail.frequency).split('(')[0] == self.schedule_type:
return True
else:
return False
def create_schedule(self):
# Create schedule
try:
frequency = self.get_frequency()
if frequency is None:
self.module.fail_json(msg='Failed to create schedule frequency object - type %s parameters' % self.schedule_type)
# Create schedule
name = self.name
schedule_info = netapp_utils.ScheduleInfo(
volume_ids=self.volumes,
snapshot_name=self.snapshot_name,
retention=self.retention
)
sched = netapp_utils.Schedule(schedule_info, name, frequency)
sched.paused = self.paused
sched.recurring = self.recurring
sched.starting_date = self.starting_date
self.create_schedule_result = self.sfe.create_schedule(sched)
except Exception as e:
self.module.fail_json(msg='Error creating schedule %s: %s' % (self.name, to_native(e.message)),
exception=traceback.format_exc())
def delete_schedule(self, schedule_id):
# delete schedule
try:
get_schedule_result = self.sfe.get_schedule(schedule_id=schedule_id)
sched = get_schedule_result.schedule
sched.to_be_deleted = True
self.sfe.modify_schedule(schedule=sched)
except Exception as e:
self.module.fail_json(msg='Error deleting schedule %s: %s' % (self.name, to_native(e.message)),
exception=traceback.format_exc())
def update_schedule(self, schedule_id):
# Update schedule
try:
get_schedule_result = self.sfe.get_schedule(schedule_id=schedule_id)
sched = get_schedule_result.schedule
# Update schedule properties
sched.frequency = self.get_frequency()
if sched.frequency is None:
self.module.fail_json(msg='Failed to create schedule frequency object - type %s parameters' % self.schedule_type)
if self.volumes is not None and len(self.volumes) > 0:
sched.schedule_info.volume_ids = self.volumes
if self.retention is not None:
sched.schedule_info.retention = self.retention
if self.snapshot_name is not None:
sched.schedule_info.snapshot_name = self.snapshot_name
if self.paused is not None:
sched.paused = self.paused
if self.recurring is not None:
sched.recurring = self.recurring
if self.starting_date is not None:
sched.starting_date = self.starting_date
# Make API call
self.sfe.modify_schedule(schedule=sched)
except Exception as e:
self.module.fail_json(msg='Error updating schedule %s: %s' % (self.name, to_native(e.message)),
exception=traceback.format_exc())
def apply(self):
# Perform pre-checks, call functions and exit
changed = False
update_schedule = False
if self.account_id is not None:
self.account_id = self.get_account_id()
if self.state == 'present' and self.volumes is not None:
if self.account_id:
self.volumes = self.get_volume_id()
else:
self.module.fail_json(msg='Specified account id does not exist')
# Getting the schedule details
schedule_detail = self.get_schedule()
if schedule_detail is None and self.state == 'present':
if len(self.volumes) > 0:
changed = True
else:
self.module.fail_json(msg='Specified volumes not on cluster')
elif schedule_detail is not None:
# Getting the schedule id
if self.state == 'absent':
changed = True
else:
# Check if we need to update the account
if self.retention is not None and schedule_detail.schedule_info.retention != self.retention:
update_schedule = True
changed = True
elif self.snapshot_name is not None and schedule_detail.schedule_info.snapshot_name != self.snapshot_name:
update_schedule = True
changed = True
elif self.paused is not None and schedule_detail.paused != self.paused:
update_schedule = True
changed = True
elif self.recurring is not None and schedule_detail.recurring != self.recurring:
update_schedule = True
changed = True
elif self.starting_date is not None and schedule_detail.starting_date != self.starting_date:
update_schedule = True
changed = True
elif self.volumes is not None and len(self.volumes) > 0:
for volumeID in schedule_detail.schedule_info.volume_ids:
if volumeID not in self.volumes:
update_schedule = True
changed = True
temp_frequency = self.get_frequency()
if temp_frequency is not None:
# Checking schedule_type changes
if self.is_same_schedule_type(schedule_detail):
# If same schedule type
if self.schedule_type == "TimeIntervalFrequency":
# Check if there is any change in schedule.frequency, If schedule_type is time_interval
if schedule_detail.frequency.days != temp_frequency.days or \
schedule_detail.frequency.hours != temp_frequency.hours or \
schedule_detail.frequency.minutes != temp_frequency.minutes:
update_schedule = True
changed = True
elif self.schedule_type == "DaysOfMonthFrequency":
# Check if there is any change in schedule.frequency, If schedule_type is days_of_month
if len(schedule_detail.frequency.monthdays) != len(temp_frequency.monthdays) or \
schedule_detail.frequency.hours != temp_frequency.hours or \
schedule_detail.frequency.minutes != temp_frequency.minutes:
update_schedule = True
changed = True
elif len(schedule_detail.frequency.monthdays) == len(temp_frequency.monthdays):
actual_frequency_monthday = schedule_detail.frequency.monthdays
temp_frequency_monthday = temp_frequency.monthdays
for monthday in actual_frequency_monthday:
if monthday not in temp_frequency_monthday:
update_schedule = True
changed = True
elif self.schedule_type == "DaysOfWeekFrequency":
# Check if there is any change in schedule.frequency, If schedule_type is days_of_week
if len(schedule_detail.frequency.weekdays) != len(temp_frequency.weekdays) or \
schedule_detail.frequency.hours != temp_frequency.hours or \
schedule_detail.frequency.minutes != temp_frequency.minutes:
update_schedule = True
changed = True
elif len(schedule_detail.frequency.weekdays) == len(temp_frequency.weekdays):
actual_frequency_weekdays = schedule_detail.frequency.weekdays
temp_frequency_weekdays = temp_frequency.weekdays
if len([actual_weekday for actual_weekday, temp_weekday in
zip(actual_frequency_weekdays, temp_frequency_weekdays) if actual_weekday != temp_weekday]) != 0:
update_schedule = True
changed = True
else:
update_schedule = True
changed = True
else:
self.module.fail_json(msg='Failed to create schedule frequency object - type %s parameters' % self.schedule_type)
result_message = " "
if changed:
if self.module.check_mode:
# Skip changes
result_message = "Check mode, skipping changes"
else:
if self.state == 'present':
if update_schedule:
self.update_schedule(schedule_detail.schedule_id)
result_message = "Snapshot Schedule modified"
else:
self.create_schedule()
result_message = "Snapshot Schedule created"
elif self.state == 'absent':
self.delete_schedule(schedule_detail.schedule_id)
result_message = "Snapshot Schedule deleted"
self.module.exit_json(changed=changed, msg=result_message)
def main():
v = ElementSWSnapShotSchedule()
v.apply()
if __name__ == '__main__':
main()
Loading…
Cancel
Save