Skip to content
This repository was archived by the owner on Oct 30, 2018. It is now read-only.

Commit 51cf9a0

Browse files
sivelGreg DeKoenigsberg
authored and
Greg DeKoenigsberg
committed
Add new module 'expect'
1 parent c842c71 commit 51cf9a0

File tree

2 files changed

+189
-0
lines changed

2 files changed

+189
-0
lines changed

commands/__init__.py

Whitespace-only changes.

commands/expect.py

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
4+
# (c) 2015, Matt Martz <[email protected]>
5+
#
6+
# This file is part of Ansible
7+
#
8+
# Ansible is free software: you can redistribute it and/or modify
9+
# it under the terms of the GNU General Public License as published by
10+
# the Free Software Foundation, either version 3 of the License, or
11+
# (at your option) any later version.
12+
#
13+
# Ansible is distributed in the hope that it will be useful,
14+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
# GNU General Public License for more details.
17+
#
18+
# You should have received a copy of the GNU General Public License
19+
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
20+
21+
import datetime
22+
23+
try:
24+
import pexpect
25+
HAS_PEXPECT = True
26+
except ImportError:
27+
HAS_PEXPECT = False
28+
29+
30+
DOCUMENTATION = '''
31+
---
32+
module: expect
33+
version_added: 2.0
34+
short_description: Executes a command and responds to prompts
35+
description:
36+
- The M(expect) module executes a command and responds to prompts
37+
- The given command will be executed on all selected nodes. It will not be
38+
processed through the shell, so variables like C($HOME) and operations
39+
like C("<"), C(">"), C("|"), and C("&") will not work
40+
options:
41+
command:
42+
description:
43+
- the command module takes command to run.
44+
required: true
45+
creates:
46+
description:
47+
- a filename, when it already exists, this step will B(not) be run.
48+
required: false
49+
removes:
50+
description:
51+
- a filename, when it does not exist, this step will B(not) be run.
52+
required: false
53+
chdir:
54+
description:
55+
- cd into this directory before running the command
56+
required: false
57+
executable:
58+
description:
59+
- change the shell used to execute the command. Should be an absolute
60+
path to the executable.
61+
required: false
62+
responses:
63+
description:
64+
- Mapping of expected string and string to respond with
65+
required: true
66+
timeout:
67+
description:
68+
- Amount of time in seconds to wait for the expected strings
69+
default: 30
70+
echo:
71+
description:
72+
- Whether or not to echo out your response strings
73+
default: false
74+
requirements:
75+
- python >= 2.6
76+
- pexpect >= 3.3
77+
notes:
78+
- If you want to run a command through the shell (say you are using C(<),
79+
C(>), C(|), etc), you must specify a shell in the command such as
80+
C(/bin/bash -c "/path/to/something | grep else")
81+
author: '"Matt Martz (@sivel)" <[email protected]>'
82+
'''
83+
84+
EXAMPLES = '''
85+
- expect:
86+
command: passwd username
87+
responses:
88+
(?i)password: "MySekretPa$$word"
89+
'''
90+
91+
92+
def main():
93+
module = AnsibleModule(
94+
argument_spec=dict(
95+
command=dict(required=True),
96+
chdir=dict(),
97+
executable=dict(),
98+
creates=dict(),
99+
removes=dict(),
100+
responses=dict(type='dict', required=True),
101+
timeout=dict(type='int', default=30),
102+
echo=dict(type='bool', default=False),
103+
)
104+
)
105+
106+
if not HAS_PEXPECT:
107+
module.fail_json(msg='The pexpect python module is required')
108+
109+
chdir = module.params['chdir']
110+
executable = module.params['executable']
111+
args = module.params['command']
112+
creates = module.params['creates']
113+
removes = module.params['removes']
114+
responses = module.params['responses']
115+
timeout = module.params['timeout']
116+
echo = module.params['echo']
117+
118+
events = dict()
119+
for key, value in responses.iteritems():
120+
events[key.decode()] = u'%s\n' % value.rstrip('\n').decode()
121+
122+
if args.strip() == '':
123+
module.fail_json(rc=256, msg="no command given")
124+
125+
if chdir:
126+
chdir = os.path.abspath(os.path.expanduser(chdir))
127+
os.chdir(chdir)
128+
129+
if creates:
130+
# do not run the command if the line contains creates=filename
131+
# and the filename already exists. This allows idempotence
132+
# of command executions.
133+
v = os.path.expanduser(creates)
134+
if os.path.exists(v):
135+
module.exit_json(
136+
cmd=args,
137+
stdout="skipped, since %s exists" % v,
138+
changed=False,
139+
stderr=False,
140+
rc=0
141+
)
142+
143+
if removes:
144+
# do not run the command if the line contains removes=filename
145+
# and the filename does not exist. This allows idempotence
146+
# of command executions.
147+
v = os.path.expanduser(removes)
148+
if not os.path.exists(v):
149+
module.exit_json(
150+
cmd=args,
151+
stdout="skipped, since %s does not exist" % v,
152+
changed=False,
153+
stderr=False,
154+
rc=0
155+
)
156+
157+
startd = datetime.datetime.now()
158+
159+
if executable:
160+
cmd = '%s %s' % (executable, args)
161+
else:
162+
cmd = args
163+
164+
try:
165+
out, rc = pexpect.runu(cmd, timeout=timeout, withexitstatus=True,
166+
events=events, cwd=chdir, echo=echo)
167+
except pexpect.ExceptionPexpect, e:
168+
module.fail_json(msg='%s' % e)
169+
170+
endd = datetime.datetime.now()
171+
delta = endd - startd
172+
173+
if out is None:
174+
out = ''
175+
176+
module.exit_json(
177+
cmd=args,
178+
stdout=out.rstrip('\r\n'),
179+
rc=rc,
180+
start=str(startd),
181+
end=str(endd),
182+
delta=str(delta),
183+
changed=True,
184+
)
185+
186+
# import module snippets
187+
from ansible.module_utils.basic import *
188+
189+
main()

0 commit comments

Comments
 (0)