Added new module to support NIOS Fixedaddress DHCP property (#49119)
Signed-off-by: Sumit Jaiswal <sjaiswal@redhat.com> * nios fixedaddr new module Signed-off-by: Sumit Jaiswal <sjaiswal@redhat.com> * updating name Signed-off-by: Sumit Jaiswal <sjaiswal@redhat.com> * added unit tests Signed-off-by: Sumit Jaiswal <sjaiswal@redhat.com>
This commit is contained in:
parent
bc6cd13874
commit
189fcb37f9
3 changed files with 467 additions and 0 deletions
|
@ -55,6 +55,8 @@ NIOS_SRV_RECORD = 'record:srv'
|
|||
NIOS_NAPTR_RECORD = 'record:naptr'
|
||||
NIOS_TXT_RECORD = 'record:txt'
|
||||
NIOS_NSGROUP = 'nsgroup'
|
||||
NIOS_IPV4_FIXED_ADDRESS = 'fixedaddress'
|
||||
NIOS_IPV6_FIXED_ADDRESS = 'ipv6fixedaddress'
|
||||
|
||||
NIOS_PROVIDER_SPEC = {
|
||||
'host': dict(),
|
||||
|
@ -368,6 +370,8 @@ class WapiModule(WapiBase):
|
|||
test_obj_filter = dict([('name', name)])
|
||||
else:
|
||||
test_obj_filter = dict([('name', name), ('view', obj_filter['view'])])
|
||||
elif (ib_obj_type == NIOS_IPV4_FIXED_ADDRESS or ib_obj_type == NIOS_IPV6_FIXED_ADDRESS and 'mac' in obj_filter):
|
||||
test_obj_filter = dict([['mac', obj_filter['mac']]])
|
||||
# check if test_obj_filter is empty copy passed obj_filter
|
||||
else:
|
||||
test_obj_filter = obj_filter
|
||||
|
|
262
lib/ansible/modules/net_tools/nios/nios_fixed_address.py
Normal file
262
lib/ansible/modules/net_tools/nios/nios_fixed_address.py
Normal file
|
@ -0,0 +1,262 @@
|
|||
#!/usr/bin/python
|
||||
# Copyright (c) 2018 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'certified'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: nios_fixed_address
|
||||
version_added: "2.8"
|
||||
author: "Sumit Jaiswal (@sjaiswal)"
|
||||
short_description: Configure Infoblox NIOS DHCP Fixed Address
|
||||
description:
|
||||
- A fixed address is a specific IP address that a DHCP server
|
||||
always assigns when a lease request comes from a particular
|
||||
MAC address of the clien.
|
||||
- Supports both IPV4 and IPV6 internet protocols
|
||||
requirements:
|
||||
- infoblox-client
|
||||
extends_documentation_fragment: nios
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Specifies the hostname with which fixed DHCP ip-address is stored
|
||||
for respective mac.
|
||||
required: false
|
||||
ipaddr:
|
||||
description:
|
||||
- IPV4/V6 address of the fixed address.
|
||||
required: true
|
||||
mac:
|
||||
description:
|
||||
- The MAC address of the interface.
|
||||
required: true
|
||||
network:
|
||||
description:
|
||||
- Specifies the network range in which ipaddr exists.
|
||||
required: true
|
||||
aliases:
|
||||
- network
|
||||
network_view:
|
||||
description:
|
||||
- Configures the name of the network view to associate with this
|
||||
configured instance.
|
||||
required: false
|
||||
default: default
|
||||
options:
|
||||
description:
|
||||
- Configures the set of DHCP options to be included as part of
|
||||
the configured network instance. This argument accepts a list
|
||||
of values (see suboptions). When configuring suboptions at
|
||||
least one of C(name) or C(num) must be specified.
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
- The name of the DHCP option to configure
|
||||
num:
|
||||
description:
|
||||
- The number of the DHCP option to configure
|
||||
value:
|
||||
description:
|
||||
- The value of the DHCP option specified by C(name)
|
||||
required: true
|
||||
use_option:
|
||||
description:
|
||||
- Only applies to a subset of options (see NIOS API documentation)
|
||||
type: bool
|
||||
default: 'yes'
|
||||
vendor_class:
|
||||
description:
|
||||
- The name of the space this DHCP option is associated to
|
||||
default: DHCP
|
||||
extattrs:
|
||||
description:
|
||||
- Allows for the configuration of Extensible Attributes on the
|
||||
instance of the object. This argument accepts a set of key / value
|
||||
pairs for configuration.
|
||||
comment:
|
||||
description:
|
||||
- Configures a text string comment to be associated with the instance
|
||||
of this object. The provided text string will be configured on the
|
||||
object instance.
|
||||
state:
|
||||
description:
|
||||
- Configures the intended state of the instance of the object on
|
||||
the NIOS server. When this value is set to C(present), the object
|
||||
is configured on the device and when this value is set to C(absent)
|
||||
the value is removed (if necessary) from the device.
|
||||
default: present
|
||||
choices:
|
||||
- present
|
||||
- absent
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: configure ipv4 dhcp fixed address
|
||||
nios_fixed_address:
|
||||
name: ipv4_fixed
|
||||
ipaddr: 192.168.10.1
|
||||
mac: 08:6d:41:e8:fd:e8
|
||||
network: 192.168.10.0/24
|
||||
network_view: default
|
||||
comment: this is a test comment
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: configure a ipv6 dhcp fixed address
|
||||
nios_fixed_address:
|
||||
name: ipv6_fixed
|
||||
ipaddr: fe80::1/10
|
||||
mac: 08:6d:41:e8:fd:e8
|
||||
network: fe80::/64
|
||||
network_view: default
|
||||
comment: this is a test comment
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: set dhcp options for a ipv4 fixed address
|
||||
nios_fixed_address:
|
||||
name: ipv4_fixed
|
||||
ipaddr: 192.168.10.1
|
||||
mac: 08:6d:41:e8:fd:e8
|
||||
network: 192.168.10.0/24
|
||||
network_view: default
|
||||
comment: this is a test comment
|
||||
options:
|
||||
- name: domain-name
|
||||
value: ansible.com
|
||||
state: present
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
- name: remove a ipv4 dhcp fixed address
|
||||
nios_fixed_address:
|
||||
name: ipv4_fixed
|
||||
ipaddr: 192.168.10.1
|
||||
mac: 08:6d:41:e8:fd:e8
|
||||
network: 192.168.10.0/24
|
||||
network_view: default
|
||||
state: absent
|
||||
provider:
|
||||
host: "{{ inventory_hostname_short }}"
|
||||
username: admin
|
||||
password: admin
|
||||
connection: local
|
||||
'''
|
||||
|
||||
RETURN = ''' # '''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible.module_utils.net_tools.nios.api import WapiModule
|
||||
from ansible.module_utils.network.common.utils import validate_ip_address, validate_ip_v6_address
|
||||
from ansible.module_utils.net_tools.nios.api import NIOS_IPV4_FIXED_ADDRESS, NIOS_IPV6_FIXED_ADDRESS
|
||||
|
||||
|
||||
def options(module):
|
||||
''' Transforms the module argument into a valid WAPI struct
|
||||
This function will transform the options argument into a structure that
|
||||
is a valid WAPI structure in the format of:
|
||||
{
|
||||
name: <value>,
|
||||
num: <value>,
|
||||
value: <value>,
|
||||
use_option: <value>,
|
||||
vendor_class: <value>
|
||||
}
|
||||
It will remove any options that are set to None since WAPI will error on
|
||||
that condition. It will also verify that either `name` or `num` is
|
||||
set in the structure but does not validate the values are equal.
|
||||
The remainder of the value validation is performed by WAPI
|
||||
'''
|
||||
options = list()
|
||||
for item in module.params['options']:
|
||||
opt = dict([(k, v) for k, v in iteritems(item) if v is not None])
|
||||
if 'name' not in opt and 'num' not in opt:
|
||||
module.fail_json(msg='one of `name` or `num` is required for option value')
|
||||
options.append(opt)
|
||||
return options
|
||||
|
||||
|
||||
def validate_ip_addr_type(ip, arg_spec, module):
|
||||
'''This function will check if the argument ip is type v4/v6 and return appropriate infoblox network type
|
||||
'''
|
||||
check_ip = ip.split('/')
|
||||
|
||||
if validate_ip_address(check_ip[0]) and 'ipaddr' in arg_spec:
|
||||
arg_spec['ipv4addr'] = arg_spec.pop('ipaddr')
|
||||
module.params['ipv4addr'] = module.params.pop('ipaddr')
|
||||
return NIOS_IPV4_FIXED_ADDRESS, arg_spec, module
|
||||
elif validate_ip_v6_address(check_ip[0]) and 'ipaddr' in arg_spec:
|
||||
arg_spec['ipv6addr'] = arg_spec.pop('ipaddr')
|
||||
module.params['ipv6addr'] = module.params.pop('ipaddr')
|
||||
return NIOS_IPV6_FIXED_ADDRESS, arg_spec, module
|
||||
|
||||
|
||||
def main():
|
||||
''' Main entry point for module execution
|
||||
'''
|
||||
option_spec = dict(
|
||||
# one of name or num is required; enforced by the function options()
|
||||
name=dict(),
|
||||
num=dict(type='int'),
|
||||
|
||||
value=dict(required=True),
|
||||
|
||||
use_option=dict(type='bool', default=True),
|
||||
vendor_class=dict(default='DHCP')
|
||||
)
|
||||
|
||||
ib_spec = dict(
|
||||
name=dict(required=True),
|
||||
ipaddr=dict(required=True, aliases=['ipaddr'], ib_req=True),
|
||||
mac=dict(required=True, aliases=['mac'], ib_req=True),
|
||||
network=dict(required=True, aliases=['network'], ib_req=True),
|
||||
network_view=dict(default='default', aliases=['network_view']),
|
||||
|
||||
options=dict(type='list', elements='dict', options=option_spec, transform=options),
|
||||
|
||||
extattrs=dict(type='dict'),
|
||||
comment=dict()
|
||||
)
|
||||
|
||||
argument_spec = dict(
|
||||
provider=dict(required=True),
|
||||
state=dict(default='present', choices=['present', 'absent'])
|
||||
)
|
||||
|
||||
argument_spec.update(ib_spec)
|
||||
argument_spec.update(WapiModule.provider_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
# to get the argument ipaddr
|
||||
obj_filter = dict([(k, module.params[k]) for k, v in iteritems(ib_spec) if v.get('ib_req')])
|
||||
# to modify argument based on ipaddr type i.e. IPV4/IPV6
|
||||
fixed_address_ip_type, ib_spec, module = validate_ip_addr_type(obj_filter['ipaddr'], ib_spec, module)
|
||||
|
||||
wapi = WapiModule(module)
|
||||
|
||||
result = wapi.run(fixed_address_ip_type, ib_spec)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
201
test/units/modules/net_tools/nios/test_nios_fixed_address.py
Normal file
201
test/units/modules/net_tools/nios/test_nios_fixed_address.py
Normal file
|
@ -0,0 +1,201 @@
|
|||
# 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/>.
|
||||
|
||||
# Make coding more python3-ish
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
from ansible.module_utils.net_tools.nios import api
|
||||
from ansible.modules.net_tools.nios import nios_fixed_address
|
||||
from units.compat.mock import patch, MagicMock, Mock
|
||||
from .test_nios_module import TestNiosModule, load_fixture
|
||||
|
||||
|
||||
class TestNiosFixedAddressModule(TestNiosModule):
|
||||
|
||||
module = nios_fixed_address
|
||||
|
||||
def setUp(self):
|
||||
super(TestNiosFixedAddressModule, self).setUp()
|
||||
self.module = MagicMock(name='ansible.modules.net_tools.nios.nios_fixed_address.WapiModule')
|
||||
self.module.check_mode = False
|
||||
self.module.params = {'provider': None}
|
||||
self.mock_wapi = patch('ansible.modules.net_tools.nios.nios_fixed_address.WapiModule')
|
||||
self.exec_command = self.mock_wapi.start()
|
||||
self.mock_wapi_run = patch('ansible.modules.net_tools.nios.nios_fixed_address.WapiModule.run')
|
||||
self.mock_wapi_run.start()
|
||||
self.load_config = self.mock_wapi_run.start()
|
||||
|
||||
def tearDown(self):
|
||||
super(TestNiosFixedAddressModule, self).tearDown()
|
||||
self.mock_wapi.stop()
|
||||
self.mock_wapi_run.stop()
|
||||
|
||||
def load_fixtures(self, commands=None):
|
||||
self.exec_command.return_value = (0, load_fixture('nios_result.txt').strip(), None)
|
||||
self.load_config.return_value = dict(diff=None, session='session')
|
||||
|
||||
def _get_wapi(self, test_object):
|
||||
wapi = api.WapiModule(self.module)
|
||||
wapi.get_object = Mock(name='get_object', return_value=test_object)
|
||||
wapi.create_object = Mock(name='create_object')
|
||||
wapi.update_object = Mock(name='update_object')
|
||||
wapi.delete_object = Mock(name='delete_object')
|
||||
return wapi
|
||||
|
||||
def test_nios_fixed_address_ipv4_create(self):
|
||||
self.module.params = {'provider': None, 'state': 'present', 'name': 'test_fa', 'ipaddr': '192.168.10.1', 'mac': '08:6d:41:e8:fd:e8',
|
||||
'network': '192.168.10.0/24', 'network_view': 'default', 'comment': None, 'extattrs': None}
|
||||
|
||||
test_object = None
|
||||
test_spec = {
|
||||
"name": {},
|
||||
"ipaddr": {"ib_req": True},
|
||||
"mac": {"ib_req": True},
|
||||
"network": {"ib_req": True},
|
||||
"network_view": {},
|
||||
"comment": {},
|
||||
"extattrs": {}
|
||||
}
|
||||
|
||||
wapi = self._get_wapi(test_object)
|
||||
res = wapi.run('testobject', test_spec)
|
||||
|
||||
self.assertTrue(res['changed'])
|
||||
wapi.create_object.assert_called_once_with('testobject', {'name': 'test_fa', 'ipaddr': '192.168.10.1', 'mac': '08:6d:41:e8:fd:e8',
|
||||
'network': '192.168.10.0/24', 'network_view': 'default'})
|
||||
|
||||
def test_nios_fixed_address_ipv4_dhcp_update(self):
|
||||
self.module.params = {'provider': None, 'state': 'present', 'name': 'test_fa', 'ipaddr': '192.168.10.1', 'mac': '08:6d:41:e8:fd:e8',
|
||||
'network': '192.168.10.0/24', 'network_view': 'default', 'comment': 'updated comment', 'extattrs': None}
|
||||
|
||||
test_object = [
|
||||
{
|
||||
"comment": "test comment",
|
||||
"name": "test_fa",
|
||||
"_ref": "network/ZG5zLm5ldHdvcmtfdmlldyQw:default/true",
|
||||
"ipaddr": "192.168.10.1",
|
||||
"mac": "08:6d:41:e8:fd:e8",
|
||||
"network": "192.168.10.0/24",
|
||||
"network_view": "default",
|
||||
"extattrs": {'options': {'name': 'test', 'value': 'ansible.com'}}
|
||||
}
|
||||
]
|
||||
|
||||
test_spec = {
|
||||
"name": {},
|
||||
"ipaddr": {"ib_req": True},
|
||||
"mac": {"ib_req": True},
|
||||
"network": {"ib_req": True},
|
||||
"network_view": {},
|
||||
"comment": {},
|
||||
"extattrs": {}
|
||||
}
|
||||
|
||||
wapi = self._get_wapi(test_object)
|
||||
res = wapi.run('testobject', test_spec)
|
||||
|
||||
self.assertTrue(res['changed'])
|
||||
|
||||
def test_nios_fixed_address_ipv4_remove(self):
|
||||
self.module.params = {'provider': None, 'state': 'absent', 'name': 'test_fa', 'ipaddr': '192.168.10.1', 'mac': '08:6d:41:e8:fd:e8',
|
||||
'network': '192.168.10.0/24', 'network_view': 'default', 'comment': None, 'extattrs': None}
|
||||
|
||||
ref = "fixedaddress/ZG5zLm5ldHdvcmtfdmlldyQw:ansible/false"
|
||||
|
||||
test_object = [{
|
||||
"comment": "test comment",
|
||||
"name": "test_fa",
|
||||
"_ref": ref,
|
||||
"ipaddr": "192.168.10.1",
|
||||
"mac": "08:6d:41:e8:fd:e8",
|
||||
"network": "192.168.10.0/24",
|
||||
"network_view": "default",
|
||||
"extattrs": {'Site': {'value': 'test'}}
|
||||
}]
|
||||
|
||||
test_spec = {
|
||||
"name": {},
|
||||
"ipaddr": {"ib_req": True},
|
||||
"mac": {"ib_req": True},
|
||||
"network": {"ib_req": True},
|
||||
"network_view": {},
|
||||
"comment": {},
|
||||
"extattrs": {}
|
||||
}
|
||||
|
||||
wapi = self._get_wapi(test_object)
|
||||
res = wapi.run('testobject', test_spec)
|
||||
|
||||
self.assertTrue(res['changed'])
|
||||
wapi.delete_object.assert_called_once_with(ref)
|
||||
|
||||
def test_nios_fixed_address_ipv6_create(self):
|
||||
self.module.params = {'provider': None, 'state': 'present', 'name': 'test_fa', 'ipaddr': 'fe80::1/10', 'mac': '08:6d:41:e8:fd:e8',
|
||||
'network': 'fe80::/64', 'network_view': 'default', 'comment': None, 'extattrs': None}
|
||||
|
||||
test_object = None
|
||||
|
||||
test_spec = {
|
||||
"name": {},
|
||||
"ipaddr": {"ib_req": True},
|
||||
"mac": {"ib_req": True},
|
||||
"network": {"ib_req": True},
|
||||
"network_view": {},
|
||||
"comment": {},
|
||||
"extattrs": {}
|
||||
}
|
||||
|
||||
wapi = self._get_wapi(test_object)
|
||||
print("WAPI: ", wapi)
|
||||
res = wapi.run('testobject', test_spec)
|
||||
|
||||
self.assertTrue(res['changed'])
|
||||
wapi.create_object.assert_called_once_with('testobject', {'name': 'test_fa', 'ipaddr': 'fe80::1/10', 'mac': '08:6d:41:e8:fd:e8',
|
||||
'network': 'fe80::/64', 'network_view': 'default'})
|
||||
|
||||
def test_nios_fixed_address_ipv6_remove(self):
|
||||
self.module.params = {'provider': None, 'state': 'absent', 'name': 'test_fa', 'ipaddr': 'fe80::1/10', 'mac': '08:6d:41:e8:fd:e8',
|
||||
'network': 'fe80::/64', 'network_view': 'default', 'comment': None, 'extattrs': None}
|
||||
|
||||
ref = "ipv6fixedaddress/ZG5zLm5ldHdvcmtfdmlldyQw:ansible/false"
|
||||
|
||||
test_object = [{
|
||||
"comment": "test comment",
|
||||
"name": "test_fa",
|
||||
"_ref": ref,
|
||||
"ipaddr": "fe80::1/10",
|
||||
"mac": "08:6d:41:e8:fd:e8",
|
||||
"network": "fe80::/64",
|
||||
"network_view": "default",
|
||||
"extattrs": {'Site': {'value': 'test'}}
|
||||
}]
|
||||
|
||||
test_spec = {
|
||||
"name": {},
|
||||
"ipaddr": {"ib_req": True},
|
||||
"mac": {"ib_req": True},
|
||||
"network": {"ib_req": True},
|
||||
"network_view": {},
|
||||
"comment": {},
|
||||
"extattrs": {}
|
||||
}
|
||||
|
||||
wapi = self._get_wapi(test_object)
|
||||
res = wapi.run('testobject', test_spec)
|
||||
|
||||
self.assertTrue(res['changed'])
|
||||
wapi.delete_object.assert_called_once_with(ref)
|
Loading…
Reference in a new issue