summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Števko <xen0l@users.noreply.github.com>2016-08-30 20:46:53 +0200
committerJohn R Barker <john@johnrbarker.com>2016-08-30 19:46:53 +0100
commit2fda8831bdf2fc47c48cd5da2a1611b2b39bdc77 (patch)
tree6608080c792a56b9939a2c01be2e30bba2f085ce
parentae2fdd5b575108bd6691253a1454978120d4fbf2 (diff)
downloadansible-modules-extras-2fda8831bdf2fc47c48cd5da2a1611b2b39bdc77.tar.gz
Add modules to configure Solaris/illumos networking (1st batch) (#2416)
* Add modules to configure Solaris/illumos networking (1st batch) * Add choices to temporary flags
-rw-r--r--network/illumos/__init__.py0
-rw-r--r--network/illumos/dladm_etherstub.py171
-rw-r--r--network/illumos/dladm_vnic.py258
-rw-r--r--network/illumos/flowadm.py503
-rw-r--r--network/illumos/ipadm_if.py222
-rw-r--r--network/illumos/ipadm_prop.py264
6 files changed, 1418 insertions, 0 deletions
diff --git a/network/illumos/__init__.py b/network/illumos/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/network/illumos/__init__.py
diff --git a/network/illumos/dladm_etherstub.py b/network/illumos/dladm_etherstub.py
new file mode 100644
index 00000000..72b2e675
--- /dev/null
+++ b/network/illumos/dladm_etherstub.py
@@ -0,0 +1,171 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# (c) 2015, Adam Števko <adam.stevko@gmail.com>
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+
+DOCUMENTATION = '''
+---
+module: dladm_etherstub
+short_description: Manage etherstubs on Solaris/illumos systems.
+description:
+ - Create or delete etherstubs on Solaris/illumos systems.
+version_added: "2.2"
+author: Adam Števko (@xen0l)
+options:
+ name:
+ description:
+ - Etherstub name.
+ required: true
+ temporary:
+ description:
+ - Specifies that the etherstub is temporary. Temporary etherstubs
+ do not persist across reboots.
+ required: false
+ default: false
+ choices: [ "true", "false" ]
+ state:
+ description:
+ - Create or delete Solaris/illumos etherstub.
+ required: false
+ default: "present"
+ choices: [ "present", "absent" ]
+'''
+
+EXAMPLES = '''
+# Create 'stub0' etherstub
+dladm_etherstub: name=stub0 state=present
+
+# Remove 'stub0 etherstub
+dladm_etherstub: name=stub0 state=absent
+'''
+
+RETURN = '''
+name:
+ description: etherstub name
+ returned: always
+ type: string
+ sample: "switch0"
+state:
+ description: state of the target
+ returned: always
+ type: string
+ sample: "present"
+temporary:
+ description: etherstub's persistence
+ returned: always
+ type: boolean
+ sample: "True"
+'''
+
+
+class Etherstub(object):
+
+ def __init__(self, module):
+ self.module = module
+
+ self.name = module.params['name']
+ self.temporary = module.params['temporary']
+ self.state = module.params['state']
+
+ def etherstub_exists(self):
+ cmd = [self.module.get_bin_path('dladm', True)]
+
+ cmd.append('show-etherstub')
+ cmd.append(self.name)
+
+ (rc, _, _) = self.module.run_command(cmd)
+
+ if rc == 0:
+ return True
+ else:
+ return False
+
+ def create_etherstub(self):
+ cmd = [self.module.get_bin_path('dladm', True)]
+
+ cmd.append('create-etherstub')
+
+ if self.temporary:
+ cmd.append('-t')
+ cmd.append(self.name)
+
+ return self.module.run_command(cmd)
+
+ def delete_etherstub(self):
+ cmd = [self.module.get_bin_path('dladm', True)]
+
+ cmd.append('delete-etherstub')
+
+ if self.temporary:
+ cmd.append('-t')
+ cmd.append(self.name)
+
+ return self.module.run_command(cmd)
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ name=dict(required=True),
+ temporary=dict(default=False, type='bool'),
+ state=dict(default='present', choices=['absent', 'present']),
+ ),
+ supports_check_mode=True
+ )
+
+ etherstub = Etherstub(module)
+
+ rc = None
+ out = ''
+ err = ''
+ result = {}
+ result['name'] = etherstub.name
+ result['state'] = etherstub.state
+ result['temporary'] = etherstub.temporary
+
+ if etherstub.state == 'absent':
+ if etherstub.etherstub_exists():
+ if module.check_mode:
+ module.exit_json(changed=True)
+ (rc, out, err) = etherstub.delete_etherstub()
+ if rc != 0:
+ module.fail_json(name=etherstub.name, msg=err, rc=rc)
+ elif etherstub.state == 'present':
+ if not etherstub.etherstub_exists():
+ if module.check_mode:
+ module.exit_json(changed=True)
+ (rc, out, err) = etherstub.create_etherstub()
+
+ if rc is not None and rc != 0:
+ module.fail_json(name=etherstub.name, msg=err, rc=rc)
+
+ if rc is None:
+ result['changed'] = False
+ else:
+ result['changed'] = True
+
+ if out:
+ result['stdout'] = out
+ if err:
+ result['stderr'] = err
+
+ module.exit_json(**result)
+
+from ansible.module_utils.basic import *
+main()
diff --git a/network/illumos/dladm_vnic.py b/network/illumos/dladm_vnic.py
new file mode 100644
index 00000000..b57edc00
--- /dev/null
+++ b/network/illumos/dladm_vnic.py
@@ -0,0 +1,258 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# (c) 2015, Adam Števko <adam.stevko@gmail.com>
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+
+DOCUMENTATION = '''
+---
+module: dladm_vnic
+short_description: Manage VNICs on Solaris/illumos systems.
+description:
+ - Create or delete VNICs on Solaris/illumos systems.
+version_added: "2.2"
+author: Adam Števko (@xen0l)
+options:
+ name:
+ description:
+ - VNIC name.
+ required: true
+ link:
+ description:
+ - VNIC underlying link name.
+ required: true
+ temporary:
+ description:
+ - Specifies that the VNIC is temporary. Temporary VNICs
+ do not persist across reboots.
+ required: false
+ default: false
+ choices: [ "true", "false" ]
+ mac:
+ description:
+ - Sets the VNIC's MAC address. Must be valid unicast MAC address.
+ required: false
+ default: false
+ aliases: [ "macaddr" ]
+ vlan:
+ description:
+ - Enable VLAN tagging for this VNIC. The VLAN tag will have id
+ I(vlan).
+ required: false
+ default: false
+ aliases: [ "vlan_id" ]
+ state:
+ description:
+ - Create or delete Solaris/illumos VNIC.
+ required: false
+ default: "present"
+ choices: [ "present", "absent" ]
+'''
+
+EXAMPLES = '''
+# Create 'vnic0' VNIC over 'bnx0' link
+dladm_vnic: name=vnic0 link=bnx0 state=present
+
+# Create VNIC with specified MAC and VLAN tag over 'aggr0'
+dladm_vnic: name=vnic1 link=aggr0 mac=2:33:af:12:ab:cd vlan=4
+
+# Remove 'vnic0' VNIC
+dladm_vnic: name=vnic0 link=bnx0 state=absent
+'''
+
+RETURN = '''
+name:
+ description: VNIC name
+ returned: always
+ type: string
+ sample: "vnic0"
+link:
+ description: VNIC underlying link name
+ returned: always
+ type: string
+ sample: "igb0"
+state:
+ description: state of the target
+ returned: always
+ type: string
+ sample: "present"
+temporary:
+ description: VNIC's persistence
+ returned: always
+ type: boolean
+ sample: "True"
+mac:
+ description: MAC address to use for VNIC
+ returned: if mac is specified
+ type: string
+ sample: "00:aa:bc:fe:11:22"
+vlan:
+ description: VLAN to use for VNIC
+ returned: success
+ type: int
+ sample: 42
+'''
+
+import re
+
+
+class VNIC(object):
+
+ UNICAST_MAC_REGEX = r'^[a-f0-9][2-9a-f0]:([a-f0-9]{2}:){4}[a-f0-9]{2}$'
+
+ def __init__(self, module):
+ self.module = module
+
+ self.name = module.params['name']
+ self.link = module.params['link']
+ self.mac = module.params['mac']
+ self.vlan = module.params['vlan']
+ self.temporary = module.params['temporary']
+ self.state = module.params['state']
+
+ def vnic_exists(self):
+ cmd = [self.module.get_bin_path('dladm', True)]
+
+ cmd.append('show-vnic')
+ cmd.append(self.name)
+
+ (rc, _, _) = self.module.run_command(cmd)
+
+ if rc == 0:
+ return True
+ else:
+ return False
+
+ def create_vnic(self):
+ cmd = [self.module.get_bin_path('dladm', True)]
+
+ cmd.append('create-vnic')
+
+ if self.temporary:
+ cmd.append('-t')
+
+ if self.mac:
+ cmd.append('-m')
+ cmd.append(self.mac)
+
+ if self.vlan:
+ cmd.append('-v')
+ cmd.append(self.vlan)
+
+ cmd.append('-l')
+ cmd.append(self.link)
+ cmd.append(self.name)
+
+ return self.module.run_command(cmd)
+
+ def delete_vnic(self):
+ cmd = [self.module.get_bin_path('dladm', True)]
+
+ cmd.append('delete-vnic')
+
+ if self.temporary:
+ cmd.append('-t')
+ cmd.append(self.name)
+
+ return self.module.run_command(cmd)
+
+ def is_valid_unicast_mac(self):
+
+ mac_re = re.match(self.UNICAST_MAC_REGEX, self.mac)
+
+ return mac_re is None
+
+ def is_valid_vlan_id(self):
+
+ return 0 <= self.vlan <= 4095
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ name=dict(required=True),
+ link=dict(required=True),
+ mac=dict(default=None, aliases=['macaddr']),
+ vlan=dict(default=None, aliases=['vlan_id']),
+ temporary=dict(default=False, type='bool'),
+ state=dict(default='present', choices=['absent', 'present']),
+ ),
+ supports_check_mode=True
+ )
+
+ vnic = VNIC(module)
+
+ rc = None
+ out = ''
+ err = ''
+ result = {}
+ result['name'] = vnic.name
+ result['link'] = vnic.link
+ result['state'] = vnic.state
+ result['temporary'] = vnic.temporary
+
+ if vnic.mac is not None:
+ if vnic.is_valid_unicast_mac():
+ module.fail_json(msg='Invalid unicast MAC address',
+ mac=vnic.mac,
+ name=vnic.name,
+ state=vnic.state,
+ link=vnic.link,
+ vlan=vnic.vlan)
+ result['mac'] = vnic.mac
+
+ if vnic.vlan is not None:
+ if vnic.is_valid_vlan_id():
+ module.fail_json(msg='Invalid VLAN tag',
+ mac=vnic.mac,
+ name=vnic.name,
+ state=vnic.state,
+ link=vnic.link,
+ vlan=vnic.vlan)
+ result['vlan'] = vnic.vlan
+
+ if vnic.state == 'absent':
+ if vnic.vnic_exists():
+ if module.check_mode:
+ module.exit_json(changed=True)
+ (rc, out, err) = vnic.delete_vnic()
+ if rc != 0:
+ module.fail_json(name=vnic.name, msg=err, rc=rc)
+ elif vnic.state == 'present':
+ if not vnic.vnic_exists():
+ if module.check_mode:
+ module.exit_json(changed=True)
+ (rc, out, err) = vnic.create_vnic()
+
+ if rc is not None and rc != 0:
+ module.fail_json(name=vnic.name, msg=err, rc=rc)
+
+ if rc is None:
+ result['changed'] = False
+ else:
+ result['changed'] = True
+
+ if out:
+ result['stdout'] = out
+ if err:
+ result['stderr'] = err
+
+ module.exit_json(**result)
+
+from ansible.module_utils.basic import *
+main()
diff --git a/network/illumos/flowadm.py b/network/illumos/flowadm.py
new file mode 100644
index 00000000..73cc91af
--- /dev/null
+++ b/network/illumos/flowadm.py
@@ -0,0 +1,503 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# (c) 2016, Adam Števko <adam.stevko@gmail.com>
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+
+DOCUMENTATION = '''
+---
+module: flowadm
+short_description: Manage bandwidth resource control and priority for protocols, services and zones.
+description:
+ - Create/modify/remove networking bandwidth and associated resources for a type of traffic on a particular link.
+version_added: "2.2"
+author: Adam Števko (@xen0l)
+options:
+ name:
+ description: >
+ - A flow is defined as a set of attributes based on Layer 3 and Layer 4
+ headers, which can be used to identify a protocol, service, or a zone.
+ required: true
+ aliases: [ 'flow' ]
+ link:
+ description:
+ - Specifiies a link to configure flow on.
+ required: false
+ local_ip:
+ description:
+ - Identifies a network flow by the local IP address.
+ required: false
+ remove_ip:
+ description:
+ - Identifies a network flow by the remote IP address.
+ required: false
+ transport:
+ description: >
+ - Specifies a Layer 4 protocol to be used. It is typically used in combination with I(local_port) to
+ identify the service that needs special attention.
+ required: false
+ local_port:
+ description:
+ - Identifies a service specified by the local port.
+ required: false
+ dsfield:
+ description: >
+ - Identifies the 8-bit differentiated services field (as defined in
+ RFC 2474). The optional dsfield_mask is used to state the bits of interest in
+ the differentiated services field when comparing with the dsfield
+ value. Both values must be in hexadecimal.
+ required: false
+ maxbw:
+ description: >
+ - Sets the full duplex bandwidth for the flow. The bandwidth is
+ specified as an integer with one of the scale suffixes(K, M, or G
+ for Kbps, Mbps, and Gbps). If no units are specified, the input
+ value will be read as Mbps.
+ required: false
+ priority:
+ description:
+ - Sets the relative priority for the flow.
+ required: false
+ default: 'medium'
+ choices: [ 'low', 'medium', 'high' ]
+ temporary:
+ description:
+ - Specifies that the configured flow is temporary. Temporary
+ flows do not persist across reboots.
+ required: false
+ default: false
+ choices: [ "true", "false" ]
+ state:
+ description:
+ - Create/delete/enable/disable an IP address on the network interface.
+ required: false
+ default: present
+ choices: [ 'absent', 'present', 'resetted' ]
+'''
+
+EXAMPLES = '''
+# Limit SSH traffic to 100M via vnic0 interface
+flowadm: link=vnic0 flow=ssh_out transport=tcp local_port=22 maxbw=100M state=present
+
+# Reset flow properties
+flowadm: name=dns state=resetted
+
+# Configure policy for EF PHB (DSCP value of 101110 from RFC 2598) with a bandwidth of 500 Mbps and a high priority.
+flowadm: link=bge0 dsfield=0x2e:0xfc maxbw=500M priority=high flow=efphb-flow state=present
+'''
+
+RETURN = '''
+name:
+ description: flow name
+ returned: always
+ type: string
+ sample: "http_drop"
+link:
+ description: flow's link
+ returned: if link is defined
+ type: string
+ sample: "vnic0"
+state:
+ description: state of the target
+ returned: always
+ type: string
+ sample: "present"
+temporary:
+ description: flow's persistence
+ returned: always
+ type: boolean
+ sample: "True"
+priority:
+ description: flow's priority
+ returned: if priority is defined
+ type: string
+ sample: "low"
+transport:
+ description: flow's transport
+ returned: if transport is defined
+ type: string
+ sample: "tcp"
+maxbw:
+ description: flow's maximum bandwidth
+ returned: if maxbw is defined
+ type: string
+ sample: "100M"
+local_Ip:
+ description: flow's local IP address
+ returned: if local_ip is defined
+ type: string
+ sample: "10.0.0.42"
+local_port:
+ description: flow's local port
+ returned: if local_port is defined
+ type: int
+ sample: 1337
+remote_Ip:
+ description: flow's remote IP address
+ returned: if remote_ip is defined
+ type: string
+ sample: "10.0.0.42"
+dsfield:
+ description: flow's differentiated services value
+ returned: if dsfield is defined
+ type: string
+ sample: "0x2e:0xfc"
+'''
+
+
+import socket
+
+SUPPORTED_TRANSPORTS = ['tcp', 'udp', 'sctp', 'icmp', 'icmpv6']
+SUPPORTED_PRIORITIES = ['low', 'medium', 'high']
+
+SUPPORTED_ATTRIBUTES = ['local_ip', 'remote_ip', 'transport', 'local_port', 'dsfield']
+SUPPORTPED_PROPERTIES = ['maxbw', 'priority']
+
+
+class Flow(object):
+
+ def __init__(self, module):
+ self.module = module
+
+ self.name = module.params['name']
+ self.link = module.params['link']
+ self.local_ip = module.params['local_ip']
+ self.remote_ip = module.params['remote_ip']
+ self.transport = module.params['transport']
+ self.local_port = module.params['local_port']
+ self.dsfield = module.params['dsfield']
+ self.maxbw = module.params['maxbw']
+ self.priority = module.params['priority']
+ self.temporary = module.params['temporary']
+ self.state = module.params['state']
+
+ self._needs_updating = {
+ 'maxbw': False,
+ 'priority': False,
+ }
+
+ @classmethod
+ def is_valid_port(cls, port):
+ return 1 <= int(port) <= 65535
+
+ @classmethod
+ def is_valid_address(cls, ip):
+
+ if ip.count('/') == 1:
+ ip_address, netmask = ip.split('/')
+ else:
+ ip_address = ip
+
+ if len(ip_address.split('.')) == 4:
+ try:
+ socket.inet_pton(socket.AF_INET, ip_address)
+ except socket.error:
+ return False
+
+ if not 0 <= netmask <= 32:
+ return False
+ else:
+ try:
+ socket.inet_pton(socket.AF_INET6, ip_address)
+ except socket.error:
+ return False
+
+ if not 0 <= netmask <= 128:
+ return False
+
+ return True
+
+ @classmethod
+ def is_hex(cls, number):
+ try:
+ int(number, 16)
+ except ValueError:
+ return False
+
+ return True
+
+ @classmethod
+ def is_valid_dsfield(cls, dsfield):
+
+ dsmask = None
+
+ if dsfield.count(':') == 1:
+ dsval = dsfield.split(':')[0]
+ else:
+ dsval, dsmask = dsfield.split(':')
+
+ if dsmask and not 0x01 <= int(dsmask, 16) <= 0xff and not 0x01 <= int(dsval, 16) <= 0xff:
+ return False
+ elif not 0x01 <= int(dsval, 16) <= 0xff:
+ return False
+
+ return True
+
+ def flow_exists(self):
+ cmd = [self.module.get_bin_path('flowadm')]
+
+ cmd.append('show-flow')
+ cmd.append(self.name)
+
+ (rc, _, _) = self.module.run_command(cmd)
+
+ if rc == 0:
+ return True
+ else:
+ return False
+
+ def delete_flow(self):
+ cmd = [self.module.get_bin_path('flowadm')]
+
+ cmd.append('remove-flow')
+ if self.temporary:
+ cmd.append('-t')
+ cmd.append(self.name)
+
+ return self.module.run_command(cmd)
+
+ def create_flow(self):
+ cmd = [self.module.get_bin_path('flowadm')]
+
+ cmd.append('add-flow')
+ cmd.append('-l')
+ cmd.append(self.link)
+
+ if self.local_ip:
+ cmd.append('-a')
+ cmd.append('local_ip=' + self.local_ip)
+
+ if self.remote_ip:
+ cmd.append('-a')
+ cmd.append('remote_ip=' + self.remote_ip)
+
+ if self.transport:
+ cmd.append('-a')
+ cmd.append('transport=' + self.transport)
+
+ if self.local_port:
+ cmd.append('-a')
+ cmd.append('local_port=' + self.local_port)
+
+ if self.dsfield:
+ cmd.append('-a')
+ cmd.append('dsfield=' + self.dsfield)
+
+ if self.maxbw:
+ cmd.append('-p')
+ cmd.append('maxbw=' + self.maxbw)
+
+ if self.priority:
+ cmd.append('-p')
+ cmd.append('priority=' + self.priority)
+
+ if self.temporary:
+ cmd.append('-t')
+ cmd.append(self.name)
+
+ return self.module.run_command(cmd)
+
+ def _query_flow_props(self):
+ cmd = [self.module.get_bin_path('flowadm')]
+
+ cmd.append('show-flowprop')
+ cmd.append('-c')
+ cmd.append('-o')
+ cmd.append('property,possible')
+ cmd.append(self.name)
+
+ return self.module.run_command(cmd)
+
+ def flow_needs_udpating(self):
+ (rc, out, err) = self._query_flow_props()
+
+ NEEDS_UPDATING = False
+
+ if rc == 0:
+ properties = (line.split(':') for line in out.rstrip().split('\n'))
+ for prop, value in properties:
+ if prop == 'maxbw' and self.maxbw != value:
+ self._needs_updating.update({prop: True})
+ NEEDS_UPDATING = True
+
+ elif prop == 'priority' and self.priority != value:
+ self._needs_updating.update({prop: True})
+ NEEDS_UPDATING = True
+
+ return NEEDS_UPDATING
+ else:
+ self.module.fail_json(msg='Error while checking flow properties: %s' % err,
+ stderr=err,
+ rc=rc)
+
+ def update_flow(self):
+ cmd = [self.module.get_bin_path('flowadm')]
+
+ cmd.append('set-flowprop')
+
+ if self.maxbw and self._needs_updating['maxbw']:
+ cmd.append('-p')
+ cmd.append('maxbw=' + self.maxbw)
+
+ if self.priority and self._needs_updating['priority']:
+ cmd.append('-p')
+ cmd.append('priority=' + self.priority)
+
+ if self.temporary:
+ cmd.append('-t')
+ cmd.append(self.name)
+
+ return self.module.run_command(cmd)
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ name=dict(required=True, aliases=['flow']),
+ link=dict(required=False),
+ local_ip=dict(required=False),
+ remote_ip=dict(required=False),
+ transport=dict(required=False, choices=SUPPORTED_TRANSPORTS),
+ local_port=dict(required=False),
+ dsfield=dict(required=False),
+ maxbw=dict(required=False),
+ priority=dict(required=False,
+ default='medium',
+ choices=SUPPORTED_PRIORITIES),
+ temporary=dict(default=False, type='bool'),
+ state=dict(required=False,
+ default='present',
+ choices=['absent', 'present', 'resetted']),
+ ),
+ mutually_exclusive=[
+ ('local_ip', 'remote_ip'),
+ ('local_ip', 'transport'),
+ ('local_ip', 'local_port'),
+ ('local_ip', 'dsfield'),
+ ('remote_ip', 'transport'),
+ ('remote_ip', 'local_port'),
+ ('remote_ip', 'dsfield'),
+ ('transport', 'dsfield'),
+ ('local_port', 'dsfield'),
+ ],
+ supports_check_mode=True
+ )
+
+ flow = Flow(module)
+
+ rc = None
+ out = ''
+ err = ''
+ result = {}
+ result['name'] = flow.name
+ result['state'] = flow.state
+ result['temporary'] = flow.temporary
+
+ if flow.link:
+ result['link'] = flow.link
+
+ if flow.maxbw:
+ result['maxbw'] = flow.maxbw
+
+ if flow.priority:
+ result['priority'] = flow.priority
+
+ if flow.local_ip:
+ if flow.is_valid_address(flow.local_ip):
+ result['local_ip'] = flow.local_ip
+
+ if flow.remote_ip:
+ if flow.is_valid_address(flow.remote_ip):
+ result['remote_ip'] = flow.remote_ip
+
+ if flow.transport:
+ result['transport'] = flow.transport
+
+ if flow.local_port:
+ if flow.is_valid_port(flow.local_port):
+ result['local_port'] = flow.local_port
+ else:
+ module.fail_json(msg='Invalid port: %s' % flow.local_port,
+ rc=1)
+
+ if flow.dsfield:
+ if flow.is_valid_dsfield(flow.dsfield):
+ result['dsfield'] = flow.dsfield
+ else:
+ module.fail_json(msg='Invalid dsfield: %s' % flow.dsfield,
+ rc=1)
+
+ if flow.state == 'absent':
+ if flow.flow_exists():
+ if module.check_mode:
+ module.exit_json(changed=True)
+
+ (rc, out, err) = flow.delete_flow()
+ if rc != 0:
+ module.fail_json(msg='Error while deleting flow: "%s"' % err,
+ name=flow.name,
+ stderr=err,
+ rc=rc)
+
+ elif flow.state == 'present':
+ if not flow.flow_exists():
+ if module.check_mode:
+ module.exit_json(changed=True)
+
+ (rc, out, err) = flow.create_flow()
+ if rc != 0:
+ module.fail_json(msg='Error while creating flow: "%s"' % err,
+ name=flow.name,
+ stderr=err,
+ rc=rc)
+ else:
+ if flow.flow_needs_udpating():
+ (rc, out, err) = flow.update_flow()
+ if rc != 0:
+ module.fail_json(msg='Error while updating flow: "%s"' % err,
+ name=flow.name,
+ stderr=err,
+ rc=rc)
+
+ elif flow.state == 'resetted':
+ if flow.flow_exists():
+ if module.check_mode:
+ module.exit_json(changed=True)
+
+ (rc, out, err) = flow.reset_flow()
+ if rc != 0:
+ module.fail_json(msg='Error while resetting flow: "%s"' % err,
+ name=flow.name,
+ stderr=err,
+ rc=rc)
+
+ if rc is None:
+ result['changed'] = False
+ else:
+ result['changed'] = True
+
+ if out:
+ result['stdout'] = out
+ if err:
+ result['stderr'] = err
+
+ module.exit_json(**result)
+
+
+from ansible.module_utils.basic import *
+main()
diff --git a/network/illumos/ipadm_if.py b/network/illumos/ipadm_if.py
new file mode 100644
index 00000000..c7419848
--- /dev/null
+++ b/network/illumos/ipadm_if.py
@@ -0,0 +1,222 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# (c) 2015, Adam Števko <adam.stevko@gmail.com>
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+
+DOCUMENTATION = '''
+---
+module: ipadm_if
+short_description: Manage IP interfaces on Solaris/illumos systems.
+description:
+ - Create, delete, enable or disable IP interfaces on Solaris/illumos
+ systems.
+version_added: "2.2"
+author: Adam Števko (@xen0l)
+options:
+ name:
+ description:
+ - IP interface name.
+ required: true
+ temporary:
+ description:
+ - Specifies that the IP interface is temporary. Temporary IP
+ interfaces do not persist across reboots.
+ required: false
+ default: false
+ choices: [ "true", "false" ]
+ state:
+ description:
+ - Create or delete Solaris/illumos IP interfaces.
+ required: false
+ default: "present"
+ choices: [ "present", "absent", "enabled", "disabled" ]
+'''
+
+EXAMPLES = '''
+# Create vnic0 interface
+ipadm_if: name=vnic0 state=enabled
+
+# Disable vnic0 interface
+ipadm_if: name=vnic0 state=disabled
+'''
+
+RETURN = '''
+name:
+ description: IP interface name
+ returned: always
+ type: string
+ sample: "vnic0"
+state:
+ description: state of the target
+ returned: always
+ type: string
+ sample: "present"
+temporary:
+ description: persistence of a IP interface
+ returned: always
+ type: boolean
+ sample: "True"
+'''
+
+
+class IPInterface(object):
+
+ def __init__(self, module):
+ self.module = module
+
+ self.name = module.params['name']
+ self.temporary = module.params['temporary']
+ self.state = module.params['state']
+
+ def interface_exists(self):
+ cmd = [self.module.get_bin_path('ipadm', True)]
+
+ cmd.append('show-if')
+ cmd.append(self.name)
+
+ (rc, _, _) = self.module.run_command(cmd)
+ if rc == 0:
+ return True
+ else:
+ return False
+
+ def interface_is_disabled(self):
+ cmd = [self.module.get_bin_path('ipadm', True)]
+
+ cmd.append('show-if')
+ cmd.append('-o')
+ cmd.append('state')
+ cmd.append(self.name)
+
+ (rc, out, err) = self.module.run_command(cmd)
+ if rc != 0:
+ self.module.fail_json(name=self.name, rc=rc, msg=err)
+
+ return 'disabled' in out
+
+ def create_interface(self):
+ cmd = [self.module.get_bin_path('ipadm', True)]
+
+ cmd.append('create-if')
+
+ if self.temporary:
+ cmd.append('-t')
+
+ cmd.append(self.name)
+
+ return self.module.run_command(cmd)
+
+ def delete_interface(self):
+ cmd = [self.module.get_bin_path('ipadm', True)]
+
+ cmd.append('delete-if')
+
+ if self.temporary:
+ cmd.append('-t')
+
+ cmd.append(self.name)
+
+ return self.module.run_command(cmd)
+
+ def enable_interface(self):
+ cmd = [self.module.get_bin_path('ipadm', True)]
+
+ cmd.append('enable-if')
+ cmd.append('-t')
+ cmd.append(self.name)
+
+ return self.module.run_command(cmd)
+
+ def disable_interface(self):
+ cmd = [self.module.get_bin_path('ipadm', True)]
+
+ cmd.append('disable-if')
+ cmd.append('-t')
+ cmd.append(self.name)
+
+ return self.module.run_command(cmd)
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ name=dict(required=True),
+ temporary=dict(default=False, type='bool'),
+ state=dict(default='present', choices=['absent',
+ 'present',
+ 'enabled',
+ 'disabled']),
+ ),
+ supports_check_mode=True
+ )
+
+ interface = IPInterface(module)
+
+ rc = None
+ out = ''
+ err = ''
+ result = {}
+ result['name'] = interface.name
+ result['state'] = interface.state
+ result['temporary'] = interface.temporary
+
+ if interface.state == 'absent':
+ if interface.interface_exists():
+ if module.check_mode:
+ module.exit_json(changed=True)
+ (rc, out, err) = interface.delete_interface()
+ if rc != 0:
+ module.fail_json(name=interface.name, msg=err, rc=rc)
+ elif interface.state == 'present':
+ if not interface.interface_exists():
+ if module.check_mode:
+ module.exit_json(changed=True)
+ (rc, out, err) = interface.create_interface()
+
+ if rc is not None and rc != 0:
+ module.fail_json(name=interface.name, msg=err, rc=rc)
+
+ elif interface.state == 'enabled':
+ if interface.interface_is_disabled():
+ (rc, out, err) = interface.enable_interface()
+
+ if rc is not None and rc != 0:
+ module.fail_json(name=interface.name, msg=err, rc=rc)
+
+ elif interface.state == 'disabled':
+ if not interface.interface_is_disabled():
+ (rc, out, err) = interface.disable_interface()
+
+ if rc is not None and rc != 0:
+ module.fail_json(name=interface.name, msg=err, rc=rc)
+
+ if rc is None:
+ result['changed'] = False
+ else:
+ result['changed'] = True
+
+ if out:
+ result['stdout'] = out
+ if err:
+ result['stderr'] = err
+
+ module.exit_json(**result)
+
+from ansible.module_utils.basic import *
+main()
diff --git a/network/illumos/ipadm_prop.py b/network/illumos/ipadm_prop.py
new file mode 100644
index 00000000..5399189a
--- /dev/null
+++ b/network/illumos/ipadm_prop.py
@@ -0,0 +1,264 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# (c) 2015, Adam Števko <adam.stevko@gmail.com>
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+
+DOCUMENTATION = '''
+---
+module: ipadm_prop
+short_description: Manage protocol properties on Solaris/illumos systems.
+description:
+ - Modify protocol properties on Solaris/illumos systems.
+version_added: "2.2"
+author: Adam Števko (@xen0l)
+options:
+ protocol:
+ description:
+ - Specifies the procotol for which we want to manage properties.
+ required: true
+ property:
+ description:
+ - Specifies the name of property we want to manage.
+ required: true
+ value:
+ description:
+ - Specifies the value we want to set for the property.
+ required: false
+ temporary:
+ description:
+ - Specifies that the property value is temporary. Temporary
+ property values do not persist across reboots.
+ required: false
+ default: false
+ choices: [ "true", "false" ]
+ state:
+ description:
+ - Set or reset the property value.
+ required: false
+ default: present
+ choices: [ "present", "absent", "reset" ]
+'''
+
+EXAMPLES = '''
+# Set TCP receive buffer size
+ipadm_prop: protocol=tcp property=recv_buf value=65536
+
+# Reset UDP send buffer size to the default value
+ipadm_prop: protocol=udp property=send_buf state=reset
+'''
+
+RETURN = '''
+protocol:
+ description: property's protocol
+ returned: always
+ type: string
+ sample: "TCP"
+property:
+ description: name of the property
+ returned: always
+ type: string
+ sample: "recv_maxbuf"
+state:
+ description: state of the target
+ returned: always
+ type: string
+ sample: "present"
+temporary:
+ description: property's persistence
+ returned: always
+ type: boolean
+ sample: "True"
+value:
+ description: value of the property
+ returned: always
+ type: int/string (depends on property)
+ sample: 1024/never
+'''
+
+SUPPORTED_PROTOCOLS = ['ipv4', 'ipv6', 'icmp', 'tcp', 'udp', 'sctp']
+
+
+class Prop(object):
+
+ def __init__(self, module):
+ self.module = module
+
+ self.protocol = module.params['protocol']
+ self.property = module.params['property']
+ self.value = module.params['value']
+ self.temporary = module.params['temporary']
+ self.state = module.params['state']
+
+ def property_exists(self):
+ cmd = [self.module.get_bin_path('ipadm')]
+
+ cmd.append('show-prop')
+ cmd.append('-p')
+ cmd.append(self.property)
+ cmd.append(self.protocol)
+
+ (rc, _, _) = self.module.run_command(cmd)
+
+ if rc == 0:
+ return True
+ else:
+ self.module.fail_json(msg='Unknown property "%s" for protocol %s' %
+ (self.property, self.protocol),
+ protocol=self.protocol,
+ property=self.property)
+
+ def property_is_modified(self):
+ cmd = [self.module.get_bin_path('ipadm')]
+
+ cmd.append('show-prop')
+ cmd.append('-c')
+ cmd.append('-o')
+ cmd.append('current,default')
+ cmd.append('-p')
+ cmd.append(self.property)
+ cmd.append(self.protocol)
+
+ (rc, out, _) = self.module.run_command(cmd)
+
+ out = out.rstrip()
+ (value, default) = out.split(':')
+
+ if rc == 0 and value == default:
+ return True
+ else:
+ return False
+
+ def property_is_set(self):
+ cmd = [self.module.get_bin_path('ipadm')]
+
+ cmd.append('show-prop')
+ cmd.append('-c')
+ cmd.append('-o')
+ cmd.append('current')
+ cmd.append('-p')
+ cmd.append(self.property)
+ cmd.append(self.protocol)
+
+ (rc, out, _) = self.module.run_command(cmd)
+
+ out = out.rstrip()
+
+ if rc == 0 and self.value == out:
+ return True
+ else:
+ return False
+
+ def set_property(self):
+ cmd = [self.module.get_bin_path('ipadm')]
+
+ cmd.append('set-prop')
+
+ if self.temporary:
+ cmd.append('-t')
+
+ cmd.append('-p')
+ cmd.append(self.property + "=" + self.value)
+ cmd.append(self.protocol)
+
+ return self.module.run_command(cmd)
+
+ def reset_property(self):
+ cmd = [self.module.get_bin_path('ipadm')]
+
+ cmd.append('reset-prop')
+
+ if self.temporary:
+ cmd.append('-t')
+
+ cmd.append('-p')
+ cmd.append(self.property)
+ cmd.append(self.protocol)
+
+ return self.module.run_command(cmd)
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ protocol=dict(required=True, choices=SUPPORTED_PROTOCOLS),
+ property=dict(required=True),
+ value=dict(required=False),
+ temporary=dict(default=False, type='bool'),
+ state=dict(
+ default='present', choices=['absent', 'present', 'reset']),
+ ),
+ supports_check_mode=True
+ )
+
+ prop = Prop(module)
+
+ rc = None
+ out = ''
+ err = ''
+ result = {}
+ result['protocol'] = prop.protocol
+ result['property'] = prop.property
+ result['state'] = prop.state
+ result['temporary'] = prop.temporary
+ if prop.value:
+ result['value'] = prop.value
+
+ if prop.state == 'absent' or prop.state == 'reset':
+ if prop.property_exists():
+ if not prop.property_is_modified():
+ if module.check_mode:
+ module.exit_json(changed=True)
+ (rc, out, err) = prop.reset_property()
+ if rc != 0:
+ module.fail_json(protocol=prop.protocol,
+ property=prop.property,
+ msg=err,
+ rc=rc)
+
+ elif prop.state == 'present':
+ if prop.value is None:
+ module.fail_json(msg='Value is mandatory with state "present"')
+
+ if prop.property_exists():
+ if not prop.property_is_set():
+ if module.check_mode:
+ module.exit_json(changed=True)
+
+ (rc, out, err) = prop.set_property()
+ if rc != 0:
+ module.fail_json(protocol=prop.protocol,
+ property=prop.property,
+ msg=err,
+ rc=rc)
+
+ if rc is None:
+ result['changed'] = False
+ else:
+ result['changed'] = True
+
+ if out:
+ result['stdout'] = out
+ if err:
+ result['stderr'] = err
+
+ module.exit_json(**result)
+
+
+from ansible.module_utils.basic import *
+main()