mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-04-26 04:11:25 -07:00
Cliconf and Netconf refactoring iosxr_interface (#33909)
* Cliconf and Netconf refactoring iosxr_interface * adds `xml` key and related changes for netconf output * * review comments changes
This commit is contained in:
parent
8a9865cb10
commit
78a14d7966
11 changed files with 875 additions and 227 deletions
|
@ -17,34 +17,49 @@ DOCUMENTATION = """
|
|||
---
|
||||
module: iosxr_interface
|
||||
version_added: "2.4"
|
||||
author: "Ganesh Nalawade (@ganeshrn)"
|
||||
author:
|
||||
- "Ganesh Nalawade (@ganeshrn)"
|
||||
- "Kedar Kekan (@kedarX)"
|
||||
short_description: Manage Interface on Cisco IOS XR network devices
|
||||
description:
|
||||
- This module provides declarative management of Interfaces
|
||||
on Cisco IOS XR network devices.
|
||||
extends_documentation_fragment: iosxr
|
||||
notes:
|
||||
- Tested against IOS XR 6.1.2
|
||||
- Tested against IOS XRv 6.1.2
|
||||
- Preconfiguration of physical interfaces is not supported with C(netconf) transport.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the Interface.
|
||||
- Name of the interface to configure in C(type + path) format. e.g. C(GigabitEthernet0/0/0/0)
|
||||
required: true
|
||||
description:
|
||||
description:
|
||||
- Description of Interface.
|
||||
- Description of Interface being configured.
|
||||
enabled:
|
||||
description:
|
||||
- Interface link status.
|
||||
- Removes the shutdown configuration, which removes the forced administrative down on the interface,
|
||||
enabling it to move to an up or down state.
|
||||
type: bool
|
||||
default: True
|
||||
active:
|
||||
description:
|
||||
- Whether the interface is C(active) or C(preconfigured). Preconfiguration allows you to configure modular
|
||||
services cards before they are inserted into the router. When the cards are inserted, they are instantly
|
||||
configured. Active cards are the ones already inserted.
|
||||
choices: ['active', 'preconfigure']
|
||||
default: active
|
||||
version_added: 2.5
|
||||
speed:
|
||||
description:
|
||||
- Interface link speed.
|
||||
- Configure the speed for an interface. Default is auto-negotiation when not configured.
|
||||
choices: ['10', '100', '1000']
|
||||
mtu:
|
||||
description:
|
||||
- Maximum size of transmit packet.
|
||||
- Sets the MTU value for the interface. Range is between 64 and 65535'
|
||||
duplex:
|
||||
description:
|
||||
- Interface link status
|
||||
- Configures the interface duplex mode. Default is auto-negotiation when not configured.
|
||||
choices: ['full', 'half']
|
||||
tx_rate:
|
||||
description:
|
||||
|
@ -53,7 +68,9 @@ options:
|
|||
description:
|
||||
- Receiver rate in bits per second (bps).
|
||||
aggregate:
|
||||
description: List of Interfaces definitions.
|
||||
description:
|
||||
- List of Interface definitions. Include multiple interface configurations together,
|
||||
one each on a seperate line
|
||||
delay:
|
||||
description:
|
||||
- Time in seconds to wait before checking for the operational state on remote
|
||||
|
@ -102,6 +119,16 @@ EXAMPLES = """
|
|||
mtu: 512
|
||||
state: present
|
||||
|
||||
- name: Create interface using aggregate along with additional params in aggregate
|
||||
iosxr_interface:
|
||||
aggregate:
|
||||
- { name: GigabitEthernet0/0/0/3, description: test-interface 3 }
|
||||
- { name: GigabitEthernet0/0/0/2, description: test-interface 2 }
|
||||
speed: 100
|
||||
duplex: full
|
||||
mtu: 512
|
||||
state: present
|
||||
|
||||
- name: Delete interface using aggregate
|
||||
iosxr_interface:
|
||||
aggregate:
|
||||
|
@ -125,240 +152,453 @@ EXAMPLES = """
|
|||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device.
|
||||
returned: always, except for the platforms that use Netconf transport to manage the device.
|
||||
description: The list of configuration mode commands sent to device with transport C(cli)
|
||||
returned: always (empty list when no commands to send)
|
||||
type: list
|
||||
sample:
|
||||
- interface GigabitEthernet0/0/0/2
|
||||
- description test-interface
|
||||
- duplex half
|
||||
- mtu 512
|
||||
|
||||
xml:
|
||||
description: NetConf rpc xml sent to device with transport C(netconf)
|
||||
returned: always (empty list when no xml rpc to send)
|
||||
type: list
|
||||
version_added: 2.5
|
||||
sample:
|
||||
- '<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
|
||||
<interface-configurations xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg">
|
||||
<interface-configuration xc:operation="merge">
|
||||
<active>act</active>
|
||||
<interface-name>GigabitEthernet0/0/0/0</interface-name>
|
||||
<description>test-interface-0</description>
|
||||
<mtus><mtu>
|
||||
<owner>GigabitEthernet</owner>
|
||||
<mtu>512</mtu>
|
||||
</mtu></mtus>
|
||||
<ethernet xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-drivers-media-eth-cfg">
|
||||
<speed>100</speed>
|
||||
<duplex>half</duplex>
|
||||
</ethernet>
|
||||
</interface-configuration>
|
||||
</interface-configurations></config>'
|
||||
"""
|
||||
import re
|
||||
|
||||
from time import sleep
|
||||
from copy import deepcopy
|
||||
import collections
|
||||
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.connection import exec_command
|
||||
from ansible.module_utils.network.iosxr.iosxr import get_config, load_config
|
||||
from ansible.module_utils.network.iosxr.iosxr import iosxr_argument_spec
|
||||
from ansible.module_utils.network.iosxr.iosxr import get_config, load_config, build_xml
|
||||
from ansible.module_utils.network.iosxr.iosxr import run_command, iosxr_argument_spec, get_oper
|
||||
from ansible.module_utils.network.iosxr.iosxr import is_netconf, is_cliconf, etree_findall, etree_find
|
||||
from ansible.module_utils.network.common.utils import conditional, remove_default_spec
|
||||
|
||||
|
||||
def validate_mtu(value, module):
|
||||
def validate_mtu(value):
|
||||
if value and not 64 <= int(value) <= 65535:
|
||||
module.fail_json(msg='mtu must be between 64 and 65535')
|
||||
return False, 'mtu must be between 64 and 65535'
|
||||
return True, None
|
||||
|
||||
|
||||
def validate_param_values(module, obj, param=None):
|
||||
if param is None:
|
||||
param = module.params
|
||||
for key in obj:
|
||||
# validate the param value (if validator func exists)
|
||||
validator = globals().get('validate_%s' % key)
|
||||
if callable(validator):
|
||||
validator(param.get(key), module)
|
||||
class ConfigBase(object):
|
||||
def __init__(self, module):
|
||||
self._module = module
|
||||
self._result = {'changed': False, 'warnings': []}
|
||||
self._want = list()
|
||||
self._have = list()
|
||||
|
||||
def validate_param_values(self, param=None):
|
||||
for key, value in param.items():
|
||||
# validate the param value (if validator func exists)
|
||||
validator = globals().get('validate_%s' % key)
|
||||
if callable(validator):
|
||||
rc, msg = validator(value)
|
||||
if not rc:
|
||||
self._module.fail_json(msg=msg)
|
||||
|
||||
def parse_shutdown(intf_config):
|
||||
for cfg in intf_config:
|
||||
match = re.search(r'%s' % 'shutdown', cfg, re.M)
|
||||
if match:
|
||||
return True
|
||||
return False
|
||||
def map_params_to_obj(self):
|
||||
aggregate = self._module.params.get('aggregate')
|
||||
if aggregate:
|
||||
for item in aggregate:
|
||||
for key in item:
|
||||
if item.get(key) is None:
|
||||
item[key] = self._module.params[key]
|
||||
|
||||
self.validate_param_values(item)
|
||||
d = item.copy()
|
||||
|
||||
def parse_config_argument(intf_config, arg):
|
||||
for cfg in intf_config:
|
||||
match = re.search(r'%s (.+)$' % arg, cfg, re.M)
|
||||
if match:
|
||||
return match.group(1)
|
||||
match = re.match(r"(^[a-z]+)([0-9/]+$)", d['name'], re.I)
|
||||
if match:
|
||||
d['owner'] = match.groups()[0]
|
||||
|
||||
if d['active'] == 'preconfigure':
|
||||
d['active'] = 'pre'
|
||||
else:
|
||||
d['active'] = 'act'
|
||||
|
||||
def search_obj_in_list(name, lst):
|
||||
for o in lst:
|
||||
if o['name'] == name:
|
||||
return o
|
||||
self._want.append(d)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def map_params_to_obj(module):
|
||||
obj = []
|
||||
|
||||
aggregate = module.params.get('aggregate')
|
||||
if aggregate:
|
||||
for item in aggregate:
|
||||
for key in item:
|
||||
if item.get(key) is None:
|
||||
item[key] = module.params[key]
|
||||
|
||||
validate_param_values(module, item, item)
|
||||
d = item.copy()
|
||||
|
||||
if d['enabled']:
|
||||
d['disable'] = False
|
||||
else:
|
||||
d['disable'] = True
|
||||
|
||||
obj.append(d)
|
||||
|
||||
else:
|
||||
validate_param_values(module, module.params)
|
||||
params = {
|
||||
'name': module.params['name'],
|
||||
'description': module.params['description'],
|
||||
'speed': module.params['speed'],
|
||||
'mtu': module.params['mtu'],
|
||||
'duplex': module.params['duplex'],
|
||||
'state': module.params['state'],
|
||||
'delay': module.params['delay'],
|
||||
'tx_rate': module.params['tx_rate'],
|
||||
'rx_rate': module.params['rx_rate']
|
||||
}
|
||||
|
||||
if module.params['enabled']:
|
||||
params.update({'disable': False})
|
||||
else:
|
||||
params.update({'disable': True})
|
||||
self.validate_param_values(self._module.params)
|
||||
params = {
|
||||
'name': self._module.params['name'],
|
||||
'description': self._module.params['description'],
|
||||
'speed': self._module.params['speed'],
|
||||
'mtu': self._module.params['mtu'],
|
||||
'duplex': self._module.params['duplex'],
|
||||
'state': self._module.params['state'],
|
||||
'delay': self._module.params['delay'],
|
||||
'tx_rate': self._module.params['tx_rate'],
|
||||
'rx_rate': self._module.params['rx_rate'],
|
||||
'enabled': self._module.params['enabled'],
|
||||
'active': self._module.params['active'],
|
||||
}
|
||||
|
||||
obj.append(params)
|
||||
return obj
|
||||
match = re.match(r"(^[a-z]+)([0-9/]+$)", params['name'], re.I)
|
||||
if match:
|
||||
params['owner'] = match.groups()[0]
|
||||
|
||||
if params['active'] == 'preconfigure':
|
||||
params['active'] = 'pre'
|
||||
else:
|
||||
params['active'] = 'act'
|
||||
|
||||
self._want.append(params)
|
||||
|
||||
|
||||
def map_config_to_obj(module):
|
||||
data = get_config(module, config_filter='interface')
|
||||
interfaces = data.strip().rstrip('!').split('!')
|
||||
class CliConfiguration(ConfigBase):
|
||||
def __init__(self, module):
|
||||
super(CliConfiguration, self).__init__(module)
|
||||
|
||||
if not interfaces:
|
||||
return list()
|
||||
def parse_shutdown(self, intf_config):
|
||||
for cfg in intf_config:
|
||||
match = re.search(r'%s' % 'shutdown', cfg, re.M)
|
||||
if match:
|
||||
return True
|
||||
return False
|
||||
|
||||
instances = list()
|
||||
def parse_config_argument(self, intf_config, arg):
|
||||
for cfg in intf_config:
|
||||
match = re.search(r'%s (.+)$' % arg, cfg, re.M)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
for interface in interfaces:
|
||||
intf_config = interface.strip().splitlines()
|
||||
def search_obj_in_list(self, name):
|
||||
for obj in self._have:
|
||||
if obj['name'] == name:
|
||||
return obj
|
||||
return None
|
||||
|
||||
name = intf_config[0].strip().split()[1]
|
||||
def map_config_to_obj(self):
|
||||
data = get_config(self._module, config_filter='interface')
|
||||
interfaces = data.strip().rstrip('!').split('!')
|
||||
|
||||
if name == 'preconfigure':
|
||||
name = intf_config[0].strip().split()[2]
|
||||
if not interfaces:
|
||||
return list()
|
||||
|
||||
obj = {
|
||||
'name': name,
|
||||
'description': parse_config_argument(intf_config, 'description'),
|
||||
'speed': parse_config_argument(intf_config, 'speed'),
|
||||
'duplex': parse_config_argument(intf_config, 'duplex'),
|
||||
'mtu': parse_config_argument(intf_config, 'mtu'),
|
||||
'disable': True if parse_shutdown(intf_config) else False,
|
||||
'state': 'present'
|
||||
}
|
||||
instances.append(obj)
|
||||
return instances
|
||||
for interface in interfaces:
|
||||
intf_config = interface.strip().splitlines()
|
||||
|
||||
name = intf_config[0].strip().split()[1]
|
||||
|
||||
active = 'act'
|
||||
if name == 'preconfigure':
|
||||
active = 'pre'
|
||||
name = intf_config[0].strip().split()[2]
|
||||
|
||||
obj = {
|
||||
'name': name,
|
||||
'description': self.parse_config_argument(intf_config, 'description'),
|
||||
'speed': self.parse_config_argument(intf_config, 'speed'),
|
||||
'duplex': self.parse_config_argument(intf_config, 'duplex'),
|
||||
'mtu': self.parse_config_argument(intf_config, 'mtu'),
|
||||
'enabled': True if not self.parse_shutdown(intf_config) else False,
|
||||
'active': active,
|
||||
'state': 'present'
|
||||
}
|
||||
self._have.append(obj)
|
||||
|
||||
def map_obj_to_commands(self):
|
||||
commands = list()
|
||||
|
||||
args = ('speed', 'description', 'duplex', 'mtu')
|
||||
for want_item in self._want:
|
||||
name = want_item['name']
|
||||
disable = not want_item['enabled']
|
||||
state = want_item['state']
|
||||
|
||||
obj_in_have = self.search_obj_in_list(name)
|
||||
interface = 'interface ' + name
|
||||
|
||||
if state == 'absent' and obj_in_have:
|
||||
commands.append('no ' + interface)
|
||||
|
||||
elif state in ('present', 'up', 'down'):
|
||||
if obj_in_have:
|
||||
for item in args:
|
||||
candidate = want_item.get(item)
|
||||
running = obj_in_have.get(item)
|
||||
if candidate != running:
|
||||
if candidate:
|
||||
cmd = interface + ' ' + item + ' ' + str(candidate)
|
||||
commands.append(cmd)
|
||||
|
||||
if disable and obj_in_have.get('enabled', False):
|
||||
commands.append(interface + ' shutdown')
|
||||
elif not disable and not obj_in_have.get('enabled', False):
|
||||
commands.append('no ' + interface + ' shutdown')
|
||||
else:
|
||||
for item in args:
|
||||
value = want_item.get(item)
|
||||
if value:
|
||||
commands.append(interface + ' ' + item + ' ' + str(value))
|
||||
if not disable:
|
||||
commands.append('no ' + interface + ' shutdown')
|
||||
self._result['commands'] = commands
|
||||
|
||||
if commands:
|
||||
commit = not self._module.check_mode
|
||||
diff = load_config(self._module, commands, commit=commit)
|
||||
if diff:
|
||||
self._result['diff'] = dict(prepared=diff)
|
||||
self._result['changed'] = True
|
||||
|
||||
def check_declarative_intent_params(self):
|
||||
failed_conditions = []
|
||||
for want_item in self._want:
|
||||
want_state = want_item.get('state')
|
||||
want_tx_rate = want_item.get('tx_rate')
|
||||
want_rx_rate = want_item.get('rx_rate')
|
||||
if want_state not in ('up', 'down') and not want_tx_rate and not want_rx_rate:
|
||||
continue
|
||||
|
||||
if self._result['changed']:
|
||||
sleep(want_item['delay'])
|
||||
|
||||
command = 'show interfaces {!s}'.format(want_item['name'])
|
||||
out = run_command(self._module, command)[0]
|
||||
|
||||
if want_state in ('up', 'down'):
|
||||
match = re.search(r'%s (\w+)' % 'line protocol is', out, re.M)
|
||||
have_state = None
|
||||
if match:
|
||||
have_state = match.group(1)
|
||||
if have_state.strip() == 'administratively':
|
||||
match = re.search(r'%s (\w+)' % 'administratively', out, re.M)
|
||||
if match:
|
||||
have_state = match.group(1)
|
||||
|
||||
if have_state is None or not conditional(want_state, have_state.strip()):
|
||||
failed_conditions.append('state ' + 'eq({!s})'.format(want_state))
|
||||
|
||||
if want_tx_rate:
|
||||
match = re.search(r'%s (\d+)' % 'output rate', out, re.M)
|
||||
have_tx_rate = None
|
||||
if match:
|
||||
have_tx_rate = match.group(1)
|
||||
|
||||
if have_tx_rate is None or not conditional(want_tx_rate, have_tx_rate.strip(), cast=int):
|
||||
failed_conditions.append('tx_rate ' + want_tx_rate)
|
||||
|
||||
if want_rx_rate:
|
||||
match = re.search(r'%s (\d+)' % 'input rate', out, re.M)
|
||||
have_rx_rate = None
|
||||
if match:
|
||||
have_rx_rate = match.group(1)
|
||||
|
||||
if have_rx_rate is None or not conditional(want_rx_rate, have_rx_rate.strip(), cast=int):
|
||||
failed_conditions.append('rx_rate ' + want_rx_rate)
|
||||
|
||||
if failed_conditions:
|
||||
msg = 'One or more conditional statements have not been satisfied'
|
||||
self._module.fail_json(msg=msg, failed_conditions=failed_conditions)
|
||||
|
||||
def run(self):
|
||||
self.map_params_to_obj()
|
||||
self.map_config_to_obj()
|
||||
self.map_obj_to_commands()
|
||||
self.check_declarative_intent_params()
|
||||
|
||||
return self._result
|
||||
|
||||
|
||||
def map_obj_to_commands(updates):
|
||||
commands = list()
|
||||
want, have = updates
|
||||
class NCConfiguration(ConfigBase):
|
||||
def __init__(self, module):
|
||||
super(NCConfiguration, self).__init__(module)
|
||||
|
||||
args = ('speed', 'description', 'duplex', 'mtu')
|
||||
for w in want:
|
||||
name = w['name']
|
||||
disable = w['disable']
|
||||
state = w['state']
|
||||
self._intf_meta = collections.OrderedDict()
|
||||
self._shut_meta = collections.OrderedDict()
|
||||
self._data_rate_meta = collections.OrderedDict()
|
||||
self._line_state_meta = collections.OrderedDict()
|
||||
|
||||
obj_in_have = search_obj_in_list(name, have)
|
||||
interface = 'interface ' + name
|
||||
def map_obj_to_xml_rpc(self):
|
||||
self._intf_meta.update([
|
||||
('interface-configuration', {'xpath': 'interface-configurations/interface-configuration', 'tag': True, 'attrib': 'operation'}),
|
||||
('a:active', {'xpath': 'interface-configurations/interface-configuration/active', 'operation': 'edit'}),
|
||||
('a:name', {'xpath': 'interface-configurations/interface-configuration/interface-name'}),
|
||||
('a:description', {'xpath': 'interface-configurations/interface-configuration/description', 'operation': 'edit'}),
|
||||
('mtus', {'xpath': 'interface-configurations/interface-configuration/mtus', 'tag': True, 'operation': 'edit'}),
|
||||
('mtu', {'xpath': 'interface-configurations/interface-configuration/mtus/mtu', 'tag': True, 'operation': 'edit'}),
|
||||
('a:owner', {'xpath': 'interface-configurations/interface-configuration/mtus/mtu/owner', 'operation': 'edit'}),
|
||||
('a:mtu', {'xpath': 'interface-configurations/interface-configuration/mtus/mtu/mtu', 'operation': 'edit'}),
|
||||
('CEthernet', {'xpath': 'interface-configurations/interface-configuration/ethernet', 'tag': True, 'operation': 'edit', 'ns': True}),
|
||||
('a:speed', {'xpath': 'interface-configurations/interface-configuration/ethernet/speed', 'operation': 'edit'}),
|
||||
('a:duplex', {'xpath': 'interface-configurations/interface-configuration/ethernet/duplex', 'operation': 'edit'}),
|
||||
])
|
||||
|
||||
if state == 'absent' and obj_in_have:
|
||||
commands.append('no ' + interface)
|
||||
self._shut_meta.update([
|
||||
('interface-configuration', {'xpath': 'interface-configurations/interface-configuration', 'tag': True}),
|
||||
('a:active', {'xpath': 'interface-configurations/interface-configuration/active', 'operation': 'edit'}),
|
||||
('a:name', {'xpath': 'interface-configurations/interface-configuration/interface-name'}),
|
||||
('shutdown', {'xpath': 'interface-configurations/interface-configuration/shutdown', 'tag': True, 'operation': 'edit', 'attrib': 'operation'}),
|
||||
])
|
||||
state = self._module.params['state']
|
||||
|
||||
_get_filter = build_xml('interface-configurations', xmap=self._intf_meta, params=self._want, opcode="filter")
|
||||
|
||||
running = get_config(self._module, source='running', config_filter=_get_filter)
|
||||
intfcfg_nodes = etree_findall(running, 'interface-configuration')
|
||||
|
||||
intf_list = set()
|
||||
shut_list = set()
|
||||
for item in intfcfg_nodes:
|
||||
intf_name = etree_find(item, 'interface-name').text
|
||||
if intf_name is not None:
|
||||
intf_list.add(intf_name)
|
||||
|
||||
if etree_find(item, 'shutdown') is not None:
|
||||
shut_list.add(intf_name)
|
||||
|
||||
intf_params = list()
|
||||
shut_params = list()
|
||||
noshut_params = list()
|
||||
for index, item in enumerate(self._want):
|
||||
if item['name'] in intf_list:
|
||||
intf_params.append(item)
|
||||
if not item['enabled']:
|
||||
shut_params.append(item)
|
||||
if item['name'] in shut_list and item['enabled']:
|
||||
noshut_params.append(item)
|
||||
|
||||
opcode = None
|
||||
if state == 'absent':
|
||||
if intf_params:
|
||||
opcode = "delete"
|
||||
elif state in ('present', 'up', 'down'):
|
||||
if obj_in_have:
|
||||
for item in args:
|
||||
candidate = w.get(item)
|
||||
running = obj_in_have.get(item)
|
||||
if candidate != running:
|
||||
if candidate:
|
||||
cmd = interface + ' ' + item + ' ' + str(candidate)
|
||||
commands.append(cmd)
|
||||
intf_params = self._want
|
||||
opcode = 'merge'
|
||||
|
||||
if disable and not obj_in_have.get('disable', False):
|
||||
commands.append(interface + ' shutdown')
|
||||
elif not disable and obj_in_have.get('disable', False):
|
||||
commands.append('no ' + interface + ' shutdown')
|
||||
else:
|
||||
for item in args:
|
||||
value = w.get(item)
|
||||
if value:
|
||||
commands.append(interface + ' ' + item + ' ' + str(value))
|
||||
if disable:
|
||||
commands.append('no ' + interface + ' shutdown')
|
||||
return commands
|
||||
self._result['xml'] = []
|
||||
_edit_filter_list = list()
|
||||
if opcode:
|
||||
_edit_filter_list.append(build_xml('interface-configurations', xmap=self._intf_meta,
|
||||
params=intf_params, opcode=opcode))
|
||||
|
||||
if opcode == 'merge':
|
||||
if len(shut_params):
|
||||
_edit_filter_list.append(build_xml('interface-configurations', xmap=self._shut_meta,
|
||||
params=shut_params, opcode='merge'))
|
||||
if len(noshut_params):
|
||||
_edit_filter_list.append(build_xml('interface-configurations', xmap=self._shut_meta,
|
||||
params=noshut_params, opcode='delete'))
|
||||
diff = None
|
||||
if len(_edit_filter_list):
|
||||
commit = not self._module.check_mode
|
||||
diff = load_config(self._module, _edit_filter_list, commit=commit, running=running,
|
||||
nc_get_filter=_get_filter)
|
||||
|
||||
def check_declarative_intent_params(module, want, result):
|
||||
failed_conditions = []
|
||||
for w in want:
|
||||
want_state = w.get('state')
|
||||
want_tx_rate = w.get('tx_rate')
|
||||
want_rx_rate = w.get('rx_rate')
|
||||
if want_state not in ('up', 'down') and not want_tx_rate and not want_rx_rate:
|
||||
continue
|
||||
if diff:
|
||||
if self._module._diff:
|
||||
self._result['diff'] = dict(prepared=diff)
|
||||
|
||||
if result['changed']:
|
||||
sleep(w['delay'])
|
||||
self._result['xml'] = _edit_filter_list
|
||||
self._result['changed'] = True
|
||||
|
||||
command = 'show interfaces %s' % w['name']
|
||||
rc, out, err = exec_command(module, command)
|
||||
if rc != 0:
|
||||
module.fail_json(msg=to_text(err, errors='surrogate_then_replace'), command=command, rc=rc)
|
||||
def check_declarative_intent_params(self):
|
||||
failed_conditions = []
|
||||
|
||||
if want_state in ('up', 'down'):
|
||||
match = re.search(r'%s (\w+)' % 'line protocol is', out, re.M)
|
||||
have_state = None
|
||||
if match:
|
||||
have_state = match.group(1)
|
||||
if have_state.strip() == 'administratively':
|
||||
match = re.search(r'%s (\w+)' % 'administratively', out, re.M)
|
||||
if match:
|
||||
have_state = match.group(1)
|
||||
self._data_rate_meta.update([
|
||||
('interfaces', {'xpath': 'infra-statistics/interfaces', 'tag': True}),
|
||||
('interface', {'xpath': 'infra-statistics/interfaces/interface', 'tag': True}),
|
||||
('a:name', {'xpath': 'infra-statistics/interfaces/interface/interface-name'}),
|
||||
('cache', {'xpath': 'infra-statistics/interfaces/interface/cache', 'tag': True}),
|
||||
('data-rate', {'xpath': 'infra-statistics/interfaces/interface/cache/data-rate', 'tag': True}),
|
||||
('input-data-rate', {'xpath': 'infra-statistics/interfaces/interface/cache/data-rate/input-data-rate', 'tag': True}),
|
||||
('output-data-rate', {'xpath': 'infra-statistics/interfaces/interface/cache/data-rate/output-data-rate', 'tag': True}),
|
||||
])
|
||||
|
||||
if have_state is None or not conditional(want_state, have_state.strip()):
|
||||
failed_conditions.append('state ' + 'eq(%s)' % want_state)
|
||||
self._line_state_meta.update([
|
||||
('data-nodes', {'xpath': 'interface-properties/data-nodes', 'tag': True}),
|
||||
('data-node', {'xpath': 'interface-properties/data-nodes/data-node', 'tag': True}),
|
||||
('system-view', {'xpath': 'interface-properties/data-nodes/data-node/system-view', 'tag': True}),
|
||||
('interfaces', {'xpath': 'interface-properties/data-nodes/data-node/system-view/interfaces', 'tag': True}),
|
||||
('interface', {'xpath': 'interface-properties/data-nodes/data-node/system-view/interfaces/interface', 'tag': True}),
|
||||
('a:name', {'xpath': 'interface-properties/data-nodes/data-node/system-view/interfaces/interface/interface-name'}),
|
||||
('line-state', {'xpath': 'interface-properties/data-nodes/data-node/system-view/interfaces/interface/line-state', 'tag': True}),
|
||||
])
|
||||
|
||||
if want_tx_rate:
|
||||
match = re.search(r'%s (\d+)' % 'output rate', out, re.M)
|
||||
have_tx_rate = None
|
||||
if match:
|
||||
have_tx_rate = match.group(1)
|
||||
_rate_filter = build_xml('infra-statistics', xmap=self._data_rate_meta, params=self._want, opcode="filter")
|
||||
out = get_oper(self._module, filter=_rate_filter)
|
||||
data_rate_list = etree_findall(out, 'interface')
|
||||
data_rate_map = dict()
|
||||
for item in data_rate_list:
|
||||
data_rate_map.update({etree_find(item, 'interface-name').text: dict()})
|
||||
data_rate_map[etree_find(item, 'interface-name').text].update({'input-data-rate': etree_find(item, 'input-data-rate').text,
|
||||
'output-data-rate': etree_find(item, 'output-data-rate').text})
|
||||
|
||||
if have_tx_rate is None or not conditional(want_tx_rate, have_tx_rate.strip(), cast=int):
|
||||
failed_conditions.append('tx_rate ' + want_tx_rate)
|
||||
_line_state_filter = build_xml('interface-properties', xmap=self._line_state_meta, params=self._want, opcode="filter")
|
||||
out = get_oper(self._module, filter=_line_state_filter)
|
||||
line_state_list = etree_findall(out, 'interface')
|
||||
line_state_map = dict()
|
||||
for item in line_state_list:
|
||||
line_state_map.update({etree_find(item, 'interface-name').text: etree_find(item, 'line-state').text})
|
||||
|
||||
if want_rx_rate:
|
||||
match = re.search(r'%s (\d+)' % 'input rate', out, re.M)
|
||||
have_rx_rate = None
|
||||
if match:
|
||||
have_rx_rate = match.group(1)
|
||||
for want_item in self._want:
|
||||
want_state = want_item.get('state')
|
||||
want_tx_rate = want_item.get('tx_rate')
|
||||
want_rx_rate = want_item.get('rx_rate')
|
||||
if want_state not in ('up', 'down') and not want_tx_rate and not want_rx_rate:
|
||||
continue
|
||||
|
||||
if have_rx_rate is None or not conditional(want_rx_rate, have_rx_rate.strip(), cast=int):
|
||||
failed_conditions.append('rx_rate ' + want_rx_rate)
|
||||
if self._result['changed']:
|
||||
sleep(want_item['delay'])
|
||||
|
||||
return failed_conditions
|
||||
if want_state in ('up', 'down'):
|
||||
if want_state not in line_state_map[want_item['name']]:
|
||||
failed_conditions.append('state ' + 'eq({!s})'.format(want_state))
|
||||
|
||||
if want_tx_rate:
|
||||
if want_tx_rate != data_rate_map[want_item['name']]['output-data-rate']:
|
||||
failed_conditions.append('tx_rate ' + want_tx_rate)
|
||||
|
||||
if want_rx_rate:
|
||||
if want_rx_rate != data_rate_map[want_item['name']]['input-data-rate']:
|
||||
failed_conditions.append('rx_rate ' + want_rx_rate)
|
||||
|
||||
if failed_conditions:
|
||||
msg = 'One or more conditional statements have not been satisfied'
|
||||
self._module.fail_json(msg=msg, failed_conditions=failed_conditions)
|
||||
|
||||
def run(self):
|
||||
self.map_params_to_obj()
|
||||
self.map_obj_to_xml_rpc()
|
||||
self.check_declarative_intent_params()
|
||||
return self._result
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
element_spec = dict(
|
||||
name=dict(),
|
||||
description=dict(),
|
||||
speed=dict(),
|
||||
name=dict(type='str'),
|
||||
description=dict(type='str'),
|
||||
speed=dict(choices=['10', '100', '1000']),
|
||||
mtu=dict(),
|
||||
duplex=dict(choices=['full', 'half']),
|
||||
enabled=dict(default=True, type='bool'),
|
||||
active=dict(default='active', type='str', choices=['active', 'preconfigure']),
|
||||
tx_rate=dict(),
|
||||
rx_rate=dict(),
|
||||
delay=dict(default=10, type='int'),
|
||||
|
@ -387,32 +627,21 @@ def main():
|
|||
mutually_exclusive=mutually_exclusive,
|
||||
supports_check_mode=True)
|
||||
|
||||
warnings = list()
|
||||
|
||||
result = {'changed': False}
|
||||
|
||||
want = map_params_to_obj(module)
|
||||
have = map_config_to_obj(module)
|
||||
|
||||
commands = map_obj_to_commands((want, have))
|
||||
|
||||
result['commands'] = commands
|
||||
result['warnings'] = warnings
|
||||
|
||||
if commands:
|
||||
commit = not module.check_mode
|
||||
diff = load_config(module, commands, commit=commit)
|
||||
if diff:
|
||||
result['diff'] = dict(prepared=diff)
|
||||
result['changed'] = True
|
||||
|
||||
failed_conditions = check_declarative_intent_params(module, want, result)
|
||||
|
||||
if failed_conditions:
|
||||
msg = 'One or more conditional statements have not been satisfied'
|
||||
module.fail_json(msg=msg, failed_conditions=failed_conditions)
|
||||
config_object = None
|
||||
if is_cliconf(module):
|
||||
module.deprecate("cli support for 'iosxr_interface' is deprecated. Use transport netconf instead",
|
||||
version='4 releases from v2.5')
|
||||
config_object = CliConfiguration(module)
|
||||
elif is_netconf(module):
|
||||
if module.params['active'] == 'preconfigure':
|
||||
module.fail_json(msg="Physical interface pre-configuration is not supported with transport 'netconf'")
|
||||
config_object = NCConfiguration(module)
|
||||
|
||||
result = {}
|
||||
if config_object:
|
||||
result = config_object.run()
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue