1#!/usr/bin/python 2 3# (c) 2017, NetApp, Inc 4# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 6from __future__ import absolute_import, division, print_function 7__metaclass__ = type 8 9 10ANSIBLE_METADATA = {'metadata_version': '1.1', 11 'status': ['deprecated'], 12 'supported_by': 'community'} 13 14 15DOCUMENTATION = ''' 16 17module: na_cdot_lun 18 19short_description: Manage NetApp cDOT luns 20extends_documentation_fragment: 21 - netapp.ontap 22version_added: '2.3' 23author: Sumit Kumar (@timuster) <sumit4@netapp.com> 24 25deprecated: 26 removed_in: '2.11' 27 why: Updated modules released with increased functionality 28 alternative: Use M(na_ontap_lun) instead. 29 30description: 31- Create, destroy, resize luns on NetApp cDOT. 32 33options: 34 35 state: 36 description: 37 - Whether the specified lun should exist or not. 38 required: true 39 choices: ['present', 'absent'] 40 41 name: 42 description: 43 - The name of the lun to manage. 44 required: true 45 46 flexvol_name: 47 description: 48 - The name of the FlexVol the lun should exist on. 49 - Required when C(state=present). 50 51 size: 52 description: 53 - The size of the lun in C(size_unit). 54 - Required when C(state=present). 55 56 size_unit: 57 description: 58 - The unit used to interpret the size parameter. 59 choices: ['bytes', 'b', 'kb', 'mb', 'gb', 'tb', 'pb', 'eb', 'zb', 'yb'] 60 default: 'gb' 61 62 force_resize: 63 description: 64 - Forcibly reduce the size. This is required for reducing the size of the LUN to avoid accidentally reducing the LUN size. 65 default: false 66 67 force_remove: 68 description: 69 - If "true", override checks that prevent a LUN from being destroyed if it is online and mapped. 70 - If "false", destroying an online and mapped LUN will fail. 71 default: false 72 73 force_remove_fenced: 74 description: 75 - If "true", override checks that prevent a LUN from being destroyed while it is fenced. 76 - If "false", attempting to destroy a fenced LUN will fail. 77 - The default if not specified is "false". This field is available in Data ONTAP 8.2 and later. 78 default: false 79 80 vserver: 81 required: true 82 description: 83 - The name of the vserver to use. 84 85''' 86 87EXAMPLES = """ 88- name: Create LUN 89 na_cdot_lun: 90 state: present 91 name: ansibleLUN 92 flexvol_name: ansibleVolume 93 vserver: ansibleVServer 94 size: 5 95 size_unit: mb 96 hostname: "{{ netapp_hostname }}" 97 username: "{{ netapp_username }}" 98 password: "{{ netapp_password }}" 99 100- name: Resize Lun 101 na_cdot_lun: 102 state: present 103 name: ansibleLUN 104 force_resize: True 105 flexvol_name: ansibleVolume 106 vserver: ansibleVServer 107 size: 5 108 size_unit: gb 109 hostname: "{{ netapp_hostname }}" 110 username: "{{ netapp_username }}" 111 password: "{{ netapp_password }}" 112""" 113 114RETURN = """ 115 116""" 117import traceback 118 119from ansible.module_utils.basic import AnsibleModule 120from ansible.module_utils._text import to_native 121import ansible.module_utils.netapp as netapp_utils 122 123HAS_NETAPP_LIB = netapp_utils.has_netapp_lib() 124 125 126class NetAppCDOTLUN(object): 127 128 def __init__(self): 129 130 self._size_unit_map = dict( 131 bytes=1, 132 b=1, 133 kb=1024, 134 mb=1024 ** 2, 135 gb=1024 ** 3, 136 tb=1024 ** 4, 137 pb=1024 ** 5, 138 eb=1024 ** 6, 139 zb=1024 ** 7, 140 yb=1024 ** 8 141 ) 142 143 self.argument_spec = netapp_utils.ontap_sf_host_argument_spec() 144 self.argument_spec.update(dict( 145 state=dict(required=True, choices=['present', 'absent']), 146 name=dict(required=True, type='str'), 147 size=dict(type='int'), 148 size_unit=dict(default='gb', 149 choices=['bytes', 'b', 'kb', 'mb', 'gb', 'tb', 150 'pb', 'eb', 'zb', 'yb'], type='str'), 151 force_resize=dict(default=False, type='bool'), 152 force_remove=dict(default=False, type='bool'), 153 force_remove_fenced=dict(default=False, type='bool'), 154 flexvol_name=dict(type='str'), 155 vserver=dict(required=True, type='str'), 156 )) 157 158 self.module = AnsibleModule( 159 argument_spec=self.argument_spec, 160 required_if=[ 161 ('state', 'present', ['flexvol_name', 'size']) 162 ], 163 supports_check_mode=True 164 ) 165 166 p = self.module.params 167 168 # set up state variables 169 self.state = p['state'] 170 self.name = p['name'] 171 self.size_unit = p['size_unit'] 172 if p['size'] is not None: 173 self.size = p['size'] * self._size_unit_map[self.size_unit] 174 else: 175 self.size = None 176 self.force_resize = p['force_resize'] 177 self.force_remove = p['force_remove'] 178 self.force_remove_fenced = p['force_remove_fenced'] 179 self.flexvol_name = p['flexvol_name'] 180 self.vserver = p['vserver'] 181 182 if HAS_NETAPP_LIB is False: 183 self.module.fail_json(msg="the python NetApp-Lib module is required") 184 else: 185 self.server = netapp_utils.setup_ontap_zapi(module=self.module, vserver=self.vserver) 186 187 def get_lun(self): 188 """ 189 Return details about the LUN 190 191 :return: Details about the lun 192 :rtype: dict 193 """ 194 195 luns = [] 196 tag = None 197 while True: 198 lun_info = netapp_utils.zapi.NaElement('lun-get-iter') 199 if tag: 200 lun_info.add_new_child('tag', tag, True) 201 202 query_details = netapp_utils.zapi.NaElement('lun-info') 203 query_details.add_new_child('vserver', self.vserver) 204 query_details.add_new_child('volume', self.flexvol_name) 205 206 query = netapp_utils.zapi.NaElement('query') 207 query.add_child_elem(query_details) 208 209 lun_info.add_child_elem(query) 210 211 result = self.server.invoke_successfully(lun_info, True) 212 if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1: 213 attr_list = result.get_child_by_name('attributes-list') 214 luns.extend(attr_list.get_children()) 215 216 tag = result.get_child_content('next-tag') 217 218 if tag is None: 219 break 220 221 # The LUNs have been extracted. 222 # Find the specified lun and extract details. 223 return_value = None 224 for lun in luns: 225 path = lun.get_child_content('path') 226 _rest, _splitter, found_name = path.rpartition('/') 227 228 if found_name == self.name: 229 size = lun.get_child_content('size') 230 231 # Find out if the lun is attached 232 attached_to = None 233 lun_id = None 234 if lun.get_child_content('mapped') == 'true': 235 lun_map_list = netapp_utils.zapi.NaElement.create_node_with_children( 236 'lun-map-list-info', **{'path': path}) 237 238 result = self.server.invoke_successfully( 239 lun_map_list, enable_tunneling=True) 240 241 igroups = result.get_child_by_name('initiator-groups') 242 if igroups: 243 for igroup_info in igroups.get_children(): 244 igroup = igroup_info.get_child_content( 245 'initiator-group-name') 246 attached_to = igroup 247 lun_id = igroup_info.get_child_content('lun-id') 248 249 return_value = { 250 'name': found_name, 251 'size': size, 252 'attached_to': attached_to, 253 'lun_id': lun_id 254 } 255 else: 256 continue 257 258 return return_value 259 260 def create_lun(self): 261 """ 262 Create LUN with requested name and size 263 """ 264 path = '/vol/%s/%s' % (self.flexvol_name, self.name) 265 lun_create = netapp_utils.zapi.NaElement.create_node_with_children( 266 'lun-create-by-size', **{'path': path, 267 'size': str(self.size), 268 'ostype': 'linux'}) 269 270 try: 271 self.server.invoke_successfully(lun_create, enable_tunneling=True) 272 except netapp_utils.zapi.NaApiError as e: 273 self.module.fail_json(msg="Error provisioning lun %s of size %s: %s" % (self.name, self.size, to_native(e)), 274 exception=traceback.format_exc()) 275 276 def delete_lun(self): 277 """ 278 Delete requested LUN 279 """ 280 path = '/vol/%s/%s' % (self.flexvol_name, self.name) 281 282 lun_delete = netapp_utils.zapi.NaElement.create_node_with_children( 283 'lun-destroy', **{'path': path, 284 'force': str(self.force_remove), 285 'destroy-fenced-lun': 286 str(self.force_remove_fenced)}) 287 288 try: 289 self.server.invoke_successfully(lun_delete, enable_tunneling=True) 290 except netapp_utils.zapi.NaApiError as e: 291 self.module.fail_json(msg="Error deleting lun %s: %s" % (path, to_native(e)), 292 exception=traceback.format_exc()) 293 294 def resize_lun(self): 295 """ 296 Resize requested LUN. 297 298 :return: True if LUN was actually re-sized, false otherwise. 299 :rtype: bool 300 """ 301 path = '/vol/%s/%s' % (self.flexvol_name, self.name) 302 303 lun_resize = netapp_utils.zapi.NaElement.create_node_with_children( 304 'lun-resize', **{'path': path, 305 'size': str(self.size), 306 'force': str(self.force_resize)}) 307 try: 308 self.server.invoke_successfully(lun_resize, enable_tunneling=True) 309 except netapp_utils.zapi.NaApiError as e: 310 if to_native(e.code) == "9042": 311 # Error 9042 denotes the new LUN size being the same as the 312 # old LUN size. This happens when there's barely any difference 313 # in the two sizes. For example, from 8388608 bytes to 314 # 8194304 bytes. This should go away if/when the default size 315 # requested/reported to/from the controller is changed to a 316 # larger unit (MB/GB/TB). 317 return False 318 else: 319 self.module.fail_json(msg="Error resizing lun %s: %s" % (path, to_native(e)), 320 exception=traceback.format_exc()) 321 322 return True 323 324 def apply(self): 325 property_changed = False 326 multiple_properties_changed = False 327 size_changed = False 328 lun_exists = False 329 lun_detail = self.get_lun() 330 331 if lun_detail: 332 lun_exists = True 333 current_size = lun_detail['size'] 334 335 if self.state == 'absent': 336 property_changed = True 337 338 elif self.state == 'present': 339 if not int(current_size) == self.size: 340 size_changed = True 341 property_changed = True 342 343 else: 344 if self.state == 'present': 345 property_changed = True 346 347 if property_changed: 348 if self.module.check_mode: 349 pass 350 else: 351 if self.state == 'present': 352 if not lun_exists: 353 self.create_lun() 354 355 else: 356 if size_changed: 357 # Ensure that size was actually changed. Please 358 # read notes in 'resize_lun' function for details. 359 size_changed = self.resize_lun() 360 if not size_changed and not \ 361 multiple_properties_changed: 362 property_changed = False 363 364 elif self.state == 'absent': 365 self.delete_lun() 366 367 changed = property_changed or size_changed 368 # TODO: include other details about the lun (size, etc.) 369 self.module.exit_json(changed=changed) 370 371 372def main(): 373 v = NetAppCDOTLUN() 374 v.apply() 375 376 377if __name__ == '__main__': 378 main() 379