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