1#!/usr/bin/python
2
3# (c) 2018-2019, NetApp, Inc
4# GNU General Public License v3.0+
5# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6
7from __future__ import absolute_import, division, print_function
8__metaclass__ = type
9
10ANSIBLE_METADATA = {'metadata_version': '1.1',
11                    'status': ['preview'],
12                    'supported_by': 'certified'}
13
14
15DOCUMENTATION = '''
16
17module: na_ontap_volume
18
19short_description: NetApp ONTAP manage volumes.
20extends_documentation_fragment:
21    - netapp.na_ontap
22version_added: '2.6'
23author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
24
25description:
26- Create or destroy or modify volumes on NetApp ONTAP.
27
28options:
29
30  state:
31    description:
32    - Whether the specified volume should exist or not.
33    choices: ['present', 'absent']
34    default: 'present'
35
36  name:
37    description:
38    - The name of the volume to manage.
39    type: str
40    required: true
41
42  vserver:
43    description:
44    - Name of the vserver to use.
45    type: str
46    required: true
47
48  from_name:
49    description:
50    - Name of the existing volume to be renamed to name.
51    type: str
52    version_added: '2.7'
53
54  is_infinite:
55    type: bool
56    description:
57      Set True if the volume is an Infinite Volume.
58      Deleting an infinite volume is asynchronous.
59
60  is_online:
61    type: bool
62    description:
63    - Whether the specified volume is online, or not.
64    default: True
65
66  aggregate_name:
67    description:
68    - The name of the aggregate the flexvol should exist on.
69    - Required when C(state=present).
70    type: str
71
72  size:
73    description:
74    - The size of the volume in (size_unit). Required when C(state=present).
75    type: int
76
77  size_unit:
78    description:
79    - The unit used to interpret the size parameter.
80    choices: ['bytes', 'b', 'kb', 'mb', 'gb', 'tb', 'pb', 'eb', 'zb', 'yb']
81    type: str
82    default: 'gb'
83
84  type:
85    description:
86    - The volume type, either read-write (RW) or data-protection (DP).
87    type: str
88
89  policy:
90    description:
91    - Name of the export policy.
92    type: str
93
94  junction_path:
95    description:
96    - Junction path of the volume.
97    - To unmount, use junction path C('').
98    type: str
99
100  space_guarantee:
101    description:
102    - Space guarantee style for the volume.
103    choices: ['none', 'file', 'volume']
104    type: str
105
106  percent_snapshot_space:
107    description:
108    - Amount of space reserved for snapshot copies of the volume.
109    type: int
110
111  volume_security_style:
112    description:
113    - The security style associated with this volume.
114    choices: ['mixed', 'ntfs', 'unified', 'unix']
115    default: 'mixed'
116    type: str
117
118  encrypt:
119    type: bool
120    description:
121    - Whether or not to enable Volume Encryption.
122    default: False
123    version_added: '2.7'
124
125  efficiency_policy:
126    description:
127    - Allows a storage efficiency policy to be set on volume creation.
128    type: str
129    version_added: '2.7'
130
131  unix_permissions:
132    description:
133    - Unix permission bits in octal or symbolic format.
134    - For example, 0 is equivalent to ------------, 777 is equivalent to ---rwxrwxrwx,both formats are accepted.
135    - The valid octal value ranges between 0 and 777 inclusive.
136    type: str
137    version_added: '2.8'
138
139  snapshot_policy:
140    description:
141    - The name of the snapshot policy.
142    - the default policy name is 'default'.
143    type: str
144    version_added: '2.8'
145
146  aggr_list:
147    description:
148    -  an array of names of aggregates to be used for FlexGroup constituents.
149    type: list
150    version_added: '2.8'
151
152  aggr_list_multiplier:
153    description:
154    -  The number of times to iterate over the aggregates listed with the aggr_list parameter when creating a FlexGroup.
155    type: int
156    version_added: '2.8'
157
158  auto_provision_as:
159    description:
160    - Automatically provision a FlexGroup volume.
161    version_added: '2.8'
162    choices: ['flexgroup']
163    type: str
164
165  snapdir_access:
166    description:
167    - This is an advanced option, the default is False.
168    - Enable the visible '.snapshot' directory that is normally present at system internal mount points.
169    - This value also turns on access to all other '.snapshot' directories in the volume.
170    type: bool
171    version_added: '2.8'
172
173  atime_update:
174    description:
175    - This is an advanced option, the default is True.
176    - If false, prevent the update of inode access times when a file is read.
177    - This value is useful for volumes with extremely high read traffic,
178      since it prevents writes to the inode file for the volume from contending with reads from other files.
179    - This field should be used carefully.
180    - That is, use this field when you know in advance that the correct access time for inodes will not be needed for files on that volume.
181    type: bool
182    version_added: '2.8'
183
184  wait_for_completion:
185    description:
186    - Set this parameter to 'true' for synchronous execution during create (wait until volume status is online)
187    - Set this parameter to 'false' for asynchronous execution
188    - For asynchronous, execution exits as soon as the request is sent, without checking volume status
189    type: bool
190    default: false
191    version_added: '2.8'
192
193  time_out:
194    description:
195    - time to wait for flexGroup creation, modification, or deletion in seconds.
196    - Error out if task is not completed in defined time.
197    - if 0, the request is asynchronous.
198    - default is set to 3 minutes.
199    default: 180
200    type: int
201    version_added: '2.8'
202
203  language:
204    description:
205    - Language to use for Volume
206    - Default uses SVM language
207    - Possible values   Language
208    - c                 POSIX
209    - ar                Arabic
210    - cs                Czech
211    - da                Danish
212    - de                German
213    - en                English
214    - en_us             English (US)
215    - es                Spanish
216    - fi                Finnish
217    - fr                French
218    - he                Hebrew
219    - hr                Croatian
220    - hu                Hungarian
221    - it                Italian
222    - ja                Japanese euc-j
223    - ja_v1             Japanese euc-j
224    - ja_jp.pck         Japanese PCK (sjis)
225    - ja_jp.932         Japanese cp932
226    - ja_jp.pck_v2      Japanese PCK (sjis)
227    - ko                Korean
228    - no                Norwegian
229    - nl                Dutch
230    - pl                Polish
231    - pt                Portuguese
232    - ro                Romanian
233    - ru                Russian
234    - sk                Slovak
235    - sl                Slovenian
236    - sv                Swedish
237    - tr                Turkish
238    - zh                Simplified Chinese
239    - zh.gbk            Simplified Chinese (GBK)
240    - zh_tw             Traditional Chinese euc-tw
241    - zh_tw.big5        Traditional Chinese Big 5
242    - To use UTF-8 as the NFS character set, append '.UTF-8' to the language code
243    type: str
244    version_added: '2.8'
245
246  qos_policy_group:
247    description:
248    - Specifies a QoS policy group to be set on volume.
249    version_added: '2.9'
250
251  qos_adaptive_policy_group:
252    description:
253    - Specifies a QoS adaptive policy group to be set on volume.
254    version_added: '2.9'
255
256  tiering_policy:
257    description:
258    - The tiering policy that is to be associated with the volume.
259    - This policy decides whether the blocks of a volume will be tiered to the capacity tier.
260    - snapshot-only policy allows tiering of only the volume snapshot copies not associated with the active file system.
261    - auto policy allows tiering of both snapshot and active file system user data to the capacity tier.
262    - backup policy on DP volumes allows all transferred user data blocks to start in the capacity tier.
263    - When set to none, the Volume blocks will not be tiered to the capacity tier.
264    - If no value specified, the volume is assigned snapshot only by default.
265    choices: ['snapshot-only', 'auto', 'backup', 'none']
266    type: str
267    version_added: '2.9'
268
269  space_slo:
270    description:
271    - Specifies the space SLO type for the volume. The space SLO type is the Service Level Objective for space management for the volume.
272    - The space SLO value is used to enforce existing volume settings so that sufficient space is set aside on the aggregate to meet the space SLO.
273    - This parameter is not supported on Infinite Volumes.
274    choices: ['none', 'thick', 'semi-thick']
275    type: str
276    version_added: '2.9'
277
278  nvfail_enabled:
279    description:
280    - If true, the controller performs additional work at boot and takeover times if it finds that there has been any potential data loss in the volume's
281      constituents due to an NVRAM failure.
282    - The volume's constituents would be put in a special state called 'in-nvfailed-state' such that protocol access is blocked.
283    - This will cause the client applications to crash and thus prevent access to stale data.
284    - To get out of this situation, the admin needs to manually clear the 'in-nvfailed-state' on the volume's constituents.
285    type: bool
286    version_added: '2.9'
287
288  vserver_dr_protection:
289    description:
290    - Specifies the protection type for the volume in a Vserver DR setup.
291    choices: ['protected', 'unprotected']
292    type: str
293    version_added: '2.9'
294
295  comment:
296    description:
297    - Sets a comment associated with the volume.
298    type: str
299    version_added: '2.9'
300'''
301
302EXAMPLES = """
303
304    - name: Create FlexVol
305      na_ontap_volume:
306        state: present
307        name: ansibleVolume12
308        is_infinite: False
309        aggregate_name: ansible_aggr
310        size: 100
311        size_unit: mb
312        space_guarantee: none
313        tiering_policy: auto
314        policy: default
315        percent_snapshot_space: 60
316        qos_policy_group: max_performance_gold
317        vserver: ansibleVServer
318        wait_for_completion: True
319        space_slo: none
320        nvfail_enabled: False
321        comment: ansible created volume
322        hostname: "{{ netapp_hostname }}"
323        username: "{{ netapp_username }}"
324        password: "{{ netapp_password }}"
325
326    - name: Volume Delete
327      na_ontap_volume:
328        state: absent
329        name: ansibleVolume12
330        aggregate_name: ansible_aggr
331        vserver: ansibleVServer
332        hostname: "{{ netapp_hostname }}"
333        username: "{{ netapp_username }}"
334        password: "{{ netapp_password }}"
335
336    - name: Make FlexVol offline
337      na_ontap_volume:
338        state: present
339        name: ansibleVolume
340        is_infinite: False
341        is_online: False
342        vserver: ansibleVServer
343        hostname: "{{ netapp_hostname }}"
344        username: "{{ netapp_username }}"
345        password: "{{ netapp_password }}"
346
347    - name: Create flexGroup volume manually
348      na_ontap_volume:
349        state: present
350        name: ansibleVolume
351        is_infinite: False
352        aggr_list: "{{ aggr_list }}"
353        aggr_list_multiplier: 2
354        size: 200
355        size_unit: mb
356        space_guarantee: none
357        policy: default
358        vserver: "{{ vserver }}"
359        hostname: "{{ netapp_hostname }}"
360        username: "{{ netapp_username }}"
361        password: "{{ netapp_password }}"
362        https: False
363        unix_permissions: 777
364        snapshot_policy: default
365        time_out: 0
366
367    - name: Create flexGroup volume auto provision as flex group
368      na_ontap_volume:
369        state: present
370        name: ansibleVolume
371        is_infinite: False
372        auto_provision_as: flexgroup
373        size: 200
374        size_unit: mb
375        space_guarantee: none
376        policy: default
377        vserver: "{{ vserver }}"
378        hostname: "{{ netapp_hostname }}"
379        username: "{{ netapp_username }}"
380        password: "{{ netapp_password }}"
381        https: False
382        unix_permissions: 777
383        snapshot_policy: default
384        time_out: 0
385
386    - name: Create FlexVol with QoS adaptive
387      na_ontap_volume:
388        state: present
389        name: ansibleVolume15
390        is_infinite: False
391        aggregate_name: ansible_aggr
392        size: 100
393        size_unit: gb
394        space_guarantee: none
395        policy: default
396        percent_snapshot_space: 10
397        qos_adaptive_policy_group: extreme
398        vserver: ansibleVServer
399        wait_for_completion: True
400        hostname: "{{ netapp_hostname }}"
401        username: "{{ netapp_username }}"
402        password: "{{ netapp_password }}"
403
404    - name: Modify volume dr protection (vserver of the volume must be in a snapmirror relationship)
405      na_ontap_volume:
406        state: present
407        name: ansibleVolume
408        vserver_dr_protection: protected
409        vserver: "{{ vserver }}"
410        hostname: "{{ netapp_hostname }}"
411        username: "{{ netapp_username }}"
412        password: "{{ netapp_password }}"
413        https: False
414
415"""
416
417RETURN = """
418"""
419
420import time
421import traceback
422import ansible.module_utils.netapp as netapp_utils
423from ansible.module_utils.netapp_module import NetAppModule
424from ansible.module_utils.basic import AnsibleModule
425from ansible.module_utils._text import to_native
426
427HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
428
429
430class NetAppOntapVolume(object):
431    '''Class with volume operations'''
432
433    def __init__(self):
434        '''Initialize module parameters'''
435        self._size_unit_map = dict(
436            bytes=1,
437            b=1,
438            kb=1024,
439            mb=1024 ** 2,
440            gb=1024 ** 3,
441            tb=1024 ** 4,
442            pb=1024 ** 5,
443            eb=1024 ** 6,
444            zb=1024 ** 7,
445            yb=1024 ** 8
446        )
447
448        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
449        self.argument_spec.update(dict(
450            state=dict(required=False, choices=[
451                       'present', 'absent'], default='present'),
452            name=dict(required=True, type='str'),
453            vserver=dict(required=True, type='str'),
454            from_name=dict(required=False, type='str'),
455            is_infinite=dict(required=False, type='bool',
456                             default=False),
457            is_online=dict(required=False, type='bool',
458                           default=True),
459            size=dict(type='int', default=None),
460            size_unit=dict(default='gb',
461                           choices=['bytes', 'b', 'kb', 'mb', 'gb', 'tb',
462                                    'pb', 'eb', 'zb', 'yb'], type='str'),
463            aggregate_name=dict(type='str', default=None),
464            type=dict(type='str', default=None),
465            policy=dict(type='str', default=None),
466            junction_path=dict(type='str', default=None),
467            space_guarantee=dict(choices=['none', 'file', 'volume'], default=None),
468            percent_snapshot_space=dict(type='int', default=None),
469            volume_security_style=dict(choices=['mixed',
470                                                'ntfs', 'unified', 'unix'],
471                                       default='mixed'),
472            encrypt=dict(required=False, type='bool', default=False),
473            efficiency_policy=dict(required=False, type='str'),
474            unix_permissions=dict(required=False, type='str'),
475            snapshot_policy=dict(required=False, type='str'),
476            aggr_list=dict(required=False, type='list'),
477            aggr_list_multiplier=dict(required=False, type='int'),
478            snapdir_access=dict(required=False, type='bool'),
479            atime_update=dict(required=False, type='bool'),
480            auto_provision_as=dict(choices=['flexgroup'], required=False, type='str'),
481            wait_for_completion=dict(required=False, type='bool', default=False),
482            time_out=dict(required=False, type='int', default=180),
483            language=dict(type='str', required=False),
484            qos_policy_group=dict(required=False, type='str'),
485            qos_adaptive_policy_group=dict(required=False, type='str'),
486            nvfail_enabled=dict(type='bool', required=False),
487            space_slo=dict(type='str', required=False, choices=['none', 'thick', 'semi-thick']),
488            tiering_policy=dict(type='str', required=False, choices=['snapshot-only', 'auto',
489                                                                     'backup', 'none']),
490            vserver_dr_protection=dict(type='str', required=False, choices=['protected', 'unprotected']),
491            comment=dict(type='str', required=False)
492
493        ))
494        self.module = AnsibleModule(
495            argument_spec=self.argument_spec,
496            supports_check_mode=True
497        )
498        self.na_helper = NetAppModule()
499        self.parameters = self.na_helper.set_parameters(self.module.params)
500        self.volume_style = None
501
502        if self.parameters.get('size'):
503            self.parameters['size'] = self.parameters['size'] * \
504                self._size_unit_map[self.parameters['size_unit']]
505        # ONTAP will return True and False as the string true and false.
506        if 'snapdir_access' in self.parameters:
507            self.parameters['snapdir_access'] = str(self.parameters['snapdir_access']).lower()
508        if 'atime_update' in self.parameters:
509            self.parameters['atime_update'] = str(self.parameters['atime_update']).lower()
510        if HAS_NETAPP_LIB is False:
511            self.module.fail_json(
512                msg="the python NetApp-Lib module is required")
513        else:
514            self.server = netapp_utils.setup_na_ontap_zapi(
515                module=self.module, vserver=self.parameters['vserver'])
516            self.cluster = netapp_utils.setup_na_ontap_zapi(module=self.module)
517
518    def volume_get_iter(self, vol_name=None):
519        """
520        Return volume-get-iter query results
521        :param vol_name: name of the volume
522        :return: NaElement
523        """
524        volume_info = netapp_utils.zapi.NaElement('volume-get-iter')
525        volume_attributes = netapp_utils.zapi.NaElement('volume-attributes')
526        volume_id_attributes = netapp_utils.zapi.NaElement('volume-id-attributes')
527        volume_id_attributes.add_new_child('name', vol_name)
528        volume_id_attributes.add_new_child('vserver', self.parameters['vserver'])
529        volume_attributes.add_child_elem(volume_id_attributes)
530        query = netapp_utils.zapi.NaElement('query')
531        query.add_child_elem(volume_attributes)
532        volume_info.add_child_elem(query)
533
534        try:
535            result = self.server.invoke_successfully(volume_info, True)
536        except netapp_utils.zapi.NaApiError as error:
537            self.module.fail_json(msg='Error fetching volume %s : %s'
538                                  % (self.parameters['name'], to_native(error)),
539                                  exception=traceback.format_exc())
540        return result
541
542    def get_volume(self, vol_name=None):
543        """
544        Return details about the volume
545        :param:
546            name : Name of the volume
547        :return: Details about the volume. None if not found.
548        :rtype: dict
549        """
550        if vol_name is None:
551            vol_name = self.parameters['name']
552        volume_get_iter = self.volume_get_iter(vol_name)
553        return_value = None
554        if volume_get_iter.get_child_by_name('num-records') and \
555                int(volume_get_iter.get_child_content('num-records')) > 0:
556
557            volume_attributes = volume_get_iter['attributes-list']['volume-attributes']
558            volume_space_attributes = volume_attributes['volume-space-attributes']
559            volume_state_attributes = volume_attributes['volume-state-attributes']
560            volume_id_attributes = volume_attributes['volume-id-attributes']
561            volume_export_attributes = volume_attributes['volume-export-attributes']
562            volume_security_unix_attributes = volume_attributes['volume-security-attributes']['volume-security-unix-attributes']
563            volume_snapshot_attributes = volume_attributes['volume-snapshot-attributes']
564            volume_performance_attributes = volume_attributes['volume-performance-attributes']
565            volume_comp_aggr_attributes = volume_attributes['volume-comp-aggr-attributes']
566            # Get volume's state (online/offline)
567            current_state = volume_state_attributes['state']
568            is_online = (current_state == "online")
569
570            return_value = {
571                'name': vol_name,
572                'size': int(volume_space_attributes['size']),
573                'is_online': is_online,
574                'policy': volume_export_attributes['policy'],
575                'unix_permissions': volume_security_unix_attributes['permissions'],
576                'snapshot_policy': volume_snapshot_attributes['snapshot-policy'],
577                'tiering_policy': volume_comp_aggr_attributes['tiering-policy']
578            }
579            if volume_space_attributes.get_child_by_name('encrypt'):
580                return_value['encrypt'] = volume_attributes['encrypt']
581            if volume_space_attributes.get_child_by_name('percentage-snapshot-reserve'):
582                return_value['percent_snapshot_space'] = int(volume_space_attributes['percentage-snapshot-reserve'])
583            if volume_space_attributes.get_child_by_name('space-slo'):
584                return_value['space_slo'] = volume_space_attributes['space-slo']
585            else:
586                return_value['space_slo'] = None
587            if volume_state_attributes.get_child_by_name('is-nvfail-enabled') is not None:
588                return_value['nvfail_enabled'] = volume_state_attributes['is-nvfail-enabled'] == 'true'
589            else:
590                return_value['nvfail_enabled'] = None
591            if volume_id_attributes.get_child_by_name('containing-aggregate-name'):
592                return_value['aggregate_name'] = volume_id_attributes['containing-aggregate-name']
593            else:
594                return_value['aggregate_name'] = None
595            if volume_id_attributes.get_child_by_name('junction-path'):
596                return_value['junction_path'] = volume_id_attributes['junction-path']
597            else:
598                return_value['junction_path'] = ''
599            if volume_id_attributes.get_child_by_name('comment'):
600                return_value['comment'] = volume_id_attributes['comment']
601            else:
602                return_value['comment'] = None
603            if volume_id_attributes.get_child_by_name('style-extended'):
604                return_value['style_extended'] = volume_id_attributes['style-extended']
605            else:
606                return_value['style_extended'] = None
607            if volume_space_attributes.get_child_by_name('space-guarantee'):
608                return_value['space_guarantee'] = volume_space_attributes['space-guarantee']
609            else:
610                return_value['space_guarantee'] = None
611            if volume_snapshot_attributes.get_child_by_name('snapdir-access-enabled'):
612                return_value['snapdir_access'] = volume_snapshot_attributes['snapdir-access-enabled']
613            else:
614                return_value['snapdir_access'] = None
615            if volume_performance_attributes.get_child_by_name('is-atime-update-enabled'):
616                return_value['atime_update'] = volume_performance_attributes['is-atime-update-enabled']
617            else:
618                return_value['atime_update'] = None
619            if volume_attributes.get_child_by_name('volume-qos-attributes'):
620                volume_qos_attributes = volume_attributes['volume-qos-attributes']
621                if volume_qos_attributes.get_child_by_name('policy-group-name'):
622                    return_value['qos_policy_group'] = volume_qos_attributes['policy-group-name']
623                else:
624                    return_value['qos_policy_group'] = None
625                if volume_qos_attributes.get_child_by_name('adaptive-policy-group-name'):
626                    return_value['qos_adaptive_policy_group'] = volume_qos_attributes['adaptive-policy-group-name']
627                else:
628                    return_value['qos_adaptive_policy_group'] = None
629            else:
630                return_value['qos_policy_group'] = None
631                return_value['qos_adaptive_policy_group'] = None
632            if volume_attributes.get_child_by_name('volume-vserver-dr-protection-attributes'):
633                volume_vserver_dr_protection_attributes = volume_attributes['volume-vserver-dr-protection-attributes']
634                if volume_vserver_dr_protection_attributes.get_child_by_name('vserver-dr-protection'):
635                    return_value['vserver_dr_protection'] = volume_vserver_dr_protection_attributes['vserver-dr-protection']
636                else:
637                    return_value['vserver_dr_protection'] = None
638
639        return return_value
640
641    def create_volume(self):
642        '''Create ONTAP volume'''
643        if self.volume_style == 'flexGroup':
644            self.create_volume_async()
645        else:
646            options = self.create_volume_options()
647            volume_create = netapp_utils.zapi.NaElement.create_node_with_children('volume-create', **options)
648            try:
649                self.server.invoke_successfully(volume_create, enable_tunneling=True)
650                if self.parameters.get('wait_for_completion'):
651                    # round off time_out
652                    retries = (self.parameters['time_out'] + 5) // 10
653                    current = self.get_volume()
654                    is_online = None if current is None else current['is_online']
655                    while not is_online and retries > 0:
656                        time.sleep(10)
657                        retries = retries - 1
658                        current = self.get_volume()
659                        is_online = None if current is None else current['is_online']
660                self.ems_log_event("volume-create")
661            except netapp_utils.zapi.NaApiError as error:
662                self.module.fail_json(msg='Error provisioning volume %s of size %s: %s'
663                                      % (self.parameters['name'], self.parameters['size'], to_native(error)),
664                                      exception=traceback.format_exc())
665
666        if self.parameters.get('efficiency_policy'):
667            self.assign_efficiency_policy()
668
669    def create_volume_async(self):
670        '''
671        create volume async.
672        '''
673        options = self.create_volume_options()
674        volume_create = netapp_utils.zapi.NaElement.create_node_with_children('volume-create-async', **options)
675        if self.parameters.get('aggr_list'):
676            aggr_list_obj = netapp_utils.zapi.NaElement('aggr-list')
677            volume_create.add_child_elem(aggr_list_obj)
678            for aggr in self.parameters['aggr_list']:
679                aggr_list_obj.add_new_child('aggr-name', aggr)
680        try:
681            result = self.server.invoke_successfully(volume_create, enable_tunneling=True)
682            self.ems_log_event("volume-create")
683        except netapp_utils.zapi.NaApiError as error:
684            self.module.fail_json(msg='Error provisioning volume %s of size %s: %s'
685                                  % (self.parameters['name'], self.parameters['size'], to_native(error)),
686                                  exception=traceback.format_exc())
687        self.check_invoke_result(result, 'create')
688
689        if self.parameters.get('efficiency_policy'):
690            self.assign_efficiency_policy_async()
691
692    def create_volume_options(self):
693        '''Set volume options for create operation'''
694        options = {}
695        if self.volume_style == 'flexGroup':
696            options['volume-name'] = self.parameters['name']
697            if self.parameters.get('aggr_list_multiplier'):
698                options['aggr-list-multiplier'] = str(self.parameters['aggr_list_multiplier'])
699            if self.parameters.get('auto_provision_as'):
700                options['auto-provision-as'] = self.parameters['auto_provision_as']
701            if self.parameters.get('space_guarantee'):
702                options['space-guarantee'] = self.parameters['space_guarantee']
703        else:
704            options['volume'] = self.parameters['name']
705            if self.parameters.get('aggregate_name') is None:
706                self.module.fail_json(msg='Error provisioning volume %s: aggregate_name is required'
707                                      % self.parameters['name'])
708            options['containing-aggr-name'] = self.parameters['aggregate_name']
709            if self.parameters.get('space_guarantee'):
710                options['space-reserve'] = self.parameters['space_guarantee']
711
712        if self.parameters.get('size'):
713            options['size'] = str(self.parameters['size'])
714        if self.parameters.get('snapshot_policy'):
715            options['snapshot-policy'] = self.parameters['snapshot_policy']
716        if self.parameters.get('unix_permissions'):
717            options['unix-permissions'] = self.parameters['unix_permissions']
718        if self.parameters.get('volume_security_style'):
719            options['volume-security-style'] = self.parameters['volume_security_style']
720        if self.parameters.get('policy'):
721            options['export-policy'] = self.parameters['policy']
722        if self.parameters.get('junction_path'):
723            options['junction-path'] = self.parameters['junction_path']
724        if self.parameters.get('comment'):
725            options['volume-comment'] = self.parameters['comment']
726        if self.parameters.get('type'):
727            options['volume-type'] = self.parameters['type']
728        if self.parameters.get('percent_snapshot_space') is not None:
729            options['percentage-snapshot-reserve'] = str(self.parameters['percent_snapshot_space'])
730        if self.parameters.get('language'):
731            options['language-code'] = self.parameters['language']
732        if self.parameters.get('qos_policy_group'):
733            options['qos-policy-group-name'] = self.parameters['qos_policy_group']
734        if self.parameters.get('qos_adaptive_policy_group'):
735            options['qos-adaptive-policy-group-name'] = self.parameters['qos_adaptive_policy_group']
736        if self.parameters.get('nvfail_enabled') is not None:
737            options['is-nvfail-enabled'] = str(self.parameters['nvfail_enabled'])
738        if self.parameters.get('space_slo'):
739            options['space-slo'] = self.parameters['space_slo']
740        if self.parameters.get('tiering_policy'):
741            options['tiering-policy'] = self.parameters['tiering_policy']
742        if self.parameters.get('encrypt'):
743            options['encrypt'] = str(self.parameters['encrypt'])
744        if self.parameters.get('vserver_dr_protection'):
745            options['vserver-dr-protection'] = self.parameters['vserver_dr_protection']
746        return options
747
748    def delete_volume(self):
749        '''Delete ONTAP volume'''
750        if self.parameters.get('is_infinite') or self.volume_style == 'flexGroup':
751            volume_delete = netapp_utils.zapi\
752                .NaElement.create_node_with_children(
753                    'volume-destroy-async', **{'volume-name': self.parameters['name'], 'unmount-and-offline': 'true'})
754        else:
755            volume_delete = netapp_utils.zapi\
756                .NaElement.create_node_with_children(
757                    'volume-destroy', **{'name': self.parameters['name'],
758                                         'unmount-and-offline': 'true'})
759        try:
760            result = self.server.invoke_successfully(volume_delete, enable_tunneling=True)
761            if self.parameters.get('is_infinite') or self.volume_style == 'flexGroup':
762                self.check_invoke_result(result, 'delete')
763            self.ems_log_event("volume-delete")
764        except netapp_utils.zapi.NaApiError as error:
765            self.module.fail_json(msg='Error deleting volume %s: %s'
766                                  % (self.parameters['name'], to_native(error)),
767                                  exception=traceback.format_exc())
768
769    def move_volume(self):
770        '''Move volume from source aggregate to destination aggregate'''
771        volume_move = netapp_utils.zapi.NaElement.create_node_with_children(
772            'volume-move-start', **{'source-volume': self.parameters['name'],
773                                    'vserver': self.parameters['vserver'],
774                                    'dest-aggr': self.parameters['aggregate_name']})
775        try:
776            self.cluster.invoke_successfully(volume_move,
777                                             enable_tunneling=True)
778            self.ems_log_event("volume-move")
779        except netapp_utils.zapi.NaApiError as error:
780            self.module.fail_json(msg='Error moving volume %s: %s'
781                                  % (self.parameters['name'], to_native(error)),
782                                  exception=traceback.format_exc())
783
784    def rename_volume(self):
785        """
786        Rename the volume.
787
788        Note: 'is_infinite' needs to be set to True in order to rename an
789        Infinite Volume. Use time_out parameter to set wait time for rename completion.
790        """
791        vol_rename_zapi, vol_name_zapi = ['volume-rename-async', 'volume-name'] if self.parameters['is_infinite']\
792            else ['volume-rename', 'volume']
793        volume_rename = netapp_utils.zapi.NaElement.create_node_with_children(
794            vol_rename_zapi, **{vol_name_zapi: self.parameters['from_name'],
795                                'new-volume-name': str(self.parameters['name'])})
796        try:
797            result = self.server.invoke_successfully(volume_rename, enable_tunneling=True)
798            if vol_rename_zapi == 'volume-rename-async':
799                self.check_invoke_result(result, 'rename')
800            self.ems_log_event("volume-rename")
801        except netapp_utils.zapi.NaApiError as error:
802            self.module.fail_json(msg='Error renaming volume %s: %s'
803                                  % (self.parameters['name'], to_native(error)),
804                                  exception=traceback.format_exc())
805
806    def resize_volume(self):
807        """
808        Re-size the volume.
809
810        Note: 'is_infinite' needs to be set to True in order to rename an
811        Infinite Volume.
812        """
813        vol_size_zapi, vol_name_zapi = ['volume-size-async', 'volume-name']\
814            if (self.parameters['is_infinite'] or self.volume_style == 'flexGroup')\
815            else ['volume-size', 'volume']
816        volume_resize = netapp_utils.zapi.NaElement.create_node_with_children(
817            vol_size_zapi, **{vol_name_zapi: self.parameters['name'],
818                              'new-size': str(self.parameters['size'])})
819        try:
820            result = self.server.invoke_successfully(volume_resize, enable_tunneling=True)
821            if vol_size_zapi == 'volume-size-async':
822                self.check_invoke_result(result, 'resize')
823            self.ems_log_event("volume-resize")
824        except netapp_utils.zapi.NaApiError as error:
825            self.module.fail_json(msg='Error re-sizing volume %s: %s'
826                                  % (self.parameters['name'], to_native(error)),
827                                  exception=traceback.format_exc())
828
829    def change_volume_state(self):
830        """
831        Change volume's state (offline/online).
832        """
833        if self.parameters['is_online']:    # Desired state is online, setup zapi APIs respectively
834            vol_state_zapi, vol_name_zapi, action = ['volume-online-async', 'volume-name', 'online']\
835                if (self.parameters['is_infinite'] or self.volume_style == 'flexGroup')\
836                else ['volume-online', 'name', 'online']
837        else:   # Desired state is offline, setup zapi APIs respectively
838            vol_state_zapi, vol_name_zapi, action = ['volume-offline-async', 'volume-name', 'offline']\
839                if (self.parameters['is_infinite'] or self.volume_style == 'flexGroup')\
840                else ['volume-offline', 'name', 'offline']
841            volume_unmount = netapp_utils.zapi.NaElement.create_node_with_children(
842                'volume-unmount', **{'volume-name': self.parameters['name']})
843        volume_change_state = netapp_utils.zapi.NaElement.create_node_with_children(
844            vol_state_zapi, **{vol_name_zapi: self.parameters['name']})
845        try:
846            if not self.parameters['is_online']:  # Unmount before offline
847                self.server.invoke_successfully(volume_unmount, enable_tunneling=True)
848            result = self.server.invoke_successfully(volume_change_state, enable_tunneling=True)
849            if self.volume_style == 'flexGroup' or self.parameters['is_infinite']:
850                self.check_invoke_result(result, action)
851            self.ems_log_event("change-state")
852        except netapp_utils.zapi.NaApiError as error:
853            state = "online" if self.parameters['is_online'] else "offline"
854            self.module.fail_json(msg='Error changing the state of volume %s to %s: %s'
855                                  % (self.parameters['name'], state, to_native(error)),
856                                  exception=traceback.format_exc())
857
858    def create_volume_attribute(self, zapi_object, parent_attribute, attribute, value):
859        """
860
861        :param parent_attribute:
862        :param child_attribute:
863        :param value:
864        :return:
865        """
866        if isinstance(parent_attribute, str):
867            vol_attribute = netapp_utils.zapi.NaElement(parent_attribute)
868            vol_attribute.add_new_child(attribute, value)
869            zapi_object.add_child_elem(vol_attribute)
870        else:
871            zapi_object.add_new_child(attribute, value)
872            parent_attribute.add_child_elem(zapi_object)
873
874    def volume_modify_attributes(self, params):
875        """
876        modify volume parameter 'policy','unix_permissions','snapshot_policy','space_guarantee', 'percent_snapshot_space',
877                                'qos_policy_group', 'qos_adaptive_policy_group'
878        """
879        # TODO: refactor this method
880        if self.volume_style == 'flexGroup' or self.parameters['is_infinite']:
881            vol_mod_iter = netapp_utils.zapi.NaElement('volume-modify-iter-async')
882        else:
883            vol_mod_iter = netapp_utils.zapi.NaElement('volume-modify-iter')
884        attributes = netapp_utils.zapi.NaElement('attributes')
885        vol_mod_attributes = netapp_utils.zapi.NaElement('volume-attributes')
886        # Volume-attributes is split in to 25 sub categories
887        # volume-space-attributes
888        vol_space_attributes = netapp_utils.zapi.NaElement('volume-space-attributes')
889        if self.parameters.get('space_guarantee'):
890            self.create_volume_attribute(vol_space_attributes, vol_mod_attributes,
891                                         'space-guarantee', self.parameters['space_guarantee'])
892        if self.parameters.get('percent_snapshot_space') is not None:
893            self.create_volume_attribute(vol_space_attributes, vol_mod_attributes,
894                                         'percentage-snapshot-reserve', str(self.parameters['percent_snapshot_space']))
895        if self.parameters.get('space_slo'):
896            self.create_volume_attribute(vol_space_attributes, vol_mod_attributes, 'space-slo', self.parameters['space_slo'])
897        # volume-snapshot-attributes
898        vol_snapshot_attributes = netapp_utils.zapi.NaElement('volume-snapshot-attributes')
899        if self.parameters.get('snapshot_policy'):
900            self.create_volume_attribute(vol_snapshot_attributes, vol_mod_attributes,
901                                         'snapshot-policy', self.parameters['snapshot_policy'])
902        if self.parameters.get('snapdir_access'):
903            self.create_volume_attribute(vol_snapshot_attributes, vol_mod_attributes,
904                                         'snapdir-access-enabled', self.parameters['snapdir_access'])
905        # volume-export-attributes
906        if self.parameters.get('policy'):
907            self.create_volume_attribute(vol_mod_attributes, 'volume-export-attributes',
908                                         'policy', self.parameters['policy'])
909        # volume-security-attributes
910        if self.parameters.get('unix_permissions'):
911            vol_security_attributes = netapp_utils.zapi.NaElement('volume-security-attributes')
912            self.create_volume_attribute(vol_security_attributes, 'volume-security-unix-attributes',
913                                         'permissions', self.parameters['unix_permissions'])
914            vol_mod_attributes.add_child_elem(vol_security_attributes)
915        # volume-performance-attributes
916        if self.parameters.get('atime_update'):
917            self.create_volume_attribute(vol_mod_attributes, 'volume-performance-attributes',
918                                         'is-atime-update-enabled', self.parameters['atime_update'])
919        # volume-qos-attributes
920        if self.parameters.get('qos_policy_group'):
921            self.create_volume_attribute(vol_mod_attributes, 'volume-qos-attributes',
922                                         'policy-group-name', self.parameters['qos_policy_group'])
923        if self.parameters.get('qos_adaptive_policy_group'):
924            self.create_volume_attribute(vol_mod_attributes, 'volume-qos-attributes',
925                                         'adaptive-policy-group-name', self.parameters['qos_adaptive_policy_group'])
926        # volume-comp-aggr-attributes
927        if params and params.get('tiering_policy'):
928            self.create_volume_attribute(vol_mod_attributes, 'volume-comp-aggr-attributes',
929                                         'tiering-policy', self.parameters['tiering_policy'])
930        # volume-state-attributes
931        if self.parameters.get('nvfail_enabled') is not None:
932            self.create_volume_attribute(vol_mod_attributes, 'volume-state-attributes', 'is-nvfail-enabled', str(self.parameters['nvfail_enabled']))
933        # volume-dr-protection-attributes
934        if self.parameters.get('vserver_dr_protection') is not None:
935            self.create_volume_attribute(vol_mod_attributes, 'volume-vserver-dr-protection-attributes',
936                                         'vserver-dr-protection', self.parameters['vserver_dr_protection'])
937        # volume-id-attributes
938        if self.parameters.get('comment') is not None:
939            self.create_volume_attribute(vol_mod_attributes, 'volume-id-attributes',
940                                         'comment', self.parameters['comment'])
941        # End of Volume-attributes sub attributes
942        attributes.add_child_elem(vol_mod_attributes)
943        query = netapp_utils.zapi.NaElement('query')
944        vol_query_attributes = netapp_utils.zapi.NaElement('volume-attributes')
945        self.create_volume_attribute(vol_query_attributes, 'volume-id-attributes',
946                                     'name', self.parameters['name'])
947        query.add_child_elem(vol_query_attributes)
948        vol_mod_iter.add_child_elem(attributes)
949        vol_mod_iter.add_child_elem(query)
950        try:
951            result = self.server.invoke_successfully(vol_mod_iter, enable_tunneling=True)
952            failures = result.get_child_by_name('failure-list')
953            if self.volume_style == 'flexGroup' or self.parameters['is_infinite']:
954                success = result.get_child_by_name('success-list')
955                success = success.get_child_by_name('volume-modify-iter-async-info')
956                results = dict()
957                for key in ('status', 'jobid'):
958                    if success.get_child_by_name(key):
959                        results[key] = success[key]
960                status = results.get('status')
961                if status == 'in_progress' and 'jobid' in results:
962                    if self.parameters['time_out'] == 0:
963                        return
964                    error = self.check_job_status(results['jobid'])
965                    if error is None:
966                        return
967                    else:
968                        self.module.fail_json(msg='Error when modify volume: %s' % error)
969                self.module.fail_json(msg='Unexpected error when modify volume: results is: %s' % repr(results))
970            # handle error if modify space, policy, or unix-permissions parameter fails
971            if failures is not None:
972                if failures.get_child_by_name('volume-modify-iter-info') is not None:
973                    return_info = 'volume-modify-iter-info'
974                    error_msg = failures.get_child_by_name(return_info).get_child_content('error-message')
975                    self.module.fail_json(msg="Error modifying volume %s: %s"
976                                          % (self.parameters['name'], error_msg),
977                                          exception=traceback.format_exc())
978                elif failures.get_child_by_name('volume-modify-iter-async-info') is not None:
979                    return_info = 'volume-modify-iter-async-info'
980                    error_msg = failures.get_child_by_name(return_info).get_child_content('error-message')
981                    self.module.fail_json(msg="Error modifying volume %s: %s"
982                                          % (self.parameters['name'], error_msg),
983                                          exception=traceback.format_exc())
984            self.ems_log_event("volume-modify")
985        except netapp_utils.zapi.NaApiError as error:
986            self.module.fail_json(msg='Error modifying volume %s: %s'
987                                  % (self.parameters['name'], to_native(error)),
988                                  exception=traceback.format_exc())
989
990    def volume_mount(self):
991        """
992        Mount an existing volume in specified junction_path
993        :return: None
994        """
995        vol_mount = netapp_utils.zapi.NaElement('volume-mount')
996        vol_mount.add_new_child('volume-name', self.parameters['name'])
997        vol_mount.add_new_child('junction-path', self.parameters['junction_path'])
998        try:
999            self.server.invoke_successfully(vol_mount, enable_tunneling=True)
1000        except netapp_utils.zapi.NaApiError as error:
1001            self.module.fail_json(msg='Error mounting volume %s on path %s: %s'
1002                                      % (self.parameters['name'], self.parameters['junction_path'],
1003                                         to_native(error)), exception=traceback.format_exc())
1004
1005    def volume_unmount(self):
1006        """
1007        Unmount an existing volume
1008        :return: None
1009        """
1010        vol_unmount = netapp_utils.zapi.NaElement.create_node_with_children(
1011            'volume-unmount', **{'volume-name': self.parameters['name']})
1012        try:
1013            self.server.invoke_successfully(vol_unmount, enable_tunneling=True)
1014        except netapp_utils.zapi.NaApiError as error:
1015            self.module.fail_json(msg='Error unmounting volume %s: %s'
1016                                      % (self.parameters['name'], to_native(error)), exception=traceback.format_exc())
1017
1018    def modify_volume(self, modify):
1019        '''Modify volume action'''
1020        for attribute in modify.keys():
1021            if attribute == 'size':
1022                self.resize_volume()
1023            if attribute == 'is_online':
1024                self.change_volume_state()
1025            if attribute == 'aggregate_name':
1026                self.move_volume()
1027            if attribute in ['space_guarantee', 'policy', 'unix_permissions', 'tiering_policy',
1028                             'snapshot_policy', 'percent_snapshot_space', 'snapdir_access', 'atime_update',
1029                             'nvfail_enabled', 'space_slo', 'qos_policy_group', 'qos_adaptive_policy_group', 'vserver_dr_protection', 'comment']:
1030                self.volume_modify_attributes(modify)
1031            if attribute == 'junction_path':
1032                if modify.get('junction_path') == '':
1033                    self.volume_unmount()
1034                else:
1035                    self.volume_mount()
1036
1037    def compare_chmod_value(self, current):
1038        """
1039        compare current unix_permissions to desire unix_permissions.
1040        :return: True if the same, False it not the same or desire unix_permissions is not valid.
1041        """
1042        desire = self.parameters
1043        if current is None:
1044            return False
1045        octal_value = ''
1046        unix_permissions = desire['unix_permissions']
1047        if unix_permissions.isdigit():
1048            return int(current['unix_permissions']) == int(unix_permissions)
1049        else:
1050            if len(unix_permissions) != 12:
1051                return False
1052            if unix_permissions[:3] != '---':
1053                return False
1054            for i in range(3, len(unix_permissions), 3):
1055                if unix_permissions[i] not in ['r', '-'] or unix_permissions[i + 1] not in ['w', '-']\
1056                        or unix_permissions[i + 2] not in ['x', '-']:
1057                    return False
1058                group_permission = self.char_to_octal(unix_permissions[i:i + 3])
1059                octal_value += str(group_permission)
1060            return int(current['unix_permissions']) == int(octal_value)
1061
1062    def char_to_octal(self, chars):
1063        """
1064        :param chars: Characters to be converted into octal values.
1065        :return: octal value of the individual group permission.
1066        """
1067        total = 0
1068        if chars[0] == 'r':
1069            total += 4
1070        if chars[1] == 'w':
1071            total += 2
1072        if chars[2] == 'x':
1073            total += 1
1074        return total
1075
1076    def get_volume_style(self, current):
1077        '''Get volume style, infinite or standard flexvol'''
1078        if current is None:
1079            if self.parameters.get('aggr_list') or self.parameters.get('aggr_list_multiplier') or self.parameters.get('auto_provision_as'):
1080                return 'flexGroup'
1081        else:
1082            if current.get('style_extended'):
1083                if current['style_extended'] == 'flexgroup':
1084                    return 'flexGroup'
1085                else:
1086                    return current['style_extended']
1087        return None
1088
1089    def get_job(self, jobid, server):
1090        """
1091        Get job details by id
1092        """
1093        job_get = netapp_utils.zapi.NaElement('job-get')
1094        job_get.add_new_child('job-id', jobid)
1095        try:
1096            result = server.invoke_successfully(job_get, enable_tunneling=True)
1097        except netapp_utils.zapi.NaApiError as error:
1098            if to_native(error.code) == "15661":
1099                # Not found
1100                return None
1101            self.module.fail_json(msg='Error fetching job info: %s' % to_native(error),
1102                                  exception=traceback.format_exc())
1103        job_info = result.get_child_by_name('attributes').get_child_by_name('job-info')
1104        results = {
1105            'job-progress': job_info['job-progress'],
1106            'job-state': job_info['job-state']
1107        }
1108        if job_info.get_child_by_name('job-completion') is not None:
1109            results['job-completion'] = job_info['job-completion']
1110        else:
1111            results['job-completion'] = None
1112        return results
1113
1114    def check_job_status(self, jobid):
1115        """
1116        Loop until job is complete
1117        """
1118        server = self.server
1119        sleep_time = 5
1120        time_out = self.parameters['time_out']
1121        results = self.get_job(jobid, server)
1122        error = 'timeout'
1123
1124        while time_out > 0:
1125            results = self.get_job(jobid, server)
1126            # If running as cluster admin, the job is owned by cluster vserver
1127            # rather than the target vserver.
1128            if results is None and server == self.server:
1129                results = netapp_utils.get_cserver(self.server)
1130                server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
1131                continue
1132            if results is None:
1133                error = 'cannot locate job with id: %d' % int(jobid)
1134                break
1135            if results['job-state'] in ('queued', 'running'):
1136                time.sleep(sleep_time)
1137                time_out -= sleep_time
1138                continue
1139            if results['job-state'] in ('success', 'failure'):
1140                break
1141            else:
1142                self.module.fail_json(msg='Unexpected job status in: %s' % repr(results))
1143
1144        if results is not None:
1145            if results['job-state'] == 'success':
1146                error = None
1147            elif results['job-state'] in ('queued', 'running'):
1148                error = 'job completion exceeded expected timer of: %s seconds' % \
1149                        self.parameters['time_out']
1150            else:
1151                if results['job-completion'] is not None:
1152                    error = results['job-completion']
1153                else:
1154                    error = results['job-progress']
1155        return error
1156
1157    def check_invoke_result(self, result, action):
1158        '''
1159        check invoked api call back result.
1160        '''
1161        results = dict()
1162        for key in ('result-status', 'result-jobid'):
1163            if result.get_child_by_name(key):
1164                results[key] = result[key]
1165        status = results.get('result-status')
1166        if status == 'in_progress' and 'result-jobid' in results:
1167            if self.parameters['time_out'] == 0:
1168                return
1169            error = self.check_job_status(results['result-jobid'])
1170            if error is None:
1171                return
1172            else:
1173                self.module.fail_json(msg='Error when %s volume: %s' % (action, error))
1174        if status == 'failed':
1175            self.module.fail_json(msg='Operation failed when %s volume.' % action)
1176
1177    def assign_efficiency_policy(self):
1178        '''Set efficiency policy'''
1179        options = {'path': '/vol/' + self.parameters['name']}
1180        efficiency_enable = netapp_utils.zapi.NaElement.create_node_with_children('sis-enable', **options)
1181        try:
1182            self.server.invoke_successfully(efficiency_enable, enable_tunneling=True)
1183        except netapp_utils.zapi.NaApiError as error:
1184            self.module.fail_json(msg='Error enable efficiency on volume %s: %s'
1185                                      % (self.parameters['name'], to_native(error)),
1186                                  exception=traceback.format_exc())
1187
1188        options['policy-name'] = self.parameters['efficiency_policy']
1189        efficiency_start = netapp_utils.zapi.NaElement.create_node_with_children('sis-set-config', **options)
1190        try:
1191            self.server.invoke_successfully(efficiency_start, enable_tunneling=True)
1192        except netapp_utils.zapi.NaApiError as error:
1193            self.module.fail_json(msg='Error setting up an efficiency policy %s on volume %s: %s'
1194                                      % (self.parameters['efficiency_policy'], self.parameters['name'], to_native(error)),
1195                                  exception=traceback.format_exc())
1196
1197    def assign_efficiency_policy_async(self):
1198        '''Set efficiency policy in asynchronous mode'''
1199        options = {'volume-name': self.parameters['name']}
1200        efficiency_enable = netapp_utils.zapi.NaElement.create_node_with_children('sis-enable-async', **options)
1201        try:
1202            result = self.server.invoke_successfully(efficiency_enable, enable_tunneling=True)
1203        except netapp_utils.zapi.NaApiError as error:
1204            self.module.fail_json(msg='Error enable efficiency on volume %s: %s'
1205                                      % (self.parameters['name'], to_native(error)),
1206                                  exception=traceback.format_exc())
1207        self.check_invoke_result(result, 'enable efficiency on')
1208
1209        options['policy-name'] = self.parameters['efficiency_policy']
1210        efficiency_start = netapp_utils.zapi.NaElement.create_node_with_children('sis-set-config-async', **options)
1211        try:
1212            result = self.server.invoke_successfully(efficiency_start, enable_tunneling=True)
1213        except netapp_utils.zapi.NaApiError as error:
1214            self.module.fail_json(msg='Error setting up an efficiency policy on volume %s: %s'
1215                                      % (self.parameters['name'], to_native(error)),
1216                                  exception=traceback.format_exc())
1217        self.check_invoke_result(result, 'set efficiency policy on')
1218
1219    def apply(self):
1220        '''Call create/modify/delete operations'''
1221        current = self.get_volume()
1222        self.volume_style = self.get_volume_style(current)
1223        # rename and create are mutually exclusive
1224        rename, cd_action, modify = None, None, None
1225        if self.parameters.get('from_name'):
1226            rename = self.na_helper.is_rename_action(self.get_volume(self.parameters['from_name']), current)
1227        else:
1228            cd_action = self.na_helper.get_cd_action(current, self.parameters)
1229        if self.parameters.get('unix_permissions'):
1230            # current stores unix_permissions' numeric value.
1231            # unix_permission in self.parameter can be either numeric or character.
1232            if self.compare_chmod_value(current):
1233                del self.parameters['unix_permissions']
1234        if cd_action is None and self.parameters['state'] == 'present':
1235            modify = self.na_helper.get_modified_attributes(current, self.parameters)
1236        if self.na_helper.changed:
1237            if self.module.check_mode:
1238                pass
1239            else:
1240                if rename:
1241                    self.rename_volume()
1242                if cd_action == 'create':
1243                    self.create_volume()
1244                    # if we create, and modify only variable are set (snapdir_access or atime_update) we need to run a modify
1245                    if 'snapdir_access' in self.parameters or 'atime_update' in self.parameters:
1246                        self.volume_modify_attributes({'snapdir_access': self.parameters['snapdir_access'],
1247                                                       'atime_update': self.parameters['atime_update']})
1248                elif cd_action == 'delete':
1249                    self.delete_volume()
1250                elif modify:
1251                    self.modify_volume(modify)
1252        self.module.exit_json(changed=self.na_helper.changed)
1253
1254    def ems_log_event(self, state):
1255        '''Autosupport log event'''
1256        if state == 'create':
1257            message = "A Volume has been created, size: " + \
1258                str(self.parameters['size']) + str(self.parameters['size_unit'])
1259        elif state == 'volume-delete':
1260            message = "A Volume has been deleted"
1261        elif state == 'volume-move':
1262            message = "A Volume has been moved"
1263        elif state == 'volume-rename':
1264            message = "A Volume has been renamed"
1265        elif state == 'volume-resize':
1266            message = "A Volume has been resized to: " + \
1267                str(self.parameters['size']) + str(self.parameters['size_unit'])
1268        elif state == 'volume-change':
1269            message = "A Volume state has been changed"
1270        else:
1271            message = "na_ontap_volume has been called"
1272        netapp_utils.ems_log_event(
1273            "na_ontap_volume", self.server, event=message)
1274
1275
1276def main():
1277    '''Apply volume operations from playbook'''
1278    obj = NetAppOntapVolume()
1279    obj.apply()
1280
1281
1282if __name__ == '__main__':
1283    main()
1284