1#!/usr/local/bin/python3.8
2# -*- coding: utf-8 -*-
3# Copyright (c) 2018 Red Hat, 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
9DOCUMENTATION = '''
10---
11module: nios_network
12author: "Peter Sprygada (@privateip)"
13short_description: Configure Infoblox NIOS network object
14deprecated:
15    why: Please install the infoblox.nios_modules collection and use the corresponding module from it.
16    alternative: infoblox.nios_modules.nios_network
17    removed_in: 5.0.0
18description:
19  - Adds and/or removes instances of network objects from
20    Infoblox NIOS servers.  This module manages NIOS C(network) objects
21    using the Infoblox WAPI interface over REST.
22  - Supports both IPV4 and IPV6 internet protocols
23requirements:
24  - infoblox-client
25extends_documentation_fragment:
26- community.general.nios
27
28options:
29  network:
30    description:
31      - Specifies the network to add or remove from the system.  The value
32        should use CIDR notation.
33    required: true
34    aliases:
35      - name
36      - cidr
37    type: str
38  network_view:
39    description:
40      - Configures the name of the network view to associate with this
41        configured instance.
42    default: default
43    type: str
44  options:
45    description:
46      - Configures the set of DHCP options to be included as part of
47        the configured network instance.  This argument accepts a list
48        of values (see suboptions).  When configuring suboptions at
49        least one of C(name) or C(num) must be specified.
50    type: list
51    elements: dict
52    suboptions:
53      name:
54        description:
55          - The name of the DHCP option to configure. The standard options are
56            C(router), C(router-templates), C(domain-name-servers), C(domain-name),
57            C(broadcast-address), C(broadcast-address-offset), C(dhcp-lease-time),
58            and C(dhcp6.name-servers).
59        type: str
60      num:
61        description:
62          - The number of the DHCP option to configure
63        type: int
64      value:
65        description:
66          - The value of the DHCP option specified by C(name)
67        required: true
68        type: str
69      use_option:
70        description:
71          - Only applies to a subset of options (see NIOS API documentation)
72        type: bool
73        default: 'yes'
74      vendor_class:
75        description:
76          - The name of the space this DHCP option is associated to
77        default: DHCP
78        type: str
79  extattrs:
80    description:
81      - Allows for the configuration of Extensible Attributes on the
82        instance of the object.  This argument accepts a set of key / value
83        pairs for configuration.
84    type: dict
85  comment:
86    description:
87      - Configures a text string comment to be associated with the instance
88        of this object.  The provided text string will be configured on the
89        object instance.
90    type: str
91  container:
92    description:
93      - If set to true it'll create the network container to be added or removed
94        from the system.
95    type: bool
96  state:
97    description:
98      - Configures the intended state of the instance of the object on
99        the NIOS server.  When this value is set to C(present), the object
100        is configured on the device and when this value is set to C(absent)
101        the value is removed (if necessary) from the device.
102    default: present
103    choices:
104      - present
105      - absent
106    type: str
107'''
108
109EXAMPLES = '''
110- name: Configure a network ipv4
111  community.general.nios_network:
112    network: 192.168.10.0/24
113    comment: this is a test comment
114    state: present
115    provider:
116      host: "{{ inventory_hostname_short }}"
117      username: admin
118      password: admin
119  connection: local
120- name: Configure a network ipv6
121  community.general.nios_network:
122    network: fe80::/64
123    comment: this is a test comment
124    state: present
125    provider:
126      host: "{{ inventory_hostname_short }}"
127      username: admin
128      password: admin
129  connection: local
130- name: Set dhcp options for a network ipv4
131  community.general.nios_network:
132    network: 192.168.10.0/24
133    comment: this is a test comment
134    options:
135      - name: domain-name
136        value: ansible.com
137    state: present
138    provider:
139      host: "{{ inventory_hostname_short }}"
140      username: admin
141      password: admin
142  connection: local
143- name: Remove a network ipv4
144  community.general.nios_network:
145    network: 192.168.10.0/24
146    state: absent
147    provider:
148      host: "{{ inventory_hostname_short }}"
149      username: admin
150      password: admin
151  connection: local
152- name: Configure a ipv4 network container
153  community.general.nios_network:
154    network: 192.168.10.0/24
155    container: true
156    comment: test network container
157    state: present
158    provider:
159      host: "{{ inventory_hostname_short }}"
160      username: admin
161      password: admin
162  connection: local
163- name: Configure a ipv6 network container
164  community.general.nios_network:
165    network: fe80::/64
166    container: true
167    comment: test network container
168    state: present
169    provider:
170      host: "{{ inventory_hostname_short }}"
171      username: admin
172      password: admin
173  connection: local
174- name: Remove a ipv4 network container
175  community.general.nios_network:
176    networkr: 192.168.10.0/24
177    container: true
178    comment: test network container
179    state: absent
180    provider:
181      host: "{{ inventory_hostname_short }}"
182      username: admin
183      password: admin
184  connection: local
185'''
186
187RETURN = ''' # '''
188
189import socket
190
191from ansible.module_utils.basic import AnsibleModule
192from ansible.module_utils.six import iteritems
193from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import WapiModule
194from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import NIOS_IPV4_NETWORK, NIOS_IPV6_NETWORK
195from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import NIOS_IPV4_NETWORK_CONTAINER, NIOS_IPV6_NETWORK_CONTAINER
196from ansible_collections.community.general.plugins.module_utils.net_tools.nios.api import normalize_ib_spec
197
198
199# The following function validate_ip_address has been taken from
200# https://github.com/ansible-collections/ansible.netcommon/blob/20124ecbb420daa0f5bb9cdaa865a952657aa0e7/plugins/module_utils/network/common/utils.py#L496
201# The code there is licensed under BSD 2-clause.
202# Copyright (c) 2016 Red Hat Inc.
203def validate_ip_address(address):
204    try:
205        socket.inet_aton(address)
206    except socket.error:
207        return False
208    return address.count(".") == 3
209
210
211# The following function validate_ip_v6_address has been taken from
212# https://github.com/ansible-collections/ansible.netcommon/blob/20124ecbb420daa0f5bb9cdaa865a952657aa0e7/plugins/module_utils/network/common/utils.py#L504
213# The code there is licensed under BSD 2-clause.
214# Copyright (c) 2016 Red Hat Inc.
215def validate_ip_v6_address(address):
216    try:
217        socket.inet_pton(socket.AF_INET6, address)
218    except socket.error:
219        return False
220    return True
221
222
223def options(module):
224    ''' Transforms the module argument into a valid WAPI struct
225    This function will transform the options argument into a structure that
226    is a valid WAPI structure in the format of:
227        {
228            name: <value>,
229            num: <value>,
230            value: <value>,
231            use_option: <value>,
232            vendor_class: <value>
233        }
234    It will remove any options that are set to None since WAPI will error on
235    that condition.  It will also verify that either `name` or `num` is
236    set in the structure but does not validate the values are equal.
237    The remainder of the value validation is performed by WAPI
238    '''
239    options = list()
240    for item in module.params['options']:
241        opt = dict([(k, v) for k, v in iteritems(item) if v is not None])
242        if 'name' not in opt and 'num' not in opt:
243            module.fail_json(msg='one of `name` or `num` is required for option value')
244        options.append(opt)
245    return options
246
247
248def check_ip_addr_type(obj_filter, ib_spec):
249    '''This function will check if the argument ip is type v4/v6 and return appropriate infoblox
250       network/networkcontainer type
251    '''
252
253    ip = obj_filter['network']
254    if 'container' in obj_filter and obj_filter['container']:
255        check_ip = ip.split('/')
256        del ib_spec['container']  # removing the container key from post arguments
257        del ib_spec['options']  # removing option argument as for network container it's not supported
258        if validate_ip_address(check_ip[0]):
259            return NIOS_IPV4_NETWORK_CONTAINER, ib_spec
260        elif validate_ip_v6_address(check_ip[0]):
261            return NIOS_IPV6_NETWORK_CONTAINER, ib_spec
262    else:
263        check_ip = ip.split('/')
264        del ib_spec['container']  # removing the container key from post arguments
265        if validate_ip_address(check_ip[0]):
266            return NIOS_IPV4_NETWORK, ib_spec
267        elif validate_ip_v6_address(check_ip[0]):
268            return NIOS_IPV6_NETWORK, ib_spec
269
270
271def check_vendor_specific_dhcp_option(module, ib_spec):
272    '''This function will check if the argument dhcp option belongs to vendor-specific and if yes then will remove
273     use_options flag which is not supported with vendor-specific dhcp options.
274    '''
275    for key, value in iteritems(ib_spec):
276        if isinstance(module.params[key], list):
277            temp_dict = module.params[key][0]
278            if 'num' in temp_dict:
279                if temp_dict['num'] in (43, 124, 125):
280                    del module.params[key][0]['use_option']
281    return ib_spec
282
283
284def main():
285    ''' Main entry point for module execution
286    '''
287    option_spec = dict(
288        # one of name or num is required; enforced by the function options()
289        name=dict(),
290        num=dict(type='int'),
291
292        value=dict(required=True),
293
294        use_option=dict(type='bool', default=True),
295        vendor_class=dict(default='DHCP')
296    )
297
298    ib_spec = dict(
299        network=dict(required=True, aliases=['name', 'cidr'], ib_req=True),
300        network_view=dict(default='default', ib_req=True),
301
302        options=dict(type='list', elements='dict', options=option_spec, transform=options),
303
304        extattrs=dict(type='dict'),
305        comment=dict(),
306        container=dict(type='bool', ib_req=True)
307    )
308
309    argument_spec = dict(
310        provider=dict(required=True),
311        state=dict(default='present', choices=['present', 'absent'])
312    )
313
314    argument_spec.update(normalize_ib_spec(ib_spec))
315    argument_spec.update(WapiModule.provider_spec)
316
317    module = AnsibleModule(argument_spec=argument_spec,
318                           supports_check_mode=True)
319
320    # to get the argument ipaddr
321    obj_filter = dict([(k, module.params[k]) for k, v in iteritems(ib_spec) if v.get('ib_req')])
322    network_type, ib_spec = check_ip_addr_type(obj_filter, ib_spec)
323
324    wapi = WapiModule(module)
325    # to check for vendor specific dhcp option
326    ib_spec = check_vendor_specific_dhcp_option(module, ib_spec)
327
328    result = wapi.run(network_type, ib_spec)
329
330    module.exit_json(**result)
331
332
333if __name__ == '__main__':
334    main()
335