1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
|
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2016-2017, Cumulus Networks <ce-ceng@cumulusnetworks.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: nclu
version_added: "2.3"
author: "Cumulus Networks"
short_description: Configure network interfaces using NCLU
description:
- Interface to the Network Command Line Utility, developed to make it easier
to configure operating systems running ifupdown2 and Quagga, such as
Cumulus Linux. Command documentation is available at
U(https://docs.cumulusnetworks.com/display/DOCS/Network+Command+Line+Utility)
options:
commands:
description:
- A list of strings containing the net commands to run. Mutually
exclusive with I(template).
template:
description:
- A single, multi-line string with jinja2 formatting. This string
will be broken by lines, and each line will be run through net.
Mutually exclusive with I(commands).
commit:
description:
- When true, performs a 'net commit' at the end of the block.
Mutually exclusive with I(atomic).
default: false
abort:
description:
- Boolean. When true, perform a 'net abort' before the block.
This cleans out any uncommitted changes in the buffer.
Mutually exclusive with I(atomic).
default: false
atomic:
description:
- When true, equivalent to both I(commit) and I(abort) being true.
Mutually exclusive with I(commit) and I(atomic).
default: false
description:
description:
- Commit description that will be recorded to the commit log if
I(commit) or I(atomic) are true.
default: "Ansible-originated commit"
'''
EXAMPLES = '''
- name: Add two interfaces without committing any changes
nclu:
commands:
- add int swp1
- add int swp2
- name: Add 48 interfaces and commit the change.
nclu:
template: |
{% for iface in range(1,49) %}
add int swp{{iface}}
{% endfor %}
commit: true
description: "Ansible - add swps1-48"
- name: Fetch Details From All Interfaces In JSON Format
nclu:
commands:
- show interface json
register: output
- name: Print Interface Details
debug:
var: output["msg"]
- name: Atomically add an interface
nclu:
commands:
- add int swp1
atomic: true
description: "Ansible - add swp1"
- name: Configure BGP AS and add 2 EBGP neighbors using BGP Unnumbered
nclu:
commands:
- add bgp autonomous-system 65000
- add bgp neighbor swp51 interface remote-as external
- add bgp neighbor swp52 interface remote-as external
commit: true
'''
RETURN = '''
changed:
description: whether the interface was changed
returned: changed
type: bool
sample: True
msg:
description: human-readable report of success or failure
returned: always
type: string
sample: "interface bond0 config updated"
'''
from ansible.module_utils.basic import AnsibleModule
def command_helper(module, command, errmsg=None):
"""Run a command, catch any nclu errors"""
(_rc, output, _err) = module.run_command("/usr/bin/net %s" % command)
if _rc or 'ERROR' in output or 'ERROR' in _err:
module.fail_json(msg=errmsg or output)
return str(output)
def check_pending(module):
"""Check the pending diff of the nclu buffer."""
pending = command_helper(module, "pending", "Error in pending config. You may want to view `net pending` on this target.")
delimeter1 = "net add/del commands since the last 'net commit'"
color1 = '\x1b[94m'
if delimeter1 in pending:
pending = pending.split(delimeter1)[0]
pending = pending.replace(color1, '')
return pending.strip()
def run_nclu(module, command_list, command_string, commit, atomic, abort, description):
_changed = False
commands = []
if command_list:
commands = command_list
elif command_string:
commands = command_string.splitlines()
do_commit = False
do_abort = abort
if commit or atomic:
do_commit = True
if atomic:
do_abort = True
if do_abort:
command_helper(module, "abort")
# First, look at the staged commands.
before = check_pending(module)
# Run all of the net commands
output_lines = []
for line in commands:
output_lines += [command_helper(module, line.strip(), "Failed on line %s" % line)]
output = "\n".join(output_lines)
# If pending changes changed, report a change.
after = check_pending(module)
if before == after:
_changed = False
else:
_changed = True
# Do the commit.
if do_commit:
result = command_helper(module, "commit description '%s'" % description)
if "commit ignored" in result:
_changed = False
command_helper(module, "abort")
elif command_helper(module, "show commit last") == "":
_changed = False
return _changed, output
def main(testing=False):
module = AnsibleModule(argument_spec=dict(
commands=dict(required=False, type='list'),
template=dict(required=False, type='str'),
description=dict(required=False, type='str', default="Ansible-originated commit"),
abort=dict(required=False, type='bool', default=False),
commit=dict(required=False, type='bool', default=False),
atomic=dict(required=False, type='bool', default=False)),
mutually_exclusive=[('commands', 'template'),
('commit', 'atomic'),
('abort', 'atomic')]
)
command_list = module.params.get('commands', None)
command_string = module.params.get('template', None)
commit = module.params.get('commit')
atomic = module.params.get('atomic')
abort = module.params.get('abort')
description = module.params.get('description')
_changed, output = run_nclu(module, command_list, command_string, commit, atomic, abort, description)
if not testing:
module.exit_json(changed=_changed, msg=output)
elif testing:
return {"changed": _changed, "msg": output}
if __name__ == '__main__':
main()
|