Added support for infoblox member object (#53174)

* changes for member module
This commit is contained in:
krisvasudevan 2019-03-12 17:48:51 +05:30 committed by Sumit Jaiswal
parent f615011de3
commit 651fc88303
3 changed files with 676 additions and 0 deletions

View file

@ -61,6 +61,7 @@ NIOS_IPV6_FIXED_ADDRESS = 'ipv6fixedaddress'
NIOS_NEXT_AVAILABLE_IP = 'func:nextavailableip' NIOS_NEXT_AVAILABLE_IP = 'func:nextavailableip'
NIOS_IPV4_NETWORK_CONTAINER = 'networkcontainer' NIOS_IPV4_NETWORK_CONTAINER = 'networkcontainer'
NIOS_IPV6_NETWORK_CONTAINER = 'ipv6networkcontainer' NIOS_IPV6_NETWORK_CONTAINER = 'ipv6networkcontainer'
NIOS_MEMBER = 'member'
NIOS_PROVIDER_SPEC = { NIOS_PROVIDER_SPEC = {
'host': dict(fallback=(env_fallback, ['INFOBLOX_HOST'])), 'host': dict(fallback=(env_fallback, ['INFOBLOX_HOST'])),
@ -136,6 +137,29 @@ def flatten_extattrs(value):
return dict([(k, v['value']) for k, v in iteritems(value)]) return dict([(k, v['value']) for k, v in iteritems(value)])
def member_normalize(member_spec):
''' Transforms the member module arguments into a valid WAPI struct
This function will transform the arguments into a structure that
is a valid WAPI structure in the format of:
{
key: <value>,
}
It will remove any arguments that are set to None since WAPI will error on
that condition.
The remainder of the value validation is performed by WAPI
'''
for key in member_spec.keys():
if isinstance(member_spec[key], dict):
member_spec[key] = member_normalize(member_spec[key])
elif isinstance(member_spec[key], list):
for x in member_spec[key]:
if isinstance(x, dict):
x = member_normalize(x)
elif member_spec[key] is None:
del member_spec[key]
return member_spec
class WapiBase(object): class WapiBase(object):
''' Base class for implementing Infoblox WAPI API ''' ''' Base class for implementing Infoblox WAPI API '''
provider_spec = {'provider': dict(type='dict', options=NIOS_PROVIDER_SPEC)} provider_spec = {'provider': dict(type='dict', options=NIOS_PROVIDER_SPEC)}
@ -245,6 +269,10 @@ class WapiModule(WapiBase):
current_object = obj_filter current_object = obj_filter
ref = None ref = None
# checks if the object type is member to normalize the attributes being passed
if (ib_obj_type == NIOS_MEMBER):
proposed_object = member_normalize(proposed_object)
# checks if the name's field has been updated # checks if the name's field has been updated
if update and new_name: if update and new_name:
proposed_object['name'] = new_name proposed_object['name'] = new_name

View file

@ -0,0 +1,486 @@
#!/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_member
version_added: "2.8"
author: "Krishna Vasudevan (@krisvasudevan)"
short_description: Configure Infoblox NIOS members
description:
- Adds and/or removes Infoblox NIOS servers. This module manages NIOS C(member) objects using the Infoblox WAPI interface over REST.
requirements:
- infoblox-client
extends_documentation_fragment: nios
options:
host_name:
description:
- Specifies the host name of the member to either add or remove from
the NIOS instance.
required: true
aliases:
- name
vip_setting:
description:
- Configures the network settings for the grid member.
required: true
suboptions:
address:
description:
- The IPv4 Address of the Grid Member
subnet_mask:
description:
- The subnet mask for the Grid Member
gateway:
description:
- The default gateway for the Grid Member
ipv6_setting:
description:
- Configures the IPv6 settings for the grid member.
required: true
suboptions:
virtual_ip:
description:
- The IPv6 Address of the Grid Member
cidr_prefix:
description:
- The IPv6 CIDR prefix for the Grid Member
gateway:
description:
- The gateway address for the Grid Member
config_addr_type:
description:
- Address configuration type (IPV4/IPV6/BOTH)
default: IPV4
comment:
description:
- A descriptive comment of the Grid member.
extattrs:
description:
- Extensible attributes associated with the object.
enable_ha:
description:
- If set to True, the member has two physical nodes (HA pair).
type: bool
router_id:
description:
- Virtual router identifier. Provide this ID if "ha_enabled" is set to "true". This is a unique VRID number (from 1 to 255) for the local subnet.
lan2_enabled:
description:
- When set to "true", the LAN2 port is enabled as an independent port or as a port for failover purposes.
type: bool
lan2_port_setting:
description:
- Settings for the Grid member LAN2 port if 'lan2_enabled' is set to "true".
suboptions:
enabled:
description:
- If set to True, then it has its own IP settings.
type: bool
network_setting:
description:
- If the 'enable' field is set to True, this defines IPv4 network settings for LAN2.
suboptions:
address:
description:
- The IPv4 Address of LAN2
subnet_mask:
description:
- The subnet mask of LAN2
gateway:
description:
- The default gateway of LAN2
v6_network_setting:
description:
- If the 'enable' field is set to True, this defines IPv6 network settings for LAN2.
suboptions:
virtual_ip:
description:
- The IPv6 Address of LAN2
cidr_prefix:
description:
- The IPv6 CIDR prefix of LAN2
gateway:
description:
- The gateway address of LAN2
platform:
description:
- Configures the Hardware Platform.
default: INFOBLOX
node_info:
description:
- Configures the node information list with detailed status report on the operations of the Grid Member.
suboptions:
lan2_physical_setting:
description:
- Physical port settings for the LAN2 interface.
suboptions:
auto_port_setting_enabled:
description:
- Enable or disalbe the auto port setting.
type: bool
duplex:
description:
- The port duplex; if speed is 1000, duplex must be FULL.
speed:
description:
- The port speed; if speed is 1000, duplex is FULL.
lan_ha_port_setting:
description:
- LAN/HA port settings for the node.
suboptions:
ha_ip_address:
description:
- HA IP address.
ha_port_setting:
description:
- Physical port settings for the HA interface.
suboptions:
auto_port_setting_enabled:
description:
- Enable or disalbe the auto port setting.
type: bool
duplex:
description:
- The port duplex; if speed is 1000, duplex must be FULL.
speed:
description:
- The port speed; if speed is 1000, duplex is FULL.
lan_port_setting:
description:
- Physical port settings for the LAN interface.
suboptions:
auto_port_setting_enabled:
description:
- Enable or disalbe the auto port setting.
type: bool
duplex:
description:
- The port duplex; if speed is 1000, duplex must be FULL.
speed:
description:
- The port speed; if speed is 1000, duplex is FULL.
mgmt_ipv6addr:
description:
- Public IPv6 address for the LAN1 interface.
mgmt_lan:
description:
- Public IPv4 address for the LAN1 interface.
mgmt_network_setting:
description:
- Network settings for the MGMT port of the node.
suboptions:
address:
description:
- The IPv4 Address of MGMT
subnet_mask:
description:
- The subnet mask of MGMT
gateway:
description:
- The default gateway of MGMT
v6_mgmt_network_setting:
description:
- The network settings for the IPv6 MGMT port of the node.
suboptions:
virtual_ip:
description:
- The IPv6 Address of MGMT
cidr_prefix:
description:
- The IPv6 CIDR prefix of MGMT
gateway:
description:
- The gateway address of MGMT
mgmt_port_setting:
description:
- Settings for the member MGMT port.
suboptions:
enabled:
description:
- Determines if MGMT port settings should be enabled.
type: bool
security_access_enabled:
description:
- Determines if security access on the MGMT port is enabled or not.
type: bool
vpn_enabled:
description:
- Determines if VPN on the MGMT port is enabled or not.
type: bool
upgrade_group:
description:
- The name of the upgrade group to which this Grid member belongs.
default: Default
use_syslog_proxy_setting:
description:
- Use flag for external_syslog_server_enable , syslog_servers, syslog_proxy_setting, syslog_size
type: bool
external_syslog_server_enable:
description:
- Determines if external syslog servers should be enabled
type: bool
syslog_servers:
description:
- The list of external syslog servers.
suboptions:
address:
description:
- The server address.
category_list:
description:
- The list of all syslog logging categories.
connection_type:
description:
- The connection type for communicating with this server.(STCP/TCP?UDP)
default: UDP
local_interface:
description:
- The local interface through which the appliance sends syslog messages to the syslog server.(ANY/LAN/MGMT)
default: ANY
message_node_id:
description:
- Identify the node in the syslog message. (HOSTNAME/IP_HOSTNAME/LAN/MGMT)
default: LAN
message_source:
description:
- The source of syslog messages to be sent to the external syslog server.
default: ANY
only_category_list:
description:
- The list of selected syslog logging categories. The appliance forwards syslog messages that belong to the selected categories.
type: bool
port:
description:
- The port this server listens on.
default: 514
severity:
description:
- The severity filter. The appliance sends log messages of the specified severity and above to the external syslog server.
default: DEBUG
pre_provisioning:
description:
- Pre-provisioning information.
suboptions:
hardware_info:
description:
- An array of structures that describe the hardware being pre-provisioned.
suboptions:
hwmodel:
description:
- Hardware model
hwtype:
description:
- Hardware type.
licenses:
description:
- An array of license types.
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: add a member to the grid with IPv4 address
nios_member:
host_name: member01.localdomain
vip_setting:
address: 192.168.1.100
subnet_mask: 255.255.255.0
gateway: 192.168.1.1
config_addr_type: IPV4
platform: VNIOS
comment: "Created by Ansible"
state: present
provider:
host: "{{ inventory_hostname_short }}"
username: admin
password: admin
connection: local
- name: add a HA member to the grid
nios_member:
host_name: memberha.localdomain
vip_setting:
address: 192.168.1.100
subnet_mask: 255.255.255.0
gateway: 192.168.1.1
config_addr_type: IPV4
platform: VNIOS
enable_ha: true
router_id: 150
node_info:
- lan_ha_port_setting:
ha_ip_address: 192.168.1.70
mgmt_lan: 192.168.1.80
- lan_ha_port_setting:
ha_ip_address: 192.168.1.71
mgmt_lan: 192.168.1.81
comment: "Created by Ansible"
state: present
provider:
host: "{{ inventory_hostname_short }}"
username: admin
password: admin
connection: local
- name: update the member with pre-provisioning details specified
nios_member:
name: member01.localdomain
pre_provisioning:
hardware_info:
- hwmodel: IB-VM-820
hwtype: IB-VNIOS
licenses:
- dns
- dhcp
- enterprise
- vnios
comment: "Updated by Ansible"
state: present
provider:
host: "{{ inventory_hostname_short }}"
username: admin
password: admin
connection: local
- name: remove the member
nios_member:
name: member01.localdomain
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.net_tools.nios.api import NIOS_MEMBER
def main():
''' Main entry point for module execution
'''
ipv4_spec = dict(
address=dict(),
subnet_mask=dict(),
gateway=dict(),
)
ipv6_spec = dict(
virtual_ip=dict(),
cidr_prefix=dict(type='int'),
gateway=dict(),
)
port_spec = dict(
auto_port_setting_enabled=dict(type='bool'),
duplex=dict(),
speed=dict(),
)
lan2_port_spec = dict(
enabled=dict(type='bool'),
network_setting=dict(type='dict', elements='dict', options=ipv4_spec),
v6_network_setting=dict(type='dict', elements='dict', options=ipv6_spec),
)
ha_port_spec = dict(
ha_ip_address=dict(),
ha_port_setting=dict(type='dict', elements='dict', options=port_spec),
lan_port_setting=dict(type='dict', elements='dict', options=port_spec),
mgmt_lan=dict(),
mgmt_ipv6addr=dict(),
)
node_spec = dict(
lan2_physical_setting=dict(type='dict', elements='dict', options=port_spec),
lan_ha_port_setting=dict(type='dict', elements='dict', options=ha_port_spec),
mgmt_network_setting=dict(type='dict', elements='dict', options=ipv4_spec),
v6_mgmt_network_setting=dict(type='dict', elements='dict', options=ipv6_spec),
)
mgmt_port_spec = dict(
enabled=dict(type='bool'),
security_access_enabled=dict(type='bool'),
vpn_enabled=dict(type='bool'),
)
syslog_spec = dict(
address=dict(),
category_list=dict(type='list'),
connection_type=dict(default='UDP'),
local_interface=dict(default='ANY'),
message_node_id=dict(default='LAN'),
message_source=dict(default='ANY'),
only_category_list=dict(type='bool'),
port=dict(type='int', default=514),
severity=dict(default='DEBUG'),
)
hw_spec = dict(
hwmodel=dict(),
hwtype=dict(),
)
pre_prov_spec = dict(
hardware_info=dict(type='list', elements='dict', options=hw_spec),
licenses=dict(type='list'),
)
ib_spec = dict(
host_name=dict(required=True, aliases=['name'], ib_req=True),
vip_setting=dict(type='dict', elements='dict', options=ipv4_spec),
ipv6_setting=dict(type='dict', elements='dict', options=ipv6_spec),
config_addr_type=dict(default='IPV4'),
comment=dict(),
enable_ha=dict(type='bool', default=False),
router_id=dict(type='int'),
lan2_enabled=dict(type='bool', default=False),
lan2_port_setting=dict(type='dict', elements='dict', options=lan2_port_spec),
platform=dict(default='INFOBLOX'),
node_info=dict(type='list', elements='dict', options=node_spec),
mgmt_port_setting=dict(type='dict', elements='dict', options=mgmt_port_spec),
upgrade_group=dict(default='Default'),
use_syslog_proxy_setting=dict(type='bool'),
external_syslog_server_enable=dict(type='bool'),
syslog_servers=dict(type='list', elements='dict', options=syslog_spec),
pre_provisioning=dict(type='dict', elements='dict', options=pre_prov_spec),
extattrs=dict(type='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)
wapi = WapiModule(module)
result = wapi.run(NIOS_MEMBER, ib_spec)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,162 @@
# 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_member
from units.compat.mock import patch, MagicMock, Mock
from .test_nios_module import TestNiosModule, load_fixture
class TestNiosMemberModule(TestNiosModule):
module = nios_member
def setUp(self):
super(TestNiosMemberModule, self).setUp()
self.module = MagicMock(name='ansible.modules.net_tools.nios.nios_member.WapiModule')
self.module.check_mode = False
self.module.params = {'provider': None}
self.mock_wapi = patch('ansible.modules.net_tools.nios.nios_member.WapiModule')
self.exec_command = self.mock_wapi.start()
self.mock_wapi_run = patch('ansible.modules.net_tools.nios.nios_member.WapiModule.run')
self.mock_wapi_run.start()
self.load_config = self.mock_wapi_run.start()
def tearDown(self):
super(TestNiosMemberModule, 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_member_create(self):
self.module.params = {'provider': None, 'state': 'present', 'host_name': 'test_member',
'vip_setting': {'address': '192.168.1.110', 'subnet_mask': '255.255.255.0', 'gateway': '192.168.1.1'},
'config_addr_type': 'IPV4', 'platform': 'VNIOS', 'comment': None, 'extattrs': None}
test_object = None
test_spec = {
"host_name": {"ib_req": True},
"vip_setting": {},
"config_addr_type": {},
"platform": {},
"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', {'host_name': 'test_member',
'vip_setting': {'address': '192.168.1.110', 'subnet_mask': '255.255.255.0',
'gateway': '192.168.1.1'},
'config_addr_type': 'IPV4', 'platform': 'VNIOS'})
def test_nios_member_update(self):
self.module.params = {'provider': None, 'state': 'present', 'host_name': 'test_member',
'vip_setting': {'address': '192.168.1.110', 'subnet_mask': '255.255.255.0', 'gateway': '192.168.1.1'},
'config_addr_type': 'IPV4', 'platform': 'VNIOS', 'comment': 'updated comment', 'extattrs': None}
test_object = [
{
"comment": "Created with Ansible",
"_ref": "member/b25lLnZpcnR1YWxfbm9kZSQ3:member01.ansible-dev.com",
"config_addr_type": "IPV4",
"host_name": "member01.ansible-dev.com",
"platform": "VNIOS",
"service_type_configuration": "ALL_V4",
"vip_setting":
{
"address": "192.168.1.100",
"dscp": 0,
"gateway": "192.168.1.1",
"primary": True,
"subnet_mask": "255.255.255.0",
"use_dscp": False
}
}
]
test_spec = {
"host_name": {"ib_req": True},
"vip_setting": {},
"config_addr_type": {},
"platform": {},
"comment": {},
"extattrs": {}
}
wapi = self._get_wapi(test_object)
res = wapi.run('testobject', test_spec)
self.assertTrue(res['changed'])
def test_nios_member_remove(self):
self.module.params = {'provider': None, 'state': 'absent', 'host_name': 'test_member',
'vip_setting': {'address': '192.168.1.110', 'subnet_mask': '255.255.255.0', 'gateway': '192.168.1.1'},
'config_addr_type': 'IPV4', 'platform': 'VNIOS', 'comment': 'updated comment', 'extattrs': None}
ref = "member/b25lLnZpcnR1YWxfbm9kZSQ3:member01.ansible-dev.com"
test_object = [
{
"comment": "Created with Ansible",
"_ref": "member/b25lLnZpcnR1YWxfbm9kZSQ3:member01.ansible-dev.com",
"config_addr_type": "IPV4",
"host_name": "member01.ansible-dev.com",
"platform": "VNIOS",
"service_type_configuration": "ALL_V4",
"vip_setting":
{
"address": "192.168.1.100",
"dscp": 0,
"gateway": "192.168.1.1",
"primary": True,
"subnet_mask": "255.255.255.0",
"use_dscp": False
}
}
]
test_spec = {
"host_name": {"ib_req": True},
"vip_setting": {},
"config_addr_type": {},
"platform": {},
"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)