mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-05-07 22:02:50 +00: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:
@@ -468,7 +468,7 @@ class DocCLI(CLI):
|
||||
if 'deprecated' in doc and doc['deprecated'] is not None and len(doc['deprecated']) > 0:
|
||||
text.append("DEPRECATED: \n")
|
||||
if isinstance(doc['deprecated'], dict):
|
||||
text.append("\tReason: %(why)s\n\tScheduled removal: Ansible %(version)s\n\tAlternatives: %(alternative)s" % doc.pop('deprecated'))
|
||||
text.append("\tReason: %(why)s\n\tWill be removed in: Ansible %(removed_in)s\n\tAlternatives: %(alternative)s" % doc.pop('deprecated'))
|
||||
else:
|
||||
text.append("%s" % doc.pop('deprecated'))
|
||||
text.append("\n")
|
||||
|
||||
@@ -16,7 +16,10 @@ DOCUMENTATION = r'''
|
||||
module: ec2_ami_find
|
||||
version_added: '2.0'
|
||||
short_description: Searches for AMIs to obtain the AMI ID and other information
|
||||
deprecated: Deprecated in 2.5. Use M(ec2_ami_facts) instead.
|
||||
deprecated:
|
||||
removed_in: "2.9"
|
||||
why: Various AWS modules have been combined and replaced with M(ec2_ami_facts).
|
||||
alternative: Use M(ec2_ami_facts) instead.
|
||||
description:
|
||||
- Returns list of matching AMIs with AMI ID, along with other useful information
|
||||
- Can search AMIs with different owners
|
||||
|
||||
@@ -16,7 +16,10 @@ DOCUMENTATION = '''
|
||||
---
|
||||
module: ec2_ami_search
|
||||
short_description: Retrieve AWS AMI information for a given operating system.
|
||||
deprecated: "Use M(ec2_ami_find) instead."
|
||||
deprecated:
|
||||
removed_in: "2.2"
|
||||
why: Various AWS modules have been combined and replaced with M(ec2_ami_facts).
|
||||
alternative: Use M(ec2_ami_find) instead.
|
||||
version_added: "1.6"
|
||||
description:
|
||||
- Look up the most recent AMI on AWS for a given operating system.
|
||||
@@ -84,124 +87,7 @@ EXAMPLES = '''
|
||||
key_name: mykey
|
||||
'''
|
||||
|
||||
import csv
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.urls import fetch_url
|
||||
|
||||
|
||||
SUPPORTED_DISTROS = ['ubuntu']
|
||||
|
||||
AWS_REGIONS = ['ap-northeast-1',
|
||||
'ap-southeast-1',
|
||||
'ap-northeast-2',
|
||||
'ap-southeast-2',
|
||||
'ap-south-1',
|
||||
'ca-central-1',
|
||||
'eu-central-1',
|
||||
'eu-west-1',
|
||||
'eu-west-2',
|
||||
'sa-east-1',
|
||||
'us-east-1',
|
||||
'us-east-2',
|
||||
'us-west-1',
|
||||
'us-west-2',
|
||||
"us-gov-west-1"]
|
||||
|
||||
|
||||
def get_url(module, url):
|
||||
""" Get url and return response """
|
||||
|
||||
r, info = fetch_url(module, url)
|
||||
if info['status'] != 200:
|
||||
# Backwards compat
|
||||
info['status_code'] = info['status']
|
||||
module.fail_json(**info)
|
||||
return r
|
||||
|
||||
|
||||
def ubuntu(module):
|
||||
""" Get the ami for ubuntu """
|
||||
|
||||
release = module.params['release']
|
||||
stream = module.params['stream']
|
||||
store = module.params['store']
|
||||
arch = module.params['arch']
|
||||
region = module.params['region']
|
||||
virt = module.params['virt']
|
||||
|
||||
url = get_ubuntu_url(release, stream)
|
||||
|
||||
req = get_url(module, url)
|
||||
reader = csv.reader(req, delimiter='\t')
|
||||
try:
|
||||
ami, aki, ari, tag, serial = lookup_ubuntu_ami(reader, release, stream,
|
||||
store, arch, region, virt)
|
||||
module.exit_json(changed=False, ami=ami, aki=aki, ari=ari, tag=tag,
|
||||
serial=serial)
|
||||
except KeyError:
|
||||
module.fail_json(msg="No matching AMI found")
|
||||
|
||||
|
||||
def lookup_ubuntu_ami(table, release, stream, store, arch, region, virt):
|
||||
""" Look up the Ubuntu AMI that matches query given a table of AMIs
|
||||
|
||||
table: an iterable that returns a row of
|
||||
(release, stream, tag, serial, region, ami, aki, ari, virt)
|
||||
release: ubuntu release name
|
||||
stream: 'server' or 'desktop'
|
||||
store: 'ebs', 'ebs-io1', 'ebs-ssd' or 'instance-store'
|
||||
arch: 'i386' or 'amd64'
|
||||
region: EC2 region
|
||||
virt: 'paravirtual' or 'hvm'
|
||||
|
||||
Returns (ami, aki, ari, tag, serial)"""
|
||||
expected = (release, stream, store, arch, region, virt)
|
||||
|
||||
for row in table:
|
||||
(actual_release, actual_stream, tag, serial,
|
||||
actual_store, actual_arch, actual_region, ami, aki, ari,
|
||||
actual_virt) = row
|
||||
actual = (actual_release, actual_stream, actual_store, actual_arch,
|
||||
actual_region, actual_virt)
|
||||
if actual == expected:
|
||||
# aki and ari are sometimes blank
|
||||
if aki == '':
|
||||
aki = None
|
||||
if ari == '':
|
||||
ari = None
|
||||
return (ami, aki, ari, tag, serial)
|
||||
|
||||
raise KeyError()
|
||||
|
||||
|
||||
def get_ubuntu_url(release, stream):
|
||||
url = "https://cloud-images.ubuntu.com/query/%s/%s/released.current.txt"
|
||||
return url % (release, stream)
|
||||
|
||||
|
||||
def main():
|
||||
arg_spec = dict(
|
||||
distro=dict(required=True, choices=SUPPORTED_DISTROS),
|
||||
release=dict(required=True),
|
||||
stream=dict(required=False, default='server',
|
||||
choices=['desktop', 'server']),
|
||||
store=dict(required=False, default='ebs',
|
||||
choices=['ebs', 'ebs-io1', 'ebs-ssd', 'instance-store']),
|
||||
arch=dict(required=False, default='amd64',
|
||||
choices=['i386', 'amd64']),
|
||||
region=dict(required=False, default='us-east-1', choices=AWS_REGIONS),
|
||||
virt=dict(required=False, default='paravirtual',
|
||||
choices=['paravirtual', 'hvm']),
|
||||
)
|
||||
module = AnsibleModule(argument_spec=arg_spec)
|
||||
distro = module.params['distro']
|
||||
|
||||
if distro == 'ubuntu':
|
||||
ubuntu(module)
|
||||
else:
|
||||
module.fail_json(msg="Unsupported distro: %s" % distro)
|
||||
|
||||
from ansible.module_utils.common.removed import removed_module
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
removed_module()
|
||||
|
||||
@@ -15,7 +15,10 @@ DOCUMENTATION = '''
|
||||
---
|
||||
module: ec2_remote_facts
|
||||
short_description: Gather facts about ec2 instances in AWS
|
||||
deprecated: Deprecated in 2.4. Use M(ec2_instance_facts) instead.
|
||||
deprecated:
|
||||
removed_in: "2.8"
|
||||
why: Replaced with boto3 version.
|
||||
alternative: Use M(ec2_instance_facts) instead.
|
||||
description:
|
||||
- Gather facts about ec2 instances in AWS
|
||||
version_added: "2.0"
|
||||
|
||||
@@ -18,10 +18,11 @@ short_description: configure AWS virtual private clouds
|
||||
description:
|
||||
- Create or terminates AWS virtual private clouds. This module has a dependency on python-boto.
|
||||
version_added: "1.4"
|
||||
deprecated: >-
|
||||
Deprecated in 2.3. Use M(ec2_vpc_net) along with supporting modules including
|
||||
M(ec2_vpc_igw), M(ec2_vpc_route_table), M(ec2_vpc_subnet), M(ec2_vpc_dhcp_options),
|
||||
M(ec2_vpc_nat_gateway), M(ec2_vpc_nacl).
|
||||
deprecated:
|
||||
removed_in: "2.5"
|
||||
why: Replaced by dedicated modules.
|
||||
alternative: Use M(ec2_vpc_net) along with supporting modules including M(ec2_vpc_igw), M(ec2_vpc_route_table), M(ec2_vpc_subnet),
|
||||
M(ec2_vpc_dhcp_options), M(ec2_vpc_nat_gateway), M(ec2_vpc_nacl).
|
||||
options:
|
||||
cidr_block:
|
||||
description:
|
||||
@@ -159,605 +160,7 @@ EXAMPLES = '''
|
||||
# the delete will fail until those dependencies are removed.
|
||||
'''
|
||||
|
||||
import time
|
||||
|
||||
try:
|
||||
import boto
|
||||
import boto.ec2
|
||||
import boto.vpc
|
||||
from boto.exception import EC2ResponseError
|
||||
|
||||
HAS_BOTO = True
|
||||
except ImportError:
|
||||
HAS_BOTO = False
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ec2 import connect_to_aws, ec2_argument_spec, get_aws_connection_info
|
||||
|
||||
|
||||
def get_vpc_info(vpc):
|
||||
"""
|
||||
Retrieves vpc information from an instance
|
||||
ID and returns it as a dictionary
|
||||
"""
|
||||
|
||||
return({
|
||||
'id': vpc.id,
|
||||
'cidr_block': vpc.cidr_block,
|
||||
'dhcp_options_id': vpc.dhcp_options_id,
|
||||
'region': vpc.region.name,
|
||||
'state': vpc.state,
|
||||
})
|
||||
|
||||
|
||||
def find_vpc(module, vpc_conn, vpc_id=None, cidr=None):
|
||||
"""
|
||||
Finds a VPC that matches a specific id or cidr + tags
|
||||
|
||||
module : AnsibleModule object
|
||||
vpc_conn: authenticated VPCConnection connection object
|
||||
|
||||
Returns:
|
||||
A VPC object that matches either an ID or CIDR and one or more tag values
|
||||
"""
|
||||
|
||||
if vpc_id is None and cidr is None:
|
||||
module.fail_json(
|
||||
msg='You must specify either a vpc_id or a cidr block + list of unique tags, aborting'
|
||||
)
|
||||
|
||||
found_vpcs = []
|
||||
|
||||
resource_tags = module.params.get('resource_tags')
|
||||
|
||||
# Check for existing VPC by cidr_block or id
|
||||
if vpc_id is not None:
|
||||
found_vpcs = vpc_conn.get_all_vpcs(None, {'vpc-id': vpc_id, 'state': 'available', })
|
||||
|
||||
else:
|
||||
previous_vpcs = vpc_conn.get_all_vpcs(None, {'cidr': cidr, 'state': 'available'})
|
||||
|
||||
for vpc in previous_vpcs:
|
||||
# Get all tags for each of the found VPCs
|
||||
vpc_tags = dict((t.name, t.value) for t in vpc_conn.get_all_tags(filters={'resource-id': vpc.id}))
|
||||
|
||||
# If the supplied list of ID Tags match a subset of the VPC Tags, we found our VPC
|
||||
if resource_tags and set(resource_tags.items()).issubset(set(vpc_tags.items())):
|
||||
found_vpcs.append(vpc)
|
||||
|
||||
found_vpc = None
|
||||
|
||||
if len(found_vpcs) == 1:
|
||||
found_vpc = found_vpcs[0]
|
||||
|
||||
if len(found_vpcs) > 1:
|
||||
module.fail_json(msg='Found more than one vpc based on the supplied criteria, aborting')
|
||||
|
||||
return (found_vpc)
|
||||
|
||||
|
||||
def routes_match(rt_list=None, rt=None, igw=None):
|
||||
"""
|
||||
Check if the route table has all routes as in given list
|
||||
|
||||
rt_list : A list if routes provided in the module
|
||||
rt : The Remote route table object
|
||||
igw : The internet gateway object for this vpc
|
||||
|
||||
Returns:
|
||||
True when there provided routes and remote routes are the same.
|
||||
False when provided routes and remote routes are different.
|
||||
"""
|
||||
|
||||
local_routes = []
|
||||
remote_routes = []
|
||||
for route in rt_list:
|
||||
route_kwargs = {
|
||||
'gateway_id': None,
|
||||
'instance_id': None,
|
||||
'interface_id': None,
|
||||
'vpc_peering_connection_id': None,
|
||||
'state': 'active'
|
||||
}
|
||||
if route['gw'] == 'igw':
|
||||
route_kwargs['gateway_id'] = igw.id
|
||||
elif route['gw'].startswith('i-'):
|
||||
route_kwargs['instance_id'] = route['gw']
|
||||
elif route['gw'].startswith('eni-'):
|
||||
route_kwargs['interface_id'] = route['gw']
|
||||
elif route['gw'].startswith('pcx-'):
|
||||
route_kwargs['vpc_peering_connection_id'] = route['gw']
|
||||
else:
|
||||
route_kwargs['gateway_id'] = route['gw']
|
||||
route_kwargs['destination_cidr_block'] = route['dest']
|
||||
local_routes.append(route_kwargs)
|
||||
for j in rt.routes:
|
||||
remote_routes.append(j.__dict__)
|
||||
match = []
|
||||
for i in local_routes:
|
||||
change = "false"
|
||||
for j in remote_routes:
|
||||
if set(i.items()).issubset(set(j.items())):
|
||||
change = "true"
|
||||
match.append(change)
|
||||
if 'false' in match:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def rtb_changed(route_tables=None, vpc_conn=None, module=None, vpc=None, igw=None):
|
||||
"""
|
||||
Checks if the remote routes match the local routes.
|
||||
|
||||
route_tables : Route_tables parameter in the module
|
||||
vpc_conn : The VPC connection object
|
||||
module : The module object
|
||||
vpc : The vpc object for this route table
|
||||
igw : The internet gateway object for this vpc
|
||||
|
||||
Returns:
|
||||
True when there is difference between the provided routes and remote routes and if subnet associations are different.
|
||||
False when both routes and subnet associations matched.
|
||||
|
||||
"""
|
||||
# We add a one for the main table
|
||||
rtb_len = len(route_tables) + 1
|
||||
remote_rtb_len = len(vpc_conn.get_all_route_tables(filters={'vpc_id': vpc.id}))
|
||||
if remote_rtb_len != rtb_len:
|
||||
return True
|
||||
for rt in route_tables:
|
||||
rt_id = None
|
||||
for sn in rt['subnets']:
|
||||
rsn = vpc_conn.get_all_subnets(filters={'cidr': sn, 'vpc_id': vpc.id})
|
||||
if len(rsn) != 1:
|
||||
module.fail_json(
|
||||
msg='The subnet {0} to associate with route_table {1} '
|
||||
'does not exist, aborting'.format(sn, rt)
|
||||
)
|
||||
nrt = vpc_conn.get_all_route_tables(filters={'vpc_id': vpc.id, 'association.subnet-id': rsn[0].id})
|
||||
if not nrt:
|
||||
return True
|
||||
else:
|
||||
nrt = nrt[0]
|
||||
if not rt_id:
|
||||
rt_id = nrt.id
|
||||
if not routes_match(rt['routes'], nrt, igw):
|
||||
return True
|
||||
continue
|
||||
else:
|
||||
if rt_id == nrt.id:
|
||||
continue
|
||||
else:
|
||||
return True
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def create_vpc(module, vpc_conn):
|
||||
"""
|
||||
Creates a new or modifies an existing VPC.
|
||||
|
||||
module : AnsibleModule object
|
||||
vpc_conn: authenticated VPCConnection connection object
|
||||
|
||||
Returns:
|
||||
A dictionary with information
|
||||
about the VPC and subnets that were launched
|
||||
"""
|
||||
|
||||
id = module.params.get('vpc_id')
|
||||
cidr_block = module.params.get('cidr_block')
|
||||
instance_tenancy = module.params.get('instance_tenancy')
|
||||
dns_support = module.params.get('dns_support')
|
||||
dns_hostnames = module.params.get('dns_hostnames')
|
||||
subnets = module.params.get('subnets')
|
||||
internet_gateway = module.params.get('internet_gateway')
|
||||
route_tables = module.params.get('route_tables')
|
||||
vpc_spec_tags = module.params.get('resource_tags')
|
||||
wait = module.params.get('wait')
|
||||
wait_timeout = int(module.params.get('wait_timeout'))
|
||||
changed = False
|
||||
|
||||
# Check for existing VPC by cidr_block + tags or id
|
||||
previous_vpc = find_vpc(module, vpc_conn, id, cidr_block)
|
||||
|
||||
if previous_vpc is not None:
|
||||
changed = False
|
||||
vpc = previous_vpc
|
||||
else:
|
||||
changed = True
|
||||
try:
|
||||
vpc = vpc_conn.create_vpc(cidr_block, instance_tenancy)
|
||||
# wait here until the vpc is available
|
||||
pending = True
|
||||
wait_timeout = time.time() + wait_timeout
|
||||
while wait and wait_timeout > time.time() and pending:
|
||||
try:
|
||||
pvpc = vpc_conn.get_all_vpcs(vpc.id)
|
||||
if hasattr(pvpc, 'state'):
|
||||
if pvpc.state == "available":
|
||||
pending = False
|
||||
elif hasattr(pvpc[0], 'state'):
|
||||
if pvpc[0].state == "available":
|
||||
pending = False
|
||||
# sometimes vpc_conn.create_vpc() will return a vpc that can't be found yet by vpc_conn.get_all_vpcs()
|
||||
# when that happens, just wait a bit longer and try again
|
||||
except boto.exception.BotoServerError as e:
|
||||
if e.error_code != 'InvalidVpcID.NotFound':
|
||||
raise
|
||||
if pending:
|
||||
time.sleep(5)
|
||||
if wait and wait_timeout <= time.time():
|
||||
# waiting took too long
|
||||
module.fail_json(msg="wait for vpc availability timeout on %s" % time.asctime())
|
||||
|
||||
except boto.exception.BotoServerError as e:
|
||||
module.fail_json(msg="%s: %s" % (e.error_code, e.error_message))
|
||||
|
||||
# Done with base VPC, now change to attributes and features.
|
||||
|
||||
# Add resource tags
|
||||
vpc_tags = dict((t.name, t.value) for t in vpc_conn.get_all_tags(filters={'resource-id': vpc.id}))
|
||||
|
||||
if not set(vpc_spec_tags.items()).issubset(set(vpc_tags.items())):
|
||||
new_tags = {}
|
||||
|
||||
for (key, value) in set(vpc_spec_tags.items()):
|
||||
if (key, value) not in set(vpc_tags.items()):
|
||||
new_tags[key] = value
|
||||
|
||||
if new_tags:
|
||||
vpc_conn.create_tags(vpc.id, new_tags)
|
||||
|
||||
# boto doesn't appear to have a way to determine the existing
|
||||
# value of the dns attributes, so we just set them.
|
||||
# It also must be done one at a time.
|
||||
vpc_conn.modify_vpc_attribute(vpc.id, enable_dns_support=dns_support)
|
||||
vpc_conn.modify_vpc_attribute(vpc.id, enable_dns_hostnames=dns_hostnames)
|
||||
|
||||
# Process all subnet properties
|
||||
if subnets is not None:
|
||||
if not isinstance(subnets, list):
|
||||
module.fail_json(msg='subnets needs to be a list of cidr blocks')
|
||||
|
||||
current_subnets = vpc_conn.get_all_subnets(filters={'vpc_id': vpc.id})
|
||||
|
||||
# First add all new subnets
|
||||
for subnet in subnets:
|
||||
add_subnet = True
|
||||
subnet_tags_current = True
|
||||
new_subnet_tags = subnet.get('resource_tags', {})
|
||||
subnet_tags_delete = []
|
||||
|
||||
for csn in current_subnets:
|
||||
if subnet['cidr'] == csn.cidr_block:
|
||||
add_subnet = False
|
||||
|
||||
# Check if AWS subnet tags are in playbook subnet tags
|
||||
existing_tags_subset_of_new_tags = (set(csn.tags.items()).issubset(set(new_subnet_tags.items())))
|
||||
# Check if subnet tags in playbook are in AWS subnet tags
|
||||
new_tags_subset_of_existing_tags = (set(new_subnet_tags.items()).issubset(set(csn.tags.items())))
|
||||
|
||||
if existing_tags_subset_of_new_tags is False:
|
||||
try:
|
||||
for item in csn.tags.items():
|
||||
if item not in new_subnet_tags.items():
|
||||
subnet_tags_delete.append(item)
|
||||
|
||||
subnet_tags_delete = [key[0] for key in subnet_tags_delete]
|
||||
delete_subnet_tag = vpc_conn.delete_tags(csn.id, subnet_tags_delete)
|
||||
changed = True
|
||||
except EC2ResponseError as e:
|
||||
module.fail_json(msg='Unable to delete resource tag, error {0}'.format(e))
|
||||
# Add new subnet tags if not current
|
||||
|
||||
if new_tags_subset_of_existing_tags is False:
|
||||
try:
|
||||
changed = True
|
||||
create_subnet_tag = vpc_conn.create_tags(csn.id, new_subnet_tags)
|
||||
|
||||
except EC2ResponseError as e:
|
||||
module.fail_json(msg='Unable to create resource tag, error: {0}'.format(e))
|
||||
|
||||
if add_subnet:
|
||||
try:
|
||||
new_subnet = vpc_conn.create_subnet(vpc.id, subnet['cidr'], subnet.get('az', None))
|
||||
new_subnet_tags = subnet.get('resource_tags', {})
|
||||
if new_subnet_tags:
|
||||
# Sometimes AWS takes its time to create a subnet and so using new subnets's id
|
||||
# to create tags results in exception.
|
||||
# boto doesn't seem to refresh 'state' of the newly created subnet, i.e.: it's always 'pending'
|
||||
# so i resorted to polling vpc_conn.get_all_subnets with the id of the newly added subnet
|
||||
while len(vpc_conn.get_all_subnets(filters={'subnet-id': new_subnet.id})) == 0:
|
||||
time.sleep(0.1)
|
||||
|
||||
vpc_conn.create_tags(new_subnet.id, new_subnet_tags)
|
||||
|
||||
changed = True
|
||||
except EC2ResponseError as e:
|
||||
module.fail_json(msg='Unable to create subnet {0}, error: {1}'.format(subnet['cidr'], e))
|
||||
|
||||
# Now delete all absent subnets
|
||||
for csubnet in current_subnets:
|
||||
delete_subnet = True
|
||||
for subnet in subnets:
|
||||
if csubnet.cidr_block == subnet['cidr']:
|
||||
delete_subnet = False
|
||||
if delete_subnet:
|
||||
try:
|
||||
vpc_conn.delete_subnet(csubnet.id)
|
||||
changed = True
|
||||
except EC2ResponseError as e:
|
||||
module.fail_json(msg='Unable to delete subnet {0}, error: {1}'.format(csubnet.cidr_block, e))
|
||||
|
||||
# Handle Internet gateway (create/delete igw)
|
||||
igw = None
|
||||
igw_id = None
|
||||
igws = vpc_conn.get_all_internet_gateways(filters={'attachment.vpc-id': vpc.id})
|
||||
if len(igws) > 1:
|
||||
module.fail_json(msg='EC2 returned more than one Internet Gateway for id %s, aborting' % vpc.id)
|
||||
if internet_gateway:
|
||||
if len(igws) != 1:
|
||||
try:
|
||||
igw = vpc_conn.create_internet_gateway()
|
||||
vpc_conn.attach_internet_gateway(igw.id, vpc.id)
|
||||
changed = True
|
||||
except EC2ResponseError as e:
|
||||
module.fail_json(msg='Unable to create Internet Gateway, error: {0}'.format(e))
|
||||
else:
|
||||
# Set igw variable to the current igw instance for use in route tables.
|
||||
igw = igws[0]
|
||||
else:
|
||||
if len(igws) > 0:
|
||||
try:
|
||||
vpc_conn.detach_internet_gateway(igws[0].id, vpc.id)
|
||||
vpc_conn.delete_internet_gateway(igws[0].id)
|
||||
changed = True
|
||||
except EC2ResponseError as e:
|
||||
module.fail_json(msg='Unable to delete Internet Gateway, error: {0}'.format(e))
|
||||
|
||||
if igw is not None:
|
||||
igw_id = igw.id
|
||||
|
||||
# Handle route tables - this may be worth splitting into a
|
||||
# different module but should work fine here. The strategy to stay
|
||||
# idempotent is to basically build all the route tables as
|
||||
# defined, track the route table ids, and then run through the
|
||||
# remote list of route tables and delete any that we didn't
|
||||
# create. This shouldn't interrupt traffic in theory, but is the
|
||||
# only way to really work with route tables over time that I can
|
||||
# think of without using painful aws ids. Hopefully boto will add
|
||||
# the replace-route-table API to make this smoother and
|
||||
# allow control of the 'main' routing table.
|
||||
if route_tables is not None:
|
||||
rtb_needs_change = rtb_changed(route_tables, vpc_conn, module, vpc, igw)
|
||||
if route_tables is not None and rtb_needs_change:
|
||||
if not isinstance(route_tables, list):
|
||||
module.fail_json(msg='route tables need to be a list of dictionaries')
|
||||
|
||||
# Work through each route table and update/create to match dictionary array
|
||||
all_route_tables = []
|
||||
for rt in route_tables:
|
||||
try:
|
||||
new_rt = vpc_conn.create_route_table(vpc.id)
|
||||
new_rt_tags = rt.get('resource_tags', None)
|
||||
if new_rt_tags:
|
||||
vpc_conn.create_tags(new_rt.id, new_rt_tags)
|
||||
for route in rt['routes']:
|
||||
route_kwargs = {}
|
||||
if route['gw'] == 'igw':
|
||||
if not internet_gateway:
|
||||
module.fail_json(
|
||||
msg='You asked for an Internet Gateway '
|
||||
'(igw) route, but you have no Internet Gateway'
|
||||
)
|
||||
route_kwargs['gateway_id'] = igw.id
|
||||
elif route['gw'].startswith('i-'):
|
||||
route_kwargs['instance_id'] = route['gw']
|
||||
elif route['gw'].startswith('eni-'):
|
||||
route_kwargs['interface_id'] = route['gw']
|
||||
elif route['gw'].startswith('pcx-'):
|
||||
route_kwargs['vpc_peering_connection_id'] = route['gw']
|
||||
else:
|
||||
route_kwargs['gateway_id'] = route['gw']
|
||||
vpc_conn.create_route(new_rt.id, route['dest'], **route_kwargs)
|
||||
|
||||
# Associate with subnets
|
||||
for sn in rt['subnets']:
|
||||
rsn = vpc_conn.get_all_subnets(filters={'cidr': sn, 'vpc_id': vpc.id})
|
||||
if len(rsn) != 1:
|
||||
module.fail_json(
|
||||
msg='The subnet {0} to associate with route_table {1} '
|
||||
'does not exist, aborting'.format(sn, rt)
|
||||
)
|
||||
rsn = rsn[0]
|
||||
|
||||
# Disassociate then associate since we don't have replace
|
||||
old_rt = vpc_conn.get_all_route_tables(
|
||||
filters={'association.subnet_id': rsn.id, 'vpc_id': vpc.id}
|
||||
)
|
||||
old_rt = [x for x in old_rt if x.id is not None]
|
||||
if len(old_rt) == 1:
|
||||
old_rt = old_rt[0]
|
||||
association_id = None
|
||||
for a in old_rt.associations:
|
||||
if a.subnet_id == rsn.id:
|
||||
association_id = a.id
|
||||
vpc_conn.disassociate_route_table(association_id)
|
||||
|
||||
vpc_conn.associate_route_table(new_rt.id, rsn.id)
|
||||
|
||||
all_route_tables.append(new_rt)
|
||||
changed = True
|
||||
except EC2ResponseError as e:
|
||||
module.fail_json(
|
||||
msg='Unable to create and associate route table {0}, error: '
|
||||
'{1}'.format(rt, e)
|
||||
)
|
||||
|
||||
# Now that we are good to go on our new route tables, delete the
|
||||
# old ones except the 'main' route table as boto can't set the main
|
||||
# table yet.
|
||||
all_rts = vpc_conn.get_all_route_tables(filters={'vpc-id': vpc.id})
|
||||
for rt in all_rts:
|
||||
if rt.id is None:
|
||||
continue
|
||||
delete_rt = True
|
||||
for newrt in all_route_tables:
|
||||
if newrt.id == rt.id:
|
||||
delete_rt = False
|
||||
break
|
||||
if delete_rt:
|
||||
rta = rt.associations
|
||||
is_main = False
|
||||
for a in rta:
|
||||
if a.main:
|
||||
is_main = True
|
||||
break
|
||||
try:
|
||||
if not is_main:
|
||||
vpc_conn.delete_route_table(rt.id)
|
||||
changed = True
|
||||
except EC2ResponseError as e:
|
||||
module.fail_json(msg='Unable to delete old route table {0}, error: {1}'.format(rt.id, e))
|
||||
|
||||
vpc_dict = get_vpc_info(vpc)
|
||||
|
||||
created_vpc_id = vpc.id
|
||||
returned_subnets = []
|
||||
current_subnets = vpc_conn.get_all_subnets(filters={'vpc_id': vpc.id})
|
||||
|
||||
for sn in current_subnets:
|
||||
returned_subnets.append({
|
||||
'resource_tags': dict((t.name, t.value) for t in vpc_conn.get_all_tags(filters={'resource-id': sn.id})),
|
||||
'cidr': sn.cidr_block,
|
||||
'az': sn.availability_zone,
|
||||
'id': sn.id,
|
||||
})
|
||||
|
||||
if subnets is not None:
|
||||
# Sort subnets by the order they were listed in the play
|
||||
order = {}
|
||||
for idx, val in enumerate(subnets):
|
||||
order[val['cidr']] = idx
|
||||
|
||||
# Number of subnets in the play
|
||||
subnets_in_play = len(subnets)
|
||||
returned_subnets.sort(key=lambda x: order.get(x['cidr'], subnets_in_play))
|
||||
|
||||
return (vpc_dict, created_vpc_id, returned_subnets, igw_id, changed)
|
||||
|
||||
|
||||
def terminate_vpc(module, vpc_conn, vpc_id=None, cidr=None):
|
||||
"""
|
||||
Terminates a VPC
|
||||
|
||||
module: Ansible module object
|
||||
vpc_conn: authenticated VPCConnection connection object
|
||||
vpc_id: a vpc id to terminate
|
||||
cidr: The cidr block of the VPC - can be used in lieu of an ID
|
||||
|
||||
Returns a dictionary of VPC information
|
||||
about the VPC terminated.
|
||||
|
||||
If the VPC to be terminated is available
|
||||
"changed" will be set to True.
|
||||
|
||||
"""
|
||||
vpc_dict = {}
|
||||
terminated_vpc_id = ''
|
||||
changed = False
|
||||
|
||||
vpc = find_vpc(module, vpc_conn, vpc_id, cidr)
|
||||
|
||||
if vpc is not None:
|
||||
if vpc.state == 'available':
|
||||
terminated_vpc_id = vpc.id
|
||||
vpc_dict = get_vpc_info(vpc)
|
||||
try:
|
||||
subnets = vpc_conn.get_all_subnets(filters={'vpc_id': vpc.id})
|
||||
for sn in subnets:
|
||||
vpc_conn.delete_subnet(sn.id)
|
||||
|
||||
igws = vpc_conn.get_all_internet_gateways(
|
||||
filters={'attachment.vpc-id': vpc.id}
|
||||
)
|
||||
for igw in igws:
|
||||
vpc_conn.detach_internet_gateway(igw.id, vpc.id)
|
||||
vpc_conn.delete_internet_gateway(igw.id)
|
||||
|
||||
rts = vpc_conn.get_all_route_tables(filters={'vpc_id': vpc.id})
|
||||
for rt in rts:
|
||||
rta = rt.associations
|
||||
is_main = False
|
||||
for a in rta:
|
||||
if a.main:
|
||||
is_main = True
|
||||
if not is_main:
|
||||
vpc_conn.delete_route_table(rt.id)
|
||||
|
||||
vpc_conn.delete_vpc(vpc.id)
|
||||
except EC2ResponseError as e:
|
||||
module.fail_json(
|
||||
msg='Unable to delete VPC {0}, error: {1}'.format(vpc.id, e)
|
||||
)
|
||||
changed = True
|
||||
vpc_dict['state'] = "terminated"
|
||||
|
||||
return (changed, vpc_dict, terminated_vpc_id)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ec2_argument_spec()
|
||||
argument_spec.update(dict(
|
||||
cidr_block=dict(),
|
||||
instance_tenancy=dict(choices=['default', 'dedicated'], default='default'),
|
||||
wait=dict(type='bool', default=False),
|
||||
wait_timeout=dict(default=300),
|
||||
dns_support=dict(type='bool', default=True),
|
||||
dns_hostnames=dict(type='bool', default=True),
|
||||
subnets=dict(type='list'),
|
||||
vpc_id=dict(),
|
||||
internet_gateway=dict(type='bool', default=False),
|
||||
resource_tags=dict(type='dict', required=True),
|
||||
route_tables=dict(type='list'),
|
||||
state=dict(choices=['present', 'absent'], default='present'),
|
||||
)
|
||||
)
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
)
|
||||
|
||||
if not HAS_BOTO:
|
||||
module.fail_json(msg='boto required for this module')
|
||||
|
||||
state = module.params.get('state')
|
||||
|
||||
region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module)
|
||||
|
||||
# If we have a region specified, connect to its endpoint.
|
||||
if region:
|
||||
try:
|
||||
vpc_conn = connect_to_aws(boto.vpc, region, **aws_connect_kwargs)
|
||||
except boto.exception.NoAuthHandlerFound as e:
|
||||
module.fail_json(msg=str(e))
|
||||
else:
|
||||
module.fail_json(msg="region must be specified")
|
||||
|
||||
igw_id = None
|
||||
if module.params.get('state') == 'absent':
|
||||
vpc_id = module.params.get('vpc_id')
|
||||
cidr = module.params.get('cidr_block')
|
||||
(changed, vpc_dict, new_vpc_id) = terminate_vpc(module, vpc_conn, vpc_id, cidr)
|
||||
subnets_changed = None
|
||||
elif module.params.get('state') == 'present':
|
||||
# Changed is always set to true when provisioning a new VPC
|
||||
(vpc_dict, new_vpc_id, subnets_changed, igw_id, changed) = create_vpc(module, vpc_conn)
|
||||
|
||||
module.exit_json(changed=changed, vpc_id=new_vpc_id, vpc=vpc_dict, igw_id=igw_id, subnets=subnets_changed)
|
||||
|
||||
from ansible.module_utils.common.removed import removed_module
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
removed_module()
|
||||
|
||||
@@ -19,7 +19,10 @@ short_description: create or terminate a virtual machine in azure
|
||||
description:
|
||||
- Creates or terminates azure instances. When created optionally waits for it to be 'running'.
|
||||
version_added: "1.7"
|
||||
deprecated: "Use M(azure_rm_virtualmachine) instead."
|
||||
deprecated:
|
||||
removed_in: "2.8"
|
||||
why: Replaced with various dedicated Azure modules.
|
||||
alternative: M(azure_rm_virtualmachine)
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
|
||||
@@ -20,7 +20,10 @@ description:
|
||||
version_added: "2.3"
|
||||
author:
|
||||
- René Moser (@resmo)
|
||||
deprecated: Deprecated in 2.4. Use M(cs_instance_nic_secondaryip) instead.
|
||||
deprecated:
|
||||
removed_in: "2.8"
|
||||
why: New module created.
|
||||
alternative: Use M(cs_instance_nic_secondaryip) instead.
|
||||
options:
|
||||
vm:
|
||||
description:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,7 +14,10 @@ DOCUMENTATION = '''
|
||||
---
|
||||
module: kubernetes
|
||||
version_added: "2.1"
|
||||
deprecated: In 2.5 use M(k8s_raw) instead.
|
||||
deprecated:
|
||||
removed_in: "2.9"
|
||||
why: This module used the oc command line tool, where as M(k8s_raw) goes over the REST API.
|
||||
alternative: Use M(k8s_raw) instead.
|
||||
short_description: Manage Kubernetes resources
|
||||
description:
|
||||
- This module can manage Kubernetes resources on an existing cluster using
|
||||
|
||||
@@ -17,7 +17,10 @@ ANSIBLE_METADATA = {
|
||||
DOCUMENTATION = """
|
||||
author:
|
||||
- "Kenneth D. Evensen (@kevensen)"
|
||||
deprecated: In 2.5 use M(openshift_raw) instead.
|
||||
deprecated:
|
||||
removed_in: "2.9"
|
||||
why: This module used the oc command line tool, where as M(openshift_raw) goes over the REST API.
|
||||
alternative: Use M(openshift_raw) instead.
|
||||
description:
|
||||
- This module allows management of resources in an OpenShift cluster. The
|
||||
inventory host can be any host with network connectivity to the OpenShift
|
||||
|
||||
@@ -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)
|
||||
@@ -16,11 +16,14 @@ ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: accelerate
|
||||
removed: True
|
||||
short_description: Enable accelerated mode on remote node
|
||||
deprecated: "Use SSH with ControlPersist instead."
|
||||
deprecated:
|
||||
removed_in: "2.4"
|
||||
why: Replaced by ControlPersist
|
||||
alternative: Use SSH with ControlPersist instead.
|
||||
removed: True
|
||||
description:
|
||||
- This module has been removed, this file is kept for historicaly documentation purposes
|
||||
- This module has been removed, this file is kept for historical documentation purposes.
|
||||
- This modules launches an ephemeral I(accelerate) daemon on the remote node which
|
||||
Ansible can use to communicate with nodes at high speed.
|
||||
- The daemon listens on a configurable port for a configurable amount of time.
|
||||
@@ -77,3 +80,8 @@ EXAMPLES = '''
|
||||
tasks:
|
||||
- command: /usr/bin/anything
|
||||
'''
|
||||
|
||||
from ansible.module_utils.common.removed import removed_module
|
||||
|
||||
if __name__ == '__main__':
|
||||
removed_module()
|
||||
|
||||
@@ -20,8 +20,9 @@ author: Ansible Core Team (@ansible)
|
||||
module: include
|
||||
short_description: Include a play or task list
|
||||
deprecated:
|
||||
The include action was too confusing, dealing with both plays and tasks, being both dynamic and static. This module
|
||||
will be removed in version 2.8. As alternatives use M(include_tasks), M(import_playbook), M(import_tasks).
|
||||
removed_in: "2.8"
|
||||
why: The include action was too confusing, dealing with both plays and tasks, being both dynamic and static. This module will be removed in version 2.8.
|
||||
alternative: Use M(include_tasks), M(import_playbook), M(import_tasks).
|
||||
description:
|
||||
- Includes a file with a list of plays or tasks to be executed in the current playbook.
|
||||
- Files with a list of plays can only be included at the top level. Lists of tasks can only be included where tasks
|
||||
|
||||
@@ -29,7 +29,10 @@ DOCUMENTATION = r'''
|
||||
---
|
||||
module: win_msi
|
||||
version_added: '1.7'
|
||||
deprecated: In 2.4 and will be removed in 2.8, use M(win_package) instead.
|
||||
deprecated:
|
||||
removed_in: "2.8"
|
||||
why: The win_msi module has a number of issues, the M(win_package) module is easier to maintain and use.
|
||||
alternative: Use M(win_package) instead.
|
||||
short_description: Installs and uninstalls Windows MSI files
|
||||
description:
|
||||
- Installs or uninstalls a Windows MSI file that is already located on the
|
||||
|
||||
Reference in New Issue
Block a user