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_license 18 19short_description: Manage NetApp cDOT protocol and feature licenses 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_license) instead. 29 30description: 31- Add or remove licenses on NetApp ONTAP. 32 33options: 34 35 remove_unused: 36 description: 37 - Remove licenses that have no controller affiliation in the cluster. 38 type: bool 39 40 remove_expired: 41 description: 42 - Remove licenses that have expired in the cluster. 43 type: bool 44 45 serial_number: 46 description: 47 - Serial number of the node associated with the license. 48 - This parameter is used primarily when removing license for a specific service. 49 - If this parameter is not provided, the cluster serial number is used by default. 50 51 licenses: 52 description: 53 - List of licenses to add or remove. 54 - Please note that trying to remove a non-existent license will throw an error. 55 suboptions: 56 base: 57 description: 58 - Cluster Base License 59 nfs: 60 description: 61 - NFS License 62 cifs: 63 description: 64 - CIFS License 65 iscsi: 66 description: 67 - iSCSI License 68 fcp: 69 description: 70 - FCP License 71 cdmi: 72 description: 73 - CDMI License 74 snaprestore: 75 description: 76 - SnapRestore License 77 snapmirror: 78 description: 79 - SnapMirror License 80 flexclone: 81 description: 82 - FlexClone License 83 snapvault: 84 description: 85 - SnapVault License 86 snaplock: 87 description: 88 - SnapLock License 89 snapmanagersuite: 90 description: 91 - SnapManagerSuite License 92 snapprotectapps: 93 description: 94 - SnapProtectApp License 95 v_storageattach: 96 description: 97 - Virtual Attached Storage License 98 99''' 100 101 102EXAMPLES = """ 103- name: Add licenses 104 na_cdot_license: 105 hostname: "{{ netapp_hostname }}" 106 username: "{{ netapp_username }}" 107 password: "{{ netapp_password }}" 108 serial_number: ################# 109 licenses: 110 nfs: ################# 111 cifs: ################# 112 iscsi: ################# 113 fcp: ################# 114 snaprestore: ################# 115 flexclone: ################# 116 117- name: Remove licenses 118 na_cdot_license: 119 hostname: "{{ netapp_hostname }}" 120 username: "{{ netapp_username }}" 121 password: "{{ netapp_password }}" 122 remove_unused: false 123 remove_expired: true 124 serial_number: ################# 125 licenses: 126 nfs: remove 127""" 128 129RETURN = """ 130 131""" 132import traceback 133 134from ansible.module_utils.basic import AnsibleModule 135from ansible.module_utils._text import to_native 136import ansible.module_utils.netapp as netapp_utils 137 138 139HAS_NETAPP_LIB = netapp_utils.has_netapp_lib() 140 141 142class NetAppCDOTLicense(object): 143 144 def __init__(self): 145 self.argument_spec = netapp_utils.ontap_sf_host_argument_spec() 146 self.argument_spec.update(dict( 147 serial_number=dict(required=False, type='str', default=None), 148 remove_unused=dict(default=False, type='bool'), 149 remove_expired=dict(default=False, type='bool'), 150 licenses=dict(default=False, type='dict'), 151 )) 152 153 self.module = AnsibleModule( 154 argument_spec=self.argument_spec, 155 supports_check_mode=False 156 ) 157 158 p = self.module.params 159 160 # set up state variables 161 self.serial_number = p['serial_number'] 162 self.remove_unused = p['remove_unused'] 163 self.remove_expired = p['remove_expired'] 164 self.licenses = p['licenses'] 165 166 if HAS_NETAPP_LIB is False: 167 self.module.fail_json(msg="the python NetApp-Lib module is required") 168 else: 169 self.server = netapp_utils.setup_ontap_zapi(module=self.module) 170 171 def get_licensing_status(self): 172 """ 173 Check licensing status 174 175 :return: package (key) and licensing status (value) 176 :rtype: dict 177 """ 178 license_status = netapp_utils.zapi.NaElement('license-v2-status-list-info') 179 result = None 180 try: 181 result = self.server.invoke_successfully(license_status, 182 enable_tunneling=False) 183 except netapp_utils.zapi.NaApiError as e: 184 self.module.fail_json(msg="Error checking license status: %s" % 185 to_native(e), exception=traceback.format_exc()) 186 187 return_dictionary = {} 188 license_v2_status = result.get_child_by_name('license-v2-status') 189 if license_v2_status: 190 for license_v2_status_info in license_v2_status.get_children(): 191 package = license_v2_status_info.get_child_content('package') 192 status = license_v2_status_info.get_child_content('method') 193 return_dictionary[package] = status 194 195 return return_dictionary 196 197 def remove_licenses(self, remove_list): 198 """ 199 Remove requested licenses 200 :param: 201 remove_list : List of packages to remove 202 203 """ 204 license_delete = netapp_utils.zapi.NaElement('license-v2-delete') 205 for package in remove_list: 206 license_delete.add_new_child('package', package) 207 208 if self.serial_number is not None: 209 license_delete.add_new_child('serial-number', self.serial_number) 210 211 try: 212 self.server.invoke_successfully(license_delete, 213 enable_tunneling=False) 214 except netapp_utils.zapi.NaApiError as e: 215 self.module.fail_json(msg="Error removing license %s" % 216 to_native(e), exception=traceback.format_exc()) 217 218 def remove_unused_licenses(self): 219 """ 220 Remove unused licenses 221 """ 222 remove_unused = netapp_utils.zapi.NaElement('license-v2-delete-unused') 223 try: 224 self.server.invoke_successfully(remove_unused, 225 enable_tunneling=False) 226 except netapp_utils.zapi.NaApiError as e: 227 self.module.fail_json(msg="Error removing unused licenses: %s" % 228 to_native(e), exception=traceback.format_exc()) 229 230 def remove_expired_licenses(self): 231 """ 232 Remove expired licenses 233 """ 234 remove_expired = netapp_utils.zapi.NaElement('license-v2-delete-expired') 235 try: 236 self.server.invoke_successfully(remove_expired, 237 enable_tunneling=False) 238 except netapp_utils.zapi.NaApiError as e: 239 self.module.fail_json(msg="Error removing expired licenses: %s" % 240 to_native(e), exception=traceback.format_exc()) 241 242 def update_licenses(self): 243 """ 244 Update licenses 245 """ 246 # Remove unused and expired licenses, if requested. 247 if self.remove_unused: 248 self.remove_unused_licenses() 249 250 if self.remove_expired: 251 self.remove_expired_licenses() 252 253 # Next, add/remove specific requested licenses. 254 license_add = netapp_utils.zapi.NaElement('license-v2-add') 255 codes = netapp_utils.zapi.NaElement('codes') 256 remove_list = [] 257 for key, value in self.licenses.items(): 258 str_value = str(value) 259 # Make sure license is not an empty string. 260 if str_value and str_value.strip(): 261 if str_value.lower() == 'remove': 262 remove_list.append(str(key).lower()) 263 else: 264 codes.add_new_child('license-code-v2', str_value) 265 266 # Remove requested licenses. 267 if len(remove_list) != 0: 268 self.remove_licenses(remove_list) 269 270 # Add requested licenses 271 if len(codes.get_children()) != 0: 272 license_add.add_child_elem(codes) 273 try: 274 self.server.invoke_successfully(license_add, 275 enable_tunneling=False) 276 except netapp_utils.zapi.NaApiError as e: 277 self.module.fail_json(msg="Error adding licenses: %s" % 278 to_native(e), exception=traceback.format_exc()) 279 280 def apply(self): 281 changed = False 282 # Add / Update licenses. 283 license_status = self.get_licensing_status() 284 self.update_licenses() 285 new_license_status = self.get_licensing_status() 286 287 if license_status != new_license_status: 288 changed = True 289 290 self.module.exit_json(changed=changed) 291 292 293def main(): 294 v = NetAppCDOTLicense() 295 v.apply() 296 297 298if __name__ == '__main__': 299 main() 300