Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

#!/usr/bin/python 

# Copyright: Ansible Project 

# 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': 'network'} 

 

 

DOCUMENTATION = """ 

--- 

module: nxos_command 

extends_documentation_fragment: nxos 

version_added: "2.1" 

author: "Peter Sprygada (@privateip)" 

short_description: Run arbitrary command on Cisco NXOS devices 

description: 

- Sends an arbitrary command to an NXOS node and returns the results 

read from the device. This module includes an 

argument that will cause the module to wait for a specific condition 

before returning or timing out if the condition is not met. 

options: 

commands: 

description: 

- The commands to send to the remote NXOS device. The resulting 

output from the command is returned. If the I(wait_for) 

argument is provided, the module is not returned until the 

condition is satisfied or the number of retires as expired. 

- The I(commands) argument also accepts an alternative form 

that allows for complex values that specify the command 

to run and the output format to return. This can be done 

on a command by command basis. The complex argument supports 

the keywords C(command) and C(output) where C(command) is the 

command to run and C(output) is one of 'text' or 'json'. 

required: true 

wait_for: 

description: 

- Specifies what to evaluate from the output of the command 

and what conditionals to apply. This argument will cause 

the task to wait for a particular conditional to be true 

before moving forward. If the conditional is not true 

by the configured retries, the task fails. See examples. 

required: false 

default: null 

aliases: ['waitfor'] 

version_added: "2.2" 

match: 

description: 

- The I(match) argument is used in conjunction with the 

I(wait_for) argument to specify the match policy. Valid 

values are C(all) or C(any). If the value is set to C(all) 

then all conditionals in the I(wait_for) must be satisfied. If 

the value is set to C(any) then only one of the values must be 

satisfied. 

required: false 

default: all 

version_added: "2.2" 

retries: 

description: 

- Specifies the number of retries a command should by tried 

before it is considered failed. The command is run on the 

target device every retry and evaluated against the I(wait_for) 

conditionals. 

required: false 

default: 10 

interval: 

description: 

- Configures the interval in seconds to wait between retries 

of the command. If the command does not pass the specified 

conditional, the interval indicates how to long to wait before 

trying the command again. 

required: false 

default: 1 

""" 

 

EXAMPLES = """ 

--- 

- name: run show version on remote devices 

nxos_command: 

commands: show version 

 

- name: run show version and check to see if output contains Cisco 

nxos_command: 

commands: show version 

wait_for: result[0] contains Cisco 

 

- name: run multiple commands on remote nodes 

nxos_command: 

commands: 

- show version 

- show interfaces 

 

- name: run multiple commands and evaluate the output 

nxos_command: 

commands: 

- show version 

- show interfaces 

wait_for: 

- result[0] contains Cisco 

- result[1] contains loopback0 

 

- name: run commands and specify the output format 

nxos_command: 

commands: 

- command: show version 

output: json 

""" 

 

RETURN = """ 

stdout: 

description: The set of responses from the commands 

returned: always apart from low level errors (such as action plugin) 

type: list 

sample: ['...', '...'] 

stdout_lines: 

description: The value of stdout split into a list 

returned: always apart from low level errors (such as action plugin) 

type: list 

sample: [['...', '...'], ['...'], ['...']] 

failed_conditions: 

description: The list of conditionals that have failed 

returned: failed 

type: list 

sample: ['...', '...'] 

""" 

import time 

 

from ansible.module_utils.basic import AnsibleModule 

from ansible.module_utils.network.common.parsing import Conditional, FailedConditionalError 

from ansible.module_utils.network.common.utils import ComplexList 

from ansible.module_utils.network.nxos.nxos import check_args, nxos_argument_spec, run_commands 

from ansible.module_utils.six import string_types 

from ansible.module_utils._text import to_native 

 

 

def to_lines(stdout): 

lines = list() 

for item in stdout: 

if isinstance(item, string_types): 

item = str(item).split('\n') 

lines.append(item) 

return lines 

 

 

def parse_commands(module, warnings): 

transform = ComplexList(dict( 

command=dict(key=True), 

output=dict(), 

prompt=dict(), 

answer=dict() 

), module) 

 

commands = transform(module.params['commands']) 

 

159 ↛ 160line 159 didn't jump to line 160, because the condition on line 159 was never true if module.check_mode: 

for item in list(commands): 

if not item['command'].startswith('show'): 

warnings.append( 

'Only show commands are supported when using check_mode, not ' 

'executing %s' % item['command'] 

) 

commands.remove(item) 

 

return commands 

 

 

def to_cli(obj): 

cmd = obj['command'] 

if obj.get('output') == 'json': 

cmd += ' | json' 

return cmd 

 

 

def main(): 

"""entry point for module execution 

""" 

argument_spec = dict( 

# { command: <str>, output: <str>, prompt: <str>, response: <str> } 

commands=dict(type='list', required=True), 

 

wait_for=dict(type='list', aliases=['waitfor']), 

match=dict(default='all', choices=['any', 'all']), 

 

retries=dict(default=10, type='int'), 

interval=dict(default=1, type='int') 

) 

 

argument_spec.update(nxos_argument_spec) 

 

module = AnsibleModule(argument_spec=argument_spec, 

supports_check_mode=True) 

 

result = {'changed': False} 

 

warnings = list() 

check_args(module, warnings) 

commands = parse_commands(module, warnings) 

result['warnings'] = warnings 

 

wait_for = module.params['wait_for'] or list() 

 

try: 

conditionals = [Conditional(c) for c in wait_for] 

except AttributeError as exc: 

module.fail_json(msg=to_native(exc)) 

 

retries = module.params['retries'] 

interval = module.params['interval'] 

match = module.params['match'] 

 

215 ↛ 234line 215 didn't jump to line 234, because the condition on line 215 was never false while retries > 0: 

responses = run_commands(module, commands) 

 

218 ↛ 219line 218 didn't jump to line 219, because the loop on line 218 never started for item in list(conditionals): 

try: 

if item(responses): 

if match == 'any': 

conditionals = list() 

break 

conditionals.remove(item) 

except FailedConditionalError as exc: 

module.fail_json(msg=to_native(exc)) 

 

228 ↛ 231line 228 didn't jump to line 231, because the condition on line 228 was never false if not conditionals: 

break 

 

time.sleep(interval) 

retries -= 1 

 

234 ↛ 235line 234 didn't jump to line 235, because the condition on line 234 was never true if conditionals: 

failed_conditions = [item.raw for item in conditionals] 

msg = 'One or more conditional statements have not be satisfied' 

module.fail_json(msg=msg, failed_conditions=failed_conditions) 

 

result.update({ 

'stdout': responses, 

'stdout_lines': to_lines(responses) 

}) 

 

module.exit_json(**result) 

 

 

247 ↛ exitline 247 didn't exit the module, because the condition on line 247 was never falseif __name__ == '__main__': 

main()