1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3 4# (c) 2016, Adam Števko <adam.stevko@gmail.com> 5# GNU General Public License v3.0+ (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 10 11DOCUMENTATION = ''' 12--- 13module: ipadm_addr 14short_description: Manage IP addresses on an interface on Solaris/illumos systems 15description: 16 - Create/delete static/dynamic IP addresses on network interfaces on Solaris/illumos systems. 17 - Up/down static/dynamic IP addresses on network interfaces on Solaris/illumos systems. 18 - Manage IPv6 link-local addresses on network interfaces on Solaris/illumos systems. 19author: Adam Števko (@xen0l) 20options: 21 address: 22 description: 23 - Specifiies an IP address to configure in CIDR notation. 24 required: false 25 aliases: [ "addr" ] 26 addrtype: 27 description: 28 - Specifiies a type of IP address to configure. 29 required: false 30 default: static 31 choices: [ 'static', 'dhcp', 'addrconf' ] 32 addrobj: 33 description: 34 - Specifies an unique IP address on the system. 35 required: true 36 temporary: 37 description: 38 - Specifies that the configured IP address is temporary. Temporary 39 IP addresses do not persist across reboots. 40 required: false 41 default: false 42 type: bool 43 wait: 44 description: 45 - Specifies the time in seconds we wait for obtaining address via DHCP. 46 required: false 47 default: 60 48 state: 49 description: 50 - Create/delete/enable/disable an IP address on the network interface. 51 required: false 52 default: present 53 choices: [ 'absent', 'present', 'up', 'down', 'enabled', 'disabled', 'refreshed' ] 54''' 55 56EXAMPLES = ''' 57- name: Configure IP address 10.0.0.1 on e1000g0 58 community.network.ipadm_addr: addr=10.0.0.1/32 addrobj=e1000g0/v4 state=present 59 60- name: Delete addrobj 61 community.network.ipadm_addr: addrobj=e1000g0/v4 state=absent 62 63- name: Configure link-local IPv6 address 64 community.network.ipadm_addr: addtype=addrconf addrobj=vnic0/v6 65 66- name: Configure address via DHCP and wait 180 seconds for address obtaining 67 community.network.ipadm_addr: addrobj=vnic0/dhcp addrtype=dhcp wait=180 68''' 69 70RETURN = ''' 71addrobj: 72 description: address object name 73 returned: always 74 type: str 75 sample: bge0/v4 76state: 77 description: state of the target 78 returned: always 79 type: str 80 sample: present 81temporary: 82 description: specifies if operation will persist across reboots 83 returned: always 84 type: bool 85 sample: True 86addrtype: 87 description: address type 88 returned: always 89 type: str 90 sample: static 91address: 92 description: IP address 93 returned: only if addrtype is 'static' 94 type: str 95 sample: 1.3.3.7/32 96wait: 97 description: time we wait for DHCP 98 returned: only if addrtype is 'dhcp' 99 type: str 100 sample: 10 101''' 102 103import socket 104 105from ansible.module_utils.basic import AnsibleModule 106 107 108SUPPORTED_TYPES = ['static', 'addrconf', 'dhcp'] 109 110 111class Addr(object): 112 113 def __init__(self, module): 114 self.module = module 115 116 self.address = module.params['address'] 117 self.addrtype = module.params['addrtype'] 118 self.addrobj = module.params['addrobj'] 119 self.temporary = module.params['temporary'] 120 self.state = module.params['state'] 121 self.wait = module.params['wait'] 122 123 def is_cidr_notation(self): 124 125 return self.address.count('/') == 1 126 127 def is_valid_address(self): 128 129 ip_address = self.address.split('/')[0] 130 131 try: 132 if len(ip_address.split('.')) == 4: 133 socket.inet_pton(socket.AF_INET, ip_address) 134 else: 135 socket.inet_pton(socket.AF_INET6, ip_address) 136 except socket.error: 137 return False 138 139 return True 140 141 def is_dhcp(self): 142 cmd = [self.module.get_bin_path('ipadm')] 143 144 cmd.append('show-addr') 145 cmd.append('-p') 146 cmd.append('-o') 147 cmd.append('type') 148 cmd.append(self.addrobj) 149 150 (rc, out, err) = self.module.run_command(cmd) 151 152 if rc == 0: 153 if out.rstrip() != 'dhcp': 154 return False 155 156 return True 157 else: 158 self.module.fail_json(msg='Wrong addrtype %s for addrobj "%s": %s' % (out, self.addrobj, err), 159 rc=rc, 160 stderr=err) 161 162 def addrobj_exists(self): 163 cmd = [self.module.get_bin_path('ipadm')] 164 165 cmd.append('show-addr') 166 cmd.append(self.addrobj) 167 168 (rc, _, _) = self.module.run_command(cmd) 169 170 if rc == 0: 171 return True 172 else: 173 return False 174 175 def delete_addr(self): 176 cmd = [self.module.get_bin_path('ipadm')] 177 178 cmd.append('delete-addr') 179 cmd.append(self.addrobj) 180 181 return self.module.run_command(cmd) 182 183 def create_addr(self): 184 cmd = [self.module.get_bin_path('ipadm')] 185 186 cmd.append('create-addr') 187 cmd.append('-T') 188 cmd.append(self.addrtype) 189 190 if self.temporary: 191 cmd.append('-t') 192 193 if self.addrtype == 'static': 194 cmd.append('-a') 195 cmd.append(self.address) 196 197 if self.addrtype == 'dhcp' and self.wait: 198 cmd.append('-w') 199 cmd.append(self.wait) 200 201 cmd.append(self.addrobj) 202 203 return self.module.run_command(cmd) 204 205 def up_addr(self): 206 cmd = [self.module.get_bin_path('ipadm')] 207 208 cmd.append('up-addr') 209 210 if self.temporary: 211 cmd.append('-t') 212 213 cmd.append(self.addrobj) 214 215 return self.module.run_command(cmd) 216 217 def down_addr(self): 218 cmd = [self.module.get_bin_path('ipadm')] 219 220 cmd.append('down-addr') 221 222 if self.temporary: 223 cmd.append('-t') 224 225 cmd.append(self.addrobj) 226 227 return self.module.run_command(cmd) 228 229 def enable_addr(self): 230 cmd = [self.module.get_bin_path('ipadm')] 231 232 cmd.append('enable-addr') 233 cmd.append('-t') 234 cmd.append(self.addrobj) 235 236 return self.module.run_command(cmd) 237 238 def disable_addr(self): 239 cmd = [self.module.get_bin_path('ipadm')] 240 241 cmd.append('disable-addr') 242 cmd.append('-t') 243 cmd.append(self.addrobj) 244 245 return self.module.run_command(cmd) 246 247 def refresh_addr(self): 248 cmd = [self.module.get_bin_path('ipadm')] 249 250 cmd.append('refresh-addr') 251 cmd.append(self.addrobj) 252 253 return self.module.run_command(cmd) 254 255 256def main(): 257 module = AnsibleModule( 258 argument_spec=dict( 259 address=dict(aliases=['addr']), 260 addrtype=dict(default='static', choices=SUPPORTED_TYPES), 261 addrobj=dict(required=True), 262 temporary=dict(default=False, type='bool'), 263 state=dict( 264 default='present', choices=['absent', 'present', 'up', 'down', 'enabled', 'disabled', 'refreshed']), 265 wait=dict(default=60, type='int'), 266 ), 267 mutually_exclusive=[ 268 ('address', 'wait'), 269 ], 270 supports_check_mode=True 271 ) 272 273 addr = Addr(module) 274 275 rc = None 276 out = '' 277 err = '' 278 result = {} 279 result['addrobj'] = addr.addrobj 280 result['state'] = addr.state 281 result['temporary'] = addr.temporary 282 result['addrtype'] = addr.addrtype 283 284 if addr.addrtype == 'static' and addr.address: 285 if addr.is_cidr_notation() and addr.is_valid_address(): 286 result['address'] = addr.address 287 else: 288 module.fail_json(msg='Invalid IP address: %s' % addr.address) 289 290 if addr.addrtype == 'dhcp' and addr.wait: 291 result['wait'] = addr.wait 292 293 if addr.state == 'absent': 294 if addr.addrobj_exists(): 295 if module.check_mode: 296 module.exit_json(changed=True) 297 298 (rc, out, err) = addr.delete_addr() 299 if rc != 0: 300 module.fail_json(msg='Error while deleting addrobj: "%s"' % err, 301 addrobj=addr.addrobj, 302 stderr=err, 303 rc=rc) 304 305 elif addr.state == 'present': 306 if not addr.addrobj_exists(): 307 if module.check_mode: 308 module.exit_json(changed=True) 309 310 (rc, out, err) = addr.create_addr() 311 if rc != 0: 312 module.fail_json(msg='Error while configuring IP address: "%s"' % err, 313 addrobj=addr.addrobj, 314 addr=addr.address, 315 stderr=err, 316 rc=rc) 317 318 elif addr.state == 'up': 319 if addr.addrobj_exists(): 320 if module.check_mode: 321 module.exit_json(changed=True) 322 323 (rc, out, err) = addr.up_addr() 324 if rc != 0: 325 module.fail_json(msg='Error while bringing IP address up: "%s"' % err, 326 addrobj=addr.addrobj, 327 stderr=err, 328 rc=rc) 329 330 elif addr.state == 'down': 331 if addr.addrobj_exists(): 332 if module.check_mode: 333 module.exit_json(changed=True) 334 335 (rc, out, err) = addr.down_addr() 336 if rc != 0: 337 module.fail_json(msg='Error while bringing IP address down: "%s"' % err, 338 addrobj=addr.addrobj, 339 stderr=err, 340 rc=rc) 341 342 elif addr.state == 'refreshed': 343 if addr.addrobj_exists(): 344 if addr.is_dhcp(): 345 if module.check_mode: 346 module.exit_json(changed=True) 347 348 (rc, out, err) = addr.refresh_addr() 349 if rc != 0: 350 module.fail_json(msg='Error while refreshing IP address: "%s"' % err, 351 addrobj=addr.addrobj, 352 stderr=err, 353 rc=rc) 354 else: 355 module.fail_json(msg='state "refreshed" cannot be used with "%s" addrtype' % addr.addrtype, 356 addrobj=addr.addrobj, 357 stderr=err, 358 rc=1) 359 360 elif addr.state == 'enabled': 361 if addr.addrobj_exists(): 362 if module.check_mode: 363 module.exit_json(changed=True) 364 365 (rc, out, err) = addr.enable_addr() 366 if rc != 0: 367 module.fail_json(msg='Error while enabling IP address: "%s"' % err, 368 addrobj=addr.addrobj, 369 stderr=err, 370 rc=rc) 371 372 elif addr.state == 'disabled': 373 if addr.addrobj_exists(): 374 if module.check_mode: 375 module.exit_json(changed=True) 376 377 (rc, out, err) = addr.disable_addr() 378 if rc != 0: 379 module.fail_json(msg='Error while disabling IP address: "%s"' % err, 380 addrobj=addr.addrobj, 381 stderr=err, 382 rc=rc) 383 384 if rc is None: 385 result['changed'] = False 386 else: 387 result['changed'] = True 388 389 if out: 390 result['stdout'] = out 391 if err: 392 result['stderr'] = err 393 394 module.exit_json(**result) 395 396 397if __name__ == '__main__': 398 main() 399