We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Fix traceback in service module when svc_cmd is None (2nd fix)
When service module is used on unsupported Linux system where init script is used directly, LinuxService.svc_cmd is None so .endswith() fails.

This extends fix from e2f20db53481128553d876109d5fbdab9f43dd5b also for state=restarted.

Fixes issue #3533

Running against Debian Lenny server:

```
$ ansible webserver.local -i webserver.local, -u root -m service -a 'name=apache2 state=restarted'
webserver.local | FAILED => failed to parse: Traceback (most recent call last):
  File "/root/.ansible/tmp/ansible-1379082596.06-63995244988034/service", line 2080, in <module>
    main()
  File "/root/.ansible/tmp/ansible-1379082596.06-63995244988034/service", line 1112, in main
    (rc, out, err) = service.modify_service_state()
  File "/root/.ansible/tmp/ansible-1379082596.06-63995244988034/service", line 299, in modify_service_state
    return self.service_control()
  File "/root/.ansible/tmp/ansible-1379082596.06-63995244988034/service", line 670, in service_control
    elif self.svc_cmd.endswith('rc-service'):
AttributeError: 'NoneType' object has no attribute 'endswith'
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lib/ansible/modules/network/junos/junos_command.py`
Content:
```
1 #!/usr/bin/python
2 #
3 # This file is part of Ansible
4 #
5 # Ansible is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # Ansible is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
17 #
18 
19 ANSIBLE_METADATA = {'status': ['preview'],
20                     'supported_by': 'core',
21                     'version': '1.0'}
22 
23 DOCUMENTATION = """
24 ---
25 module: junos_command
26 version_added: "2.1"
27 author: "Peter Sprygada (@privateip)"
28 short_description: Execute arbitrary commands on a remote device running Junos
29 description:
30   - Network devices running the Junos operating system provide a command
31     driven interface both over CLI and RPC.  This module provides an
32     interface to execute commands using these functions and return the
33     results to the Ansible playbook.  In addition, this
34     module can specify a set of conditionals to be evaluated against the
35     returned output, only returning control to the playbook once the
36     entire set of conditionals has been met.
37 extends_documentation_fragment: junos
38 options:
39   commands:
40     description:
41       - The C(commands) to send to the remote device over the Netconf
42         transport.  The resulting output from the command
43         is returned.  If the I(wait_for) argument is provided, the
44         module is not returned until the condition is satisfied or
45         the number of I(retries) has been exceeded.
46     required: false
47     default: null
48   rpcs:
49     description:
50       - The C(rpcs) argument accepts a list of RPCs to be executed
51         over a netconf session and the results from the RPC execution
52         is return to the playbook via the modules results dictionary.
53     required: false
54     default: null
55   wait_for:
56     description:
57       - Specifies what to evaluate from the output of the command
58         and what conditionals to apply.  This argument will cause
59         the task to wait for a particular conditional to be true
60         before moving forward.   If the conditional is not true
61         by the configured retries, the task fails.  See examples.
62     required: false
63     default: null
64     aliases: ['waitfor']
65     version_added: "2.2"
66   match:
67     description:
68       - The I(match) argument is used in conjunction with the
69         I(wait_for) argument to specify the match policy.  Valid
70         values are C(all) or C(any).  If the value is set to C(all)
71         then all conditionals in the I(wait_for) must be satisfied.  If
72         the value is set to C(any) then only one of the values must be
73         satisfied.
74     required: false
75     default: all
76     choices: ['any', 'all']
77     version_added: "2.2"
78   retries:
79     description:
80       - Specifies the number of retries a command should by tried
81         before it is considered failed.  The command is run on the
82         target device every retry and evaluated against the I(waitfor)
83         conditionals.
84     required: false
85     default: 10
86   interval:
87     description:
88       - Configures the interval in seconds to wait between retries
89         of the command.  If the command does not pass the specified
90         conditional, the interval indicates how to long to wait before
91         trying the command again.
92     required: false
93     default: 1
94   format:
95     description:
96       - Configures the encoding scheme to use when serializing output
97         from the device.  This handles how to properly understand the
98         output and apply the conditionals path to the result set.
99     required: false
100     default: 'xml'
101     choices: ['xml', 'text']
102 requirements:
103   - junos-eznc
104 notes:
105   - This module requires the netconf system service be enabled on
106     the remote device being managed
107 """
108 
109 EXAMPLES = """
110 # Note: examples below use the following provider dict to handle
111 #       transport and authentication to the node.
112 vars:
113   netconf:
114     host: "{{ inventory_hostname }}"
115     username: ansible
116     password: Ansible
117 
118 - name: run a set of commands
119   junos_command:
120     commands: ['show version', 'show ip route']
121     provider: "{{ netconf }}"
122 
123 - name: run a command with a conditional applied to the second command
124   junos_command:
125     commands:
126       - show version
127       - show interfaces fxp0
128     waitfor:
129       - "result[1].interface-information.physical-interface.name eq fxp0"
130     provider: "{{ netconf }}"
131 
132 - name: collect interface information using rpc
133   junos_command:
134     rpcs:
135       - "get_interface_information interface=em0 media=True"
136       - "get_interface_information interface=fxp0 media=True"
137     provider: "{{ netconf }}"
138 """
139 
140 RETURN = """
141 stdout:
142   description: The output from the commands read from the device
143   returned: always
144   type: list
145   sample: ['...', '...']
146 
147 stdout_lines:
148   description: The output read from the device split into lines
149   returned: always
150   type: list
151   sample: [['...', '...'], ['...', '...']]
152 
153 failed_conditionals:
154   description: the conditionals that failed
155   returned: failed
156   type: list
157   sample: ['...', '...']
158 """
159 
160 import ansible.module_utils.junos
161 from ansible.module_utils.basic import get_exception
162 from ansible.module_utils.network import NetworkModule, NetworkError
163 from ansible.module_utils.netcli import CommandRunner
164 from ansible.module_utils.netcli import AddCommandError, FailedConditionsError
165 from ansible.module_utils.netcli import FailedConditionalError, AddConditionError
166 from ansible.module_utils.junos import xml_to_json
167 from ansible.module_utils.six import string_types
168 
169 VALID_KEYS = {
170     'cli': frozenset(['command', 'output', 'prompt', 'response']),
171     'rpc': frozenset(['command', 'output'])
172 }
173 
174 
175 def to_lines(stdout):
176     for item in stdout:
177         if isinstance(item, string_types):
178             item = str(item).split('\n')
179         yield item
180 
181 def parse(module, command_type):
182     if command_type == 'cli':
183         items = module.params['commands']
184     elif command_type == 'rpc':
185         items = module.params['rpcs']
186 
187     parsed = list()
188     for item in (items or list()):
189         if isinstance(item, string_types):
190             item = dict(command=item, output=None)
191         elif 'command' not in item:
192             module.fail_json(msg='command keyword argument is required')
193         elif item.get('output') not in [None, 'text', 'xml']:
194             module.fail_json(msg='invalid output specified for command'
195                                  'Supported values are `text` or `xml`')
196         elif not set(item.keys()).issubset(VALID_KEYS[command_type]):
197             module.fail_json(msg='unknown command keyword specified.  Valid '
198                                  'values are %s' % ', '.join(VALID_KEYS[command_type]))
199 
200         if not item['output']:
201             item['output'] = module.params['display']
202 
203         item['command_type'] = command_type
204 
205         # show configuration [options] will return as text
206         if item['command'].startswith('show configuration'):
207             item['output'] = 'text'
208 
209         parsed.append(item)
210 
211     return parsed
212 
213 
214 def main():
215     """main entry point for Ansible module
216     """
217 
218     spec = dict(
219         commands=dict(type='list'),
220         rpcs=dict(type='list'),
221 
222         display=dict(default='xml', choices=['text', 'xml'],
223                      aliases=['format', 'output']),
224 
225         wait_for=dict(type='list', aliases=['waitfor']),
226         match=dict(default='all', choices=['all', 'any']),
227 
228         retries=dict(default=10, type='int'),
229         interval=dict(default=1, type='int'),
230 
231         transport=dict(default='netconf', choices=['netconf'])
232     )
233 
234     mutually_exclusive = [('commands', 'rpcs')]
235 
236     module = NetworkModule(argument_spec=spec,
237                            mutually_exclusive=mutually_exclusive,
238                            supports_check_mode=True)
239 
240     commands = list()
241     for key in VALID_KEYS.keys():
242         commands.extend(list(parse(module, key)))
243 
244     conditionals = module.params['wait_for'] or list()
245 
246     warnings = list()
247 
248     runner = CommandRunner(module)
249 
250     for cmd in commands:
251         if module.check_mode and not cmd['command'].startswith('show'):
252             warnings.append('only show commands are supported when using '
253                             'check mode, not executing `%s`' % cmd['command'])
254         else:
255             if cmd['command'].startswith('co'):
256                 module.fail_json(msg='junos_command does not support running '
257                                      'config mode commands.  Please use '
258                                      'junos_config instead')
259             try:
260                 runner.add_command(**cmd)
261             except AddCommandError:
262                 exc = get_exception()
263                 warnings.append('duplicate command detected: %s' % cmd)
264 
265     try:
266         for item in conditionals:
267             runner.add_conditional(item)
268     except (ValueError, AddConditionError):
269         exc = get_exception()
270         module.fail_json(msg=str(exc), condition=exc.condition)
271 
272     runner.retries = module.params['retries']
273     runner.interval = module.params['interval']
274     runner.match = module.params['match']
275 
276     try:
277         runner.run()
278     except FailedConditionsError:
279         exc = get_exception()
280         module.fail_json(msg=str(exc), failed_conditions=exc.failed_conditions)
281     except FailedConditionalError:
282         exc = get_exception()
283         module.fail_json(msg=str(exc), failed_conditional=exc.failed_conditional)
284     except NetworkError:
285         exc = get_exception()
286         module.fail_json(msg=str(exc))
287 
288     result = dict(changed=False, stdout=list())
289 
290     for cmd in commands:
291         try:
292             output = runner.get_command(cmd['command'], cmd.get('output'))
293         except ValueError:
294             output = 'command not executed due to check_mode, see warnings'
295         result['stdout'].append(output)
296 
297     result['warnings'] = warnings
298     result['stdout_lines'] = list(to_lines(result['stdout']))
299 
300     module.exit_json(**result)
301 
302 
303 if __name__ == '__main__':
304     main()
305
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lib/ansible/modules/network/junos/junos_command.py b/lib/ansible/modules/network/junos/junos_command.py
--- a/lib/ansible/modules/network/junos/junos_command.py
+++ b/lib/ansible/modules/network/junos/junos_command.py
@@ -98,12 +98,13 @@
         output and apply the conditionals path to the result set.
     required: false
     default: 'xml'
-    choices: ['xml', 'text']
+    choices: ['xml', 'text', 'json']
 requirements:
   - junos-eznc
 notes:
   - This module requires the netconf system service be enabled on
-    the remote device being managed
+    the remote device being managed. 'json' format is supported
+    for JUNON version >= 14.2
 """
 
 EXAMPLES = """
@@ -219,7 +220,7 @@
         commands=dict(type='list'),
         rpcs=dict(type='list'),
 
-        display=dict(default='xml', choices=['text', 'xml'],
+        display=dict(default='xml', choices=['text', 'xml', 'json'],
                      aliases=['format', 'output']),
 
         wait_for=dict(type='list', aliases=['waitfor']),

{"golden_diff": "diff --git a/lib/ansible/modules/network/junos/junos_command.py b/lib/ansible/modules/network/junos/junos_command.py\n--- a/lib/ansible/modules/network/junos/junos_command.py\n+++ b/lib/ansible/modules/network/junos/junos_command.py\n@@ -98,12 +98,13 @@\n         output and apply the conditionals path to the result set.\n     required: false\n     default: 'xml'\n-    choices: ['xml', 'text']\n+    choices: ['xml', 'text', 'json']\n requirements:\n   - junos-eznc\n notes:\n   - This module requires the netconf system service be enabled on\n-    the remote device being managed\n+    the remote device being managed. 'json' format is supported\n+    for JUNON version >= 14.2\n \"\"\"\n \n EXAMPLES = \"\"\"\n@@ -219,7 +220,7 @@\n         commands=dict(type='list'),\n         rpcs=dict(type='list'),\n \n-        display=dict(default='xml', choices=['text', 'xml'],\n+        display=dict(default='xml', choices=['text', 'xml', 'json'],\n                      aliases=['format', 'output']),\n \n         wait_for=dict(type='list', aliases=['waitfor']),\n", "issue": "Fix traceback in service module when svc_cmd is None (2nd fix)\nWhen service module is used on unsupported Linux system where init script is used directly, LinuxService.svc_cmd is None so .endswith() fails.\n\nThis extends fix from e2f20db53481128553d876109d5fbdab9f43dd5b also for state=restarted.\n\nFixes issue #3533\n\nRunning against Debian Lenny server:\n\n```\n$ ansible webserver.local -i webserver.local, -u root -m service -a 'name=apache2 state=restarted'\nwebserver.local | FAILED => failed to parse: Traceback (most recent call last):\n  File \"/root/.ansible/tmp/ansible-1379082596.06-63995244988034/service\", line 2080, in <module>\n    main()\n  File \"/root/.ansible/tmp/ansible-1379082596.06-63995244988034/service\", line 1112, in main\n    (rc, out, err) = service.modify_service_state()\n  File \"/root/.ansible/tmp/ansible-1379082596.06-63995244988034/service\", line 299, in modify_service_state\n    return self.service_control()\n  File \"/root/.ansible/tmp/ansible-1379082596.06-63995244988034/service\", line 670, in service_control\n    elif self.svc_cmd.endswith('rc-service'):\nAttributeError: 'NoneType' object has no attribute 'endswith'\n```\n\n", "before_files": [{"content": "#!/usr/bin/python\n#\n# This file is part of Ansible\n#\n# Ansible is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# Ansible is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.\n#\n\nANSIBLE_METADATA = {'status': ['preview'],\n                    'supported_by': 'core',\n                    'version': '1.0'}\n\nDOCUMENTATION = \"\"\"\n---\nmodule: junos_command\nversion_added: \"2.1\"\nauthor: \"Peter Sprygada (@privateip)\"\nshort_description: Execute arbitrary commands on a remote device running Junos\ndescription:\n  - Network devices running the Junos operating system provide a command\n    driven interface both over CLI and RPC.  This module provides an\n    interface to execute commands using these functions and return the\n    results to the Ansible playbook.  In addition, this\n    module can specify a set of conditionals to be evaluated against the\n    returned output, only returning control to the playbook once the\n    entire set of conditionals has been met.\nextends_documentation_fragment: junos\noptions:\n  commands:\n    description:\n      - The C(commands) to send to the remote device over the Netconf\n        transport.  The resulting output from the command\n        is returned.  If the I(wait_for) argument is provided, the\n        module is not returned until the condition is satisfied or\n        the number of I(retries) has been exceeded.\n    required: false\n    default: null\n  rpcs:\n    description:\n      - The C(rpcs) argument accepts a list of RPCs to be executed\n        over a netconf session and the results from the RPC execution\n        is return to the playbook via the modules results dictionary.\n    required: false\n    default: null\n  wait_for:\n    description:\n      - Specifies what to evaluate from the output of the command\n        and what conditionals to apply.  This argument will cause\n        the task to wait for a particular conditional to be true\n        before moving forward.   If the conditional is not true\n        by the configured retries, the task fails.  See examples.\n    required: false\n    default: null\n    aliases: ['waitfor']\n    version_added: \"2.2\"\n  match:\n    description:\n      - The I(match) argument is used in conjunction with the\n        I(wait_for) argument to specify the match policy.  Valid\n        values are C(all) or C(any).  If the value is set to C(all)\n        then all conditionals in the I(wait_for) must be satisfied.  If\n        the value is set to C(any) then only one of the values must be\n        satisfied.\n    required: false\n    default: all\n    choices: ['any', 'all']\n    version_added: \"2.2\"\n  retries:\n    description:\n      - Specifies the number of retries a command should by tried\n        before it is considered failed.  The command is run on the\n        target device every retry and evaluated against the I(waitfor)\n        conditionals.\n    required: false\n    default: 10\n  interval:\n    description:\n      - Configures the interval in seconds to wait between retries\n        of the command.  If the command does not pass the specified\n        conditional, the interval indicates how to long to wait before\n        trying the command again.\n    required: false\n    default: 1\n  format:\n    description:\n      - Configures the encoding scheme to use when serializing output\n        from the device.  This handles how to properly understand the\n        output and apply the conditionals path to the result set.\n    required: false\n    default: 'xml'\n    choices: ['xml', 'text']\nrequirements:\n  - junos-eznc\nnotes:\n  - This module requires the netconf system service be enabled on\n    the remote device being managed\n\"\"\"\n\nEXAMPLES = \"\"\"\n# Note: examples below use the following provider dict to handle\n#       transport and authentication to the node.\nvars:\n  netconf:\n    host: \"{{ inventory_hostname }}\"\n    username: ansible\n    password: Ansible\n\n- name: run a set of commands\n  junos_command:\n    commands: ['show version', 'show ip route']\n    provider: \"{{ netconf }}\"\n\n- name: run a command with a conditional applied to the second command\n  junos_command:\n    commands:\n      - show version\n      - show interfaces fxp0\n    waitfor:\n      - \"result[1].interface-information.physical-interface.name eq fxp0\"\n    provider: \"{{ netconf }}\"\n\n- name: collect interface information using rpc\n  junos_command:\n    rpcs:\n      - \"get_interface_information interface=em0 media=True\"\n      - \"get_interface_information interface=fxp0 media=True\"\n    provider: \"{{ netconf }}\"\n\"\"\"\n\nRETURN = \"\"\"\nstdout:\n  description: The output from the commands read from the device\n  returned: always\n  type: list\n  sample: ['...', '...']\n\nstdout_lines:\n  description: The output read from the device split into lines\n  returned: always\n  type: list\n  sample: [['...', '...'], ['...', '...']]\n\nfailed_conditionals:\n  description: the conditionals that failed\n  returned: failed\n  type: list\n  sample: ['...', '...']\n\"\"\"\n\nimport ansible.module_utils.junos\nfrom ansible.module_utils.basic import get_exception\nfrom ansible.module_utils.network import NetworkModule, NetworkError\nfrom ansible.module_utils.netcli import CommandRunner\nfrom ansible.module_utils.netcli import AddCommandError, FailedConditionsError\nfrom ansible.module_utils.netcli import FailedConditionalError, AddConditionError\nfrom ansible.module_utils.junos import xml_to_json\nfrom ansible.module_utils.six import string_types\n\nVALID_KEYS = {\n    'cli': frozenset(['command', 'output', 'prompt', 'response']),\n    'rpc': frozenset(['command', 'output'])\n}\n\n\ndef to_lines(stdout):\n    for item in stdout:\n        if isinstance(item, string_types):\n            item = str(item).split('\\n')\n        yield item\n\ndef parse(module, command_type):\n    if command_type == 'cli':\n        items = module.params['commands']\n    elif command_type == 'rpc':\n        items = module.params['rpcs']\n\n    parsed = list()\n    for item in (items or list()):\n        if isinstance(item, string_types):\n            item = dict(command=item, output=None)\n        elif 'command' not in item:\n            module.fail_json(msg='command keyword argument is required')\n        elif item.get('output') not in [None, 'text', 'xml']:\n            module.fail_json(msg='invalid output specified for command'\n                                 'Supported values are `text` or `xml`')\n        elif not set(item.keys()).issubset(VALID_KEYS[command_type]):\n            module.fail_json(msg='unknown command keyword specified.  Valid '\n                                 'values are %s' % ', '.join(VALID_KEYS[command_type]))\n\n        if not item['output']:\n            item['output'] = module.params['display']\n\n        item['command_type'] = command_type\n\n        # show configuration [options] will return as text\n        if item['command'].startswith('show configuration'):\n            item['output'] = 'text'\n\n        parsed.append(item)\n\n    return parsed\n\n\ndef main():\n    \"\"\"main entry point for Ansible module\n    \"\"\"\n\n    spec = dict(\n        commands=dict(type='list'),\n        rpcs=dict(type='list'),\n\n        display=dict(default='xml', choices=['text', 'xml'],\n                     aliases=['format', 'output']),\n\n        wait_for=dict(type='list', aliases=['waitfor']),\n        match=dict(default='all', choices=['all', 'any']),\n\n        retries=dict(default=10, type='int'),\n        interval=dict(default=1, type='int'),\n\n        transport=dict(default='netconf', choices=['netconf'])\n    )\n\n    mutually_exclusive = [('commands', 'rpcs')]\n\n    module = NetworkModule(argument_spec=spec,\n                           mutually_exclusive=mutually_exclusive,\n                           supports_check_mode=True)\n\n    commands = list()\n    for key in VALID_KEYS.keys():\n        commands.extend(list(parse(module, key)))\n\n    conditionals = module.params['wait_for'] or list()\n\n    warnings = list()\n\n    runner = CommandRunner(module)\n\n    for cmd in commands:\n        if module.check_mode and not cmd['command'].startswith('show'):\n            warnings.append('only show commands are supported when using '\n                            'check mode, not executing `%s`' % cmd['command'])\n        else:\n            if cmd['command'].startswith('co'):\n                module.fail_json(msg='junos_command does not support running '\n                                     'config mode commands.  Please use '\n                                     'junos_config instead')\n            try:\n                runner.add_command(**cmd)\n            except AddCommandError:\n                exc = get_exception()\n                warnings.append('duplicate command detected: %s' % cmd)\n\n    try:\n        for item in conditionals:\n            runner.add_conditional(item)\n    except (ValueError, AddConditionError):\n        exc = get_exception()\n        module.fail_json(msg=str(exc), condition=exc.condition)\n\n    runner.retries = module.params['retries']\n    runner.interval = module.params['interval']\n    runner.match = module.params['match']\n\n    try:\n        runner.run()\n    except FailedConditionsError:\n        exc = get_exception()\n        module.fail_json(msg=str(exc), failed_conditions=exc.failed_conditions)\n    except FailedConditionalError:\n        exc = get_exception()\n        module.fail_json(msg=str(exc), failed_conditional=exc.failed_conditional)\n    except NetworkError:\n        exc = get_exception()\n        module.fail_json(msg=str(exc))\n\n    result = dict(changed=False, stdout=list())\n\n    for cmd in commands:\n        try:\n            output = runner.get_command(cmd['command'], cmd.get('output'))\n        except ValueError:\n            output = 'command not executed due to check_mode, see warnings'\n        result['stdout'].append(output)\n\n    result['warnings'] = warnings\n    result['stdout_lines'] = list(to_lines(result['stdout']))\n\n    module.exit_json(**result)\n\n\nif __name__ == '__main__':\n    main()\n", "path": "lib/ansible/modules/network/junos/junos_command.py"}], "after_files": [{"content": "#!/usr/bin/python\n#\n# This file is part of Ansible\n#\n# Ansible is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# Ansible is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.\n#\n\nANSIBLE_METADATA = {'status': ['preview'],\n                    'supported_by': 'core',\n                    'version': '1.0'}\n\nDOCUMENTATION = \"\"\"\n---\nmodule: junos_command\nversion_added: \"2.1\"\nauthor: \"Peter Sprygada (@privateip)\"\nshort_description: Execute arbitrary commands on a remote device running Junos\ndescription:\n  - Network devices running the Junos operating system provide a command\n    driven interface both over CLI and RPC.  This module provides an\n    interface to execute commands using these functions and return the\n    results to the Ansible playbook.  In addition, this\n    module can specify a set of conditionals to be evaluated against the\n    returned output, only returning control to the playbook once the\n    entire set of conditionals has been met.\nextends_documentation_fragment: junos\noptions:\n  commands:\n    description:\n      - The C(commands) to send to the remote device over the Netconf\n        transport.  The resulting output from the command\n        is returned.  If the I(wait_for) argument is provided, the\n        module is not returned until the condition is satisfied or\n        the number of I(retries) has been exceeded.\n    required: false\n    default: null\n  rpcs:\n    description:\n      - The C(rpcs) argument accepts a list of RPCs to be executed\n        over a netconf session and the results from the RPC execution\n        is return to the playbook via the modules results dictionary.\n    required: false\n    default: null\n  wait_for:\n    description:\n      - Specifies what to evaluate from the output of the command\n        and what conditionals to apply.  This argument will cause\n        the task to wait for a particular conditional to be true\n        before moving forward.   If the conditional is not true\n        by the configured retries, the task fails.  See examples.\n    required: false\n    default: null\n    aliases: ['waitfor']\n    version_added: \"2.2\"\n  match:\n    description:\n      - The I(match) argument is used in conjunction with the\n        I(wait_for) argument to specify the match policy.  Valid\n        values are C(all) or C(any).  If the value is set to C(all)\n        then all conditionals in the I(wait_for) must be satisfied.  If\n        the value is set to C(any) then only one of the values must be\n        satisfied.\n    required: false\n    default: all\n    choices: ['any', 'all']\n    version_added: \"2.2\"\n  retries:\n    description:\n      - Specifies the number of retries a command should by tried\n        before it is considered failed.  The command is run on the\n        target device every retry and evaluated against the I(waitfor)\n        conditionals.\n    required: false\n    default: 10\n  interval:\n    description:\n      - Configures the interval in seconds to wait between retries\n        of the command.  If the command does not pass the specified\n        conditional, the interval indicates how to long to wait before\n        trying the command again.\n    required: false\n    default: 1\n  format:\n    description:\n      - Configures the encoding scheme to use when serializing output\n        from the device.  This handles how to properly understand the\n        output and apply the conditionals path to the result set.\n    required: false\n    default: 'xml'\n    choices: ['xml', 'text', 'json']\nrequirements:\n  - junos-eznc\nnotes:\n  - This module requires the netconf system service be enabled on\n    the remote device being managed. 'json' format is supported\n    for JUNON version >= 14.2\n\"\"\"\n\nEXAMPLES = \"\"\"\n# Note: examples below use the following provider dict to handle\n#       transport and authentication to the node.\nvars:\n  netconf:\n    host: \"{{ inventory_hostname }}\"\n    username: ansible\n    password: Ansible\n\n- name: run a set of commands\n  junos_command:\n    commands: ['show version', 'show ip route']\n    provider: \"{{ netconf }}\"\n\n- name: run a command with a conditional applied to the second command\n  junos_command:\n    commands:\n      - show version\n      - show interfaces fxp0\n    waitfor:\n      - \"result[1].interface-information.physical-interface.name eq fxp0\"\n    provider: \"{{ netconf }}\"\n\n- name: collect interface information using rpc\n  junos_command:\n    rpcs:\n      - \"get_interface_information interface=em0 media=True\"\n      - \"get_interface_information interface=fxp0 media=True\"\n    provider: \"{{ netconf }}\"\n\"\"\"\n\nRETURN = \"\"\"\nstdout:\n  description: The output from the commands read from the device\n  returned: always\n  type: list\n  sample: ['...', '...']\n\nstdout_lines:\n  description: The output read from the device split into lines\n  returned: always\n  type: list\n  sample: [['...', '...'], ['...', '...']]\n\nfailed_conditionals:\n  description: the conditionals that failed\n  returned: failed\n  type: list\n  sample: ['...', '...']\n\"\"\"\n\nimport ansible.module_utils.junos\nfrom ansible.module_utils.basic import get_exception\nfrom ansible.module_utils.network import NetworkModule, NetworkError\nfrom ansible.module_utils.netcli import CommandRunner\nfrom ansible.module_utils.netcli import AddCommandError, FailedConditionsError\nfrom ansible.module_utils.netcli import FailedConditionalError, AddConditionError\nfrom ansible.module_utils.junos import xml_to_json\nfrom ansible.module_utils.six import string_types\n\nVALID_KEYS = {\n    'cli': frozenset(['command', 'output', 'prompt', 'response']),\n    'rpc': frozenset(['command', 'output'])\n}\n\n\ndef to_lines(stdout):\n    for item in stdout:\n        if isinstance(item, string_types):\n            item = str(item).split('\\n')\n        yield item\n\ndef parse(module, command_type):\n    if command_type == 'cli':\n        items = module.params['commands']\n    elif command_type == 'rpc':\n        items = module.params['rpcs']\n\n    parsed = list()\n    for item in (items or list()):\n        if isinstance(item, string_types):\n            item = dict(command=item, output=None)\n        elif 'command' not in item:\n            module.fail_json(msg='command keyword argument is required')\n        elif item.get('output') not in [None, 'text', 'xml']:\n            module.fail_json(msg='invalid output specified for command'\n                                 'Supported values are `text` or `xml`')\n        elif not set(item.keys()).issubset(VALID_KEYS[command_type]):\n            module.fail_json(msg='unknown command keyword specified.  Valid '\n                                 'values are %s' % ', '.join(VALID_KEYS[command_type]))\n\n        if not item['output']:\n            item['output'] = module.params['display']\n\n        item['command_type'] = command_type\n\n        # show configuration [options] will return as text\n        if item['command'].startswith('show configuration'):\n            item['output'] = 'text'\n\n        parsed.append(item)\n\n    return parsed\n\n\ndef main():\n    \"\"\"main entry point for Ansible module\n    \"\"\"\n\n    spec = dict(\n        commands=dict(type='list'),\n        rpcs=dict(type='list'),\n\n        display=dict(default='xml', choices=['text', 'xml', 'json'],\n                     aliases=['format', 'output']),\n\n        wait_for=dict(type='list', aliases=['waitfor']),\n        match=dict(default='all', choices=['all', 'any']),\n\n        retries=dict(default=10, type='int'),\n        interval=dict(default=1, type='int'),\n\n        transport=dict(default='netconf', choices=['netconf'])\n    )\n\n    mutually_exclusive = [('commands', 'rpcs')]\n\n    module = NetworkModule(argument_spec=spec,\n                           mutually_exclusive=mutually_exclusive,\n                           supports_check_mode=True)\n\n    commands = list()\n    for key in VALID_KEYS.keys():\n        commands.extend(list(parse(module, key)))\n\n    conditionals = module.params['wait_for'] or list()\n\n    warnings = list()\n\n    runner = CommandRunner(module)\n\n    for cmd in commands:\n        if module.check_mode and not cmd['command'].startswith('show'):\n            warnings.append('only show commands are supported when using '\n                            'check mode, not executing `%s`' % cmd['command'])\n        else:\n            if cmd['command'].startswith('co'):\n                module.fail_json(msg='junos_command does not support running '\n                                     'config mode commands.  Please use '\n                                     'junos_config instead')\n            try:\n                runner.add_command(**cmd)\n            except AddCommandError:\n                exc = get_exception()\n                warnings.append('duplicate command detected: %s' % cmd)\n\n    try:\n        for item in conditionals:\n            runner.add_conditional(item)\n    except (ValueError, AddConditionError):\n        exc = get_exception()\n        module.fail_json(msg=str(exc), condition=exc.condition)\n\n    runner.retries = module.params['retries']\n    runner.interval = module.params['interval']\n    runner.match = module.params['match']\n\n    try:\n        runner.run()\n    except FailedConditionsError:\n        exc = get_exception()\n        module.fail_json(msg=str(exc), failed_conditions=exc.failed_conditions)\n    except FailedConditionalError:\n        exc = get_exception()\n        module.fail_json(msg=str(exc), failed_conditional=exc.failed_conditional)\n    except NetworkError:\n        exc = get_exception()\n        module.fail_json(msg=str(exc))\n\n    result = dict(changed=False, stdout=list())\n\n    for cmd in commands:\n        try:\n            output = runner.get_command(cmd['command'], cmd.get('output'))\n        except ValueError:\n            output = 'command not executed due to check_mode, see warnings'\n        result['stdout'].append(output)\n\n    result['warnings'] = warnings\n    result['stdout_lines'] = list(to_lines(result['stdout']))\n\n    module.exit_json(**result)\n\n\nif __name__ == '__main__':\n    main()\n", "path": "lib/ansible/modules/network/junos/junos_command.py"}]}