summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cloud/amazon/ec2.py75
-rw-r--r--cloud/amazon/ec2_asg.py2
-rw-r--r--cloud/docker/docker_service.py12
-rw-r--r--cloud/openstack/os_network.py8
-rw-r--r--files/assemble.py45
-rw-r--r--files/unarchive.py40
-rw-r--r--network/dellos10/dellos10_template.py180
-rw-r--r--network/dellos6/dellos6_template.py174
-rwxr-xr-xnetwork/dellos9/dellos9_template.py186
-rw-r--r--network/eos/_eos_template.py (renamed from network/eos/eos_template.py)1
-rw-r--r--network/eos/eos_command.py16
-rw-r--r--network/eos/eos_facts.py19
-rw-r--r--network/ios/_ios_template.py (renamed from network/ios/ios_template.py)4
-rw-r--r--network/ios/ios_command.py8
-rw-r--r--network/ios/ios_facts.py20
-rw-r--r--network/iosxr/_iosxr_template.py (renamed from network/iosxr/iosxr_template.py)1
-rw-r--r--network/iosxr/iosxr_command.py8
-rw-r--r--network/iosxr/iosxr_facts.py20
-rw-r--r--network/junos/_junos_template.py (renamed from network/junos/junos_template.py)35
-rw-r--r--network/junos/junos_command.py35
-rw-r--r--network/junos/junos_config.py48
-rw-r--r--network/junos/junos_package.py2
-rw-r--r--network/nxos/_nxos_template.py (renamed from network/nxos/nxos_template.py)1
-rw-r--r--network/nxos/nxos_aaa_server.py6
-rw-r--r--network/nxos/nxos_acl.py13
-rw-r--r--network/nxos/nxos_acl_interface.py11
-rw-r--r--network/nxos/nxos_command.py10
-rw-r--r--network/nxos/nxos_feature.py9
-rw-r--r--network/nxos/nxos_hsrp.py17
-rw-r--r--network/nxos/nxos_igmp_interface.py15
-rw-r--r--network/nxos/nxos_igmp_snooping.py2
-rw-r--r--network/nxos/nxos_install_os.py12
-rw-r--r--network/nxos/nxos_interface.py20
-rw-r--r--network/nxos/nxos_ip_interface.py13
-rw-r--r--network/nxos/nxos_mtu.py9
-rw-r--r--network/nxos/nxos_ntp.py9
-rw-r--r--network/nxos/nxos_ntp_auth.py9
-rw-r--r--network/nxos/nxos_ntp_options.py9
-rw-r--r--network/nxos/nxos_pim_interface.py9
-rw-r--r--network/nxos/nxos_portchannel.py7
-rw-r--r--network/nxos/nxos_smu.py20
-rw-r--r--network/nxos/nxos_snmp_community.py9
-rw-r--r--network/nxos/nxos_snmp_contact.py9
-rw-r--r--network/nxos/nxos_snmp_host.py9
-rw-r--r--network/nxos/nxos_snmp_location.py9
-rw-r--r--network/nxos/nxos_snmp_traps.py9
-rw-r--r--network/nxos/nxos_snmp_user.py9
-rw-r--r--network/nxos/nxos_switchport.py9
-rw-r--r--network/nxos/nxos_udld.py9
-rw-r--r--network/nxos/nxos_udld_interface.py9
-rw-r--r--network/nxos/nxos_vlan.py16
-rw-r--r--network/nxos/nxos_vpc.py13
-rw-r--r--network/nxos/nxos_vpc_interface.py13
-rw-r--r--network/nxos/nxos_vrf.py15
-rw-r--r--network/nxos/nxos_vrf_interface.py15
-rw-r--r--network/nxos/nxos_vrrp.py13
-rw-r--r--network/nxos/nxos_vtp_domain.py9
-rw-r--r--network/nxos/nxos_vtp_password.py9
-rw-r--r--network/nxos/nxos_vtp_version.py9
-rw-r--r--network/openswitch/_ops_template.py (renamed from network/openswitch/ops_template.py)6
-rw-r--r--network/openswitch/ops_command.py9
-rw-r--r--network/openswitch/ops_facts.py11
-rw-r--r--network/vyos/vyos_command.py8
-rw-r--r--network/vyos/vyos_config.py9
-rw-r--r--network/vyos/vyos_facts.py16
-rw-r--r--packaging/os/apt.py112
-rw-r--r--shippable.yml14
-rw-r--r--system/mount.py199
-rw-r--r--utilities/logic/async_wrapper.py64
-rw-r--r--utilities/logic/include.py59
-rw-r--r--windows/async_wrapper.ps14
-rw-r--r--windows/setup.ps15
72 files changed, 943 insertions, 906 deletions
diff --git a/cloud/amazon/ec2.py b/cloud/amazon/ec2.py
index 3bc6e4d3..b6f30b25 100644
--- a/cloud/amazon/ec2.py
+++ b/cloud/amazon/ec2.py
@@ -796,6 +796,63 @@ def boto_supports_param_in_spot_request(ec2, param):
method = getattr(ec2, 'request_spot_instances')
return param in method.func_code.co_varnames
+def await_spot_requests(module, ec2, spot_requests, count):
+ """
+ Wait for a group of spot requests to be fulfilled, or fail.
+
+ module: Ansible module object
+ ec2: authenticated ec2 connection object
+ spot_requests: boto.ec2.spotinstancerequest.SpotInstanceRequest object returned by ec2.request_spot_instances
+ count: Total number of instances to be created by the spot requests
+
+ Returns:
+ list of instance ID's created by the spot request(s)
+ """
+ spot_wait_timeout = int(module.params.get('spot_wait_timeout'))
+ wait_complete = time.time() + spot_wait_timeout
+
+ spot_req_inst_ids = dict()
+ while time.time() < wait_complete:
+ reqs = ec2.get_all_spot_instance_requests()
+ for sirb in spot_requests:
+ if sirb.id in spot_req_inst_ids:
+ continue
+ for sir in reqs:
+ if sir.id != sirb.id:
+ continue # this is not our spot instance
+ if sir.instance_id is not None:
+ spot_req_inst_ids[sirb.id] = sir.instance_id
+ elif sir.state == 'open':
+ continue # still waiting, nothing to do here
+ elif sir.state == 'active':
+ continue # Instance is created already, nothing to do here
+ elif sir.state == 'failed':
+ module.fail_json(msg="Spot instance request %s failed with status %s and fault %s:%s" % (
+ sir.id, sir.status.code, sir.fault.code, sir.fault.message))
+ elif sir.state == 'cancelled':
+ module.fail_json(msg="Spot instance request %s was cancelled before it could be fulfilled." % sir.id)
+ elif sir.state == 'closed':
+ # instance is terminating or marked for termination
+ # this may be intentional on the part of the operator,
+ # or it may have been terminated by AWS due to capacity,
+ # price, or group constraints in this case, we'll fail
+ # the module if the reason for the state is anything
+ # other than termination by user. Codes are documented at
+ # http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/spot-bid-status.html
+ if sir.status.code == 'instance-terminated-by-user':
+ # do nothing, since the user likely did this on purpose
+ pass
+ else:
+ spot_msg = "Spot instance request %s was closed by AWS with the status %s and fault %s:%s"
+ module.fail_json(msg=spot_msg % (sir.id, sir.status.code, sir.fault.code, sir.fault.message))
+
+ if len(spot_req_inst_ids) < count:
+ time.sleep(5)
+ else:
+ return spot_req_inst_ids.values()
+ module.fail_json(msg = "wait for spot requests timeout on %s" % time.asctime())
+
+
def enforce_count(module, ec2, vpc):
exact_count = module.params.get('exact_count')
@@ -1106,23 +1163,7 @@ def create_instances(module, ec2, vpc, override_count=None):
# Now we have to do the intermediate waiting
if wait:
- spot_req_inst_ids = dict()
- spot_wait_timeout = time.time() + spot_wait_timeout
- while spot_wait_timeout > time.time():
- reqs = ec2.get_all_spot_instance_requests()
- for sirb in res:
- if sirb.id in spot_req_inst_ids:
- continue
- for sir in reqs:
- if sir.id == sirb.id and sir.instance_id is not None:
- spot_req_inst_ids[sirb.id] = sir.instance_id
- if len(spot_req_inst_ids) < count:
- time.sleep(5)
- else:
- break
- if spot_wait_timeout <= time.time():
- module.fail_json(msg = "wait for spot requests timeout on %s" % time.asctime())
- instids = spot_req_inst_ids.values()
+ instids = await_spot_requests(module, ec2, res, count)
except boto.exception.BotoServerError as e:
module.fail_json(msg = "Instance creation failed => %s: %s" % (e.error_code, e.error_message))
diff --git a/cloud/amazon/ec2_asg.py b/cloud/amazon/ec2_asg.py
index d9ed2295..c63ebdef 100644
--- a/cloud/amazon/ec2_asg.py
+++ b/cloud/amazon/ec2_asg.py
@@ -778,7 +778,7 @@ def wait_for_term_inst(connection, module, term_instances):
lifecycle = instance_facts[i]['lifecycle_state']
health = instance_facts[i]['health_status']
log.debug("Instance {0} has state of {1},{2}".format(i,lifecycle,health ))
- if lifecycle == 'Terminating' or healthy == 'Unhealthy':
+ if lifecycle == 'Terminating' or health == 'Unhealthy':
count += 1
time.sleep(10)
diff --git a/cloud/docker/docker_service.py b/cloud/docker/docker_service.py
index db4352c9..3e7d4335 100644
--- a/cloud/docker/docker_service.py
+++ b/cloud/docker/docker_service.py
@@ -466,9 +466,11 @@ try:
from compose.cli.command import project_from_options
from compose.service import ConvergenceStrategy
from compose.cli.main import convergence_strategy_from_opts, build_action_from_opts, image_type_from_opt
+ from compose.const import DEFAULT_TIMEOUT
except ImportError as exc:
HAS_COMPOSE = False
HAS_COMPOSE_EXC = str(exc)
+ DEFAULT_TIMEOUT = 10
from ansible.module_utils.docker_common import *
@@ -653,7 +655,8 @@ class ContainerManager(DockerBaseClass):
strategy=converge,
do_build=do_build,
detached=detached,
- remove_orphans=self.remove_orphans)
+ remove_orphans=self.remove_orphans,
+ timeout=self.timeout)
except Exception as exc:
self.client.fail("Error starting project - %s" % str(exc))
@@ -828,7 +831,7 @@ class ContainerManager(DockerBaseClass):
if not self.check_mode and result['changed']:
try:
- self.project.stop(service_names=service_names)
+ self.project.stop(service_names=service_names, timeout=self.timeout)
except Exception as exc:
self.client.fail("Error stopping services for %s - %s" % (self.project.name, str(exc)))
@@ -855,7 +858,7 @@ class ContainerManager(DockerBaseClass):
if not self.check_mode and result['changed']:
try:
- self.project.restart(service_names=service_names)
+ self.project.restart(service_names=service_names, timeout=self.timeout)
except Exception as exc:
self.client.fail("Error restarting services for %s - %s" % (self.project.name, str(exc)))
@@ -903,7 +906,8 @@ def main():
dependencies=dict(type='bool', default=True),
pull=dict(type='bool', default=False),
nocache=dict(type='bool', default=False),
- debug=dict(type='bool', default=False)
+ debug=dict(type='bool', default=False),
+ timeout=dict(type='int', default=DEFAULT_TIMEOUT)
)
mutually_exclusive = [
diff --git a/cloud/openstack/os_network.py b/cloud/openstack/os_network.py
index 9a0c2516..d80267e8 100644
--- a/cloud/openstack/os_network.py
+++ b/cloud/openstack/os_network.py
@@ -222,8 +222,12 @@ def main():
if provider and StrictVersion(shade.__version__) < StrictVersion('1.5.0'):
module.fail_json(msg="Shade >= 1.5.0 required to use provider options")
- net = cloud.create_network(name, shared, admin_state_up,
- external, provider, project_id)
+ if project_id is not None:
+ net = cloud.create_network(name, shared, admin_state_up,
+ external, provider, project_id)
+ else:
+ net = cloud.create_network(name, shared, admin_state_up,
+ external, provider)
changed = True
else:
changed = False
diff --git a/files/assemble.py b/files/assemble.py
index 6eea02e5..39edbdd3 100644
--- a/files/assemble.py
+++ b/files/assemble.py
@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
# (c) 2012, Stephen Fromm <sfromm@gmail.com>
+# (c) 2016, Toshio Kuratomi <tkuratomi@ansible.com>
#
# This file is part of Ansible
#
@@ -18,11 +19,6 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-import os
-import os.path
-import tempfile
-import re
-
DOCUMENTATION = '''
---
module: assemble
@@ -108,42 +104,53 @@ EXAMPLES = '''
- assemble: src=/etc/ssh/conf.d/ dest=/etc/ssh/sshd_config validate='/usr/sbin/sshd -t -f %s'
'''
+import codecs
+import os
+import os.path
+import re
+import tempfile
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.pycompat24 import get_exception
+from ansible.module_utils.six import b
+
+
# ===========================================
# Support method
def assemble_from_fragments(src_path, delimiter=None, compiled_regexp=None, ignore_hidden=False):
''' assemble a file from a directory of fragments '''
tmpfd, temp_path = tempfile.mkstemp()
- tmp = os.fdopen(tmpfd,'w')
+ tmp = os.fdopen(tmpfd, 'wb')
delimit_me = False
add_newline = False
for f in sorted(os.listdir(src_path)):
if compiled_regexp and not compiled_regexp.search(f):
continue
- fragment = "%s/%s" % (src_path, f)
+ fragment = u"%s/%s" % (src_path, f)
if not os.path.isfile(fragment) or (ignore_hidden and os.path.basename(fragment).startswith('.')):
continue
- fragment_content = file(fragment).read()
+ fragment_content = open(fragment, 'rb').read()
# always put a newline between fragments if the previous fragment didn't end with a newline.
if add_newline:
- tmp.write('\n')
+ tmp.write(b('\n'))
# delimiters should only appear between fragments
if delimit_me:
if delimiter:
# un-escape anything like newlines
- delimiter = delimiter.decode('unicode-escape')
+ delimiter = codecs.escape_decode(delimiter)[0]
tmp.write(delimiter)
# always make sure there's a newline after the
# delimiter, so lines don't run together
- if delimiter[-1] != '\n':
- tmp.write('\n')
+ if delimiter[-1] != b('\n'):
+ tmp.write(b('\n'))
tmp.write(fragment_content)
delimit_me = True
- if fragment_content.endswith('\n'):
+ if fragment_content.endswith(b('\n')):
add_newline = False
else:
add_newline = True
@@ -151,6 +158,7 @@ def assemble_from_fragments(src_path, delimiter=None, compiled_regexp=None, igno
tmp.close()
return temp_path
+
def cleanup(path, result=None):
# cleanup just in case
if os.path.exists(path):
@@ -162,8 +170,6 @@ def cleanup(path, result=None):
if result is not None:
result['warnings'] = ['Unable to remove temp file (%s): %s' % (path, str(e))]
-# ==============================================================
-# main
def main():
@@ -201,7 +207,7 @@ def main():
if not os.path.isdir(src):
module.fail_json(msg="Source (%s) is not a directory" % src)
- if regexp != None:
+ if regexp is not None:
try:
compiled_regexp = re.compile(regexp)
except re.error:
@@ -248,8 +254,5 @@ def main():
result['msg'] = "OK"
module.exit_json(**result)
-# import module snippets
-from ansible.module_utils.basic import *
-
-main()
-
+if __name__ == '__main__':
+ main()
diff --git a/files/unarchive.py b/files/unarchive.py
index 0eb8e49e..8af1c410 100644
--- a/files/unarchive.py
+++ b/files/unarchive.py
@@ -558,12 +558,12 @@ class ZipArchive(object):
def can_handle_archive(self):
if not self.cmd_path:
- return False
+ return False, 'Command "unzip" not found.'
cmd = [ self.cmd_path, '-l', self.src ]
rc, out, err = self.module.run_command(cmd)
if rc == 0:
- return True
- return False
+ return True, None
+ return False, 'Command "%s" could not handle archive.' % self.cmd_path
# class to handle gzipped tar files
@@ -586,6 +586,21 @@ class TgzArchive(object):
self.zipflag = '-z'
self._files_in_archive = []
+ if self.cmd_path:
+ self.tar_type = self._get_tar_type()
+ else:
+ self.tar_type = None
+
+ def _get_tar_type(self):
+ cmd = [self.cmd_path, '--version']
+ (rc, out, err) = self.module.run_command(cmd)
+ tar_type = None
+ if out.startswith('bsdtar'):
+ tar_type = 'bsd'
+ elif out.startswith('tar') and 'GNU' in out:
+ tar_type = 'gnu'
+ return tar_type
+
@property
def files_in_archive(self, force_refresh=False):
if self._files_in_archive and not force_refresh:
@@ -678,16 +693,19 @@ class TgzArchive(object):
def can_handle_archive(self):
if not self.cmd_path:
- return False
+ return False, 'Commands "gtar" and "tar" not found.'
+
+ if self.tar_type != 'gnu':
+ return False, 'Command "%s" detected as tar type %s. GNU tar required.' % (self.cmd_path, self.tar_type)
try:
if self.files_in_archive:
- return True
+ return True, None
except UnarchiveError:
- pass
+ return False, 'Command "%s" could not handle archive.' % self.cmd_path
# Errors and no files in archive assume that we weren't able to
# properly unarchive it
- return False
+ return False, 'Command "%s" found no files in archive.' % self.cmd_path
# class to handle tar files that aren't compressed
@@ -715,11 +733,15 @@ class TarXzArchive(TgzArchive):
# try handlers in order and return the one that works or bail if none work
def pick_handler(src, dest, file_args, module):
handlers = [ZipArchive, TgzArchive, TarArchive, TarBzipArchive, TarXzArchive]
+ reasons = set()
for handler in handlers:
obj = handler(src, dest, file_args, module)
- if obj.can_handle_archive():
+ (can_handle, reason) = obj.can_handle_archive()
+ if can_handle:
return obj
- module.fail_json(msg='Failed to find handler for "%s". Make sure the required command to extract the file is installed.' % src)
+ reasons.add(reason)
+ reason_msg = ' '.join(reasons)
+ module.fail_json(msg='Failed to find handler for "%s". Make sure the required command to extract the file is installed. %s' % (src, reason_msg))
def main():
diff --git a/network/dellos10/dellos10_template.py b/network/dellos10/dellos10_template.py
deleted file mode 100644
index b9bec6a3..00000000
--- a/network/dellos10/dellos10_template.py
+++ /dev/null
@@ -1,180 +0,0 @@
-#!/usr/bin/python
-#
-# (c) 2015 Peter Sprygada, <psprygada@ansible.com>
-#
-# Copyright (c) 2016 Dell Inc.
-#
-# This file is part of Ansible
-#
-# Ansible is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Ansible is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-#
-DOCUMENTATION = """
----
-module: dellos10_template
-version_added: "2.2"
-author: "Senthil Kumar Ganesan (@skg-net)"
-short_description: Manage Dell OS10 device configurations over SSH.
-description:
- - Manages Dell OS10 network device configurations over SSH. This module
- allows implementors to work with the device running-config. It
- provides a way to push a set of commands onto a network device
- by evaluating the current running-config and only pushing configuration
- commands that are not already configured. The config source can
- be a set of commands or a template.
-extends_documentation_fragment: dellos10
-options:
- src:
- description:
- - The path to the config source. The source can be either a
- file with config or a template that will be merged during
- runtime. By default the task will first search for the source
- file in role or playbook root folder in templates unless a full
- path to the file is given.
- required: true
- force:
- description:
- - The force argument instructs the module not to consider the
- current device running-config. When set to true, this will
- cause the module to push the contents of I(src) into the device
- without first checking if already configured. This argument is
- mutually exclusive with I(config).
- required: false
- default: false
- choices: [ "true", "false" ]
- backup:
- description:
- - When this argument is configured true, the module will backup
- the running-config from the node prior to making any changes.
- The backup file will be written to backup_{{ hostname }} in
- the root of the playbook directory. This argument is
- mutually exclusive with I(config).
-
- required: false
- default: false
- choices: [ "true", "false" ]
- config:
- description:
- - The module, by default, will connect to the remote device and
- retrieve the current running-config to use as a base for comparing
- against the contents of source. There are times when it is not
- desirable to have the task get the current running-config for
- every task. The I(config) argument allows the implementer to
- pass in the configuration to use as the base config for
- comparison. This argument is mutually exclusive with
- I(force) and I(backup).
-
- required: false
- default: null
-"""
-
-EXAMPLES = """
-- name: push a configuration onto the device
- dellos10_template:
- host: hostname
- username: foo
- src: config.j2
-
-- name: forceable push a configuration onto the device
- dellos10_template:
- host: hostname
- username: foo
- src: config.j2
- force: yes
-
-- name: provide the base configuration for comparison
- dellos10_template:
- host: hostname
- username: foo
- src: candidate_config.txt
- config: current_config.txt
-"""
-
-RETURN = """
-updates:
- description: The set of commands that will be pushed to the remote device
- returned: always
- type: list
- sample: ['...', '...']
-
-_backup:
- description: The current running config of the remote device.
- returned: when running config is present in the remote device.
- type: list
- sample: ['...', '...']
-
-responses:
- description: The set of responses from issuing the commands on the device
- returned: when not check_mode
- type: list
- sample: ['...', '...']
-"""
-from ansible.module_utils.netcfg import NetworkConfig, dumps
-from ansible.module_utils.network import NetworkModule
-import ansible.module_utils.dellos10
-
-
-def get_config(module):
- config = module.params['config'] or dict()
- if not config and not module.params['force']:
- config = module.config.get_config()
- return config
-
-
-def main():
- """ main entry point for module execution
- """
-
- argument_spec = dict(
- src=dict(),
- force=dict(default=False, type='bool'),
- backup=dict(default=False, type='bool'),
- config=dict(),
- )
-
- mutually_exclusive = [('config', 'backup'), ('config', 'force')]
-
- module = NetworkModule(argument_spec=argument_spec,
- mutually_exclusive=mutually_exclusive,
- supports_check_mode=True)
-
- result = dict(changed=False)
-
- candidate = NetworkConfig(contents=module.params['src'], indent=1)
-
-
- contents = get_config(module)
-
- if contents:
- config = NetworkConfig(contents=contents[0], indent=1)
- result['_backup'] = contents[0]
-
- commands = list()
- if not module.params['force']:
- commands = dumps(candidate.difference(config), 'commands')
- else:
- commands = str(candidate)
-
- if commands:
- commands = commands.split('\n')
- if not module.check_mode:
- response = module.config(commands)
- result['responses'] = response
- result['changed'] = True
-
- result['updates'] = commands
- module.exit_json(**result)
-
-
-if __name__ == '__main__':
- main()
diff --git a/network/dellos6/dellos6_template.py b/network/dellos6/dellos6_template.py
deleted file mode 100644
index a1209b46..00000000
--- a/network/dellos6/dellos6_template.py
+++ /dev/null
@@ -1,174 +0,0 @@
-#!/usr/bin/python
-#
-# (c) 2015 Peter Sprygada, <psprygada@ansible.com>
-#
-# Copyright (c) 2016 Dell Inc.
-#
-# This file is part of Ansible
-#
-# Ansible is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Ansible is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-#
-DOCUMENTATION = """
----
-module: dellos6_template
-version_added: "2.2"
-author: "Dhivya P (@dhivyap)"
-short_description: Manage Dell OS6 device configurations over SSH.
-description:
- - Manages Dell OS6 network device configurations over SSH. This module
- allows implementors to work with the device running-config. It
- provides a way to push a set of commands onto a network device
- by evaluating the current running-config and only pushing configuration
- commands that are not already configured. The config source can
- be a set of commands or a template.
-extends_documentation_fragment: dellos6
-options:
- src:
- description:
- - The path to the config source. The source can be either a
- file with config or a template that will be merged during
- runtime. By default the task will first search for the source
- file in role or playbook root folder in templates unless a full
- path to the file is given.
- required: true
- force:
- description:
- - The force argument instructs the module not to consider the
- current device running-config. When set to true, this will
- cause the module to push the contents of I(src) into the device
- without first checking if already configured. This argument is
- mutually exclusive with I(config).
- required: false
- default: false
- choices: [ "true", "false" ]
- backup:
- description:
- - When this argument is configured true, the module will backup
- the running-config from the node prior to making any changes.
- The backup file will be written to backup_{{ hostname }} in
- the root of the playbook directory. This argument is
- mutually exclusive with I(config).
- required: false
- default: false
- choices: [ "true", "false" ]
- config:
- description:
- - The module, by default, will connect to the remote device and
- retrieve the current running-config to use as a base for comparing
- against the contents of source. There are times when it is not
- desirable to have the task get the current running-config for
- every task. The I(config) argument allows the implementer to
- pass in the configuration to use as the base config for
- comparison. This argument is mutually exclusive with
- I(force) and I(backup).
- required: false
- default: null
-"""
-
-EXAMPLES = """
-- name: push a configuration onto the device
- dellos6_template:
- host: hostname
- username: foo
- src: config.j2
-
-- name: forceable push a configuration onto the device
- dellos6_template:
- host: hostname
- username: foo
- src: config.j2
- force: yes
-
-- name: provide the base configuration for comparison
- dellos6_template:
- host: hostname
- username: foo
- src: candidate_config.txt
- config: current_config.txt
-"""
-
-RETURN = """
-updates:
- description: The set of commands that will be pushed to the remote device
- returned: always
- type: list
- sample: ['...', '...']
-
-_backup:
- description: The current running config of the remote device.
- returned: when running config is present in the remote device.
- type: list
- sample: ['...', '...']
-
-responses:
- description: The set of responses from issuing the commands on the device
- returned: when not check_mode
- type: list
- sample: ['...', '...']
-"""
-from ansible.module_utils.netcfg import dumps
-from ansible.module_utils.network import NetworkModule
-from ansible.module_utils.dellos6 import Dellos6NetworkConfig
-
-
-def get_config(module):
- config = module.params['config'] or dict()
- if not config and not module.params['force']:
- config = module.config.get_config()
- return config
-
-
-def main():
- """ main entry point for module execution
- """
-
- argument_spec = dict(
- src=dict(),
- force=dict(default=False, type='bool'),
- backup=dict(default=False, type='bool'),
- config=dict(),
- )
- mutually_exclusive = [('config', 'backup'), ('config', 'force')]
-
- module = NetworkModule(argument_spec=argument_spec,
- mutually_exclusive=mutually_exclusive,
- supports_check_mode=True)
-
- result = dict(changed=False)
- candidate = Dellos6NetworkConfig(contents=module.params['src'], indent=0)
-
- contents = get_config(module)
- if contents:
- config = Dellos6NetworkConfig(contents=contents[0], indent=0)
- result['_backup'] = contents[0]
- commands = list()
-
- if not module.params['force']:
- commands = dumps(candidate.difference(config), 'commands')
- else:
- commands = str(candidate)
-
- if commands:
- commands = commands.split('\n')
- if not module.check_mode:
- response = module.config(commands)
- result['responses'] = response
- result['changed'] = True
-
- result['updates'] = commands
- module.exit_json(**result)
-
-
-if __name__ == '__main__':
- main()
diff --git a/network/dellos9/dellos9_template.py b/network/dellos9/dellos9_template.py
deleted file mode 100755
index 60cd00ab..00000000
--- a/network/dellos9/dellos9_template.py
+++ /dev/null
@@ -1,186 +0,0 @@
-#!/usr/bin/python
-#
-# (c) 2015 Peter Sprygada, <psprygada@ansible.com>
-#
-# Copyright (c) 2016 Dell Inc.
-#
-# This file is part of Ansible
-#
-# Ansible is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Ansible is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-#
-DOCUMENTATION = """
----
-module: dellos9_template
-version_added: "2.2"
-author: "Dhivya P (@dhivyap)"
-short_description: Manage Dell OS9 device configurations over SSH.
-description:
- - Manages Dell OS9 network device configurations over SSH. This module
- allows implementors to work with the device running-config. It
- provides a way to push a set of commands onto a network device
- by evaluating the current running-config and only pushing configuration
- commands that are not already configured. The config source can
- be a set of commands or a template.
-extends_documentation_fragment: dellos9
-options:
- src:
- description:
- - The path to the config source. The source can be either a
- file with config or a template that will be merged during
- runtime. By default the task will first search for the source
- file in role or playbook root folder in templates unless a full
- path to the file is given.
- required: true
- force:
- description:
- - The force argument instructs the module not to consider the
- current device running-config. When set to true, this will
- cause the module to push the contents of I(src) into the device
- without first checking if already configured. This argument is
- mutually exclusive with I(config).
- required: false
- default: false
- choices: [ "true", "false" ]
- backup:
- description:
- - When this argument is configured true, the module will backup
- the running-config from the node prior to making any changes.
- The backup file will be written to backup_{{ hostname }} in
- the root of the playbook directory. This argument is
- mutually exclusive with I(config).
-
- required: false
- default: false
- choices: [ "true", "false" ]
- config:
- description:
- - The module, by default, will connect to the remote device and
- retrieve the current running-config to use as a base for comparing
- against the contents of source. There are times when it is not
- desirable to have the task get the current running-config for
- every task. The I(config) argument allows the implementer to
- pass in the configuration to use as the base config for
- comparison. This argument is mutually exclusive with
- I(force) and I(backup).
-
- required: false
- default: null
-notes:
- - This module requires Dell OS9 version 9.10.0.1P13 or above.
-
- - This module requires to increase the ssh connection rate limit.
- Use the following command I(ip ssh connection-rate-limit 60)
- to configure the same. This can be done via M(dnos_config) module
- as well.
-"""
-
-EXAMPLES = """
-- name: push a configuration onto the device
- dellos9_template:
- host: hostname
- username: foo
- src: config.j2
-
-- name: forceable push a configuration onto the device
- dellos9_template:
- host: hostname
- username: foo
- src: config.j2
- force: yes
-
-- name: provide the base configuration for comparison
- dellos9_template:
- host: hostname
- username: foo
- src: candidate_config.txt
- config: current_config.txt
-"""
-
-RETURN = """
-updates:
- description: The set of commands that will be pushed to the remote device
- returned: always
- type: list
- sample: ['...', '...']
-
-_backup:
- description: The current running config of the remote device.
- returned: when running config is present in the remote device.
- type: list
- sample: ['...', '...']
-
-responses:
- description: The set of responses from issuing the commands on the device
- returned: when not check_mode
- type: list
- sample: ['...', '...']
-"""
-from ansible.module_utils.netcfg import NetworkConfig, dumps
-from ansible.module_utils.network import NetworkModule
-import ansible.module_utils.dellos9
-
-
-def get_config(module):
- config = module.params['config'] or dict()
- if not config and not module.params['force']:
- config = module.config.get_config()
- return config
-
-
-def main():
- """ main entry point for module execution
- """
-
- argument_spec = dict(
- src=dict(),
- force=dict(default=False, type='bool'),
- backup=dict(default=False, type='bool'),
- config=dict(),
- )
-
- mutually_exclusive = [('config', 'backup'), ('config', 'force')]
-
- module = NetworkModule(argument_spec=argument_spec,
- mutually_exclusive=mutually_exclusive,
- supports_check_mode=True)
-
- result = dict(changed=False)
-
- candidate = NetworkConfig(contents=module.params['src'], indent=1)
-
- contents = get_config(module)
-
- if contents:
- config = NetworkConfig(contents=contents[0], indent=1)
- result['_backup'] = contents[0]
-
- commands = list()
- if not module.params['force']:
- commands = dumps(candidate.difference(config), 'commands')
- else:
- commands = str(candidate)
-
- if commands:
- commands = commands.split('\n')
- if not module.check_mode:
- response = module.config(commands)
- result['responses'] = response
- result['changed'] = True
-
- result['updates'] = commands
- module.exit_json(**result)
-
-
-if __name__ == '__main__':
- main()
diff --git a/network/eos/eos_template.py b/network/eos/_eos_template.py
index a5d0f38c..d546b0c9 100644
--- a/network/eos/eos_template.py
+++ b/network/eos/_eos_template.py
@@ -28,6 +28,7 @@ description:
by evaluating the current running-config and only pushing configuration
commands that are not already configured. The config source can
be a set of commands or a template.
+deprecated: Deprecated in 2.2. Use eos_config instead
extends_documentation_fragment: eos
options:
src:
diff --git a/network/eos/eos_command.py b/network/eos/eos_command.py
index b0ab3286..e1a0f806 100644
--- a/network/eos/eos_command.py
+++ b/network/eos/eos_command.py
@@ -149,21 +149,22 @@ import ansible.module_utils.eos
from ansible.module_utils.basic import get_exception
from ansible.module_utils.network import NetworkModule, NetworkError
from ansible.module_utils.netcli import CommandRunner
-from ansible.module_utils.netcli import AddCommandError
+from ansible.module_utils.netcli import AddCommandError, AddConditionError
from ansible.module_utils.netcli import FailedConditionsError
from ansible.module_utils.netcli import FailedConditionalError
+from ansible.module_utils.six import string_types
VALID_KEYS = ['command', 'output', 'prompt', 'response']
def to_lines(stdout):
for item in stdout:
- if isinstance(item, basestring):
+ if isinstance(item, string_types):
item = str(item).split('\n')
yield item
def parse_commands(module):
for cmd in module.params['commands']:
- if isinstance(cmd, basestring):
+ if isinstance(cmd, string_types):
cmd = dict(command=cmd, output=None)
elif 'command' not in cmd:
module.fail_json(msg='command keyword argument is required')
@@ -211,8 +212,13 @@ def main():
exc = get_exception()
warnings.append('duplicate command detected: %s' % cmd)
- for item in conditionals:
- runner.add_conditional(item)
+ try:
+ for item in conditionals:
+ runner.add_conditional(item)
+ except AddConditionError:
+ exc = get_exception()
+ module.fail_json(msg=str(exc), condition=exc.condition)
+
runner.retries = module.params['retries']
runner.interval = module.params['interval']
diff --git a/network/eos/eos_facts.py b/network/eos/eos_facts.py
index 89cc699a..aa914536 100644
--- a/network/eos/eos_facts.py
+++ b/network/eos/eos_facts.py
@@ -141,8 +141,8 @@ ansible_net_neighbors:
"""
import re
-from ansible.module_utils.basic import get_exception
from ansible.module_utils.netcli import CommandRunner, AddCommandError
+from ansible.module_utils.six import iteritems
from ansible.module_utils.eos import NetworkModule
@@ -163,6 +163,10 @@ class FactsBase(object):
self.load_commands()
+ def load_commands(self):
+ raise NotImplementedError
+
+
class Default(FactsBase):
SYSTEM_MAP = {
@@ -178,7 +182,7 @@ class Default(FactsBase):
def populate(self):
data = self.runner.get_command('show version', 'json')
- for key, value in self.SYSTEM_MAP.iteritems():
+ for key, value in iteritems(self.SYSTEM_MAP):
if key in data:
self.facts[value] = data[key]
@@ -256,10 +260,10 @@ class Interfaces(FactsBase):
def populate_interfaces(self, data):
facts = dict()
- for key, value in data['interfaces'].iteritems():
+ for key, value in iteritems(data['interfaces']):
intf = dict()
- for remote, local in self.INTERFACE_MAP.iteritems():
+ for remote, local in iteritems(self.INTERFACE_MAP):
if remote in value:
intf[local] = value[remote]
@@ -336,7 +340,8 @@ def main():
exclude = False
if subset not in VALID_SUBSETS:
- module.fail_json(msg='Bad subset')
+ module.fail_json(msg='Subset must be one of [%s], got %s' %
+ (', '.join(VALID_SUBSETS), subset))
if exclude:
exclude_subsets.add(subset)
@@ -365,11 +370,10 @@ def main():
inst.populate()
facts.update(inst.facts)
except Exception:
- raise
module.exit_json(out=module.from_json(runner.items))
ansible_facts = dict()
- for key, value in facts.iteritems():
+ for key, value in iteritems(facts):
key = 'ansible_net_%s' % key
ansible_facts[key] = value
@@ -378,4 +382,3 @@ def main():
if __name__ == '__main__':
main()
-
diff --git a/network/ios/ios_template.py b/network/ios/_ios_template.py
index 52e82f26..5791dfbf 100644
--- a/network/ios/ios_template.py
+++ b/network/ios/_ios_template.py
@@ -28,6 +28,7 @@ description:
by evaluating the current running-config and only pushing configuration
commands that are not already configured. The config source can
be a set of commands or a template.
+deprecated: Deprecated in 2.2. Use eos_config instead
extends_documentation_fragment: ios
options:
src:
@@ -114,8 +115,9 @@ responses:
type: list
sample: ['...', '...']
"""
+import ansible.module_utils.ios
from ansible.module_utils.netcfg import NetworkConfig, dumps
-from ansible.module_utils.ios import NetworkModule, NetworkError
+from ansible.module_utils.ios import NetworkModule
def get_config(module):
config = module.params['config'] or dict()
diff --git a/network/ios/ios_command.py b/network/ios/ios_command.py
index 5f2e1278..4204a73a 100644
--- a/network/ios/ios_command.py
+++ b/network/ios/ios_command.py
@@ -139,22 +139,24 @@ failed_conditions:
type: list
sample: ['...', '...']
"""
+import ansible.module_utils.ios
from ansible.module_utils.basic import get_exception
from ansible.module_utils.netcli import CommandRunner
from ansible.module_utils.netcli import AddCommandError, FailedConditionsError
-from ansible.module_utils.ios import NetworkModule, NetworkError
+from ansible.module_utils.network import NetworkModule, NetworkError
+from ansible.module_utils.six import string_types
VALID_KEYS = ['command', 'prompt', 'response']
def to_lines(stdout):
for item in stdout:
- if isinstance(item, basestring):
+ if isinstance(item, string_types):
item = str(item).split('\n')
yield item
def parse_commands(module):
for cmd in module.params['commands']:
- if isinstance(cmd, basestring):
+ if isinstance(cmd, string_types):
cmd = dict(command=cmd, output=None)
elif 'command' not in cmd:
module.fail_json(msg='command keyword argument is required')
diff --git a/network/ios/ios_facts.py b/network/ios/ios_facts.py
index 884e9b5b..699637d9 100644
--- a/network/ios/ios_facts.py
+++ b/network/ios/ios_facts.py
@@ -126,9 +126,11 @@ ansible_net_neighbors:
import re
import itertools
-from ansible.module_utils.basic import get_exception
+import ansible.module_utils.ios
from ansible.module_utils.netcli import CommandRunner, AddCommandError
-from ansible.module_utils.ios import NetworkModule
+from ansible.module_utils.network import NetworkModule
+from ansible.module_utils.six import iteritems
+from ansible.module_utils.six.moves import zip
def add_command(runner, command):
@@ -147,6 +149,9 @@ class FactsBase(object):
self.commands()
+ def commands(self):
+ raise NotImplementedError
+
class Default(FactsBase):
def commands(self):
@@ -199,7 +204,7 @@ class Hardware(FactsBase):
self.facts['filesystems'] = self.parse_filesystems(data)
data = self.runner.get_command('show memory statistics | include Processor')
- match = re.findall('\s(\d+)\s', data)
+ match = re.findall(r'\s(\d+)\s', data)
if match:
self.facts['memtotal_mb'] = int(match[0]) / 1024
self.facts['memfree_mb'] = int(match[1]) / 1024
@@ -244,7 +249,7 @@ class Interfaces(FactsBase):
def populate_interfaces(self, interfaces):
facts = dict()
- for key, value in interfaces.iteritems():
+ for key, value in iteritems(interfaces):
intf = dict()
intf['description'] = self.parse_description(value)
intf['macaddress'] = self.parse_macaddress(value)
@@ -266,11 +271,11 @@ class Interfaces(FactsBase):
return facts
def populate_ipv6_interfaces(self, data):
- for key, value in data.iteritems():
+ for key, value in iteritems(data):
self.facts['interfaces'][key]['ipv6'] = list()
addresses = re.findall(r'\s+(.+), subnet', value, re.M)
subnets = re.findall(r', subnet is (.+)$', value, re.M)
- for addr, subnet in itertools.izip(addresses, subnets):
+ for addr, subnet in zip(addresses, subnets):
ipv6 = dict(address=addr.strip(), subnet=subnet.strip())
self.add_ip_address(addr.strip(), 'ipv6')
self.facts['interfaces'][key]['ipv6'].append(ipv6)
@@ -297,6 +302,7 @@ class Interfaces(FactsBase):
def parse_interfaces(self, data):
parsed = dict()
+ key = ''
for line in data.split('\n'):
if len(line) == 0:
continue
@@ -444,7 +450,7 @@ def main():
module.exit_json(out=module.from_json(runner.items))
ansible_facts = dict()
- for key, value in facts.iteritems():
+ for key, value in iteritems(facts):
key = 'ansible_net_%s' % key
ansible_facts[key] = value
diff --git a/network/iosxr/iosxr_template.py b/network/iosxr/_iosxr_template.py
index 55a98fc2..79ddaa42 100644
--- a/network/iosxr/iosxr_template.py
+++ b/network/iosxr/_iosxr_template.py
@@ -28,6 +28,7 @@ description:
by evaluating the current running-config and only pushing configuration
commands that are not already configured. The config source can
be a set of commands or a template.
+deprecated: Deprecated in 2.2. Use eos_config instead
extends_documentation_fragment: iosxr
options:
src:
diff --git a/network/iosxr/iosxr_command.py b/network/iosxr/iosxr_command.py
index 1d6acc88..b66d1ec3 100644
--- a/network/iosxr/iosxr_command.py
+++ b/network/iosxr/iosxr_command.py
@@ -138,22 +138,24 @@ failed_conditions:
type: list
sample: ['...', '...']
"""
+import ansible.module_utils.iosxr
from ansible.module_utils.basic import get_exception
from ansible.module_utils.netcli import CommandRunner
from ansible.module_utils.netcli import AddCommandError, FailedConditionsError
-from ansible.module_utils.iosxr import NetworkModule, NetworkError
+from ansible.module_utils.network import NetworkModule, NetworkError
+from ansible.module_utils.six import string_types
VALID_KEYS = ['command', 'output', 'prompt', 'response']
def to_lines(stdout):
for item in stdout:
- if isinstance(item, basestring):
+ if isinstance(item, string_types):
item = str(item).split('\n')
yield item
def parse_commands(module):
for cmd in module.params['commands']:
- if isinstance(cmd, basestring):
+ if isinstance(cmd, string_types):
cmd = dict(command=cmd, output=None)
elif 'command' not in cmd:
module.fail_json(msg='command keyword argument is required')
diff --git a/network/iosxr/iosxr_facts.py b/network/iosxr/iosxr_facts.py
index f045080e..7af1023f 100644
--- a/network/iosxr/iosxr_facts.py
+++ b/network/iosxr/iosxr_facts.py
@@ -116,11 +116,12 @@ ansible_net_neighbors:
type: dict
"""
import re
-import itertools
-from ansible.module_utils.basic import get_exception
+import ansible.module_utils.iosxr
from ansible.module_utils.netcli import CommandRunner, AddCommandError
-from ansible.module_utils.iosxr import NetworkModule
+from ansible.module_utils.network import NetworkModule
+from ansible.module_utils.six import iteritems
+from ansible.module_utils.six.moves import zip
def add_command(runner, command):
@@ -139,6 +140,9 @@ class FactsBase(object):
self.commands()
+ def commands(self):
+ raise NotImplementedError
+
class Default(FactsBase):
def commands(self):
@@ -223,7 +227,7 @@ class Interfaces(FactsBase):
def populate_interfaces(self, interfaces):
facts = dict()
- for key, value in interfaces.iteritems():
+ for key, value in iteritems(interfaces):
intf = dict()
intf['description'] = self.parse_description(value)
intf['macaddress'] = self.parse_macaddress(value)
@@ -244,11 +248,11 @@ class Interfaces(FactsBase):
return facts
def populate_ipv6_interfaces(self, data):
- for key, value in data.iteritems():
+ for key, value in iteritems(data):
self.facts['interfaces'][key]['ipv6'] = list()
addresses = re.findall(r'\s+(.+), subnet', value, re.M)
subnets = re.findall(r', subnet is (.+)$', value, re.M)
- for addr, subnet in itertools.izip(addresses, subnets):
+ for addr, subnet in zip(addresses, subnets):
ipv6 = dict(address=addr.strip(), subnet=subnet.strip())
self.add_ip_address(addr.strip(), 'ipv6')
self.facts['interfaces'][key]['ipv6'].append(ipv6)
@@ -276,6 +280,7 @@ class Interfaces(FactsBase):
def parse_interfaces(self, data):
parsed = dict()
+ key = ''
for line in data.split('\n'):
if len(line) == 0:
continue
@@ -416,11 +421,10 @@ def main():
inst.populate()
facts.update(inst.facts)
except Exception:
- raise
module.exit_json(out=module.from_json(runner.items))
ansible_facts = dict()
- for key, value in facts.iteritems():
+ for key, value in iteritems(facts):
key = 'ansible_net_%s' % key
ansible_facts[key] = value
diff --git a/network/junos/junos_template.py b/network/junos/_junos_template.py
index 2f8e64d5..c50deeef 100644
--- a/network/junos/junos_template.py
+++ b/network/junos/_junos_template.py
@@ -27,6 +27,7 @@ description:
from a template file onto a remote device running Junos. The
module will return the differences in configuration if the diff
option is specified on the Ansible command line
+deprecated: Deprecated in 2.2. Use eos_config instead
extends_documentation_fragment: junos
options:
src:
@@ -100,7 +101,10 @@ EXAMPLES = """
src: config.j2
action: overwrite
"""
-from ansible.module_utils.junos import NetworkModule
+import ansible.module_utils.junos
+
+from ansible.module_utils.basic import get_exception
+from ansible.module_utils.network import NetworkModule, NetworkError
DEFAULT_COMMENT = 'configured by junos_template'
@@ -123,24 +127,35 @@ def main():
confirm = module.params['confirm']
commit = not module.check_mode
+ replace = False
+ overwrite = False
+
action = module.params['action']
+ if action == 'overwrite':
+ overwrite = True
+ elif action == 'replace':
+ replace = True
src = module.params['src']
fmt = module.params['config_format']
if action == 'overwrite' and fmt == 'set':
module.fail_json(msg="overwrite cannot be used when format is "
- "set per junos documentation")
+ "set per junos-pyez documentation")
results = dict(changed=False)
- results['_backup'] = str(module.config.get_config()).strip()
-
- diff = module.config.load_config(src, action=action, comment=comment,
- format=fmt, commit=commit, confirm=confirm)
-
- if diff:
- results['changed'] = True
- results['diff'] = dict(prepared=diff)
+ results['_backup'] = unicode(module.config.get_config()).strip()
+
+ try:
+ diff = module.config.load_config(src, commit=commit, replace=replace,
+ confirm=confirm, comment=comment, config_format=fmt)
+
+ if diff:
+ results['changed'] = True
+ results['diff'] = dict(prepared=diff)
+ except NetworkError:
+ exc = get_exception()
+ module.fail_json(msg=str(exc), **exc.kwargs)
module.exit_json(**results)
diff --git a/network/junos/junos_command.py b/network/junos/junos_command.py
index fd812858..6fc2b0a4 100644
--- a/network/junos/junos_command.py
+++ b/network/junos/junos_command.py
@@ -151,23 +151,16 @@ failed_conditionals:
retured: failed
type: list
sample: ['...', '...']
-
-xml:
- description: The raw XML reply from the device
- returned: when format is xml
- type: list
- sample: [['...', '...'], ['...', '...']]
"""
-import re
import ansible.module_utils.junos
-
-
from ansible.module_utils.basic import get_exception
from ansible.module_utils.network import NetworkModule, NetworkError
from ansible.module_utils.netcli import CommandRunner
from ansible.module_utils.netcli import AddCommandError, FailedConditionsError
+from ansible.module_utils.netcli import FailedConditionalError, AddConditionError
from ansible.module_utils.junos import xml_to_json
+from ansible.module_utils.six import string_types
VALID_KEYS = {
'cli': frozenset(['command', 'output', 'prompt', 'response']),
@@ -177,7 +170,7 @@ VALID_KEYS = {
def to_lines(stdout):
for item in stdout:
- if isinstance(item, basestring):
+ if isinstance(item, string_types):
item = str(item).split('\n')
yield item
@@ -189,7 +182,7 @@ def parse(module, command_type):
parsed = list()
for item in (items or list()):
- if isinstance(item, basestring):
+ if isinstance(item, string_types):
item = dict(command=item, output=None)
elif 'command' not in item:
module.fail_json(msg='command keyword argument is required')
@@ -205,6 +198,10 @@ def parse(module, command_type):
item['command_type'] = command_type
+ # show configuration [options] will return as text
+ if item['command'].startswith('show configuration'):
+ item['output'] = 'text'
+
parsed.append(item)
return parsed
@@ -261,8 +258,12 @@ def main():
exc = get_exception()
warnings.append('duplicate command detected: %s' % cmd)
- for item in conditionals:
- runner.add_conditional(item)
+ try:
+ for item in conditionals:
+ runner.add_conditional(item)
+ except (ValueError, AddConditionError):
+ exc = get_exception()
+ module.fail_json(msg=str(exc), condition=exc.condition)
runner.retries = module.params['retries']
runner.interval = module.params['interval']
@@ -273,24 +274,23 @@ def main():
except FailedConditionsError:
exc = get_exception()
module.fail_json(msg=str(exc), failed_conditions=exc.failed_conditions)
+ except FailedConditionalError:
+ exc = get_exception()
+ module.fail_json(msg=str(exc), failed_conditional=exc.failed_conditional)
except NetworkError:
exc = get_exception()
module.fail_json(msg=str(exc))
result = dict(changed=False, stdout=list())
- xmlout = list()
for cmd in commands:
try:
output = runner.get_command(cmd['command'], cmd.get('output'))
- xmlout.append(output)
- output = xml_to_json(output)
except ValueError:
output = 'command not executed due to check_mode, see warnings'
result['stdout'].append(output)
result['warnings'] = warnings
- result['xml'] = xmlout
result['stdout_lines'] = list(to_lines(result['stdout']))
module.exit_json(**result)
@@ -298,4 +298,3 @@ def main():
if __name__ == '__main__':
main()
-
diff --git a/network/junos/junos_config.py b/network/junos/junos_config.py
index d7458d95..afa711bd 100644
--- a/network/junos/junos_config.py
+++ b/network/junos/junos_config.py
@@ -166,6 +166,7 @@ import ansible.module_utils.junos
from ansible.module_utils.basic import get_exception
from ansible.module_utils.network import NetworkModule, NetworkError
+from ansible.module_utils.netcfg import NetworkConfig
DEFAULT_COMMENT = 'configured by junos_config'
@@ -189,6 +190,46 @@ def guess_format(config):
return 'text'
+def config_to_commands(config):
+ set_format = config.startswith('set') or config.startswith('delete')
+ candidate = NetworkConfig(indent=4, contents=config, device_os='junos')
+ if not set_format:
+ candidate = [c.line for c in candidate.items]
+ commands = list()
+ # this filters out less specific lines
+ for item in candidate:
+ for index, entry in enumerate(commands):
+ if item.startswith(entry):
+ del commands[index]
+ break
+ commands.append(item)
+
+ else:
+ commands = str(candidate).split('\n')
+
+ return commands
+
+def diff_commands(commands, config):
+ config = [unicode(c).replace("'", '') for c in config]
+
+ updates = list()
+ visited = set()
+
+ for item in commands:
+ if not item.startswith('set') and not item.startswith('delete'):
+ raise ValueError('line must start with either `set` or `delete`')
+
+ elif item.startswith('set') and item[4:] not in config:
+ updates.append(item)
+
+ elif item.startswith('delete'):
+ for entry in config:
+ if entry.startswith(item[7:]) and item not in visited:
+ updates.append(item)
+ visited.add(item)
+
+ return updates
+
def load_config(module, result):
candidate = module.params['lines'] or module.params['src']
@@ -204,6 +245,13 @@ def load_config(module, result):
config_format = 'set'
kwargs['config_format'] = config_format
+ # this is done to filter out `delete ...` statements which map to
+ # nothing in the config as that will cause an exception to be raised
+ if config_format == 'set':
+ config = module.config.get_config()
+ config = config_to_commands(config)
+ candidate = diff_commands(candidate, config)
+
diff = module.config.load_config(candidate, **kwargs)
if diff:
diff --git a/network/junos/junos_package.py b/network/junos/junos_package.py
index 78a0f024..e893ee9d 100644
--- a/network/junos/junos_package.py
+++ b/network/junos/junos_package.py
@@ -94,7 +94,7 @@ EXAMPLES = """
"""
import ansible.module_utils.junos
-from ansible.module_utils.newtork import NetworkModule
+from ansible.module_utils.network import NetworkModule
try:
from jnpr.junos.utils.sw import SW
diff --git a/network/nxos/nxos_template.py b/network/nxos/_nxos_template.py
index c8cc638f..d8c8caf9 100644
--- a/network/nxos/nxos_template.py
+++ b/network/nxos/_nxos_template.py
@@ -28,6 +28,7 @@ description:
by evaluating the current running-config and only pushing configuration
commands that are not already configured. The config source can
be a set of commands or a template.
+deprecated: Deprecated in 2.2. Use eos_config instead
extends_documentation_fragment: nxos
options:
src:
diff --git a/network/nxos/nxos_aaa_server.py b/network/nxos/nxos_aaa_server.py
index 43cc3425..33988657 100644
--- a/network/nxos/nxos_aaa_server.py
+++ b/network/nxos/nxos_aaa_server.py
@@ -349,7 +349,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -543,7 +543,7 @@ def main():
command = default_aaa_server(existing, proposed, server_type)
if command:
commands.append(command)
-
+
cmds = flatten_list(commands)
if cmds:
if module.check_mode:
@@ -552,6 +552,8 @@ def main():
changed = True
execute_config_command(cmds, module)
end_state = get_aaa_server_info(server_type, module)
+ if 'configure' in cmds:
+ cmds.pop(0)
results = {}
results['proposed'] = proposed
diff --git a/network/nxos/nxos_acl.py b/network/nxos/nxos_acl.py
index d2e13a3d..b0e3223e 100644
--- a/network/nxos/nxos_acl.py
+++ b/network/nxos/nxos_acl.py
@@ -439,7 +439,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds, output=command_type)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -624,6 +624,15 @@ def execute_config_command(commands, module):
clie = get_exception()
module.fail_json(msg='Error sending CLI commands',
error=str(clie), commands=commands)
+ except AttributeError:
+ try:
+ commands.insert(0, 'configure')
+ module.cli.add_commands(commands, output='config')
+ module.cli.run_commands()
+ except ShellError:
+ clie = get_exception()
+ module.fail_json(msg='Error sending CLI commands',
+ error=str(clie), commands=commands)
def main():
@@ -787,6 +796,8 @@ def main():
execute_config_command(cmds, module)
changed = True
new_existing_core, end_state, seqs = get_acl(module, name, seq)
+ if 'configure' in cmds:
+ cmds.pop(0)
results['proposed'] = proposed
results['existing'] = existing_core
diff --git a/network/nxos/nxos_acl_interface.py b/network/nxos/nxos_acl_interface.py
index 07060df4..83ab0d41 100644
--- a/network/nxos/nxos_acl_interface.py
+++ b/network/nxos/nxos_acl_interface.py
@@ -449,6 +449,15 @@ def execute_config_command(commands, module):
clie = get_exception()
module.fail_json(msg='Error sending CLI commands',
error=str(clie), commands=commands)
+ except AttributeError:
+ try:
+ commands.insert(0, 'configure')
+ module.cli.add_commands(commands, output='config')
+ module.cli.run_commands()
+ except ShellError:
+ clie = get_exception()
+ module.fail_json(msg='Error sending CLI commands',
+ error=str(clie), commands=commands)
def main():
@@ -510,6 +519,8 @@ def main():
interfaces_acls, this_dir_acl_intf = other_existing_acl(
end_state_acls, interface, direction)
end_state = this_dir_acl_intf
+ if 'configure' in cmds:
+ cmds.pop(0)
else:
cmds = []
diff --git a/network/nxos/nxos_command.py b/network/nxos/nxos_command.py
index 9dc01a91..af302c2f 100644
--- a/network/nxos/nxos_command.py
+++ b/network/nxos/nxos_command.py
@@ -155,7 +155,7 @@ from ansible.module_utils.network import NetworkModule, NetworkError
from ansible.module_utils.netcli import CommandRunner
from ansible.module_utils.netcli import FailedConditionsError
from ansible.module_utils.netcli import FailedConditionalError
-from ansible.module_utils.netcli import AddCommandError
+from ansible.module_utils.netcli import AddCommandError, AddConditionError
VALID_KEYS = ['command', 'output', 'prompt', 'response']
@@ -214,8 +214,12 @@ def main():
exc = get_exception()
warnings.append('duplicate command detected: %s' % cmd)
- for item in conditionals:
- runner.add_conditional(item)
+ try:
+ for item in conditionals:
+ runner.add_conditional(item)
+ except AddConditionError:
+ exc = get_exception()
+ module.fail_json(msg=str(exc), condition=exc.condition)
runner.retries = module.params['retries']
runner.interval = module.params['interval']
diff --git a/network/nxos/nxos_feature.py b/network/nxos/nxos_feature.py
index 8b9b831d..15d49cc7 100644
--- a/network/nxos/nxos_feature.py
+++ b/network/nxos/nxos_feature.py
@@ -298,7 +298,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds, output=command_type)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -399,7 +399,10 @@ def validate_feature(module, mode='show'):
'sla sender': 'sla_sender',
'ssh': 'sshServer',
'tacacs+': 'tacacs',
- 'telnet': 'telnetServer'},
+ 'telnet': 'telnetServer',
+ 'ethernet-link-oam': 'elo',
+ 'port-security': 'eth_port_sec'
+ },
'config':
{
'nve': 'nv overlay',
@@ -413,6 +416,8 @@ def validate_feature(module, mode='show'):
'sshServer': 'ssh',
'tacacs': 'tacacs+',
'telnetServer': 'telnet',
+ 'elo': 'ethernet-link-oam',
+ 'eth_port_sec': 'port-security'
}
}
diff --git a/network/nxos/nxos_hsrp.py b/network/nxos/nxos_hsrp.py
index be7cbd5c..b218d295 100644
--- a/network/nxos/nxos_hsrp.py
+++ b/network/nxos/nxos_hsrp.py
@@ -277,12 +277,21 @@ def load_config(module, candidate):
def execute_config_command(commands, module):
try:
- response = module.configure(commands)
+ output = module.configure(commands)
except ShellError:
clie = get_exception()
module.fail_json(msg='Error sending CLI commands',
error=str(clie), commands=commands)
- return response
+ except AttributeError:
+ try:
+ commands.insert(0, 'configure')
+ module.cli.add_commands(commands, output='config')
+ output = module.cli.run_commands()
+ except ShellError:
+ clie = get_exception()
+ module.fail_json(msg='Error sending CLI commands',
+ error=str(clie), commands=commands)
+ return output
def get_cli_body_ssh(command, response, module):
@@ -329,7 +338,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds, output=command_type)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -675,6 +684,8 @@ def main():
validate_config(body, vip, module)
changed = True
end_state = get_hsrp_group(group, interface, module)
+ if 'configure' in commands:
+ commands.pop(0)
results = {}
results['proposed'] = proposed
diff --git a/network/nxos/nxos_igmp_interface.py b/network/nxos/nxos_igmp_interface.py
index 5f74d34e..1f5aa5d9 100644
--- a/network/nxos/nxos_igmp_interface.py
+++ b/network/nxos/nxos_igmp_interface.py
@@ -430,7 +430,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds, output=command_type)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -715,6 +715,15 @@ def execute_config_command(commands, module):
clie = get_exception()
module.fail_json(msg='Error sending CLI commands',
error=str(clie), commands=commands)
+ except AttributeError:
+ try:
+ commands.insert(0, 'configure')
+ module.cli.add_commands(commands, output='config')
+ module.cli.run_commands()
+ except ShellError:
+ clie = get_exception()
+ module.fail_json(msg='Error sending CLI commands',
+ error=str(clie), commands=commands)
def main():
@@ -881,6 +890,8 @@ def main():
execute_config_command(cmds, module)
changed = True
end_state = get_igmp_interface(module, interface)
+ if 'configure' in cmds:
+ cmds.pop(0)
results['proposed'] = proposed
results['existing'] = existing_copy
@@ -892,4 +903,4 @@ def main():
if __name__ == '__main__':
- main()
+ main() \ No newline at end of file
diff --git a/network/nxos/nxos_igmp_snooping.py b/network/nxos/nxos_igmp_snooping.py
index 1e11a924..8b8ad662 100644
--- a/network/nxos/nxos_igmp_snooping.py
+++ b/network/nxos/nxos_igmp_snooping.py
@@ -537,6 +537,8 @@ def main():
changed = True
execute_config_command(cmds, module)
end_state = get_igmp_snooping(module)
+ if 'configure' in cmds:
+ cmds.pop(0)
results['proposed'] = proposed
results['existing'] = existing
diff --git a/network/nxos/nxos_install_os.py b/network/nxos/nxos_install_os.py
index 566b03f5..4f5150e6 100644
--- a/network/nxos/nxos_install_os.py
+++ b/network/nxos/nxos_install_os.py
@@ -27,7 +27,10 @@ notes:
- The module will fail due to timeout issues, but the install will go on
anyway. Ansible's block and rescue can be leveraged to handle this kind
of failure and check actual module results. See EXAMPLE for more about
- this.
+ this. The first task on the rescue block is needed to make sure the
+ device has completed all checks and it started to reboot. The second
+ task is needed to wait the device to come back up. Last two tasks are
+ used to verify the installation process's been successful.
- Do not include full file paths, just the name of the file(s) stored on
the top level flash directory.
- You must know if your platform supports taking a kickstart image as a
@@ -62,6 +65,13 @@ EXAMPLES = '''
password: "{{ pwd }}"
transport: nxapi
rescue:
+ - name: Wait for device to perform checks
+ wait_for:
+ port: 22
+ state: stopped
+ timeout: 300
+ delay: 60
+ host: "{{ inventory_hostname }}"
- name: Wait for device to come back up
wait_for:
port: 22
diff --git a/network/nxos/nxos_interface.py b/network/nxos/nxos_interface.py
index b8ca51fe..927e93d3 100644
--- a/network/nxos/nxos_interface.py
+++ b/network/nxos/nxos_interface.py
@@ -291,9 +291,6 @@ def load_config(module, candidate):
return result
# END OF COMMON CODE
-BOOLEANS_TRUE = ['yes', 'on', '1', 'true', 'True', 1, True]
-BOOLEANS_FALSE = ['no', 'off', '0', 'false', 'False', 0, False]
-ACCEPTED = BOOLEANS_TRUE + BOOLEANS_FALSE
def is_default_interface(interface, module):
"""Checks to see if interface exists and if it is a default config
@@ -697,6 +694,15 @@ def execute_config_command(commands, module):
clie = get_exception()
module.fail_json(msg='Error sending CLI commands',
error=str(clie), commands=commands)
+ except AttributeError:
+ try:
+ commands.insert(0, 'configure')
+ module.cli.add_commands(commands, output='config')
+ module.cli.run_commands()
+ except ShellError:
+ clie = get_exception()
+ module.fail_json(msg='Error sending CLI commands',
+ error=str(clie), commands=commands)
def get_cli_body_ssh(command, response, module):
@@ -741,7 +747,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds, output=command_type)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -804,8 +810,7 @@ def main():
interface_type=dict(required=False,
choices=['loopback', 'portchannel', 'svi', 'nve']),
ip_forward=dict(required=False, choices=['enable', 'disable']),
- fabric_forwarding_anycast_gateway=dict(required=False, type='bool',
- choices=ACCEPTED),
+ fabric_forwarding_anycast_gateway=dict(required=False, type='bool'),
state=dict(choices=['absent', 'present', 'default'],
default='present', required=False),
include_defaults=dict(default=True),
@@ -926,6 +931,7 @@ def main():
normalized_interface)
else:
end_state = get_interfaces_dict(module)[interface_type]
+ cmds = [cmd for cmd in cmds if cmd != 'configure']
results = {}
results['proposed'] = proposed
@@ -938,4 +944,4 @@ def main():
if __name__ == '__main__':
- main()
+ main() \ No newline at end of file
diff --git a/network/nxos/nxos_ip_interface.py b/network/nxos/nxos_ip_interface.py
index fa9abfd7..eef79543 100644
--- a/network/nxos/nxos_ip_interface.py
+++ b/network/nxos/nxos_ip_interface.py
@@ -258,6 +258,15 @@ def execute_config_command(commands, module):
clie = get_exception()
module.fail_json(msg='Error sending CLI commands',
error=str(clie), commands=commands)
+ except AttributeError:
+ try:
+ commands.insert(0, 'configure')
+ module.cli.add_commands(commands, output='config')
+ module.cli.run_commands()
+ except ShellError:
+ clie = get_exception()
+ module.fail_json(msg='Error sending CLI commands',
+ error=str(clie), commands=commands)
def get_cli_body_ssh(command, response, module):
@@ -303,7 +312,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds, output=command_type)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -680,6 +689,8 @@ def main():
changed = True
end_state, address_list = get_ip_interface(interface, version,
module)
+ if 'configure' in cmds:
+ cmds.pop(0)
results = {}
results['proposed'] = proposed
diff --git a/network/nxos/nxos_mtu.py b/network/nxos/nxos_mtu.py
index e8cd0f20..26fccca6 100644
--- a/network/nxos/nxos_mtu.py
+++ b/network/nxos/nxos_mtu.py
@@ -304,10 +304,7 @@ def get_cli_body_ssh(command, response, module):
body = response
else:
try:
- if isinstance(response[0], str):
- body = [json.loads(response[0])]
- else:
- body = response
+ body = [json.loads(response[0])]
except ValueError:
module.fail_json(msg='Command does not support JSON output',
command=command)
@@ -336,7 +333,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -580,6 +577,8 @@ def main():
end_state = get_mtu(interface, module)
else:
end_state = get_system_mtu(module)
+ if 'configure' in cmds:
+ cmds.pop(0)
results = {}
results['proposed'] = proposed
diff --git a/network/nxos/nxos_ntp.py b/network/nxos/nxos_ntp.py
index 81de38be..18cbe2b1 100644
--- a/network/nxos/nxos_ntp.py
+++ b/network/nxos/nxos_ntp.py
@@ -310,10 +310,7 @@ def get_cli_body_ssh(command, response, module):
body = response
else:
try:
- if isinstance(response[0], str):
- body = [json.loads(response[0])]
- else:
- body = response
+ body = [json.loads(response[0])]
except ValueError:
module.fail_json(msg='Command does not support JSON output',
command=command)
@@ -342,7 +339,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -615,6 +612,8 @@ def main():
changed = True
execute_config_command(cmds, module)
end_state = get_ntp_existing(address, peer_type, module)[0]
+ if 'configure' in cmds:
+ cmds.pop(0)
results = {}
results['proposed'] = proposed
diff --git a/network/nxos/nxos_ntp_auth.py b/network/nxos/nxos_ntp_auth.py
index 28da3a74..11617099 100644
--- a/network/nxos/nxos_ntp_auth.py
+++ b/network/nxos/nxos_ntp_auth.py
@@ -308,10 +308,7 @@ def get_cli_body_ssh(command, response, module):
body = response
else:
try:
- if isinstance(response[0], str):
- body = [json.loads(response[0])]
- else:
- body = response
+ body = [json.loads(response[0])]
except ValueError:
module.fail_json(msg='Command does not support JSON output',
command=command)
@@ -340,7 +337,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -555,6 +552,8 @@ def main():
delta = dict(set(end_state.iteritems()).difference(existing.iteritems()))
if delta or (len(existing) != len(end_state)):
changed = True
+ if 'configure' in cmds:
+ cmds.pop(0)
results = {}
results['proposed'] = proposed
diff --git a/network/nxos/nxos_ntp_options.py b/network/nxos/nxos_ntp_options.py
index 69dd954c..9c030132 100644
--- a/network/nxos/nxos_ntp_options.py
+++ b/network/nxos/nxos_ntp_options.py
@@ -289,10 +289,7 @@ def get_cli_body_ssh(command, response, module):
body = response
else:
try:
- if isinstance(response[0], str):
- body = [json.loads(response[0])]
- else:
- body = response
+ body = [json.loads(response[0])]
except ValueError:
module.fail_json(msg='Command does not support JSON output',
command=command)
@@ -321,7 +318,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -501,6 +498,8 @@ def main():
changed = True
execute_config_command(cmds, module)
end_state = get_ntp_options(module)
+ if 'configure' in cmds:
+ cmds.pop(0)
results = {}
results['proposed'] = proposed
diff --git a/network/nxos/nxos_pim_interface.py b/network/nxos/nxos_pim_interface.py
index 68f0cbb7..9556b03a 100644
--- a/network/nxos/nxos_pim_interface.py
+++ b/network/nxos/nxos_pim_interface.py
@@ -373,10 +373,7 @@ def get_cli_body_ssh(command, response, module, text=False):
body = response
else:
try:
- if isinstance(response[0], str):
- body = [json.loads(response[0])]
- else:
- body = response
+ body = [json.loads(response[0])]
except ValueError:
module.fail_json(msg='Command does not support JSON output',
command=command)
@@ -405,7 +402,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -915,6 +912,8 @@ def main():
time.sleep(1)
get_existing = get_pim_interface(module, interface)
end_state, jp_bidir, isauth = local_existing(get_existing)
+ if 'configure' in cmds:
+ cmds.pop(0)
results['proposed'] = proposed
results['existing'] = existing
diff --git a/network/nxos/nxos_portchannel.py b/network/nxos/nxos_portchannel.py
index 68af16c8..48985a13 100644
--- a/network/nxos/nxos_portchannel.py
+++ b/network/nxos/nxos_portchannel.py
@@ -343,10 +343,7 @@ def execute_config_command(commands, module):
def get_cli_body_ssh(command, response, module):
try:
- if isinstance(response[0], str):
- body = [json.loads(response[0])]
- else:
- body = response
+ body = [json.loads(response[0])]
except ValueError:
module.fail_json(msg='Command does not support JSON output',
command=command)
@@ -375,7 +372,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds, output=command_type)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
diff --git a/network/nxos/nxos_smu.py b/network/nxos/nxos_smu.py
index 310fe7fc..f89aeb37 100644
--- a/network/nxos/nxos_smu.py
+++ b/network/nxos/nxos_smu.py
@@ -256,7 +256,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds, output=command_type)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -286,12 +286,21 @@ def remote_file_exists(module, dst, file_system='bootflash:'):
def execute_config_command(commands, module):
try:
- response = module.configure(commands)
+ output = module.configure(commands)
except ShellError:
clie = get_exception()
module.fail_json(msg='Error sending CLI commands',
error=str(clie), commands=commands)
- return response
+ except AttributeError:
+ try:
+ commands.insert(0, 'configure')
+ module.cli.add_commands(commands, output='config')
+ output = module.cli.run_commands()
+ except ShellError:
+ clie = get_exception()
+ module.fail_json(msg='Error sending CLI commands',
+ error=str(clie), commands=commands)
+ return output
def apply_patch(module, commands):
@@ -353,11 +362,14 @@ def main():
if not module.check_mode and commands:
try:
apply_patch(module, commands)
- changed=True
+ changed = True
except ShellError:
e = get_exception()
module.fail_json(msg=str(e))
+ if 'configure' in commands:
+ commands.pop(0)
+
module.exit_json(changed=changed,
pkg=pkg,
file_system=file_system,
diff --git a/network/nxos/nxos_snmp_community.py b/network/nxos/nxos_snmp_community.py
index 137dd771..5abc428f 100644
--- a/network/nxos/nxos_snmp_community.py
+++ b/network/nxos/nxos_snmp_community.py
@@ -283,10 +283,7 @@ def get_cli_body_ssh(command, response, module):
body = response
else:
try:
- if isinstance(response[0], str):
- body = [json.loads(response[0])]
- else:
- body = response
+ body = [json.loads(response[0])]
except ValueError:
module.fail_json(msg='Command does not support JSON output',
command=command)
@@ -315,7 +312,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -485,6 +482,8 @@ def main():
changed = True
execute_config_command(cmds, module)
end_state = get_snmp_community(module, community)
+ if 'configure' in cmds:
+ cmds.pop(0)
results = {}
results['proposed'] = proposed
diff --git a/network/nxos/nxos_snmp_contact.py b/network/nxos/nxos_snmp_contact.py
index e6982a8a..aaaeab4f 100644
--- a/network/nxos/nxos_snmp_contact.py
+++ b/network/nxos/nxos_snmp_contact.py
@@ -268,10 +268,7 @@ def get_cli_body_ssh(command, response, module):
body = response
else:
try:
- if isinstance(response[0], str):
- body = [json.loads(response[0])]
- else:
- body = response
+ body = [json.loads(response[0])]
except ValueError:
module.fail_json(msg='Command does not support JSON output',
command=command)
@@ -300,7 +297,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -383,6 +380,8 @@ def main():
changed = True
execute_config_command(cmds, module)
end_state = get_snmp_contact(module)
+ if 'configure' in cmds:
+ cmds.pop(0)
results = {}
results['proposed'] = proposed
diff --git a/network/nxos/nxos_snmp_host.py b/network/nxos/nxos_snmp_host.py
index e767f1ce..92f050fe 100644
--- a/network/nxos/nxos_snmp_host.py
+++ b/network/nxos/nxos_snmp_host.py
@@ -316,10 +316,7 @@ def get_cli_body_ssh(command, response, module):
body = response
else:
try:
- if isinstance(response[0], str):
- body = [json.loads(response[0])]
- else:
- body = response
+ body = [json.loads(response[0])]
except ValueError:
module.fail_json(msg='Command does not support JSON output',
command=command)
@@ -348,7 +345,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -621,6 +618,8 @@ def main():
changed = True
execute_config_command(cmds, module)
end_state = get_snmp_host(snmp_host, module)
+ if 'configure' in cmds:
+ cmds.pop(0)
if store:
existing['vrf_filter'] = store
diff --git a/network/nxos/nxos_snmp_location.py b/network/nxos/nxos_snmp_location.py
index b829b83c..29d5cf57 100644
--- a/network/nxos/nxos_snmp_location.py
+++ b/network/nxos/nxos_snmp_location.py
@@ -275,10 +275,7 @@ def get_cli_body_ssh(command, response, module):
body = response
else:
try:
- if isinstance(response[0], str):
- body = [json.loads(response[0])]
- else:
- body = response
+ body = [json.loads(response[0])]
except ValueError:
module.fail_json(msg='Command does not support JSON output',
command=command)
@@ -307,7 +304,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -403,6 +400,8 @@ def main():
changed = True
execute_config_command(cmds, module)
end_state = get_snmp_location(module)
+ if 'configure' in cmds:
+ cmds.pop(0)
results = {}
results['proposed'] = proposed
diff --git a/network/nxos/nxos_snmp_traps.py b/network/nxos/nxos_snmp_traps.py
index c48eafa0..63f1a3e3 100644
--- a/network/nxos/nxos_snmp_traps.py
+++ b/network/nxos/nxos_snmp_traps.py
@@ -286,10 +286,7 @@ def get_cli_body_ssh(command, response, module):
body = response
else:
try:
- if isinstance(response[0], str):
- body = [json.loads(response[0])]
- else:
- body = response
+ body = [json.loads(response[0])]
except ValueError:
module.fail_json(msg='Command does not support JSON output',
command=command)
@@ -318,7 +315,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -481,6 +478,8 @@ def main():
changed = True
execute_config_command(cmds, module)
end_state = get_snmp_traps(group, module)
+ if 'configure' in cmds:
+ cmds.pop(0)
results = {}
results['proposed'] = proposed
diff --git a/network/nxos/nxos_snmp_user.py b/network/nxos/nxos_snmp_user.py
index 9e1c0517..db10d51f 100644
--- a/network/nxos/nxos_snmp_user.py
+++ b/network/nxos/nxos_snmp_user.py
@@ -297,10 +297,7 @@ def get_cli_body_ssh(command, response, module, text=False):
body = response
else:
try:
- if isinstance(response[0], str):
- body = [json.loads(response[0])]
- else:
- body = response
+ body = [json.loads(response[0])]
except ValueError:
module.fail_json(msg='Command does not support JSON output',
command=command)
@@ -329,7 +326,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -541,6 +538,8 @@ def main():
changed = True
execute_config_command(cmds, module)
end_state = get_snmp_user(user, module)
+ if 'configure' in cmds:
+ cmds.pop(0)
if store:
existing['group'] = store
diff --git a/network/nxos/nxos_switchport.py b/network/nxos/nxos_switchport.py
index 49fce57a..1b6cbf70 100644
--- a/network/nxos/nxos_switchport.py
+++ b/network/nxos/nxos_switchport.py
@@ -607,10 +607,7 @@ def get_cli_body_ssh(command, response, module):
body = response
else:
try:
- if isinstance(response[0], str):
- body = [json.loads(response[0])]
- else:
- body = response
+ body = [json.loads(response[0])]
except ValueError:
module.fail_json(msg='Command does not support JSON output',
command=command)
@@ -639,7 +636,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -792,6 +789,8 @@ def main():
changed = True
execute_config_command(cmds, module)
end_state = get_switchport(interface, module)
+ if 'configure' in cmds:
+ cmds.pop(0)
results = {}
results['proposed'] = proposed
diff --git a/network/nxos/nxos_udld.py b/network/nxos/nxos_udld.py
index cd377870..7c903fcf 100644
--- a/network/nxos/nxos_udld.py
+++ b/network/nxos/nxos_udld.py
@@ -293,10 +293,7 @@ def get_cli_body_ssh(command, response, module):
body = response
else:
try:
- if isinstance(response[0], str):
- body = [json.loads(response[0])]
- else:
- body = response
+ body = [json.loads(response[0])]
except ValueError:
module.fail_json(msg='Command does not support JSON output',
command=command)
@@ -325,7 +322,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -487,6 +484,8 @@ def main():
changed = True
execute_config_command(cmds, module)
end_state = get_udld_global(module)
+ if 'configure' in cmds:
+ cmds.pop(0)
results = {}
results['proposed'] = proposed
diff --git a/network/nxos/nxos_udld_interface.py b/network/nxos/nxos_udld_interface.py
index 593aaa3f..44bb3c5e 100644
--- a/network/nxos/nxos_udld_interface.py
+++ b/network/nxos/nxos_udld_interface.py
@@ -292,10 +292,7 @@ def get_cli_body_ssh(command, response, module):
body = response
else:
try:
- if isinstance(response[0], str):
- body = [json.loads(response[0])]
- else:
- body = response
+ body = [json.loads(response[0])]
except ValueError:
module.fail_json(msg='Command does not support JSON output',
command=command)
@@ -324,7 +321,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -500,6 +497,8 @@ def main():
changed = True
execute_config_command(cmds, module)
end_state = get_udld_interface(module, interface)
+ if 'configure' in cmds:
+ cmds.pop(0)
results = {}
results['proposed'] = proposed
diff --git a/network/nxos/nxos_vlan.py b/network/nxos/nxos_vlan.py
index c7071c10..cb06bd84 100644
--- a/network/nxos/nxos_vlan.py
+++ b/network/nxos/nxos_vlan.py
@@ -456,6 +456,15 @@ def execute_config_command(commands, module):
clie = get_exception()
module.fail_json(msg='Error sending CLI commands',
error=str(clie), commands=commands)
+ except AttributeError:
+ try:
+ commands.insert(0, 'configure')
+ module.cli.add_commands(commands, output='config')
+ module.cli.run_commands()
+ except ShellError:
+ clie = get_exception()
+ module.fail_json(msg='Error sending CLI commands',
+ error=str(clie), commands=commands)
def get_cli_body_ssh(command, response, module):
@@ -500,7 +509,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds, output=command_type)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -564,7 +573,6 @@ def main():
proposed_vlans_list = numerical_sort(vlan_range_to_list(
vlan_id or vlan_range))
existing_vlans_list = numerical_sort(get_list_of_vlans(module))
-
commands = []
existing = None
@@ -599,7 +607,7 @@ def main():
end_state_vlans_list = existing_vlans_list
if commands:
- if existing:
+ if existing.get('mapped_vni'):
if (existing.get('mapped_vni') != proposed.get('mapped_vni') and
existing.get('mapped_vni') != '0' and proposed.get('mapped_vni') != 'default'):
commands.insert(1, 'no vn-segment')
@@ -610,6 +618,8 @@ def main():
execute_config_command(commands, module)
changed = True
end_state_vlans_list = numerical_sort(get_list_of_vlans(module))
+ if 'configure' in commands:
+ commands.pop(0)
if vlan_id:
end_state = get_vlan(vlan_id, module)
diff --git a/network/nxos/nxos_vpc.py b/network/nxos/nxos_vpc.py
index 62635e84..3fae9cee 100644
--- a/network/nxos/nxos_vpc.py
+++ b/network/nxos/nxos_vpc.py
@@ -305,6 +305,15 @@ def execute_config_command(commands, module):
clie = get_exception()
module.fail_json(msg='Error sending CLI commands',
error=str(clie), commands=commands)
+ except AttributeError:
+ try:
+ commands.insert(0, 'configure')
+ module.cli.add_commands(commands, output='config')
+ module.cli.run_commands()
+ except ShellError:
+ clie = get_exception()
+ module.fail_json(msg='Error sending CLI commands',
+ error=str(clie), commands=commands)
def get_cli_body_ssh(command, response, module):
@@ -349,7 +358,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds, output=command_type)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -629,6 +638,8 @@ def main():
changed = True
execute_config_command(cmds, module)
end_state = get_vpc(module)
+ if 'configure' in cmds:
+ cmds.pop(0)
results = {}
results['proposed'] = proposed
diff --git a/network/nxos/nxos_vpc_interface.py b/network/nxos/nxos_vpc_interface.py
index 4480c587..a621c1b1 100644
--- a/network/nxos/nxos_vpc_interface.py
+++ b/network/nxos/nxos_vpc_interface.py
@@ -257,6 +257,15 @@ def execute_config_command(commands, module):
clie = get_exception()
module.fail_json(msg='Error sending CLI commands',
error=str(clie), commands=commands)
+ except AttributeError:
+ try:
+ commands.insert(0, 'configure')
+ module.cli.add_commands(commands, output='config')
+ response = module.cli.run_commands()
+ except ShellError:
+ clie = get_exception()
+ module.fail_json(msg='Error sending CLI commands',
+ error=str(clie), commands=commands)
return response
@@ -300,7 +309,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds, output=command_type)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -563,6 +572,8 @@ def main():
if 'error' in output.lower():
module.fail_json(msg=output.replace('\n', ''))
end_state = get_portchannel_vpc_config(module, portchannel)
+ if 'configure' in cmds:
+ cmds.pop(0)
results = {}
results['proposed'] = proposed
diff --git a/network/nxos/nxos_vrf.py b/network/nxos/nxos_vrf.py
index bcce2872..40e11a70 100644
--- a/network/nxos/nxos_vrf.py
+++ b/network/nxos/nxos_vrf.py
@@ -274,6 +274,15 @@ def execute_config_command(commands, module):
clie = get_exception()
module.fail_json(msg='Error sending CLI commands',
error=str(clie), commands=commands)
+ except AttributeError:
+ try:
+ commands.insert(0, 'configure')
+ module.cli.add_commands(commands, output='config')
+ module.cli.run_commands()
+ except ShellError:
+ clie = get_exception()
+ module.fail_json(msg='Error sending CLI commands',
+ error=str(clie), commands=commands)
def get_cli_body_ssh_vrf(module, command, response):
@@ -316,7 +325,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds, output=command_type)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -503,6 +512,8 @@ def main():
execute_config_command(commands, module)
changed = True
end_state = get_vrf(vrf, module)
+ if 'configure' in commands:
+ commands.pop(0)
results = {}
results['proposed'] = proposed
@@ -515,4 +526,4 @@ def main():
if __name__ == '__main__':
- main()
+ main() \ No newline at end of file
diff --git a/network/nxos/nxos_vrf_interface.py b/network/nxos/nxos_vrf_interface.py
index c0742468..fe80c0f8 100644
--- a/network/nxos/nxos_vrf_interface.py
+++ b/network/nxos/nxos_vrf_interface.py
@@ -251,6 +251,15 @@ def execute_config_command(commands, module):
clie = get_exception()
module.fail_json(msg='Error sending CLI commands',
error=str(clie), commands=commands)
+ except AttributeError:
+ try:
+ commands.insert(0, 'configure')
+ module.cli.add_commands(commands, output='config')
+ module.cli.run_commands()
+ except ShellError:
+ clie = get_exception()
+ module.fail_json(msg='Error sending CLI commands',
+ error=str(clie), commands=commands)
def get_cli_body_ssh_vrf_interface(command, response, module):
@@ -290,7 +299,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds, output=command_type)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -466,6 +475,8 @@ def main():
changed = True
changed_vrf = get_interface_info(interface, module)
end_state = dict(interface=interface, vrf=changed_vrf)
+ if 'configure' in commands:
+ commands.pop(0)
results = {}
results['proposed'] = proposed
@@ -481,4 +492,4 @@ def main():
if __name__ == '__main__':
- main()
+ main() \ No newline at end of file
diff --git a/network/nxos/nxos_vrrp.py b/network/nxos/nxos_vrrp.py
index 073ee8d2..c389a90d 100644
--- a/network/nxos/nxos_vrrp.py
+++ b/network/nxos/nxos_vrrp.py
@@ -277,6 +277,15 @@ def execute_config_command(commands, module):
clie = get_exception()
module.fail_json(msg='Error sending CLI commands',
error=str(clie), commands=commands)
+ except AttributeError:
+ try:
+ commands.insert(0, 'configure')
+ module.cli.add_commands(commands, output='config')
+ module.cli.run_commands()
+ except ShellError:
+ clie = get_exception()
+ module.fail_json(msg='Error sending CLI commands',
+ error=str(clie), commands=commands)
def get_cli_body_ssh_vrrp(command, response, module):
@@ -323,7 +332,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds, output=command_type)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -619,6 +628,8 @@ def main():
execute_config_command(cmds, module)
changed = True
end_state = get_existing_vrrp(interface, group, module, name)
+ if 'configure' in cmds:
+ cmds.pop(0)
results = {}
results['proposed'] = proposed
diff --git a/network/nxos/nxos_vtp_domain.py b/network/nxos/nxos_vtp_domain.py
index 7ea1b8c8..806b01f7 100644
--- a/network/nxos/nxos_vtp_domain.py
+++ b/network/nxos/nxos_vtp_domain.py
@@ -269,10 +269,7 @@ def get_cli_body_ssh(command, response, module):
body = response
else:
try:
- if isinstance(response[0], str):
- body = [json.loads(response[0])]
- else:
- body = response
+ body = [json.loads(response[0])]
except ValueError:
module.fail_json(msg='Command does not support JSON output',
command=command)
@@ -301,7 +298,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -405,6 +402,8 @@ def main():
changed = True
execute_config_command(cmds, module)
end_state = get_vtp_config(module)
+ if 'configure' in cmds:
+ cmds.pop(0)
results = {}
results['proposed'] = proposed
diff --git a/network/nxos/nxos_vtp_password.py b/network/nxos/nxos_vtp_password.py
index d4528b70..1f78c8e2 100644
--- a/network/nxos/nxos_vtp_password.py
+++ b/network/nxos/nxos_vtp_password.py
@@ -286,10 +286,7 @@ def get_cli_body_ssh(command, response, module):
body = response
else:
try:
- if isinstance(response[0], str):
- body = [json.loads(response[0])]
- else:
- body = response
+ body = [json.loads(response[0])]
except ValueError:
module.fail_json(msg='Command does not support JSON output',
command=command)
@@ -318,7 +315,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -462,6 +459,8 @@ def main():
changed = True
execute_config_command(cmds, module)
end_state = get_vtp_config(module)
+ if 'configure' in cmds:
+ cmds.pop(0)
results = {}
results['proposed'] = proposed
diff --git a/network/nxos/nxos_vtp_version.py b/network/nxos/nxos_vtp_version.py
index 0d24e9c3..90fad190 100644
--- a/network/nxos/nxos_vtp_version.py
+++ b/network/nxos/nxos_vtp_version.py
@@ -264,10 +264,7 @@ def get_cli_body_ssh(command, response, module):
body = response
else:
try:
- if isinstance(response[0], str):
- body = [json.loads(response[0])]
- else:
- body = response
+ body = [json.loads(response[0])]
except ValueError:
module.fail_json(msg='Command does not support JSON output',
command=command)
@@ -296,7 +293,7 @@ def execute_show(cmds, module, command_type=None):
module.cli.add_commands(cmds, output=command_type)
response = module.cli.run_commands()
else:
- module.cli.add_commands(cmds)
+ module.cli.add_commands(cmds, raw=True)
response = module.cli.run_commands()
except ShellError:
clie = get_exception()
@@ -400,6 +397,8 @@ def main():
changed = True
execute_config_command(cmds, module)
end_state = get_vtp_config(module)
+ if 'configure' in cmds:
+ cmds.pop(0)
results = {}
results['proposed'] = proposed
diff --git a/network/openswitch/ops_template.py b/network/openswitch/_ops_template.py
index 48f21c0c..2fb21c26 100644
--- a/network/openswitch/ops_template.py
+++ b/network/openswitch/_ops_template.py
@@ -28,6 +28,7 @@ description:
against a provided candidate configuration. If there are changes, the
candidate configuration is merged with the current configuration and
pushed into OpenSwitch
+deprecated: Deprecated in 2.2. Use eos_config instead
extends_documentation_fragment: openswitch
options:
src:
@@ -95,10 +96,11 @@ responses:
type: list
sample: [...]
"""
-import copy
+import ansible.module_utils.openswitch
from ansible.module_utils.netcfg import NetworkConfig, dumps
-from ansible.module_utils.openswitch import NetworkModule
+from ansible.module_utils.network import NetworkModule
+from ansible.module_utils.openswitch import HAS_OPS
def get_config(module):
diff --git a/network/openswitch/ops_command.py b/network/openswitch/ops_command.py
index f74ef619..43dab36a 100644
--- a/network/openswitch/ops_command.py
+++ b/network/openswitch/ops_command.py
@@ -126,22 +126,24 @@ failed_conditions:
type: list
sample: ['...', '...']
"""
+import ansible.module_utils.openswitch
from ansible.module_utils.basic import get_exception
from ansible.module_utils.netcli import CommandRunner
from ansible.module_utils.netcli import AddCommandError, FailedConditionsError
-from ansible.module_utils.openswitch import NetworkModule, NetworkError
+from ansible.module_utils.network import NetworkModule, NetworkError
+from ansible.module_utils.six import string_types
VALID_KEYS = ['command', 'prompt', 'response']
def to_lines(stdout):
for item in stdout:
- if isinstance(item, basestring):
+ if isinstance(item, string_types):
item = str(item).split('\n')
yield item
def parse_commands(module):
for cmd in module.params['commands']:
- if isinstance(cmd, basestring):
+ if isinstance(cmd, string_types):
cmd = dict(command=cmd, output=None)
elif 'command' not in cmd:
module.fail_json(msg='command keyword argument is required')
@@ -220,4 +222,3 @@ def main():
if __name__ == '__main__':
main()
-
diff --git a/network/openswitch/ops_facts.py b/network/openswitch/ops_facts.py
index 162dc710..5be10b38 100644
--- a/network/openswitch/ops_facts.py
+++ b/network/openswitch/ops_facts.py
@@ -171,9 +171,10 @@ endpoints:
"""
import re
-from ansible.module_utils.basic import get_exception
+import ansible.module_utils.openswitch
from ansible.module_utils.netcli import CommandRunner, AddCommandError
-from ansible.module_utils.openswitch import NetworkModule
+from ansible.module_utils.network import NetworkModule
+from ansible.module_utils.six import iteritems
def add_command(runner, command):
@@ -196,6 +197,9 @@ class FactsBase(object):
if self.transport == 'cli':
self.commands()
+ def commands(self):
+ raise NotImplementedError
+
def populate(self):
getattr(self, self.transport)()
@@ -395,11 +399,10 @@ def main():
inst.populate()
facts.update(inst.facts)
except Exception:
- raise
module.exit_json(out=module.from_json(runner.items))
ansible_facts = dict()
- for key, value in facts.iteritems():
+ for key, value in iteritems(facts):
# this is to maintain capability with ops_facts 2.1
if key.startswith('_'):
ansible_facts[key[1:]] = value
diff --git a/network/vyos/vyos_command.py b/network/vyos/vyos_command.py
index 49e41b61..0d09c432 100644
--- a/network/vyos/vyos_command.py
+++ b/network/vyos/vyos_command.py
@@ -126,22 +126,24 @@ warnings:
type: list
sample: ['...', '...']
"""
+import ansible.module_utils.vyos
from ansible.module_utils.basic import get_exception
from ansible.module_utils.netcli import CommandRunner
from ansible.module_utils.netcli import AddCommandError, FailedConditionsError
-from ansible.module_utils.vyos import NetworkModule, NetworkError
+from ansible.module_utils.network import NetworkModule, NetworkError
+from ansible.module_utils.six import string_types
VALID_KEYS = ['command', 'output', 'prompt', 'response']
def to_lines(stdout):
for item in stdout:
- if isinstance(item, basestring):
+ if isinstance(item, string_types):
item = str(item).split('\n')
yield item
def parse_commands(module):
for cmd in module.params['commands']:
- if isinstance(cmd, basestring):
+ if isinstance(cmd, string_types):
cmd = dict(command=cmd, output=None)
elif 'command' not in cmd:
module.fail_json(msg='command keyword argument is required')
diff --git a/network/vyos/vyos_config.py b/network/vyos/vyos_config.py
index 706f6491..7ee3a272 100644
--- a/network/vyos/vyos_config.py
+++ b/network/vyos/vyos_config.py
@@ -244,12 +244,11 @@ def run(module, result):
result['updates'] = updates
- if module.params['update'] != 'check':
- load_config(module, updates, result)
+ load_config(module, updates, result)
- if result.get('filtered'):
- result['warnings'].append('Some configuration commands where '
- 'removed, please see the filtered key')
+ if result.get('filtered'):
+ result['warnings'].append('Some configuration commands where '
+ 'removed, please see the filtered key')
def main():
diff --git a/network/vyos/vyos_facts.py b/network/vyos/vyos_facts.py
index 3c09f365..5be19a78 100644
--- a/network/vyos/vyos_facts.py
+++ b/network/vyos/vyos_facts.py
@@ -100,8 +100,11 @@ ansible_net_gather_subset:
"""
import re
-from ansible.module_utils.netcmd import CommandRunner
-from ansible.module_utils.vyos import NetworkModule
+import ansible.module_utils.vyos
+from ansible.module_utils.pycompat24 import get_exception
+from ansible.module_utils.netcli import CommandRunner
+from ansible.module_utils.network import NetworkModule
+from ansible.module_utils.six import iteritems
class FactsBase(object):
@@ -112,6 +115,9 @@ class FactsBase(object):
self.commands()
+ def commands(self):
+ raise NotImplementedError
+
class Default(FactsBase):
@@ -160,7 +166,7 @@ class Config(FactsBase):
entry = None
for line in commits.split('\n'):
- match = re.match('(\d+)\s+(.+)by(.+)via(.+)', line)
+ match = re.match(r'(\d+)\s+(.+)by(.+)via(.+)', line)
if match:
if entry:
entries.append(entry)
@@ -288,7 +294,7 @@ def main():
for key in runable_subsets:
instances.append(FACT_SUBSETS[key](runner))
- runner.run_commands()
+ runner.run()
try:
for inst in instances:
@@ -299,7 +305,7 @@ def main():
module.fail_json(msg='unknown failure', output=runner.items, exc=str(exc))
ansible_facts = dict()
- for key, value in facts.iteritems():
+ for key, value in iteritems(facts):
key = 'ansible_net_%s' % key
ansible_facts[key] = value
diff --git a/packaging/os/apt.py b/packaging/os/apt.py
index 29b8fb8f..eeaf4aa6 100644
--- a/packaging/os/apt.py
+++ b/packaging/os/apt.py
@@ -47,9 +47,9 @@ options:
choices: [ "yes", "no" ]
cache_valid_time:
description:
- - If C(update_cache) is specified and the last run is less or equal than I(cache_valid_time) seconds ago, the C(update_cache) gets skipped.
+ - Update the apt cache if its older than the I(cache_valid_time). This option is set in seconds.
required: false
- default: no
+ default: 0
purge:
description:
- Will force purging of configuration files if the module state is set to I(absent).
@@ -696,12 +696,37 @@ def download(module, deb):
return deb
+def get_cache_mtime():
+ """Return mtime of a valid apt cache file.
+ Stat the apt cache file and if no cache file is found return 0
+ :returns: ``int``
+ """
+ if os.path.exists(APT_UPDATE_SUCCESS_STAMP_PATH):
+ return os.stat(APT_UPDATE_SUCCESS_STAMP_PATH).st_mtime
+ elif os.path.exists(APT_LISTS_PATH):
+ return os.stat(APT_LISTS_PATH).st_mtime
+ else:
+ return 0
+
+
+def get_updated_cache_time():
+ """Return the mtime time stamp and the updated cache time.
+ Always retrieve the mtime of the apt cache or set the `cache_mtime`
+ variable to 0
+ :returns: ``tuple``
+ """
+ cache_mtime = get_cache_mtime()
+ mtimestamp = datetime.datetime.fromtimestamp(cache_mtime)
+ updated_cache_time = int(time.mktime(mtimestamp.timetuple()))
+ return mtimestamp, updated_cache_time
+
+
def main():
module = AnsibleModule(
argument_spec = dict(
state = dict(default='present', choices=['installed', 'latest', 'removed', 'absent', 'present', 'build-dep']),
update_cache = dict(default=False, aliases=['update-cache'], type='bool'),
- cache_valid_time = dict(type='int'),
+ cache_valid_time = dict(type='int', default=0),
purge = dict(default=False, type='bool'),
package = dict(default=None, aliases=['pkg', 'name'], type='list'),
deb = dict(default=None, type='path'),
@@ -772,30 +797,16 @@ def main():
# reopen cache w/ modified config
cache.open(progress=None)
+
+ mtimestamp, updated_cache_time = get_updated_cache_time()
+ # Cache valid time is default 0, which will update the cache if
+ # needed and `update_cache` was set to true
+ updated_cache = False
if p['update_cache']:
- # Default is: always update the cache
- cache_valid = False
now = datetime.datetime.now()
- if p.get('cache_valid_time', False):
- try:
- mtime = os.stat(APT_UPDATE_SUCCESS_STAMP_PATH).st_mtime
- except:
- # Looks like the update-success-stamp is not available
- # Fallback: Checking the mtime of the lists
- try:
- mtime = os.stat(APT_LISTS_PATH).st_mtime
- except:
- # No mtime could be read. We update the cache to be safe
- mtime = False
-
- if mtime:
- tdelta = datetime.timedelta(seconds=p['cache_valid_time'])
- mtimestamp = datetime.datetime.fromtimestamp(mtime)
- if mtimestamp + tdelta >= now:
- cache_valid = True
- updated_cache_time = int(time.mktime(mtimestamp.timetuple()))
-
- if cache_valid is not True:
+ tdelta = datetime.timedelta(seconds=p['cache_valid_time'])
+ if not mtimestamp + tdelta >= now:
+ # Retry to update the cache up to 3 times
for retry in range(3):
try:
cache.update()
@@ -806,12 +817,16 @@ def main():
module.fail_json(msg='Failed to update apt cache.')
cache.open(progress=None)
updated_cache = True
- updated_cache_time = int(time.mktime(now.timetuple()))
+ mtimestamp, updated_cache_time = get_updated_cache_time()
+
+ # If theres nothing else to do exit. This will set state as
+ # changed based on if the cache was updated.
if not p['package'] and not p['upgrade'] and not p['deb']:
- module.exit_json(changed=False, cache_updated=updated_cache, cache_update_time=updated_cache_time)
- else:
- updated_cache = False
- updated_cache_time = 0
+ module.exit_json(
+ changed=updated_cache,
+ cache_updated=updated_cache,
+ cache_update_time=updated_cache_time
+ )
force_yes = p['force']
@@ -843,16 +858,33 @@ def main():
state_upgrade = True
if p['state'] == 'build-dep':
state_builddep = True
- result = install(module, packages, cache, upgrade=state_upgrade,
- default_release=p['default_release'],
- install_recommends=install_recommends,
- force=force_yes, dpkg_options=dpkg_options,
- build_dep=state_builddep, autoremove=autoremove,
- only_upgrade=p['only_upgrade'],
- allow_unauthenticated=allow_unauthenticated)
- (success, retvals) = result
- retvals['cache_updated']=updated_cache
- retvals['cache_update_time']=updated_cache_time
+
+ success, retvals = install(
+ module,
+ packages,
+ cache,
+ upgrade=state_upgrade,
+ default_release=p['default_release'],
+ install_recommends=install_recommends,
+ force=force_yes,
+ dpkg_options=dpkg_options,
+ build_dep=state_builddep,
+ autoremove=autoremove,
+ only_upgrade=p['only_upgrade'],
+ allow_unauthenticated=allow_unauthenticated
+ )
+
+ # Store if the cache has been updated
+ retvals['cache_updated'] = updated_cache
+ # Store when the update time was last
+ retvals['cache_update_time'] = updated_cache_time
+ # If the cache was updated and the general state change was set to
+ # False make sure that the change in cache state is acurately
+ # updated by setting the general changed state to the same as
+ # the cache state.
+ if updated_cache and not retvals['changed']:
+ retvals['changed'] = updated_cache
+
if success:
module.exit_json(**retvals)
else:
diff --git a/shippable.yml b/shippable.yml
index a8dd0fc5..f5314c72 100644
--- a/shippable.yml
+++ b/shippable.yml
@@ -9,22 +9,22 @@ matrix:
- env: TEST=none
include:
- env: TEST=integration IMAGE=ansible/ansible:centos6
- - env: TEST=integration IMAGE=ansible/ansible:centos7
- - env: TEST=integration IMAGE=ansible/ansible:fedora-rawhide
- - env: TEST=integration IMAGE=ansible/ansible:fedora23
- - env: TEST=integration IMAGE=ansible/ansible:opensuseleap
+ - env: TEST=integration IMAGE=ansible/ansible:centos7 PRIVILEGED=true
+ - env: TEST=integration IMAGE=ansible/ansible:fedora-rawhide PRIVILEGED=true
+ - env: TEST=integration IMAGE=ansible/ansible:fedora23 PRIVILEGED=true
+ - env: TEST=integration IMAGE=ansible/ansible:opensuseleap PRIVILEGED=true
- env: TEST=integration IMAGE=ansible/ansible:ubuntu1204 PRIVILEGED=true
- env: TEST=integration IMAGE=ansible/ansible:ubuntu1404 PRIVILEGED=true
- - env: TEST=integration IMAGE=ansible/ansible:ubuntu1604
+ - env: TEST=integration IMAGE=ansible/ansible:ubuntu1604 PRIVILEGED=true
- - env: TEST=integration IMAGE=ansible/ansible:ubuntu1604py3 PYTHON3=1
+ - env: TEST=integration IMAGE=ansible/ansible:ubuntu1604py3 PYTHON3=1 PRIVILEGED=true
- env: TEST=integration PLATFORM=windows VERSION=2008-SP2
- env: TEST=integration PLATFORM=windows VERSION=2008-R2_SP1
- env: TEST=integration PLATFORM=windows VERSION=2012-RTM
- env: TEST=integration PLATFORM=windows VERSION=2012-R2_RTM
- - env: TEST=integration PLATFORM=freebsd VERSION=10.3-STABLE
+ - env: TEST=integration PLATFORM=freebsd VERSION=10.3-STABLE PRIVILEGED=true
- env: TEST=integration PLATFORM=osx VERSION=10.11
diff --git a/system/mount.py b/system/mount.py
index 26b8eb56..ea76a0a9 100644
--- a/system/mount.py
+++ b/system/mount.py
@@ -28,7 +28,6 @@ from ansible.module_utils.ismount import ismount
from ansible.module_utils.pycompat24 import get_exception
from ansible.module_utils.six import iteritems
import os
-import re
DOCUMENTATION = '''
@@ -354,7 +353,7 @@ def umount(module, dest):
# from @jupeter -- https://github.com/ansible/ansible-modules-core/pull/2923
# @jtyr -- https://github.com/ansible/ansible-modules-core/issues/4439
# and @abadger to relicense from GPLv3+
-def is_bind_mounted(module, dest, src=None, fstype=None):
+def is_bind_mounted(module, linux_mounts, dest, src=None, fstype=None):
"""Return whether the dest is bind mounted
:arg module: The AnsibleModule (used for helper functions)
@@ -364,62 +363,161 @@ def is_bind_mounted(module, dest, src=None, fstype=None):
ensure that we are detecting that the correct source is mounted there.
:kwarg fstype: The filesystem type. If specified this is also used to
help ensure that we are detecting the right mount.
+ :kwarg linux_mounts: Cached list of mounts for Linux.
:returns: True if the dest is mounted with src otherwise False.
"""
is_mounted = False
- bin_path = module.get_bin_path('mount', required=True)
- cmd = '%s -l' % bin_path
if get_platform() == 'Linux':
- bin_path = module.get_bin_path('findmnt', required=True)
- cmd = '%s -nr %s' % (bin_path, dest)
+ if src is None:
+ # That's for unmounted/absent
+ if dest in linux_mounts:
+ is_mounted = True
+ else:
+ # That's for mounted
+ if dest in linux_mounts and linux_mounts[dest]['src'] == src:
+ is_mounted = True
+ else:
+ bin_path = module.get_bin_path('mount', required=True)
+ cmd = '%s -l' % bin_path
+ rc, out, err = module.run_command(cmd)
+ mounts = []
- rc, out, err = module.run_command(cmd)
- mounts = []
+ if len(out):
+ mounts = to_native(out).strip().split('\n')
- if len(out):
- mounts = to_native(out).strip().split('\n')
+ for mnt in mounts:
+ arguments = mnt.split()
- mount_pattern = re.compile('\[(.*)\]')
+ if (
+ (arguments[0] == src or src is None) and
+ arguments[2] == dest and
+ (arguments[4] == fstype or fstype is None)):
+ is_mounted = True
- for mnt in mounts:
- arguments = mnt.split()
+ if is_mounted:
+ break
- if get_platform() == 'Linux':
- source = arguments[1]
- result = mount_pattern.search(arguments[1])
+ return is_mounted
- # This is only for LVM and tmpfs mounts
- if result is not None and len(result.groups()) == 1:
- source = result.group(1)
- if src is None:
- # That's for unmounted/absent
- if arguments[0] == dest:
- is_mounted = True
- else:
- # That's for mounted
- if arguments[0] == dest and source == src:
- is_mounted = True
- elif arguments[0] == dest and src.endswith(source):
- # Check if it's tmpfs mount
- sub_path = src[:len(src)-len(source)]
+def get_linux_mounts(module):
+ """Gather mount information"""
+
+ mntinfo_file = "/proc/self/mountinfo"
+
+ try:
+ f = open(mntinfo_file)
+ except IOError:
+ module.fail_json(msg="Cannot open file %s" % mntinfo_file)
+
+ lines = map(str.strip, f.readlines())
+
+ try:
+ f.close()
+ except IOError:
+ module.fail_json(msg="Cannot close file %s" % mntinfo_file)
+
+ mntinfo = []
+
+ for line in lines:
+ fields = line.split()
+
+ record = {
+ 'root': fields[3],
+ 'dst': fields[4],
+ 'opts': fields[5],
+ 'fields': fields[6:-4],
+ 'fs': fields[-3],
+ 'src': fields[-2],
+ }
+
+ mntinfo.append(record)
+
+ mounts = {}
+ for i, mnt in enumerate(mntinfo):
+ src = mnt['src']
+
+ if mnt['fs'] == 'tmpfs' and mnt['root'] != '/':
+ # == Example:
+ # 65 19 0:35 / /tmp rw shared:25 - tmpfs tmpfs rw
+ # 210 65 0:35 /aaa /tmp/bbb rw shared:25 - tmpfs tmpfs rw
+ # == Expected result:
+ # src=/tmp/aaa
+ # ==
+
+ shared = None
+
+ # Search for the shared field
+ for fld in mnt['fields']:
+ if fld.startswith('shared'):
+ shared = fld
+
+ if shared is None:
+ continue
+
+ dest = None
+
+ # Search fo the record with the same field
+ for j, m in enumerate(mntinfo):
+ if j < i:
+ if shared in m['fields']:
+ dest = m['dst']
+ else:
+ break
+
+ if dest is not None:
+ src = "%s%s" % (dest, mnt['root'])
+ else:
+ continue
+
+ elif mnt['root'] != '/' and len(mnt['fields']) > 0:
+ # == Example:
+ # 67 19 8:18 / /mnt/disk2 rw shared:26 - ext4 /dev/sdb2 rw
+ # 217 65 8:18 /test /tmp/ccc rw shared:26 - ext4 /dev/sdb2 rw
+ # == Expected result:
+ # src=/mnt/disk2/test
+ # ==
+
+ # Search for parent
+ for j, m in enumerate(mntinfo):
+ if j < i:
+ if m['src'] == mnt['src']:
+ src = "%s%s" % (m['dst'], mnt['root'])
+ else:
+ break
+
+ elif mnt['root'] != '/' and len(mnt['fields']) == 0:
+ # == Example 1:
+ # 27 20 8:1 /tmp/aaa /tmp/bbb rw - ext4 /dev/sdb2 rw
+ # == Example 2:
+ # 204 136 253:2 /rootfs / rw - ext4 /dev/sdb2 rw
+ # 141 140 253:2 /rootfs/tmp/aaa /tmp/bbb rw - ext4 /dev/sdb2 rw
+ # == Expected result:
+ # src=/tmp/aaa
+ # ==
+
+ src = mnt['root']
+
+ # Search for parent
+ for j, m in enumerate(mntinfo):
+ if j < i:
if (
- is_bind_mounted(module, sub_path, 'tmpfs') and
- source == src[len(sub_path):]):
- is_mounted = True
- elif (
- (arguments[0] == src or src is None) and
- arguments[2] == dest and
- (arguments[4] == fstype or fstype is None)):
- is_mounted = True
-
- if is_mounted:
- break
+ m['src'] == mnt['src'] and
+ mnt['root'].startswith(m['root'])):
+ src = src.replace("%s/" % m['root'], '/', 1)
+ else:
+ break
- return is_mounted
+ mounts[mnt['dst']] = {
+ 'src': src,
+ 'opts': mnt['opts'],
+ 'fs': mnt['fs']
+ }
+
+ return mounts
def main():
@@ -460,7 +558,7 @@ def main():
else:
args = dict(
name=module.params['name'],
- opts='default',
+ opts='defaults',
dump='0',
passno='0',
fstab='/etc/fstab'
@@ -470,6 +568,14 @@ def main():
if get_platform() == 'FreeBSD':
args['opts'] = 'rw'
+ linux_mounts = []
+
+ # Cache all mounts here in order we have consistent results if we need to
+ # call is_bind_mouted() multiple times
+ if get_platform() == 'Linux':
+ linux_mounts = get_linux_mounts(module)
+
+ # Override defaults with user specified params
for key in ('src', 'fstype', 'passno', 'opts', 'dump', 'fstab'):
if module.params[key] is not None:
args[key] = module.params[key]
@@ -502,7 +608,7 @@ def main():
name, changed = unset_mount(module, args)
if changed and not module.check_mode:
- if ismount(name) or is_bind_mounted(module, name):
+ if ismount(name) or is_bind_mounted(module, linux_mounts, name):
res, msg = umount(module, name)
if res:
@@ -516,7 +622,7 @@ def main():
e = get_exception()
module.fail_json(msg="Error rmdir %s: %s" % (name, str(e)))
elif state == 'unmounted':
- if ismount(name) or is_bind_mounted(module, name):
+ if ismount(name) or is_bind_mounted(module, linux_mounts, name):
if not module.check_mode:
res, msg = umount(module, name)
@@ -544,7 +650,8 @@ def main():
elif 'bind' in args.get('opts', []):
changed = True
- if is_bind_mounted(module, name, args['src'], args['fstype']):
+ if is_bind_mounted(
+ module, linux_mounts, name, args['src'], args['fstype']):
changed = False
if changed and not module.check_mode:
diff --git a/utilities/logic/async_wrapper.py b/utilities/logic/async_wrapper.py
index 822f9dc9..05054504 100644
--- a/utilities/logic/async_wrapper.py
+++ b/utilities/logic/async_wrapper.py
@@ -70,6 +70,50 @@ def daemonize_self():
os.dup2(dev_null.fileno(), sys.stdout.fileno())
os.dup2(dev_null.fileno(), sys.stderr.fileno())
+# NB: this function copied from module_utils/json_utils.py. Ensure any changes are propagated there.
+# FUTURE: AnsibleModule-ify this module so it's Ansiballz-compatible and can use the module_utils copy of this function.
+def _filter_non_json_lines(data):
+ '''
+ Used to filter unrelated output around module JSON output, like messages from
+ tcagetattr, or where dropbear spews MOTD on every single command (which is nuts).
+
+ Filters leading lines before first line-starting occurrence of '{' or '[', and filter all
+ trailing lines after matching close character (working from the bottom of output).
+ '''
+ warnings = []
+
+ # Filter initial junk
+ lines = data.splitlines()
+
+ for start, line in enumerate(lines):
+ line = line.strip()
+ if line.startswith(u'{'):
+ endchar = u'}'
+ break
+ elif line.startswith(u'['):
+ endchar = u']'
+ break
+ else:
+ raise ValueError('No start of json char found')
+
+ # Filter trailing junk
+ lines = lines[start:]
+
+ for reverse_end_offset, line in enumerate(reversed(lines)):
+ if line.strip().endswith(endchar):
+ break
+ else:
+ raise ValueError('No end of json char found')
+
+ if reverse_end_offset > 0:
+ # Trailing junk is uncommon and can point to things the user might
+ # want to change. So print a warning if we find any
+ trailing_junk = lines[len(lines) - reverse_end_offset:]
+ warnings.append('Module invocation had junk after the JSON data: %s' % '\n'.join(trailing_junk))
+
+ lines = lines[:(len(lines) - reverse_end_offset)]
+
+ return ('\n'.join(lines), warnings)
def _run_module(wrapped_cmd, jid, job_path):
@@ -82,6 +126,8 @@ def _run_module(wrapped_cmd, jid, job_path):
result = {}
outdata = ''
+ filtered_outdata = ''
+ stderr = ''
try:
cmd = shlex.split(wrapped_cmd)
script = subprocess.Popen(cmd, shell=False, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
@@ -89,7 +135,19 @@ def _run_module(wrapped_cmd, jid, job_path):
if PY3:
outdata = outdata.decode('utf-8', 'surrogateescape')
stderr = stderr.decode('utf-8', 'surrogateescape')
- result = json.loads(outdata)
+
+ (filtered_outdata, json_warnings) = _filter_non_json_lines(outdata)
+
+ result = json.loads(filtered_outdata)
+
+ if json_warnings:
+ # merge JSON junk warnings with any existing module warnings
+ module_warnings = result.get('warnings', [])
+ if type(module_warnings) is not list:
+ module_warnings = [module_warnings]
+ module_warnings.extend(json_warnings)
+ result['warnings'] = module_warnings
+
if stderr:
result['stderr'] = stderr
jobfile.write(json.dumps(result))
@@ -100,11 +158,13 @@ def _run_module(wrapped_cmd, jid, job_path):
"failed": 1,
"cmd" : wrapped_cmd,
"msg": str(e),
+ "outdata": outdata, # temporary notice only
+ "stderr": stderr
}
result['ansible_job_id'] = jid
jobfile.write(json.dumps(result))
- except:
+ except (ValueError, Exception):
result = {
"failed" : 1,
"cmd" : wrapped_cmd,
diff --git a/utilities/logic/include.py b/utilities/logic/include.py
new file mode 100644
index 00000000..c251c501
--- /dev/null
+++ b/utilities/logic/include.py
@@ -0,0 +1,59 @@
+#!/usr/bin/python
+# -*- mode: python -*-
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+
+DOCUMENTATION = '''
+---
+author:
+ - "Ansible Core Team (@ansible)"
+module: include
+short_description: include a play or task list.
+description:
+ - Loads 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 normally run (in play).
+ - Before 2.0 all includes were 'static', executed at play load time.
+ - Since 2.0 task includes are dynamic and behave more like real tasks. This means they can be looped, skipped and use variables from any source.
+ Ansible tries to auto detect this, use the `static` directive (new in 2.1) to bypass autodetection.
+version_added: "0.6"
+options:
+ free-form:
+ description:
+ - This module allows you to specify the name of the file directly w/o any other options.
+notes:
+ - This is really not a module, though it appears as such, this is a feature of the Ansible Engine, as such it cannot be overridden the same way a module can.
+'''
+
+EXAMPLES = """
+# include a play after another play
+- hosts: localhost
+ tasks:
+ - debug: msg="play1"
+
+- include: otherplays.yml
+
+
+# include task list in play
+- hosts: all
+ tasks:
+ - debug: msg=task1
+ - include: stuff.yml
+ - debug: msg=task10
+
+# dyanmic include task list in play
+- hosts: all
+ tasks:
+ - debug: msg=task1
+ - include: {{hostvar}}.yml
+ static: no
+ when: hostvar is defined
+"""
+
+RETURN = """
+# this module does not return anything except plays or tasks to execute
+"""
diff --git a/windows/async_wrapper.ps1 b/windows/async_wrapper.ps1
index a4f90094..54d820fe 100644
--- a/windows/async_wrapper.ps1
+++ b/windows/async_wrapper.ps1
@@ -324,6 +324,10 @@ Function Start-Watchdog {
# FUTURE: use CreateProcess + stream redirection to watch for/return quick watchdog failures?
$result = $([wmiclass]"Win32_Process").Create($exec_path, $null, $pstartup)
+ # On fast + idle machines, the process never starts without this delay. Hopefully the switch to
+ # Win32 process launch will make this unnecessary.
+ Sleep -Seconds 1
+
$watchdog_pid = $result.ProcessId
return $watchdog_pid
diff --git a/windows/setup.ps1 b/windows/setup.ps1
index ff7ee2e5..fef2ade2 100644
--- a/windows/setup.ps1
+++ b/windows/setup.ps1
@@ -17,9 +17,7 @@
# WANT_JSON
# POWERSHELL_COMMON
-# enabled $params (David O'Brien, 06/08/2015)
-$params = Parse-Args $args;
-
+$params = Parse-Args $args -supports_check_mode $true
Function Get-CustomFacts {
[cmdletBinding()]
@@ -127,7 +125,6 @@ Set-Attr $result.ansible_facts "ansible_owner_contact" ([string] $win32_cs.Prima
Set-Attr $result.ansible_facts "ansible_user_dir" $env:userprofile
Set-Attr $result.ansible_facts "ansible_user_gecos" "" # Win32_UserAccount.FullName is probably the right thing here, but it can be expensive to get on large domains
Set-Attr $result.ansible_facts "ansible_user_id" $env:username
-Set-Attr $result.ansible_facts "ansible_user_uid" ([int] $user.User.Value.Substring(42))
Set-Attr $result.ansible_facts "ansible_user_sid" $user.User.Value
$date = New-Object psobject