mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-07-22 12:50:22 -07:00
Module deprecation: docs, scheme and tests (#34100)
Enforce module deprecation. After module has reached the end of it's deprecation cycle we will replace it with a docs stub. * Replace deprecated modules with docs-only sub * Use of deprecated past deprecation cycle gives meaningful message (see examples below) * Enforce documentation.deprecation dict via `schema.py` * Update `ansible-doc` and web docs to display documentation.deprecation * Document that structure in `dev_guide` * Ensure that all modules starting with `_` have a `deprecation:` block * Ensure `deprecation:` block is only used on modules that start with `_` * `removed_in` A string which represents when this module needs **deleting** * CHANGELOG.md and porting_guide_2.5.rst list removed modules as well as alternatives * CHANGELOG.md links to porting guide index To ensure that meaningful messages are given to the user if they try to use a module at the end of it's deprecation cycle we enforce the module to contain: ```python if __name__ == '__main__': removed_module() ```
This commit is contained in:
parent
7c83f006c0
commit
a23c95023b
66 changed files with 241 additions and 4438 deletions
|
@ -18,7 +18,10 @@ version_added: "1.1"
|
|||
short_description: Manages Citrix NetScaler entities
|
||||
description:
|
||||
- Manages Citrix NetScaler server and service entities.
|
||||
deprecated: In 2.4 use M(netscaler_service) and M(netscaler_server) instead.
|
||||
deprecated:
|
||||
removed_in: "2.8"
|
||||
why: Replaced with Citrix maintained version.
|
||||
alternative: Use M(netscaler_service) and M(netscaler_server) instead.
|
||||
options:
|
||||
nsc_host:
|
||||
description:
|
||||
|
|
|
@ -19,7 +19,10 @@ module: cl_bond
|
|||
version_added: "2.1"
|
||||
author: "Cumulus Networks (@CumulusNetworks)"
|
||||
short_description: Configures a bond port on Cumulus Linux
|
||||
deprecated: Deprecated in 2.3. Use M(nclu) instead.
|
||||
deprecated:
|
||||
removed_in: "2.5"
|
||||
why: The M(nclu) module is designed to be easier to use for individuals who are new to Cumulus Linux by exposing the NCLU interface in an automatable way.
|
||||
alternative: Use M(nclu) instead.
|
||||
description:
|
||||
- Configures a bond interface on Cumulus Linux To configure a bridge port
|
||||
use the cl_bridge module. To configure any other type of interface use the
|
||||
|
@ -209,281 +212,7 @@ msg:
|
|||
sample: "interface bond0 config updated"
|
||||
'''
|
||||
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
# handy helper for calling system calls.
|
||||
# calls AnsibleModule.run_command and prints a more appropriate message
|
||||
# exec_path - path to file to execute, with all its arguments.
|
||||
# E.g "/sbin/ip -o link show"
|
||||
# failure_msg - what message to print on failure
|
||||
def run_cmd(module, exec_path):
|
||||
(_rc, out, _err) = module.run_command(exec_path)
|
||||
if _rc > 0:
|
||||
if re.search('cannot find interface', _err):
|
||||
return '[{}]'
|
||||
failure_msg = "Failed; %s Error: %s" % (exec_path, _err)
|
||||
module.fail_json(msg=failure_msg)
|
||||
else:
|
||||
return out
|
||||
|
||||
|
||||
def current_iface_config(module):
|
||||
# due to a bug in ifquery, have to check for presence of interface file
|
||||
# and not rely solely on ifquery. when bug is fixed, this check can be
|
||||
# removed
|
||||
_ifacename = module.params.get('name')
|
||||
_int_dir = module.params.get('location')
|
||||
module.custom_current_config = {}
|
||||
if os.path.exists(_int_dir + '/' + _ifacename):
|
||||
_cmd = "/sbin/ifquery -o json %s" % (module.params.get('name'))
|
||||
module.custom_current_config = module.from_json(
|
||||
run_cmd(module, _cmd))[0]
|
||||
|
||||
|
||||
def build_address(module):
|
||||
# if addr_method == 'dhcp', don't add IP address
|
||||
if module.params.get('addr_method') == 'dhcp':
|
||||
return
|
||||
_ipv4 = module.params.get('ipv4')
|
||||
_ipv6 = module.params.get('ipv6')
|
||||
_addresslist = []
|
||||
if _ipv4 and len(_ipv4) > 0:
|
||||
_addresslist += _ipv4
|
||||
|
||||
if _ipv6 and len(_ipv6) > 0:
|
||||
_addresslist += _ipv6
|
||||
if len(_addresslist) > 0:
|
||||
module.custom_desired_config['config']['address'] = ' '.join(
|
||||
_addresslist)
|
||||
|
||||
|
||||
def build_vids(module):
|
||||
_vids = module.params.get('vids')
|
||||
if _vids and len(_vids) > 0:
|
||||
module.custom_desired_config['config']['bridge-vids'] = ' '.join(_vids)
|
||||
|
||||
|
||||
def build_pvid(module):
|
||||
_pvid = module.params.get('pvid')
|
||||
if _pvid:
|
||||
module.custom_desired_config['config']['bridge-pvid'] = str(_pvid)
|
||||
|
||||
|
||||
def conv_bool_to_str(_value):
|
||||
if isinstance(_value, bool):
|
||||
if _value is True:
|
||||
return 'yes'
|
||||
else:
|
||||
return 'no'
|
||||
return _value
|
||||
|
||||
|
||||
def conv_array_to_str(_value):
|
||||
if isinstance(_value, list):
|
||||
return ' '.join(_value)
|
||||
return _value
|
||||
|
||||
|
||||
def build_generic_attr(module, _attr):
|
||||
_value = module.params.get(_attr)
|
||||
_value = conv_bool_to_str(_value)
|
||||
_value = conv_array_to_str(_value)
|
||||
if _value:
|
||||
module.custom_desired_config['config'][
|
||||
re.sub('_', '-', _attr)] = str(_value)
|
||||
|
||||
|
||||
def build_alias_name(module):
|
||||
alias_name = module.params.get('alias_name')
|
||||
if alias_name:
|
||||
module.custom_desired_config['config']['alias'] = alias_name
|
||||
|
||||
|
||||
def build_addr_method(module):
|
||||
_addr_method = module.params.get('addr_method')
|
||||
if _addr_method:
|
||||
module.custom_desired_config['addr_family'] = 'inet'
|
||||
module.custom_desired_config['addr_method'] = _addr_method
|
||||
|
||||
|
||||
def build_vrr(module):
|
||||
_virtual_ip = module.params.get('virtual_ip')
|
||||
_virtual_mac = module.params.get('virtual_mac')
|
||||
vrr_config = []
|
||||
if _virtual_ip:
|
||||
vrr_config.append(_virtual_mac)
|
||||
vrr_config.append(_virtual_ip)
|
||||
module.custom_desired_config.get('config')['address-virtual'] = \
|
||||
' '.join(vrr_config)
|
||||
|
||||
|
||||
def add_glob_to_array(_bondmems):
|
||||
"""
|
||||
goes through each bond member if it sees a dash add glob
|
||||
before it
|
||||
"""
|
||||
result = []
|
||||
if isinstance(_bondmems, list):
|
||||
for _entry in _bondmems:
|
||||
if re.search('-', _entry):
|
||||
_entry = 'glob ' + _entry
|
||||
result.append(_entry)
|
||||
return ' '.join(result)
|
||||
return _bondmems
|
||||
|
||||
|
||||
def build_bond_attr(module, _attr):
|
||||
_value = module.params.get(_attr)
|
||||
_value = conv_bool_to_str(_value)
|
||||
_value = add_glob_to_array(_value)
|
||||
if _value:
|
||||
module.custom_desired_config['config'][
|
||||
'bond-' + re.sub('_', '-', _attr)] = str(_value)
|
||||
|
||||
|
||||
def build_desired_iface_config(module):
|
||||
"""
|
||||
take parameters defined and build ifupdown2 compatible hash
|
||||
"""
|
||||
module.custom_desired_config = {
|
||||
'addr_family': None,
|
||||
'auto': True,
|
||||
'config': {},
|
||||
'name': module.params.get('name')
|
||||
}
|
||||
|
||||
for _attr in ['slaves', 'mode', 'xmit_hash_policy',
|
||||
'miimon', 'lacp_rate', 'lacp_bypass_allow',
|
||||
'lacp_bypass_period', 'lacp_bypass_all_active',
|
||||
'min_links']:
|
||||
build_bond_attr(module, _attr)
|
||||
|
||||
build_addr_method(module)
|
||||
build_address(module)
|
||||
build_vids(module)
|
||||
build_pvid(module)
|
||||
build_alias_name(module)
|
||||
build_vrr(module)
|
||||
|
||||
for _attr in ['mtu', 'mstpctl_portnetwork', 'mstpctl_portadminedge'
|
||||
'mstpctl_bpduguard', 'clag_id',
|
||||
'lacp_bypass_priority']:
|
||||
build_generic_attr(module, _attr)
|
||||
|
||||
|
||||
def config_dict_changed(module):
|
||||
"""
|
||||
return true if 'config' dict in hash is different
|
||||
between desired and current config
|
||||
"""
|
||||
current_config = module.custom_current_config.get('config')
|
||||
desired_config = module.custom_desired_config.get('config')
|
||||
return current_config != desired_config
|
||||
|
||||
|
||||
def config_changed(module):
|
||||
"""
|
||||
returns true if config has changed
|
||||
"""
|
||||
if config_dict_changed(module):
|
||||
return True
|
||||
# check if addr_method is changed
|
||||
return module.custom_desired_config.get('addr_method') != \
|
||||
module.custom_current_config.get('addr_method')
|
||||
|
||||
|
||||
def replace_config(module):
|
||||
temp = tempfile.NamedTemporaryFile()
|
||||
desired_config = module.custom_desired_config
|
||||
# by default it will be something like /etc/network/interfaces.d/swp1
|
||||
final_location = module.params.get('location') + '/' + \
|
||||
module.params.get('name')
|
||||
final_text = ''
|
||||
_fh = open(final_location, 'w')
|
||||
# make sure to put hash in array or else ifquery will fail
|
||||
# write to temp file
|
||||
try:
|
||||
temp.write(module.jsonify([desired_config]))
|
||||
# need to seek to 0 so that data is written to tempfile.
|
||||
temp.seek(0)
|
||||
_cmd = "/sbin/ifquery -a -i %s -t json" % (temp.name)
|
||||
final_text = run_cmd(module, _cmd)
|
||||
finally:
|
||||
temp.close()
|
||||
|
||||
try:
|
||||
_fh.write(final_text)
|
||||
finally:
|
||||
_fh.close()
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
slaves=dict(required=True, type='list'),
|
||||
name=dict(required=True, type='str'),
|
||||
ipv4=dict(type='list'),
|
||||
ipv6=dict(type='list'),
|
||||
alias_name=dict(type='str'),
|
||||
addr_method=dict(type='str',
|
||||
choices=['', 'dhcp']),
|
||||
mtu=dict(type='str'),
|
||||
virtual_ip=dict(type='str'),
|
||||
virtual_mac=dict(type='str'),
|
||||
vids=dict(type='list'),
|
||||
pvid=dict(type='str'),
|
||||
mstpctl_portnetwork=dict(type='bool'),
|
||||
mstpctl_portadminedge=dict(type='bool'),
|
||||
mstpctl_bpduguard=dict(type='bool'),
|
||||
clag_id=dict(type='str'),
|
||||
min_links=dict(type='int', default=1),
|
||||
mode=dict(type='str', default='802.3ad'),
|
||||
miimon=dict(type='int', default=100),
|
||||
xmit_hash_policy=dict(type='str', default='layer3+4'),
|
||||
lacp_rate=dict(type='int', default=1),
|
||||
lacp_bypass_allow=dict(type='int', choices=[0, 1]),
|
||||
lacp_bypass_all_active=dict(type='int', choices=[0, 1]),
|
||||
lacp_bypass_priority=dict(type='list'),
|
||||
lacp_bypass_period=dict(type='int'),
|
||||
location=dict(type='str',
|
||||
default='/etc/network/interfaces.d')
|
||||
),
|
||||
mutually_exclusive=[['lacp_bypass_priority', 'lacp_bypass_all_active']],
|
||||
required_together=[['virtual_ip', 'virtual_mac']]
|
||||
)
|
||||
|
||||
# if using the jinja default filter, this resolves to
|
||||
# create an list with an empty string ['']. The following
|
||||
# checks all lists and removes it, so that functions expecting
|
||||
# an empty list, get this result. May upstream this fix into
|
||||
# the AnsibleModule code to have it check for this.
|
||||
for k, _param in module.params.items():
|
||||
if isinstance(_param, list):
|
||||
module.params[k] = [x for x in _param if x]
|
||||
|
||||
_location = module.params.get('location')
|
||||
if not os.path.exists(_location):
|
||||
_msg = "%s does not exist." % (_location)
|
||||
module.fail_json(msg=_msg)
|
||||
return # for testing purposes only
|
||||
|
||||
ifacename = module.params.get('name')
|
||||
_changed = False
|
||||
_msg = "interface %s config not changed" % (ifacename)
|
||||
current_iface_config(module)
|
||||
build_desired_iface_config(module)
|
||||
if config_changed(module):
|
||||
replace_config(module)
|
||||
_msg = "interface %s config updated" % (ifacename)
|
||||
_changed = True
|
||||
|
||||
module.exit_json(changed=_changed, msg=_msg)
|
||||
|
||||
from ansible.module_utils.common.removed import removed_module
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
removed_module()
|
||||
|
|
|
@ -19,7 +19,10 @@ module: cl_bridge
|
|||
version_added: "2.1"
|
||||
author: "Cumulus Networks (@CumulusNetworks)"
|
||||
short_description: Configures a bridge port on Cumulus Linux
|
||||
deprecated: Deprecated in 2.3. Use M(nclu) instead.
|
||||
deprecated:
|
||||
removed_in: "2.5"
|
||||
why: The M(nclu) module is designed to be easier to use for individuals who are new to Cumulus Linux by exposing the NCLU interface in an automatable way.
|
||||
alternative: Use M(nclu) instead.
|
||||
description:
|
||||
- Configures a bridge interface on Cumulus Linux To configure a bond port
|
||||
use the cl_bond module. To configure any other type of interface use the
|
||||
|
@ -157,258 +160,7 @@ msg:
|
|||
sample: "interface bond0 config updated"
|
||||
'''
|
||||
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
# handy helper for calling system calls.
|
||||
# calls AnsibleModule.run_command and prints a more appropriate message
|
||||
# exec_path - path to file to execute, with all its arguments.
|
||||
# E.g "/sbin/ip -o link show"
|
||||
# failure_msg - what message to print on failure
|
||||
def run_cmd(module, exec_path):
|
||||
(_rc, out, _err) = module.run_command(exec_path)
|
||||
if _rc > 0:
|
||||
if re.search('cannot find interface', _err):
|
||||
return '[{}]'
|
||||
failure_msg = "Failed; %s Error: %s" % (exec_path, _err)
|
||||
module.fail_json(msg=failure_msg)
|
||||
else:
|
||||
return out
|
||||
|
||||
|
||||
def current_iface_config(module):
|
||||
# due to a bug in ifquery, have to check for presence of interface file
|
||||
# and not rely solely on ifquery. when bug is fixed, this check can be
|
||||
# removed
|
||||
_ifacename = module.params.get('name')
|
||||
_int_dir = module.params.get('location')
|
||||
module.custom_current_config = {}
|
||||
if os.path.exists(_int_dir + '/' + _ifacename):
|
||||
_cmd = "/sbin/ifquery -o json %s" % (module.params.get('name'))
|
||||
module.custom_current_config = module.from_json(
|
||||
run_cmd(module, _cmd))[0]
|
||||
|
||||
|
||||
def build_address(module):
|
||||
# if addr_method == 'dhcp', don't add IP address
|
||||
if module.params.get('addr_method') == 'dhcp':
|
||||
return
|
||||
_ipv4 = module.params.get('ipv4')
|
||||
_ipv6 = module.params.get('ipv6')
|
||||
_addresslist = []
|
||||
if _ipv4 and len(_ipv4) > 0:
|
||||
_addresslist += _ipv4
|
||||
|
||||
if _ipv6 and len(_ipv6) > 0:
|
||||
_addresslist += _ipv6
|
||||
if len(_addresslist) > 0:
|
||||
module.custom_desired_config['config']['address'] = ' '.join(
|
||||
_addresslist)
|
||||
|
||||
|
||||
def build_vids(module):
|
||||
_vids = module.params.get('vids')
|
||||
if _vids and len(_vids) > 0:
|
||||
module.custom_desired_config['config']['bridge-vids'] = ' '.join(_vids)
|
||||
|
||||
|
||||
def build_pvid(module):
|
||||
_pvid = module.params.get('pvid')
|
||||
if _pvid:
|
||||
module.custom_desired_config['config']['bridge-pvid'] = str(_pvid)
|
||||
|
||||
|
||||
def conv_bool_to_str(_value):
|
||||
if isinstance(_value, bool):
|
||||
if _value is True:
|
||||
return 'yes'
|
||||
else:
|
||||
return 'no'
|
||||
return _value
|
||||
|
||||
|
||||
def build_generic_attr(module, _attr):
|
||||
_value = module.params.get(_attr)
|
||||
_value = conv_bool_to_str(_value)
|
||||
if _value:
|
||||
module.custom_desired_config['config'][
|
||||
re.sub('_', '-', _attr)] = str(_value)
|
||||
|
||||
|
||||
def build_alias_name(module):
|
||||
alias_name = module.params.get('alias_name')
|
||||
if alias_name:
|
||||
module.custom_desired_config['config']['alias'] = alias_name
|
||||
|
||||
|
||||
def build_addr_method(module):
|
||||
_addr_method = module.params.get('addr_method')
|
||||
if _addr_method:
|
||||
module.custom_desired_config['addr_family'] = 'inet'
|
||||
module.custom_desired_config['addr_method'] = _addr_method
|
||||
|
||||
|
||||
def build_vrr(module):
|
||||
_virtual_ip = module.params.get('virtual_ip')
|
||||
_virtual_mac = module.params.get('virtual_mac')
|
||||
vrr_config = []
|
||||
if _virtual_ip:
|
||||
vrr_config.append(_virtual_mac)
|
||||
vrr_config.append(_virtual_ip)
|
||||
module.custom_desired_config.get('config')['address-virtual'] = \
|
||||
' '.join(vrr_config)
|
||||
|
||||
|
||||
def add_glob_to_array(_bridgemems):
|
||||
"""
|
||||
goes through each bridge member if it sees a dash add glob
|
||||
before it
|
||||
"""
|
||||
result = []
|
||||
if isinstance(_bridgemems, list):
|
||||
for _entry in _bridgemems:
|
||||
if re.search('-', _entry):
|
||||
_entry = 'glob ' + _entry
|
||||
result.append(_entry)
|
||||
return ' '.join(result)
|
||||
return _bridgemems
|
||||
|
||||
|
||||
def build_bridge_attr(module, _attr):
|
||||
_value = module.params.get(_attr)
|
||||
_value = conv_bool_to_str(_value)
|
||||
_value = add_glob_to_array(_value)
|
||||
if _value:
|
||||
module.custom_desired_config['config'][
|
||||
'bridge-' + re.sub('_', '-', _attr)] = str(_value)
|
||||
|
||||
|
||||
def build_desired_iface_config(module):
|
||||
"""
|
||||
take parameters defined and build ifupdown2 compatible hash
|
||||
"""
|
||||
module.custom_desired_config = {
|
||||
'addr_family': None,
|
||||
'auto': True,
|
||||
'config': {},
|
||||
'name': module.params.get('name')
|
||||
}
|
||||
|
||||
for _attr in ['vlan_aware', 'pvid', 'ports', 'stp']:
|
||||
build_bridge_attr(module, _attr)
|
||||
|
||||
build_addr_method(module)
|
||||
build_address(module)
|
||||
build_vids(module)
|
||||
build_alias_name(module)
|
||||
build_vrr(module)
|
||||
for _attr in ['mtu', 'mstpctl_treeprio']:
|
||||
build_generic_attr(module, _attr)
|
||||
|
||||
|
||||
def config_dict_changed(module):
|
||||
"""
|
||||
return true if 'config' dict in hash is different
|
||||
between desired and current config
|
||||
"""
|
||||
current_config = module.custom_current_config.get('config')
|
||||
desired_config = module.custom_desired_config.get('config')
|
||||
return current_config != desired_config
|
||||
|
||||
|
||||
def config_changed(module):
|
||||
"""
|
||||
returns true if config has changed
|
||||
"""
|
||||
if config_dict_changed(module):
|
||||
return True
|
||||
# check if addr_method is changed
|
||||
return module.custom_desired_config.get('addr_method') != \
|
||||
module.custom_current_config.get('addr_method')
|
||||
|
||||
|
||||
def replace_config(module):
|
||||
temp = tempfile.NamedTemporaryFile()
|
||||
desired_config = module.custom_desired_config
|
||||
# by default it will be something like /etc/network/interfaces.d/swp1
|
||||
final_location = module.params.get('location') + '/' + \
|
||||
module.params.get('name')
|
||||
final_text = ''
|
||||
_fh = open(final_location, 'w')
|
||||
# make sure to put hash in array or else ifquery will fail
|
||||
# write to temp file
|
||||
try:
|
||||
temp.write(module.jsonify([desired_config]))
|
||||
# need to seek to 0 so that data is written to tempfile.
|
||||
temp.seek(0)
|
||||
_cmd = "/sbin/ifquery -a -i %s -t json" % (temp.name)
|
||||
final_text = run_cmd(module, _cmd)
|
||||
finally:
|
||||
temp.close()
|
||||
|
||||
try:
|
||||
_fh.write(final_text)
|
||||
finally:
|
||||
_fh.close()
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
ports=dict(required=True, type='list'),
|
||||
name=dict(required=True, type='str'),
|
||||
ipv4=dict(type='list'),
|
||||
ipv6=dict(type='list'),
|
||||
alias_name=dict(type='str'),
|
||||
addr_method=dict(type='str',
|
||||
choices=['', 'dhcp']),
|
||||
mtu=dict(type='str'),
|
||||
virtual_ip=dict(type='str'),
|
||||
virtual_mac=dict(type='str'),
|
||||
vids=dict(type='list'),
|
||||
pvid=dict(type='str'),
|
||||
mstpctl_treeprio=dict(type='str'),
|
||||
vlan_aware=dict(type='bool'),
|
||||
stp=dict(type='bool', default='yes'),
|
||||
location=dict(type='str',
|
||||
default='/etc/network/interfaces.d')
|
||||
),
|
||||
required_together=[
|
||||
['virtual_ip', 'virtual_mac']
|
||||
]
|
||||
)
|
||||
|
||||
# if using the jinja default filter, this resolves to
|
||||
# create an list with an empty string ['']. The following
|
||||
# checks all lists and removes it, so that functions expecting
|
||||
# an empty list, get this result. May upstream this fix into
|
||||
# the AnsibleModule code to have it check for this.
|
||||
for k, _param in module.params.items():
|
||||
if isinstance(_param, list):
|
||||
module.params[k] = [x for x in _param if x]
|
||||
|
||||
_location = module.params.get('location')
|
||||
if not os.path.exists(_location):
|
||||
_msg = "%s does not exist." % (_location)
|
||||
module.fail_json(msg=_msg)
|
||||
return # for testing purposes only
|
||||
|
||||
ifacename = module.params.get('name')
|
||||
_changed = False
|
||||
_msg = "interface %s config not changed" % (ifacename)
|
||||
current_iface_config(module)
|
||||
build_desired_iface_config(module)
|
||||
if config_changed(module):
|
||||
replace_config(module)
|
||||
_msg = "interface %s config updated" % (ifacename)
|
||||
_changed = True
|
||||
|
||||
module.exit_json(changed=_changed, msg=_msg)
|
||||
|
||||
from ansible.module_utils.common.removed import removed_module
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
removed_module()
|
||||
|
|
|
@ -19,7 +19,10 @@ module: cl_img_install
|
|||
version_added: "2.1"
|
||||
author: "Cumulus Networks (@CumulusNetworks)"
|
||||
short_description: Install a different Cumulus Linux version.
|
||||
deprecated: Deprecated in 2.3. The image slot system no longer exists in Cumulus Linux.
|
||||
deprecated:
|
||||
removed_in: "2.5"
|
||||
why: The image slot system no longer exists in Cumulus Linux.
|
||||
alternative: n/a
|
||||
description:
|
||||
- install a different version of Cumulus Linux in the inactive slot. For
|
||||
more details go the Image Management User Guide at
|
||||
|
@ -103,213 +106,7 @@ msg:
|
|||
sample: "interface bond0 config updated"
|
||||
'''
|
||||
|
||||
import re
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, platform
|
||||
from ansible.module_utils.six.moves.urllib import parse as urlparse
|
||||
|
||||
|
||||
def check_url(module, url):
|
||||
parsed_url = urlparse(url)
|
||||
if len(parsed_url.path) > 0:
|
||||
sch = parsed_url.scheme
|
||||
if (sch == 'http' or sch == 'https' or len(parsed_url.scheme) == 0):
|
||||
return True
|
||||
module.fail_json(msg="Image Path URL. Wrong Format %s" % (url))
|
||||
return False
|
||||
|
||||
|
||||
def run_cl_cmd(module, cmd, check_rc=True):
|
||||
try:
|
||||
(rc, out, err) = module.run_command(cmd, check_rc=check_rc)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=e.strerror)
|
||||
# trim last line as it is always empty
|
||||
ret = out.splitlines()
|
||||
return ret
|
||||
|
||||
|
||||
def get_slot_info(module):
|
||||
slots = {}
|
||||
slots['1'] = {}
|
||||
slots['2'] = {}
|
||||
active_slotnum = get_active_slot(module)
|
||||
primary_slotnum = get_primary_slot_num(module)
|
||||
for _num in range(1, 3):
|
||||
slot = slots[str(_num)]
|
||||
slot['version'] = get_slot_version(module, str(_num))
|
||||
if _num == int(active_slotnum):
|
||||
slot['active'] = True
|
||||
if _num == int(primary_slotnum):
|
||||
slot['primary'] = True
|
||||
return slots
|
||||
|
||||
|
||||
def get_slot_version(module, slot_num):
|
||||
lsb_release = check_mnt_root_lsb_release(slot_num)
|
||||
switch_firm_ver = check_fw_print_env(module, slot_num)
|
||||
_version = module.sw_version
|
||||
if lsb_release == _version or switch_firm_ver == _version:
|
||||
return _version
|
||||
elif lsb_release:
|
||||
return lsb_release
|
||||
else:
|
||||
return switch_firm_ver
|
||||
|
||||
|
||||
def check_mnt_root_lsb_release(slot_num):
|
||||
_path = '/mnt/root-rw/config%s/etc/lsb-release' % (slot_num)
|
||||
try:
|
||||
lsb_release = open(_path)
|
||||
lines = lsb_release.readlines()
|
||||
for line in lines:
|
||||
_match = re.search('DISTRIB_RELEASE=([0-9a-zA-Z.]+)', line)
|
||||
if _match:
|
||||
return _match.group(1).split('-')[0]
|
||||
except:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def check_fw_print_env(module, slot_num):
|
||||
cmd = None
|
||||
if platform.machine() == 'ppc':
|
||||
cmd = "/usr/sbin/fw_printenv -n cl.ver%s" % (slot_num)
|
||||
fw_output = run_cl_cmd(module, cmd)
|
||||
return fw_output[0].split('-')[0]
|
||||
elif platform.machine() == 'x86_64':
|
||||
cmd = "/usr/bin/grub-editenv list"
|
||||
grub_output = run_cl_cmd(module, cmd)
|
||||
for _line in grub_output:
|
||||
_regex_str = re.compile('cl.ver' + slot_num + r'=([\w.]+)-')
|
||||
m0 = re.match(_regex_str, _line)
|
||||
if m0:
|
||||
return m0.group(1)
|
||||
|
||||
|
||||
def get_primary_slot_num(module):
|
||||
cmd = None
|
||||
if platform.machine() == 'ppc':
|
||||
cmd = "/usr/sbin/fw_printenv -n cl.active"
|
||||
return ''.join(run_cl_cmd(module, cmd))
|
||||
elif platform.machine() == 'x86_64':
|
||||
cmd = "/usr/bin/grub-editenv list"
|
||||
grub_output = run_cl_cmd(module, cmd)
|
||||
for _line in grub_output:
|
||||
_regex_str = re.compile(r'cl.active=(\d)')
|
||||
m0 = re.match(_regex_str, _line)
|
||||
if m0:
|
||||
return m0.group(1)
|
||||
|
||||
|
||||
def get_active_slot(module):
|
||||
try:
|
||||
cmdline = open('/proc/cmdline').readline()
|
||||
except:
|
||||
module.fail_json(msg='Failed to open /proc/cmdline. ' +
|
||||
'Unable to determine active slot')
|
||||
|
||||
_match = re.search(r'active=(\d+)', cmdline)
|
||||
if _match:
|
||||
return _match.group(1)
|
||||
return None
|
||||
|
||||
|
||||
def install_img(module):
|
||||
src = module.params.get('src')
|
||||
_version = module.sw_version
|
||||
app_path = '/usr/cumulus/bin/cl-img-install -f %s' % (src)
|
||||
run_cl_cmd(module, app_path)
|
||||
perform_switch_slot = module.params.get('switch_slot')
|
||||
if perform_switch_slot is True:
|
||||
check_sw_version(module)
|
||||
else:
|
||||
_changed = True
|
||||
_msg = "Cumulus Linux Version " + _version + " successfully" + \
|
||||
" installed in alternate slot"
|
||||
module.exit_json(changed=_changed, msg=_msg)
|
||||
|
||||
|
||||
def switch_slot(module, slotnum):
|
||||
_switch_slot = module.params.get('switch_slot')
|
||||
if _switch_slot is True:
|
||||
app_path = '/usr/cumulus/bin/cl-img-select %s' % (slotnum)
|
||||
run_cl_cmd(module, app_path)
|
||||
|
||||
|
||||
def determine_sw_version(module):
|
||||
_version = module.params.get('version')
|
||||
_filename = ''
|
||||
# Use _version if user defines it
|
||||
if _version:
|
||||
module.sw_version = _version
|
||||
return
|
||||
else:
|
||||
_filename = module.params.get('src').split('/')[-1]
|
||||
_match = re.search(r'\d+\W\d+\W\w+', _filename)
|
||||
if _match:
|
||||
module.sw_version = re.sub(r'\W', '.', _match.group())
|
||||
return
|
||||
_msg = 'Unable to determine version from file %s' % (_filename)
|
||||
module.exit_json(changed=False, msg=_msg)
|
||||
|
||||
|
||||
def check_sw_version(module):
|
||||
slots = get_slot_info(module)
|
||||
_version = module.sw_version
|
||||
perform_switch_slot = module.params.get('switch_slot')
|
||||
for _num, slot in slots.items():
|
||||
if slot['version'] == _version:
|
||||
if 'active' in slot:
|
||||
_msg = "Version %s is installed in the active slot" \
|
||||
% (_version)
|
||||
module.exit_json(changed=False, msg=_msg)
|
||||
else:
|
||||
_msg = "Version " + _version + \
|
||||
" is installed in the alternate slot. "
|
||||
if 'primary' not in slot:
|
||||
if perform_switch_slot is True:
|
||||
switch_slot(module, _num)
|
||||
_msg = _msg + \
|
||||
"cl-img-select has made the alternate " + \
|
||||
"slot the primary slot. " +\
|
||||
"Next reboot, switch will load " + _version + "."
|
||||
module.exit_json(changed=True, msg=_msg)
|
||||
else:
|
||||
_msg = _msg + \
|
||||
"Next reboot will not load " + _version + ". " + \
|
||||
"switch_slot keyword set to 'no'."
|
||||
module.exit_json(changed=False, msg=_msg)
|
||||
else:
|
||||
if perform_switch_slot is True:
|
||||
_msg = _msg + \
|
||||
"Next reboot, switch will load " + _version + "."
|
||||
module.exit_json(changed=False, msg=_msg)
|
||||
else:
|
||||
_msg = _msg + \
|
||||
'switch_slot set to "no". ' + \
|
||||
'No further action to take'
|
||||
module.exit_json(changed=False, msg=_msg)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
src=dict(required=True, type='str'),
|
||||
version=dict(type='str'),
|
||||
switch_slot=dict(type='bool', default=False),
|
||||
),
|
||||
)
|
||||
|
||||
determine_sw_version(module)
|
||||
_url = module.params.get('src')
|
||||
|
||||
check_sw_version(module)
|
||||
|
||||
check_url(module, _url)
|
||||
|
||||
install_img(module)
|
||||
|
||||
from ansible.module_utils.common.removed import removed_module
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
removed_module()
|
||||
|
|
|
@ -20,7 +20,10 @@ version_added: "2.1"
|
|||
author: "Cumulus Networks (@CumulusNetworks)"
|
||||
short_description: Configures a front panel port, loopback or
|
||||
management port on Cumulus Linux.
|
||||
deprecated: Deprecated in 2.3. Use M(nclu) instead.
|
||||
deprecated:
|
||||
removed_in: "2.5"
|
||||
why: The M(nclu) module is designed to be easier to use for individuals who are new to Cumulus Linux by exposing the NCLU interface in an automatable way.
|
||||
alternative: Use M(nclu) instead.
|
||||
description:
|
||||
- Configures a front panel, sub-interface, SVI, management or loopback port
|
||||
on a Cumulus Linux switch. For bridge ports use the cl_bridge module. For
|
||||
|
@ -202,249 +205,7 @@ msg:
|
|||
sample: "interface bond0 config updated"
|
||||
'''
|
||||
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
# handy helper for calling system calls.
|
||||
# calls AnsibleModule.run_command and prints a more appropriate message
|
||||
# exec_path - path to file to execute, with all its arguments.
|
||||
# E.g "/sbin/ip -o link show"
|
||||
# failure_msg - what message to print on failure
|
||||
def run_cmd(module, exec_path):
|
||||
(_rc, out, _err) = module.run_command(exec_path)
|
||||
if _rc > 0:
|
||||
if re.search('cannot find interface', _err):
|
||||
return '[{}]'
|
||||
failure_msg = "Failed; %s Error: %s" % (exec_path, _err)
|
||||
module.fail_json(msg=failure_msg)
|
||||
else:
|
||||
return out
|
||||
|
||||
|
||||
def current_iface_config(module):
|
||||
# due to a bug in ifquery, have to check for presence of interface file
|
||||
# and not rely solely on ifquery. when bug is fixed, this check can be
|
||||
# removed
|
||||
_ifacename = module.params.get('name')
|
||||
_int_dir = module.params.get('location')
|
||||
module.custom_current_config = {}
|
||||
if os.path.exists(_int_dir + '/' + _ifacename):
|
||||
_cmd = "/sbin/ifquery -o json %s" % (module.params.get('name'))
|
||||
module.custom_current_config = module.from_json(
|
||||
run_cmd(module, _cmd))[0]
|
||||
|
||||
|
||||
def build_address(module):
|
||||
# if addr_method == 'dhcp', don't add IP address
|
||||
if module.params.get('addr_method') == 'dhcp':
|
||||
return
|
||||
_ipv4 = module.params.get('ipv4')
|
||||
_ipv6 = module.params.get('ipv6')
|
||||
_addresslist = []
|
||||
if _ipv4 and len(_ipv4) > 0:
|
||||
_addresslist += _ipv4
|
||||
if _ipv6 and len(_ipv6) > 0:
|
||||
_addresslist += _ipv6
|
||||
if len(_addresslist) > 0:
|
||||
module.custom_desired_config['config']['address'] = ' '.join(
|
||||
_addresslist)
|
||||
|
||||
|
||||
def build_vids(module):
|
||||
_vids = module.params.get('vids')
|
||||
if _vids and len(_vids) > 0:
|
||||
module.custom_desired_config['config']['bridge-vids'] = ' '.join(_vids)
|
||||
|
||||
|
||||
def build_pvid(module):
|
||||
_pvid = module.params.get('pvid')
|
||||
if _pvid:
|
||||
module.custom_desired_config['config']['bridge-pvid'] = str(_pvid)
|
||||
|
||||
|
||||
def build_speed(module):
|
||||
_speed = module.params.get('speed')
|
||||
if _speed:
|
||||
module.custom_desired_config['config']['link-speed'] = str(_speed)
|
||||
module.custom_desired_config['config']['link-duplex'] = 'full'
|
||||
|
||||
|
||||
def conv_bool_to_str(_value):
|
||||
if isinstance(_value, bool):
|
||||
if _value is True:
|
||||
return 'yes'
|
||||
else:
|
||||
return 'no'
|
||||
return _value
|
||||
|
||||
|
||||
def build_generic_attr(module, _attr):
|
||||
_value = module.params.get(_attr)
|
||||
_value = conv_bool_to_str(_value)
|
||||
if _value:
|
||||
module.custom_desired_config['config'][
|
||||
re.sub('_', '-', _attr)] = str(_value)
|
||||
|
||||
|
||||
def build_alias_name(module):
|
||||
alias_name = module.params.get('alias_name')
|
||||
if alias_name:
|
||||
module.custom_desired_config['config']['alias'] = alias_name
|
||||
|
||||
|
||||
def build_addr_method(module):
|
||||
_addr_method = module.params.get('addr_method')
|
||||
if _addr_method:
|
||||
module.custom_desired_config['addr_family'] = 'inet'
|
||||
module.custom_desired_config['addr_method'] = _addr_method
|
||||
|
||||
|
||||
def build_vrr(module):
|
||||
_virtual_ip = module.params.get('virtual_ip')
|
||||
_virtual_mac = module.params.get('virtual_mac')
|
||||
vrr_config = []
|
||||
if _virtual_ip:
|
||||
vrr_config.append(_virtual_mac)
|
||||
vrr_config.append(_virtual_ip)
|
||||
module.custom_desired_config.get('config')['address-virtual'] = \
|
||||
' '.join(vrr_config)
|
||||
|
||||
|
||||
def build_desired_iface_config(module):
|
||||
"""
|
||||
take parameters defined and build ifupdown2 compatible hash
|
||||
"""
|
||||
module.custom_desired_config = {
|
||||
'addr_family': None,
|
||||
'auto': True,
|
||||
'config': {},
|
||||
'name': module.params.get('name')
|
||||
}
|
||||
|
||||
build_addr_method(module)
|
||||
build_address(module)
|
||||
build_vids(module)
|
||||
build_pvid(module)
|
||||
build_speed(module)
|
||||
build_alias_name(module)
|
||||
build_vrr(module)
|
||||
for _attr in ['mtu', 'mstpctl_portnetwork', 'mstpctl_portadminedge',
|
||||
'mstpctl_bpduguard', 'clagd_enable',
|
||||
'clagd_priority', 'clagd_peer_ip',
|
||||
'clagd_sys_mac', 'clagd_args']:
|
||||
build_generic_attr(module, _attr)
|
||||
|
||||
|
||||
def config_dict_changed(module):
|
||||
"""
|
||||
return true if 'config' dict in hash is different
|
||||
between desired and current config
|
||||
"""
|
||||
current_config = module.custom_current_config.get('config')
|
||||
desired_config = module.custom_desired_config.get('config')
|
||||
return current_config != desired_config
|
||||
|
||||
|
||||
def config_changed(module):
|
||||
"""
|
||||
returns true if config has changed
|
||||
"""
|
||||
if config_dict_changed(module):
|
||||
return True
|
||||
# check if addr_method is changed
|
||||
return module.custom_desired_config.get('addr_method') != \
|
||||
module.custom_current_config.get('addr_method')
|
||||
|
||||
|
||||
def replace_config(module):
|
||||
temp = tempfile.NamedTemporaryFile()
|
||||
desired_config = module.custom_desired_config
|
||||
# by default it will be something like /etc/network/interfaces.d/swp1
|
||||
final_location = module.params.get('location') + '/' + \
|
||||
module.params.get('name')
|
||||
final_text = ''
|
||||
_fh = open(final_location, 'w')
|
||||
# make sure to put hash in array or else ifquery will fail
|
||||
# write to temp file
|
||||
try:
|
||||
temp.write(module.jsonify([desired_config]))
|
||||
# need to seek to 0 so that data is written to tempfile.
|
||||
temp.seek(0)
|
||||
_cmd = "/sbin/ifquery -a -i %s -t json" % (temp.name)
|
||||
final_text = run_cmd(module, _cmd)
|
||||
finally:
|
||||
temp.close()
|
||||
|
||||
try:
|
||||
_fh.write(final_text)
|
||||
finally:
|
||||
_fh.close()
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
name=dict(required=True, type='str'),
|
||||
ipv4=dict(type='list'),
|
||||
ipv6=dict(type='list'),
|
||||
alias_name=dict(type='str'),
|
||||
addr_method=dict(type='str',
|
||||
choices=['', 'loopback', 'dhcp']),
|
||||
speed=dict(type='str'),
|
||||
mtu=dict(type='str'),
|
||||
virtual_ip=dict(type='str'),
|
||||
virtual_mac=dict(type='str'),
|
||||
vids=dict(type='list'),
|
||||
pvid=dict(type='str'),
|
||||
mstpctl_portnetwork=dict(type='bool'),
|
||||
mstpctl_portadminedge=dict(type='bool'),
|
||||
mstpctl_bpduguard=dict(type='bool'),
|
||||
clagd_enable=dict(type='bool'),
|
||||
clagd_priority=dict(type='str'),
|
||||
clagd_peer_ip=dict(type='str'),
|
||||
clagd_sys_mac=dict(type='str'),
|
||||
clagd_args=dict(type='str'),
|
||||
location=dict(type='str',
|
||||
default='/etc/network/interfaces.d')
|
||||
),
|
||||
required_together=[
|
||||
['virtual_ip', 'virtual_mac'],
|
||||
['clagd_enable', 'clagd_priority',
|
||||
'clagd_peer_ip', 'clagd_sys_mac']
|
||||
]
|
||||
)
|
||||
|
||||
# if using the jinja default filter, this resolves to
|
||||
# create an list with an empty string ['']. The following
|
||||
# checks all lists and removes it, so that functions expecting
|
||||
# an empty list, get this result. May upstream this fix into
|
||||
# the AnsibleModule code to have it check for this.
|
||||
for k, _param in module.params.items():
|
||||
if isinstance(_param, list):
|
||||
module.params[k] = [x for x in _param if x]
|
||||
|
||||
_location = module.params.get('location')
|
||||
if not os.path.exists(_location):
|
||||
_msg = "%s does not exist." % (_location)
|
||||
module.fail_json(msg=_msg)
|
||||
return # for testing purposes only
|
||||
|
||||
ifacename = module.params.get('name')
|
||||
_changed = False
|
||||
_msg = "interface %s config not changed" % (ifacename)
|
||||
current_iface_config(module)
|
||||
build_desired_iface_config(module)
|
||||
if config_changed(module):
|
||||
replace_config(module)
|
||||
_msg = "interface %s config updated" % (ifacename)
|
||||
_changed = True
|
||||
|
||||
module.exit_json(changed=_changed, msg=_msg)
|
||||
|
||||
from ansible.module_utils.common.removed import removed_module
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
removed_module()
|
||||
|
|
|
@ -19,7 +19,10 @@ module: cl_interface_policy
|
|||
version_added: "2.1"
|
||||
author: "Cumulus Networks (@CumulusNetworks)"
|
||||
short_description: Configure interface enforcement policy on Cumulus Linux
|
||||
deprecated: Deprecated in 2.3. Use M(nclu) instead.
|
||||
deprecated:
|
||||
removed_in: "2.5"
|
||||
why: The M(nclu) module is designed to be easier to use for individuals who are new to Cumulus Linux by exposing the NCLU interface in an automatable way.
|
||||
alternative: Use M(nclu) instead.
|
||||
description:
|
||||
- This module affects the configuration files located in the interfaces
|
||||
folder defined by ifupdown2. Interfaces port and port ranges listed in the
|
||||
|
@ -64,82 +67,8 @@ msg:
|
|||
type: string
|
||||
sample: "interface bond0 config updated"
|
||||
'''
|
||||
import os
|
||||
import re
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
# get list of interface files that are currently "configured".
|
||||
# doesn't mean actually applied to the system, but most likely are
|
||||
def read_current_int_dir(module):
|
||||
module.custom_currentportlist = os.listdir(module.params.get('location'))
|
||||
|
||||
|
||||
# take the allowed list and convert it to into a list
|
||||
# of ports.
|
||||
def convert_allowed_list_to_port_range(module):
|
||||
allowedlist = module.params.get('allowed')
|
||||
for portrange in allowedlist:
|
||||
module.custom_allowedportlist += breakout_portrange(portrange)
|
||||
|
||||
|
||||
def breakout_portrange(prange):
|
||||
_m0 = re.match(r'(\w+[a-z.])(\d+)?-?(\d+)?(\w+)?', prange.strip())
|
||||
# no range defined
|
||||
if _m0.group(3) is None:
|
||||
return [_m0.group(0)]
|
||||
else:
|
||||
portarray = []
|
||||
intrange = range(int(_m0.group(2)), int(_m0.group(3)) + 1)
|
||||
for _int in intrange:
|
||||
portarray.append(''.join([_m0.group(1),
|
||||
str(_int),
|
||||
str(_m0.group(4) or '')
|
||||
]
|
||||
)
|
||||
)
|
||||
return portarray
|
||||
|
||||
|
||||
# deletes the interface files
|
||||
def unconfigure_interfaces(module):
|
||||
currentportset = set(module.custom_currentportlist)
|
||||
allowedportset = set(module.custom_allowedportlist)
|
||||
remove_list = currentportset.difference(allowedportset)
|
||||
fileprefix = module.params.get('location')
|
||||
module.msg = "remove config for interfaces %s" % (', '.join(remove_list))
|
||||
for _file in remove_list:
|
||||
os.unlink(fileprefix + _file)
|
||||
|
||||
|
||||
# check to see if policy should be enforced
|
||||
# returns true if policy needs to be enforced
|
||||
# that is delete interface files
|
||||
def int_policy_enforce(module):
|
||||
currentportset = set(module.custom_currentportlist)
|
||||
allowedportset = set(module.custom_allowedportlist)
|
||||
return not currentportset.issubset(allowedportset)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
allowed=dict(type='list', required=True),
|
||||
location=dict(type='str', default='/etc/network/interfaces.d/')
|
||||
),
|
||||
)
|
||||
module.custom_currentportlist = []
|
||||
module.custom_allowedportlist = []
|
||||
module.changed = False
|
||||
module.msg = 'configured port list is part of allowed port list'
|
||||
read_current_int_dir(module)
|
||||
convert_allowed_list_to_port_range(module)
|
||||
if int_policy_enforce(module):
|
||||
module.changed = True
|
||||
unconfigure_interfaces(module)
|
||||
module.exit_json(changed=module.changed, msg=module.msg)
|
||||
|
||||
from ansible.module_utils.common.removed import removed_module
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
removed_module()
|
||||
|
|
|
@ -18,8 +18,11 @@ DOCUMENTATION = '''
|
|||
module: cl_license
|
||||
version_added: "2.1"
|
||||
author: "Cumulus Networks (@CumulusNetworks)"
|
||||
short_description: Install licenses fo Cumulus Linux
|
||||
deprecated: Deprecated in 2.3.
|
||||
short_description: Install licenses for Cumulus Linux
|
||||
deprecated:
|
||||
why: The M(nclu) module is designed to be easier to use for individuals who are new to Cumulus Linux by exposing the NCLU interface in an automatable way.
|
||||
removed_in: "2.5"
|
||||
alternative: Use M(nclu) instead.
|
||||
description:
|
||||
- Installs a Cumulus Linux license. The module reports no change of status
|
||||
when a license is installed.
|
||||
|
@ -100,43 +103,7 @@ msg:
|
|||
sample: "interface bond0 config updated"
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
CL_LICENSE_PATH = '/usr/cumulus/bin/cl-license'
|
||||
|
||||
|
||||
def install_license(module):
|
||||
# license is not installed, install it
|
||||
_url = module.params.get('src')
|
||||
(_rc, out, _err) = module.run_command("%s -i %s" % (CL_LICENSE_PATH, _url))
|
||||
if _rc > 0:
|
||||
module.fail_json(msg=_err)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
src=dict(required=True, type='str'),
|
||||
force=dict(type='bool', default=False)
|
||||
),
|
||||
)
|
||||
|
||||
# check if license is installed
|
||||
# if force is enabled then set return code to nonzero
|
||||
if module.params.get('force') is True:
|
||||
_rc = 10
|
||||
else:
|
||||
(_rc, out, _err) = module.run_command(CL_LICENSE_PATH)
|
||||
if _rc == 0:
|
||||
module.msg = "No change. License already installed"
|
||||
module.changed = False
|
||||
else:
|
||||
install_license(module)
|
||||
module.msg = "License installation completed"
|
||||
module.changed = True
|
||||
module.exit_json(changed=module.changed, msg=module.msg)
|
||||
|
||||
from ansible.module_utils.common.removed import removed_module
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
removed_module()
|
||||
|
|
|
@ -19,7 +19,10 @@ module: cl_ports
|
|||
version_added: "2.1"
|
||||
author: "Cumulus Networks (@CumulusNetworks)"
|
||||
short_description: Configure Cumulus Switch port attributes (ports.conf)
|
||||
deprecated: Deprecated in 2.3. Use M(nclu) instead.
|
||||
deprecated:
|
||||
removed_in: "2.5"
|
||||
why: The M(nclu) module is designed to be easier to use for individuals who are new to Cumulus Linux by exposing the NCLU interface in an automatable way.
|
||||
alternative: Use M(nclu) instead.
|
||||
description:
|
||||
- Set the initial port attribute defined in the Cumulus Linux ports.conf,
|
||||
file. This module does not do any error checking at the moment. Be careful
|
||||
|
@ -77,139 +80,8 @@ msg:
|
|||
type: string
|
||||
sample: "interface bond0 config updated"
|
||||
'''
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
import shutil
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
|
||||
PORTS_CONF = '/etc/cumulus/ports.conf'
|
||||
|
||||
|
||||
def hash_existing_ports_conf(module):
|
||||
module.ports_conf_hash = {}
|
||||
if not os.path.exists(PORTS_CONF):
|
||||
return False
|
||||
|
||||
try:
|
||||
existing_ports_conf = open(PORTS_CONF).readlines()
|
||||
except IOError as e:
|
||||
_msg = "Failed to open %s: %s" % (PORTS_CONF, to_native(e))
|
||||
module.fail_json(msg=_msg)
|
||||
return # for testing only should return on module.fail_json
|
||||
|
||||
for _line in existing_ports_conf:
|
||||
_m0 = re.match(r'^(\d+)=(\w+)', _line)
|
||||
if _m0:
|
||||
_portnum = int(_m0.group(1))
|
||||
_speed = _m0.group(2)
|
||||
module.ports_conf_hash[_portnum] = _speed
|
||||
|
||||
|
||||
def generate_new_ports_conf_hash(module):
|
||||
new_ports_conf_hash = {}
|
||||
convert_hash = {
|
||||
'speed_40g_div_4': '40G/4',
|
||||
'speed_4_by_10g': '4x10G',
|
||||
'speed_10g': '10G',
|
||||
'speed_40g': '40G'
|
||||
}
|
||||
for k in module.params.keys():
|
||||
port_range = module.params[k]
|
||||
port_setting = convert_hash[k]
|
||||
if port_range:
|
||||
port_range = [x for x in port_range if x]
|
||||
for port_str in port_range:
|
||||
port_range_str = port_str.replace('swp', '').split('-')
|
||||
if len(port_range_str) == 1:
|
||||
new_ports_conf_hash[int(port_range_str[0])] = \
|
||||
port_setting
|
||||
else:
|
||||
int_range = map(int, port_range_str)
|
||||
portnum_range = range(int_range[0], int_range[1] + 1)
|
||||
for i in portnum_range:
|
||||
new_ports_conf_hash[i] = port_setting
|
||||
module.new_ports_hash = new_ports_conf_hash
|
||||
|
||||
|
||||
def compare_new_and_old_port_conf_hash(module):
|
||||
ports_conf_hash_copy = module.ports_conf_hash.copy()
|
||||
module.ports_conf_hash.update(module.new_ports_hash)
|
||||
port_num_length = len(module.ports_conf_hash.keys())
|
||||
orig_port_num_length = len(ports_conf_hash_copy.keys())
|
||||
if port_num_length != orig_port_num_length:
|
||||
module.fail_json(msg="Port numbering is wrong. \
|
||||
Too many or two few ports configured")
|
||||
return False
|
||||
elif ports_conf_hash_copy == module.ports_conf_hash:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def make_copy_of_orig_ports_conf(module):
|
||||
if os.path.exists(PORTS_CONF + '.orig'):
|
||||
return
|
||||
|
||||
try:
|
||||
shutil.copyfile(PORTS_CONF, PORTS_CONF + '.orig')
|
||||
except IOError as e:
|
||||
_msg = "Failed to save the original %s: %s" % (PORTS_CONF, to_native(e))
|
||||
module.fail_json(msg=_msg)
|
||||
return # for testing only
|
||||
|
||||
|
||||
def write_to_ports_conf(module):
|
||||
"""
|
||||
use tempfile to first write out config in temp file
|
||||
then write to actual location. may help prevent file
|
||||
corruption. Ports.conf is a critical file for Cumulus.
|
||||
Don't want to corrupt this file under any circumstance.
|
||||
"""
|
||||
temp = tempfile.NamedTemporaryFile()
|
||||
try:
|
||||
try:
|
||||
temp.write('# Managed By Ansible\n')
|
||||
for k in sorted(module.ports_conf_hash.keys()):
|
||||
port_setting = module.ports_conf_hash[k]
|
||||
_str = "%s=%s\n" % (k, port_setting)
|
||||
temp.write(_str)
|
||||
temp.seek(0)
|
||||
shutil.copyfile(temp.name, PORTS_CONF)
|
||||
except IOError as e:
|
||||
module.fail_json(msg="Failed to write to %s: %s" % (PORTS_CONF, to_native(e)))
|
||||
finally:
|
||||
temp.close()
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
speed_40g_div_4=dict(type='list'),
|
||||
speed_4_by_10g=dict(type='list'),
|
||||
speed_10g=dict(type='list'),
|
||||
speed_40g=dict(type='list')
|
||||
),
|
||||
required_one_of=[['speed_40g_div_4',
|
||||
'speed_4_by_10g',
|
||||
'speed_10g',
|
||||
'speed_40g']]
|
||||
)
|
||||
|
||||
_changed = False
|
||||
hash_existing_ports_conf(module)
|
||||
generate_new_ports_conf_hash(module)
|
||||
if compare_new_and_old_port_conf_hash(module):
|
||||
make_copy_of_orig_ports_conf(module)
|
||||
write_to_ports_conf(module)
|
||||
_changed = True
|
||||
_msg = "/etc/cumulus/ports.conf changed"
|
||||
else:
|
||||
_msg = 'No change in /etc/ports.conf'
|
||||
module.exit_json(changed=_changed, msg=_msg)
|
||||
|
||||
from ansible.module_utils.common.removed import removed_module
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
removed_module()
|
||||
|
|
|
@ -24,7 +24,10 @@ DOCUMENTATION = '''
|
|||
---
|
||||
module: nxos_ip_interface
|
||||
version_added: "2.1"
|
||||
deprecated: Deprecated in 2.5. Use M(nxos_l3_interface) instead.
|
||||
deprecated:
|
||||
removed_in: "2.9"
|
||||
why: Replaced with common C(*_l3_interface) network modules.
|
||||
alternative: Use M(nxos_l3_interface) instead.
|
||||
short_description: Manages L3 attributes for IPv4 and IPv6 interfaces.
|
||||
description:
|
||||
- Manages Layer 3 attributes for IPv4 and IPv6 interfaces.
|
||||
|
|
|
@ -25,7 +25,10 @@ DOCUMENTATION = '''
|
|||
module: nxos_mtu
|
||||
extends_documentation_fragment: nxos
|
||||
version_added: "2.2"
|
||||
deprecated: Deprecated in 2.3 use M(nxos_system)'s C(mtu) option.
|
||||
deprecated:
|
||||
removed_in: "2.5"
|
||||
why: Replaced with common C(*_system) network modules.
|
||||
alternative: Use M(nxos_system)'s C(system_mtu) option. To specify an interfaces MTU use M(nxos_interface).
|
||||
short_description: Manages MTU settings on Nexus switch.
|
||||
description:
|
||||
- Manages MTU settings on Nexus switch.
|
||||
|
@ -121,264 +124,8 @@ changed:
|
|||
type: boolean
|
||||
sample: true
|
||||
'''
|
||||
from ansible.module_utils.network.nxos.nxos import load_config, run_commands
|
||||
from ansible.module_utils.network.nxos.nxos import nxos_argument_spec, check_args
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
def execute_show_command(command, module):
|
||||
if 'show run' not in command:
|
||||
output = 'json'
|
||||
else:
|
||||
output = 'text'
|
||||
cmds = [{
|
||||
'command': command,
|
||||
'output': output,
|
||||
}]
|
||||
|
||||
body = run_commands(module, cmds)
|
||||
return body
|
||||
|
||||
|
||||
def flatten_list(command_lists):
|
||||
flat_command_list = []
|
||||
for command in command_lists:
|
||||
if isinstance(command, list):
|
||||
flat_command_list.extend(command)
|
||||
else:
|
||||
flat_command_list.append(command)
|
||||
return flat_command_list
|
||||
|
||||
|
||||
def get_mtu(interface, module):
|
||||
command = 'show interface {0}'.format(interface)
|
||||
mtu = {}
|
||||
|
||||
body = execute_show_command(command, module)
|
||||
|
||||
try:
|
||||
mtu_table = body[0]['TABLE_interface']['ROW_interface']
|
||||
mtu['mtu'] = str(
|
||||
mtu_table.get('eth_mtu',
|
||||
mtu_table.get('svi_mtu', 'unreadable_via_api')))
|
||||
mtu['sysmtu'] = get_system_mtu(module)['sysmtu']
|
||||
except KeyError:
|
||||
mtu = {}
|
||||
|
||||
return mtu
|
||||
|
||||
|
||||
def get_system_mtu(module):
|
||||
command = 'show run all | inc jumbomtu'
|
||||
sysmtu = ''
|
||||
|
||||
body = execute_show_command(command, module)
|
||||
|
||||
if body:
|
||||
sysmtu = str(body[0].split(' ')[-1])
|
||||
try:
|
||||
sysmtu = int(sysmtu)
|
||||
except:
|
||||
sysmtu = ""
|
||||
|
||||
return dict(sysmtu=str(sysmtu))
|
||||
|
||||
|
||||
def get_commands_config_mtu(delta, interface):
|
||||
CONFIG_ARGS = {
|
||||
'mtu': 'mtu {mtu}',
|
||||
'sysmtu': 'system jumbomtu {sysmtu}',
|
||||
}
|
||||
|
||||
commands = []
|
||||
for param, value in delta.items():
|
||||
command = CONFIG_ARGS.get(param, 'DNE').format(**delta)
|
||||
if command and command != 'DNE':
|
||||
commands.append(command)
|
||||
command = None
|
||||
mtu_check = delta.get('mtu', None)
|
||||
if mtu_check:
|
||||
commands.insert(0, 'interface {0}'.format(interface))
|
||||
return commands
|
||||
|
||||
|
||||
def get_commands_remove_mtu(delta, interface):
|
||||
CONFIG_ARGS = {
|
||||
'mtu': 'no mtu {mtu}',
|
||||
'sysmtu': 'no system jumbomtu {sysmtu}',
|
||||
}
|
||||
commands = []
|
||||
for param, value in delta.items():
|
||||
command = CONFIG_ARGS.get(param, 'DNE').format(**delta)
|
||||
if command and command != 'DNE':
|
||||
commands.append(command)
|
||||
command = None
|
||||
mtu_check = delta.get('mtu', None)
|
||||
if mtu_check:
|
||||
commands.insert(0, 'interface {0}'.format(interface))
|
||||
return commands
|
||||
|
||||
|
||||
def get_interface_type(interface):
|
||||
if interface.upper().startswith('ET'):
|
||||
return 'ethernet'
|
||||
elif interface.upper().startswith('VL'):
|
||||
return 'svi'
|
||||
elif interface.upper().startswith('LO'):
|
||||
return 'loopback'
|
||||
elif interface.upper().startswith('MG'):
|
||||
return 'management'
|
||||
elif interface.upper().startswith('MA'):
|
||||
return 'management'
|
||||
elif interface.upper().startswith('PO'):
|
||||
return 'portchannel'
|
||||
else:
|
||||
return 'unknown'
|
||||
|
||||
|
||||
def is_default(interface, module):
|
||||
command = 'show run interface {0}'.format(interface)
|
||||
|
||||
try:
|
||||
body = execute_show_command(command, module)[0]
|
||||
if body == 'DNE':
|
||||
return 'DNE'
|
||||
else:
|
||||
raw_list = body.split('\n')
|
||||
if raw_list[-1].startswith('interface'):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except (KeyError):
|
||||
return 'DNE'
|
||||
|
||||
|
||||
def get_interface_mode(interface, intf_type, module):
|
||||
command = 'show interface {0}'.format(interface)
|
||||
mode = 'unknown'
|
||||
interface_table = {}
|
||||
body = execute_show_command(command, module)
|
||||
|
||||
try:
|
||||
interface_table = body[0]['TABLE_interface']['ROW_interface']
|
||||
except (KeyError, AttributeError, IndexError):
|
||||
return mode
|
||||
|
||||
if intf_type in ['ethernet', 'portchannel']:
|
||||
mode = str(interface_table.get('eth_mode', 'layer3'))
|
||||
if mode in ['access', 'trunk']:
|
||||
mode = 'layer2'
|
||||
elif mode == 'routed':
|
||||
mode = 'layer3'
|
||||
elif intf_type in ['loopback', 'svi']:
|
||||
mode = 'layer3'
|
||||
return mode
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = dict(
|
||||
mtu=dict(type='str'),
|
||||
interface=dict(type='str'),
|
||||
sysmtu=dict(type='str'),
|
||||
state=dict(choices=['absent', 'present'], default='present'),
|
||||
)
|
||||
|
||||
argument_spec.update(nxos_argument_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
required_together=[['mtu', 'interface']],
|
||||
supports_check_mode=True)
|
||||
|
||||
warnings = list()
|
||||
check_args(module, warnings)
|
||||
|
||||
interface = module.params['interface']
|
||||
mtu = module.params['mtu']
|
||||
sysmtu = module.params['sysmtu']
|
||||
state = module.params['state']
|
||||
|
||||
if sysmtu and (interface or mtu):
|
||||
module.fail_json(msg='Proper usage-- either just use the sysmtu param '
|
||||
'or use interface AND mtu params')
|
||||
|
||||
if interface:
|
||||
intf_type = get_interface_type(interface)
|
||||
if intf_type != 'ethernet':
|
||||
if is_default(interface, module) == 'DNE':
|
||||
module.fail_json(msg='Invalid interface. It does not exist '
|
||||
'on the switch.')
|
||||
|
||||
existing = get_mtu(interface, module)
|
||||
else:
|
||||
existing = get_system_mtu(module)
|
||||
|
||||
if interface and mtu:
|
||||
if intf_type == 'loopback':
|
||||
module.fail_json(msg='Cannot set MTU for loopback interface.')
|
||||
mode = get_interface_mode(interface, intf_type, module)
|
||||
if mode == 'layer2':
|
||||
if intf_type in ['ethernet', 'portchannel']:
|
||||
if mtu not in [existing['sysmtu'], '1500']:
|
||||
module.fail_json(msg='MTU on L2 interfaces can only be set'
|
||||
' to the system default (1500) or '
|
||||
'existing sysmtu value which is '
|
||||
' {0}'.format(existing['sysmtu']))
|
||||
elif mode == 'layer3':
|
||||
if intf_type in ['ethernet', 'portchannel', 'svi']:
|
||||
if ((int(mtu) < 576 or int(mtu) > 9216) or
|
||||
((int(mtu) % 2) != 0)):
|
||||
module.fail_json(msg='Invalid MTU for Layer 3 interface'
|
||||
'needs to be an even number between'
|
||||
'576 and 9216')
|
||||
if sysmtu:
|
||||
if ((int(sysmtu) < 576 or int(sysmtu) > 9216 or
|
||||
((int(sysmtu) % 2) != 0))):
|
||||
module.fail_json(msg='Invalid MTU- needs to be an even '
|
||||
'number between 576 and 9216')
|
||||
|
||||
args = dict(mtu=mtu, sysmtu=sysmtu)
|
||||
proposed = dict((k, v) for k, v in args.items() if v is not None)
|
||||
delta = dict(set(proposed.items()).difference(existing.items()))
|
||||
|
||||
changed = False
|
||||
end_state = existing
|
||||
commands = []
|
||||
|
||||
if state == 'present':
|
||||
if delta:
|
||||
command = get_commands_config_mtu(delta, interface)
|
||||
commands.append(command)
|
||||
|
||||
elif state == 'absent':
|
||||
common = set(proposed.items()).intersection(existing.items())
|
||||
if common:
|
||||
command = get_commands_remove_mtu(dict(common), interface)
|
||||
commands.append(command)
|
||||
|
||||
cmds = flatten_list(commands)
|
||||
if cmds:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True, commands=cmds)
|
||||
else:
|
||||
changed = True
|
||||
load_config(module, cmds)
|
||||
if interface:
|
||||
end_state = get_mtu(interface, module)
|
||||
else:
|
||||
end_state = get_system_mtu(module)
|
||||
if 'configure' in cmds:
|
||||
cmds.pop(0)
|
||||
|
||||
results = {}
|
||||
results['proposed'] = proposed
|
||||
results['existing'] = existing
|
||||
results['end_state'] = end_state
|
||||
results['updates'] = cmds
|
||||
results['changed'] = changed
|
||||
results['warnings'] = warnings
|
||||
|
||||
module.exit_json(**results)
|
||||
|
||||
from ansible.module_utils.common.removed import removed_module
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
removed_module()
|
||||
|
|
|
@ -25,7 +25,10 @@ DOCUMENTATION = '''
|
|||
module: nxos_portchannel
|
||||
extends_documentation_fragment: nxos
|
||||
version_added: "2.2"
|
||||
deprecated: Deprecated in 2.5. Use M(nxos_linkagg) instead.
|
||||
deprecated:
|
||||
removed_in: "2.9"
|
||||
why: Replaced with common C(*_linkagg) network modules.
|
||||
alternative: Use M(nxos_linkagg) instead.
|
||||
short_description: Manages port-channel interfaces.
|
||||
description:
|
||||
- Manages port-channel specific configuration parameters.
|
||||
|
|
|
@ -25,7 +25,10 @@ DOCUMENTATION = '''
|
|||
module: nxos_switchport
|
||||
extends_documentation_fragment: nxos
|
||||
version_added: "2.1"
|
||||
deprecated: Use M(nxos_l2_interface) instead.
|
||||
deprecated:
|
||||
removed_in: "2.9"
|
||||
why: Replaced with generic version.
|
||||
alternative: Use M(nxos_l2_interface) instead.
|
||||
short_description: Manages Layer 2 switchport interfaces.
|
||||
description:
|
||||
- Manages Layer 2 interfaces
|
||||
|
|
|
@ -30,7 +30,10 @@ author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)"
|
|||
version_added: "2.3"
|
||||
requirements:
|
||||
- pan-python
|
||||
deprecated: In 2.4 use M(panos_nat_rule) instead.
|
||||
deprecated:
|
||||
removed_in: "2.8"
|
||||
why: M(panos_nat_rule) uses next generation SDK (PanDevice).
|
||||
alternative: Use M(panos_nat_rule) instead.
|
||||
options:
|
||||
ip_address:
|
||||
description:
|
||||
|
@ -143,7 +146,7 @@ RETURN = '''
|
|||
'''
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'status': ['deprecated'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'status': ['deprecated'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
|
@ -35,7 +35,10 @@ description:
|
|||
traffic is applied, the more specific rules must precede the more general ones.
|
||||
author: "Ivan Bojer (@ivanbojer)"
|
||||
version_added: "2.3"
|
||||
deprecated: In 2.4 use M(panos_security_rule) instead.
|
||||
deprecated:
|
||||
removed_in: "2.8"
|
||||
why: Renamed to M(panos_security_rule) in order to align with API calls and UI object references, which also has extra support for PanDevice SDK.
|
||||
alternative: Use M(panos_security_rule) instead.
|
||||
requirements:
|
||||
- pan-python can be obtained from PyPi U(https://pypi.python.org/pypi/pan-python)
|
||||
- pandevice can be obtained from PyPi U(https://pypi.python.org/pypi/pandevice)
|
Loading…
Add table
Add a link
Reference in a new issue