We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[BUG] iptable insert 
The following doesn't appear to handle a situation where the last rule in the INPUT chain is a DROP and we've witnessed behavior where the installer is appending the etcd 8443 rule after the DROP. 

"os_firewall_manage_iptables.py" line 155 of 273

```
            # Naively assume that if the last row is a REJECT rule, then
            # we can add insert our rule right before it, otherwise we
            # assume that we can just append the rule.
            if (last_rule_num and last_rule_target
                    and last_rule_target == 'REJECT'):
                # insert rule
                cmd = self.cmd + ['-I', self.jump_rule_chain,
                                  str(last_rule_num)]
            else:
                # append rule
                cmd = self.cmd + ['-A', self.jump_rule_chain]
```

Suggested Fix

```
           # Naively assume that if the last row is a REJECT rule, then
            # we can add insert our rule right before it, otherwise we
            # assume that we can just append the rule.
            if (last_rule_num and last_rule_target
                    and ( last_rule_target == 'REJECT' || last_rule_target == 'DROP')):
                # insert rule
                cmd = self.cmd + ['-I', self.jump_rule_chain,
                                  str(last_rule_num)]
            else:
                # append rule
                cmd = self.cmd + ['-A', self.jump_rule_chain]
            cmd += ['-j', self.chain]
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `roles/os_firewall/library/os_firewall_manage_iptables.py`
Content:
```
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3 # vim: expandtab:tabstop=4:shiftwidth=4
4 # pylint: disable=fixme, missing-docstring
5 from subprocess import call, check_output
6 
7 DOCUMENTATION = '''
8 ---
9 module: os_firewall_manage_iptables
10 short_description: This module manages iptables rules for a given chain
11 author: Jason DeTiberus
12 requirements: [ ]
13 '''
14 EXAMPLES = '''
15 '''
16 
17 
18 class IpTablesError(Exception):
19     def __init__(self, msg, cmd, exit_code, output):
20         super(IpTablesError, self).__init__(msg)
21         self.msg = msg
22         self.cmd = cmd
23         self.exit_code = exit_code
24         self.output = output
25 
26 
27 class IpTablesAddRuleError(IpTablesError):
28     pass
29 
30 
31 class IpTablesRemoveRuleError(IpTablesError):
32     pass
33 
34 
35 class IpTablesSaveError(IpTablesError):
36     pass
37 
38 
39 class IpTablesCreateChainError(IpTablesError):
40     def __init__(self, chain, msg, cmd, exit_code, output): # pylint: disable=too-many-arguments, line-too-long
41         super(IpTablesCreateChainError, self).__init__(msg, cmd, exit_code,
42                                                        output)
43         self.chain = chain
44 
45 
46 class IpTablesCreateJumpRuleError(IpTablesError):
47     def __init__(self, chain, msg, cmd, exit_code, output): # pylint: disable=too-many-arguments, line-too-long
48         super(IpTablesCreateJumpRuleError, self).__init__(msg, cmd, exit_code,
49                                                           output)
50         self.chain = chain
51 
52 
53 # TODO: impliment rollbacks for any events that where successful and an
54 # exception was thrown later. for example, when the chain is created
55 # successfully, but the add/remove rule fails.
56 class IpTablesManager(object): # pylint: disable=too-many-instance-attributes
57     def __init__(self, module):
58         self.module = module
59         self.ip_version = module.params['ip_version']
60         self.check_mode = module.check_mode
61         self.chain = module.params['chain']
62         self.create_jump_rule = module.params['create_jump_rule']
63         self.jump_rule_chain = module.params['jump_rule_chain']
64         self.cmd = self.gen_cmd()
65         self.save_cmd = self.gen_save_cmd()
66         self.output = []
67         self.changed = False
68 
69     def save(self):
70         try:
71             self.output.append(check_output(self.save_cmd,
72                                             stderr=subprocess.STDOUT))
73         except subprocess.CalledProcessError as ex:
74             raise IpTablesSaveError(
75                 msg="Failed to save iptables rules",
76                 cmd=ex.cmd, exit_code=ex.returncode, output=ex.output)
77 
78     def verify_chain(self):
79         if not self.chain_exists():
80             self.create_chain()
81         if self.create_jump_rule and not self.jump_rule_exists():
82             self.create_jump()
83 
84     def add_rule(self, port, proto):
85         rule = self.gen_rule(port, proto)
86         if not self.rule_exists(rule):
87             self.verify_chain()
88 
89             if self.check_mode:
90                 self.changed = True
91                 self.output.append("Create rule for %s %s" % (proto, port))
92             else:
93                 cmd = self.cmd + ['-A'] + rule
94                 try:
95                     self.output.append(check_output(cmd))
96                     self.changed = True
97                     self.save()
98                 except subprocess.CalledProcessError as ex:
99                     raise IpTablesCreateChainError(
100                         chain=self.chain,
101                         msg="Failed to create rule for "
102                             "%s %s" % (proto, port),
103                         cmd=ex.cmd, exit_code=ex.returncode,
104                         output=ex.output)
105 
106     def remove_rule(self, port, proto):
107         rule = self.gen_rule(port, proto)
108         if self.rule_exists(rule):
109             if self.check_mode:
110                 self.changed = True
111                 self.output.append("Remove rule for %s %s" % (proto, port))
112             else:
113                 cmd = self.cmd + ['-D'] + rule
114                 try:
115                     self.output.append(check_output(cmd))
116                     self.changed = True
117                     self.save()
118                 except subprocess.CalledProcessError as ex:
119                     raise IpTablesRemoveRuleError(
120                         chain=self.chain,
121                         msg="Failed to remove rule for %s %s" % (proto, port),
122                         cmd=ex.cmd, exit_code=ex.returncode, output=ex.output)
123 
124     def rule_exists(self, rule):
125         check_cmd = self.cmd + ['-C'] + rule
126         return True if call(check_cmd) == 0 else False
127 
128     def gen_rule(self, port, proto):
129         return [self.chain, '-p', proto, '-m', 'state', '--state', 'NEW',
130                 '-m', proto, '--dport', str(port), '-j', 'ACCEPT']
131 
132     def create_jump(self):
133         if self.check_mode:
134             self.changed = True
135             self.output.append("Create jump rule for chain %s" % self.chain)
136         else:
137             try:
138                 cmd = self.cmd + ['-L', self.jump_rule_chain, '--line-numbers']
139                 output = check_output(cmd, stderr=subprocess.STDOUT)
140 
141                 # break the input rules into rows and columns
142                 input_rules = [s.split() for s in output.split('\n')]
143 
144                 # Find the last numbered rule
145                 last_rule_num = None
146                 last_rule_target = None
147                 for rule in input_rules[:-1]:
148                     if rule:
149                         try:
150                             last_rule_num = int(rule[0])
151                         except ValueError:
152                             continue
153                         last_rule_target = rule[1]
154 
155                 # Naively assume that if the last row is a REJECT rule, then
156                 # we can add insert our rule right before it, otherwise we
157                 # assume that we can just append the rule.
158                 if (last_rule_num and last_rule_target
159                         and last_rule_target == 'REJECT'):
160                     # insert rule
161                     cmd = self.cmd + ['-I', self.jump_rule_chain,
162                                       str(last_rule_num)]
163                 else:
164                     # append rule
165                     cmd = self.cmd + ['-A', self.jump_rule_chain]
166                 cmd += ['-j', self.chain]
167                 output = check_output(cmd, stderr=subprocess.STDOUT)
168                 self.changed = True
169                 self.output.append(output)
170                 self.save()
171             except subprocess.CalledProcessError as ex:
172                 if '--line-numbers' in ex.cmd:
173                     raise IpTablesCreateJumpRuleError(
174                         chain=self.chain,
175                         msg=("Failed to query existing " +
176                              self.jump_rule_chain +
177                              " rules to determine jump rule location"),
178                         cmd=ex.cmd, exit_code=ex.returncode,
179                         output=ex.output)
180                 else:
181                     raise IpTablesCreateJumpRuleError(
182                         chain=self.chain,
183                         msg=("Failed to create jump rule for chain " +
184                              self.chain),
185                         cmd=ex.cmd, exit_code=ex.returncode,
186                         output=ex.output)
187 
188     def create_chain(self):
189         if self.check_mode:
190             self.changed = True
191             self.output.append("Create chain %s" % self.chain)
192         else:
193             try:
194                 cmd = self.cmd + ['-N', self.chain]
195                 self.output.append(check_output(cmd,
196                                                 stderr=subprocess.STDOUT))
197                 self.changed = True
198                 self.output.append("Successfully created chain %s" %
199                                    self.chain)
200                 self.save()
201             except subprocess.CalledProcessError as ex:
202                 raise IpTablesCreateChainError(
203                     chain=self.chain,
204                     msg="Failed to create chain: %s" % self.chain,
205                     cmd=ex.cmd, exit_code=ex.returncode, output=ex.output
206                     )
207 
208     def jump_rule_exists(self):
209         cmd = self.cmd + ['-C', self.jump_rule_chain, '-j', self.chain]
210         return True if call(cmd) == 0 else False
211 
212     def chain_exists(self):
213         cmd = self.cmd + ['-L', self.chain]
214         return True if call(cmd) == 0 else False
215 
216     def gen_cmd(self):
217         cmd = 'iptables' if self.ip_version == 'ipv4' else 'ip6tables'
218         return ["/usr/sbin/%s" % cmd]
219 
220     def gen_save_cmd(self): # pylint: disable=no-self-use
221         return ['/usr/libexec/iptables/iptables.init', 'save']
222 
223 
224 def main():
225     module = AnsibleModule(
226         argument_spec=dict(
227             name=dict(required=True),
228             action=dict(required=True, choices=['add', 'remove',
229                                                 'verify_chain']),
230             chain=dict(required=False, default='OS_FIREWALL_ALLOW'),
231             create_jump_rule=dict(required=False, type='bool', default=True),
232             jump_rule_chain=dict(required=False, default='INPUT'),
233             protocol=dict(required=False, choices=['tcp', 'udp']),
234             port=dict(required=False, type='int'),
235             ip_version=dict(required=False, default='ipv4',
236                             choices=['ipv4', 'ipv6']),
237         ),
238         supports_check_mode=True
239     )
240 
241     action = module.params['action']
242     protocol = module.params['protocol']
243     port = module.params['port']
244 
245     if action in ['add', 'remove']:
246         if not protocol:
247             error = "protocol is required when action is %s" % action
248             module.fail_json(msg=error)
249         if not port:
250             error = "port is required when action is %s" % action
251             module.fail_json(msg=error)
252 
253     iptables_manager = IpTablesManager(module)
254 
255     try:
256         if action == 'add':
257             iptables_manager.add_rule(port, protocol)
258         elif action == 'remove':
259             iptables_manager.remove_rule(port, protocol)
260         elif action == 'verify_chain':
261             iptables_manager.verify_chain()
262     except IpTablesError as ex:
263         module.fail_json(msg=ex.msg)
264 
265     return module.exit_json(changed=iptables_manager.changed,
266                             output=iptables_manager.output)
267 
268 
269 # pylint: disable=redefined-builtin, unused-wildcard-import, wildcard-import
270 # import module snippets
271 from ansible.module_utils.basic import *
272 if __name__ == '__main__':
273     main()
274
```
--- 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/roles/os_firewall/library/os_firewall_manage_iptables.py b/roles/os_firewall/library/os_firewall_manage_iptables.py
--- a/roles/os_firewall/library/os_firewall_manage_iptables.py
+++ b/roles/os_firewall/library/os_firewall_manage_iptables.py
@@ -37,14 +37,14 @@
 
 
 class IpTablesCreateChainError(IpTablesError):
-    def __init__(self, chain, msg, cmd, exit_code, output): # pylint: disable=too-many-arguments, line-too-long
+    def __init__(self, chain, msg, cmd, exit_code, output): # pylint: disable=too-many-arguments, line-too-long, redefined-outer-name
         super(IpTablesCreateChainError, self).__init__(msg, cmd, exit_code,
                                                        output)
         self.chain = chain
 
 
 class IpTablesCreateJumpRuleError(IpTablesError):
-    def __init__(self, chain, msg, cmd, exit_code, output): # pylint: disable=too-many-arguments, line-too-long
+    def __init__(self, chain, msg, cmd, exit_code, output): # pylint: disable=too-many-arguments, line-too-long, redefined-outer-name
         super(IpTablesCreateJumpRuleError, self).__init__(msg, cmd, exit_code,
                                                           output)
         self.chain = chain
@@ -152,11 +152,11 @@
                             continue
                         last_rule_target = rule[1]
 
-                # Naively assume that if the last row is a REJECT rule, then
-                # we can add insert our rule right before it, otherwise we
+                # Naively assume that if the last row is a REJECT or DROP rule,
+                # then we can insert our rule right before it, otherwise we
                 # assume that we can just append the rule.
                 if (last_rule_num and last_rule_target
-                        and last_rule_target == 'REJECT'):
+                        and last_rule_target in ['REJECT', 'DROP']):
                     # insert rule
                     cmd = self.cmd + ['-I', self.jump_rule_chain,
                                       str(last_rule_num)]

{"golden_diff": "diff --git a/roles/os_firewall/library/os_firewall_manage_iptables.py b/roles/os_firewall/library/os_firewall_manage_iptables.py\n--- a/roles/os_firewall/library/os_firewall_manage_iptables.py\n+++ b/roles/os_firewall/library/os_firewall_manage_iptables.py\n@@ -37,14 +37,14 @@\n \n \n class IpTablesCreateChainError(IpTablesError):\n-    def __init__(self, chain, msg, cmd, exit_code, output): # pylint: disable=too-many-arguments, line-too-long\n+    def __init__(self, chain, msg, cmd, exit_code, output): # pylint: disable=too-many-arguments, line-too-long, redefined-outer-name\n         super(IpTablesCreateChainError, self).__init__(msg, cmd, exit_code,\n                                                        output)\n         self.chain = chain\n \n \n class IpTablesCreateJumpRuleError(IpTablesError):\n-    def __init__(self, chain, msg, cmd, exit_code, output): # pylint: disable=too-many-arguments, line-too-long\n+    def __init__(self, chain, msg, cmd, exit_code, output): # pylint: disable=too-many-arguments, line-too-long, redefined-outer-name\n         super(IpTablesCreateJumpRuleError, self).__init__(msg, cmd, exit_code,\n                                                           output)\n         self.chain = chain\n@@ -152,11 +152,11 @@\n                             continue\n                         last_rule_target = rule[1]\n \n-                # Naively assume that if the last row is a REJECT rule, then\n-                # we can add insert our rule right before it, otherwise we\n+                # Naively assume that if the last row is a REJECT or DROP rule,\n+                # then we can insert our rule right before it, otherwise we\n                 # assume that we can just append the rule.\n                 if (last_rule_num and last_rule_target\n-                        and last_rule_target == 'REJECT'):\n+                        and last_rule_target in ['REJECT', 'DROP']):\n                     # insert rule\n                     cmd = self.cmd + ['-I', self.jump_rule_chain,\n                                       str(last_rule_num)]\n", "issue": "[BUG] iptable insert \nThe following doesn't appear to handle a situation where the last rule in the INPUT chain is a DROP and we've witnessed behavior where the installer is appending the etcd 8443 rule after the DROP. \n\n\"os_firewall_manage_iptables.py\" line 155 of 273\n\n```\n            # Naively assume that if the last row is a REJECT rule, then\n            # we can add insert our rule right before it, otherwise we\n            # assume that we can just append the rule.\n            if (last_rule_num and last_rule_target\n                    and last_rule_target == 'REJECT'):\n                # insert rule\n                cmd = self.cmd + ['-I', self.jump_rule_chain,\n                                  str(last_rule_num)]\n            else:\n                # append rule\n                cmd = self.cmd + ['-A', self.jump_rule_chain]\n```\n\nSuggested Fix\n\n```\n           # Naively assume that if the last row is a REJECT rule, then\n            # we can add insert our rule right before it, otherwise we\n            # assume that we can just append the rule.\n            if (last_rule_num and last_rule_target\n                    and ( last_rule_target == 'REJECT' || last_rule_target == 'DROP')):\n                # insert rule\n                cmd = self.cmd + ['-I', self.jump_rule_chain,\n                                  str(last_rule_num)]\n            else:\n                # append rule\n                cmd = self.cmd + ['-A', self.jump_rule_chain]\n            cmd += ['-j', self.chain]\n```\n\n", "before_files": [{"content": "#!/usr/bin/python\n# -*- coding: utf-8 -*-\n# vim: expandtab:tabstop=4:shiftwidth=4\n# pylint: disable=fixme, missing-docstring\nfrom subprocess import call, check_output\n\nDOCUMENTATION = '''\n---\nmodule: os_firewall_manage_iptables\nshort_description: This module manages iptables rules for a given chain\nauthor: Jason DeTiberus\nrequirements: [ ]\n'''\nEXAMPLES = '''\n'''\n\n\nclass IpTablesError(Exception):\n    def __init__(self, msg, cmd, exit_code, output):\n        super(IpTablesError, self).__init__(msg)\n        self.msg = msg\n        self.cmd = cmd\n        self.exit_code = exit_code\n        self.output = output\n\n\nclass IpTablesAddRuleError(IpTablesError):\n    pass\n\n\nclass IpTablesRemoveRuleError(IpTablesError):\n    pass\n\n\nclass IpTablesSaveError(IpTablesError):\n    pass\n\n\nclass IpTablesCreateChainError(IpTablesError):\n    def __init__(self, chain, msg, cmd, exit_code, output): # pylint: disable=too-many-arguments, line-too-long\n        super(IpTablesCreateChainError, self).__init__(msg, cmd, exit_code,\n                                                       output)\n        self.chain = chain\n\n\nclass IpTablesCreateJumpRuleError(IpTablesError):\n    def __init__(self, chain, msg, cmd, exit_code, output): # pylint: disable=too-many-arguments, line-too-long\n        super(IpTablesCreateJumpRuleError, self).__init__(msg, cmd, exit_code,\n                                                          output)\n        self.chain = chain\n\n\n# TODO: impliment rollbacks for any events that where successful and an\n# exception was thrown later. for example, when the chain is created\n# successfully, but the add/remove rule fails.\nclass IpTablesManager(object): # pylint: disable=too-many-instance-attributes\n    def __init__(self, module):\n        self.module = module\n        self.ip_version = module.params['ip_version']\n        self.check_mode = module.check_mode\n        self.chain = module.params['chain']\n        self.create_jump_rule = module.params['create_jump_rule']\n        self.jump_rule_chain = module.params['jump_rule_chain']\n        self.cmd = self.gen_cmd()\n        self.save_cmd = self.gen_save_cmd()\n        self.output = []\n        self.changed = False\n\n    def save(self):\n        try:\n            self.output.append(check_output(self.save_cmd,\n                                            stderr=subprocess.STDOUT))\n        except subprocess.CalledProcessError as ex:\n            raise IpTablesSaveError(\n                msg=\"Failed to save iptables rules\",\n                cmd=ex.cmd, exit_code=ex.returncode, output=ex.output)\n\n    def verify_chain(self):\n        if not self.chain_exists():\n            self.create_chain()\n        if self.create_jump_rule and not self.jump_rule_exists():\n            self.create_jump()\n\n    def add_rule(self, port, proto):\n        rule = self.gen_rule(port, proto)\n        if not self.rule_exists(rule):\n            self.verify_chain()\n\n            if self.check_mode:\n                self.changed = True\n                self.output.append(\"Create rule for %s %s\" % (proto, port))\n            else:\n                cmd = self.cmd + ['-A'] + rule\n                try:\n                    self.output.append(check_output(cmd))\n                    self.changed = True\n                    self.save()\n                except subprocess.CalledProcessError as ex:\n                    raise IpTablesCreateChainError(\n                        chain=self.chain,\n                        msg=\"Failed to create rule for \"\n                            \"%s %s\" % (proto, port),\n                        cmd=ex.cmd, exit_code=ex.returncode,\n                        output=ex.output)\n\n    def remove_rule(self, port, proto):\n        rule = self.gen_rule(port, proto)\n        if self.rule_exists(rule):\n            if self.check_mode:\n                self.changed = True\n                self.output.append(\"Remove rule for %s %s\" % (proto, port))\n            else:\n                cmd = self.cmd + ['-D'] + rule\n                try:\n                    self.output.append(check_output(cmd))\n                    self.changed = True\n                    self.save()\n                except subprocess.CalledProcessError as ex:\n                    raise IpTablesRemoveRuleError(\n                        chain=self.chain,\n                        msg=\"Failed to remove rule for %s %s\" % (proto, port),\n                        cmd=ex.cmd, exit_code=ex.returncode, output=ex.output)\n\n    def rule_exists(self, rule):\n        check_cmd = self.cmd + ['-C'] + rule\n        return True if call(check_cmd) == 0 else False\n\n    def gen_rule(self, port, proto):\n        return [self.chain, '-p', proto, '-m', 'state', '--state', 'NEW',\n                '-m', proto, '--dport', str(port), '-j', 'ACCEPT']\n\n    def create_jump(self):\n        if self.check_mode:\n            self.changed = True\n            self.output.append(\"Create jump rule for chain %s\" % self.chain)\n        else:\n            try:\n                cmd = self.cmd + ['-L', self.jump_rule_chain, '--line-numbers']\n                output = check_output(cmd, stderr=subprocess.STDOUT)\n\n                # break the input rules into rows and columns\n                input_rules = [s.split() for s in output.split('\\n')]\n\n                # Find the last numbered rule\n                last_rule_num = None\n                last_rule_target = None\n                for rule in input_rules[:-1]:\n                    if rule:\n                        try:\n                            last_rule_num = int(rule[0])\n                        except ValueError:\n                            continue\n                        last_rule_target = rule[1]\n\n                # Naively assume that if the last row is a REJECT rule, then\n                # we can add insert our rule right before it, otherwise we\n                # assume that we can just append the rule.\n                if (last_rule_num and last_rule_target\n                        and last_rule_target == 'REJECT'):\n                    # insert rule\n                    cmd = self.cmd + ['-I', self.jump_rule_chain,\n                                      str(last_rule_num)]\n                else:\n                    # append rule\n                    cmd = self.cmd + ['-A', self.jump_rule_chain]\n                cmd += ['-j', self.chain]\n                output = check_output(cmd, stderr=subprocess.STDOUT)\n                self.changed = True\n                self.output.append(output)\n                self.save()\n            except subprocess.CalledProcessError as ex:\n                if '--line-numbers' in ex.cmd:\n                    raise IpTablesCreateJumpRuleError(\n                        chain=self.chain,\n                        msg=(\"Failed to query existing \" +\n                             self.jump_rule_chain +\n                             \" rules to determine jump rule location\"),\n                        cmd=ex.cmd, exit_code=ex.returncode,\n                        output=ex.output)\n                else:\n                    raise IpTablesCreateJumpRuleError(\n                        chain=self.chain,\n                        msg=(\"Failed to create jump rule for chain \" +\n                             self.chain),\n                        cmd=ex.cmd, exit_code=ex.returncode,\n                        output=ex.output)\n\n    def create_chain(self):\n        if self.check_mode:\n            self.changed = True\n            self.output.append(\"Create chain %s\" % self.chain)\n        else:\n            try:\n                cmd = self.cmd + ['-N', self.chain]\n                self.output.append(check_output(cmd,\n                                                stderr=subprocess.STDOUT))\n                self.changed = True\n                self.output.append(\"Successfully created chain %s\" %\n                                   self.chain)\n                self.save()\n            except subprocess.CalledProcessError as ex:\n                raise IpTablesCreateChainError(\n                    chain=self.chain,\n                    msg=\"Failed to create chain: %s\" % self.chain,\n                    cmd=ex.cmd, exit_code=ex.returncode, output=ex.output\n                    )\n\n    def jump_rule_exists(self):\n        cmd = self.cmd + ['-C', self.jump_rule_chain, '-j', self.chain]\n        return True if call(cmd) == 0 else False\n\n    def chain_exists(self):\n        cmd = self.cmd + ['-L', self.chain]\n        return True if call(cmd) == 0 else False\n\n    def gen_cmd(self):\n        cmd = 'iptables' if self.ip_version == 'ipv4' else 'ip6tables'\n        return [\"/usr/sbin/%s\" % cmd]\n\n    def gen_save_cmd(self): # pylint: disable=no-self-use\n        return ['/usr/libexec/iptables/iptables.init', 'save']\n\n\ndef main():\n    module = AnsibleModule(\n        argument_spec=dict(\n            name=dict(required=True),\n            action=dict(required=True, choices=['add', 'remove',\n                                                'verify_chain']),\n            chain=dict(required=False, default='OS_FIREWALL_ALLOW'),\n            create_jump_rule=dict(required=False, type='bool', default=True),\n            jump_rule_chain=dict(required=False, default='INPUT'),\n            protocol=dict(required=False, choices=['tcp', 'udp']),\n            port=dict(required=False, type='int'),\n            ip_version=dict(required=False, default='ipv4',\n                            choices=['ipv4', 'ipv6']),\n        ),\n        supports_check_mode=True\n    )\n\n    action = module.params['action']\n    protocol = module.params['protocol']\n    port = module.params['port']\n\n    if action in ['add', 'remove']:\n        if not protocol:\n            error = \"protocol is required when action is %s\" % action\n            module.fail_json(msg=error)\n        if not port:\n            error = \"port is required when action is %s\" % action\n            module.fail_json(msg=error)\n\n    iptables_manager = IpTablesManager(module)\n\n    try:\n        if action == 'add':\n            iptables_manager.add_rule(port, protocol)\n        elif action == 'remove':\n            iptables_manager.remove_rule(port, protocol)\n        elif action == 'verify_chain':\n            iptables_manager.verify_chain()\n    except IpTablesError as ex:\n        module.fail_json(msg=ex.msg)\n\n    return module.exit_json(changed=iptables_manager.changed,\n                            output=iptables_manager.output)\n\n\n# pylint: disable=redefined-builtin, unused-wildcard-import, wildcard-import\n# import module snippets\nfrom ansible.module_utils.basic import *\nif __name__ == '__main__':\n    main()\n", "path": "roles/os_firewall/library/os_firewall_manage_iptables.py"}], "after_files": [{"content": "#!/usr/bin/python\n# -*- coding: utf-8 -*-\n# vim: expandtab:tabstop=4:shiftwidth=4\n# pylint: disable=fixme, missing-docstring\nfrom subprocess import call, check_output\n\nDOCUMENTATION = '''\n---\nmodule: os_firewall_manage_iptables\nshort_description: This module manages iptables rules for a given chain\nauthor: Jason DeTiberus\nrequirements: [ ]\n'''\nEXAMPLES = '''\n'''\n\n\nclass IpTablesError(Exception):\n    def __init__(self, msg, cmd, exit_code, output):\n        super(IpTablesError, self).__init__(msg)\n        self.msg = msg\n        self.cmd = cmd\n        self.exit_code = exit_code\n        self.output = output\n\n\nclass IpTablesAddRuleError(IpTablesError):\n    pass\n\n\nclass IpTablesRemoveRuleError(IpTablesError):\n    pass\n\n\nclass IpTablesSaveError(IpTablesError):\n    pass\n\n\nclass IpTablesCreateChainError(IpTablesError):\n    def __init__(self, chain, msg, cmd, exit_code, output): # pylint: disable=too-many-arguments, line-too-long, redefined-outer-name\n        super(IpTablesCreateChainError, self).__init__(msg, cmd, exit_code,\n                                                       output)\n        self.chain = chain\n\n\nclass IpTablesCreateJumpRuleError(IpTablesError):\n    def __init__(self, chain, msg, cmd, exit_code, output): # pylint: disable=too-many-arguments, line-too-long, redefined-outer-name\n        super(IpTablesCreateJumpRuleError, self).__init__(msg, cmd, exit_code,\n                                                          output)\n        self.chain = chain\n\n\n# TODO: impliment rollbacks for any events that where successful and an\n# exception was thrown later. for example, when the chain is created\n# successfully, but the add/remove rule fails.\nclass IpTablesManager(object): # pylint: disable=too-many-instance-attributes\n    def __init__(self, module):\n        self.module = module\n        self.ip_version = module.params['ip_version']\n        self.check_mode = module.check_mode\n        self.chain = module.params['chain']\n        self.create_jump_rule = module.params['create_jump_rule']\n        self.jump_rule_chain = module.params['jump_rule_chain']\n        self.cmd = self.gen_cmd()\n        self.save_cmd = self.gen_save_cmd()\n        self.output = []\n        self.changed = False\n\n    def save(self):\n        try:\n            self.output.append(check_output(self.save_cmd,\n                                            stderr=subprocess.STDOUT))\n        except subprocess.CalledProcessError as ex:\n            raise IpTablesSaveError(\n                msg=\"Failed to save iptables rules\",\n                cmd=ex.cmd, exit_code=ex.returncode, output=ex.output)\n\n    def verify_chain(self):\n        if not self.chain_exists():\n            self.create_chain()\n        if self.create_jump_rule and not self.jump_rule_exists():\n            self.create_jump()\n\n    def add_rule(self, port, proto):\n        rule = self.gen_rule(port, proto)\n        if not self.rule_exists(rule):\n            self.verify_chain()\n\n            if self.check_mode:\n                self.changed = True\n                self.output.append(\"Create rule for %s %s\" % (proto, port))\n            else:\n                cmd = self.cmd + ['-A'] + rule\n                try:\n                    self.output.append(check_output(cmd))\n                    self.changed = True\n                    self.save()\n                except subprocess.CalledProcessError as ex:\n                    raise IpTablesCreateChainError(\n                        chain=self.chain,\n                        msg=\"Failed to create rule for \"\n                            \"%s %s\" % (proto, port),\n                        cmd=ex.cmd, exit_code=ex.returncode,\n                        output=ex.output)\n\n    def remove_rule(self, port, proto):\n        rule = self.gen_rule(port, proto)\n        if self.rule_exists(rule):\n            if self.check_mode:\n                self.changed = True\n                self.output.append(\"Remove rule for %s %s\" % (proto, port))\n            else:\n                cmd = self.cmd + ['-D'] + rule\n                try:\n                    self.output.append(check_output(cmd))\n                    self.changed = True\n                    self.save()\n                except subprocess.CalledProcessError as ex:\n                    raise IpTablesRemoveRuleError(\n                        chain=self.chain,\n                        msg=\"Failed to remove rule for %s %s\" % (proto, port),\n                        cmd=ex.cmd, exit_code=ex.returncode, output=ex.output)\n\n    def rule_exists(self, rule):\n        check_cmd = self.cmd + ['-C'] + rule\n        return True if call(check_cmd) == 0 else False\n\n    def gen_rule(self, port, proto):\n        return [self.chain, '-p', proto, '-m', 'state', '--state', 'NEW',\n                '-m', proto, '--dport', str(port), '-j', 'ACCEPT']\n\n    def create_jump(self):\n        if self.check_mode:\n            self.changed = True\n            self.output.append(\"Create jump rule for chain %s\" % self.chain)\n        else:\n            try:\n                cmd = self.cmd + ['-L', self.jump_rule_chain, '--line-numbers']\n                output = check_output(cmd, stderr=subprocess.STDOUT)\n\n                # break the input rules into rows and columns\n                input_rules = [s.split() for s in output.split('\\n')]\n\n                # Find the last numbered rule\n                last_rule_num = None\n                last_rule_target = None\n                for rule in input_rules[:-1]:\n                    if rule:\n                        try:\n                            last_rule_num = int(rule[0])\n                        except ValueError:\n                            continue\n                        last_rule_target = rule[1]\n\n                # Naively assume that if the last row is a REJECT or DROP rule,\n                # then we can insert our rule right before it, otherwise we\n                # assume that we can just append the rule.\n                if (last_rule_num and last_rule_target\n                        and last_rule_target in ['REJECT', 'DROP']):\n                    # insert rule\n                    cmd = self.cmd + ['-I', self.jump_rule_chain,\n                                      str(last_rule_num)]\n                else:\n                    # append rule\n                    cmd = self.cmd + ['-A', self.jump_rule_chain]\n                cmd += ['-j', self.chain]\n                output = check_output(cmd, stderr=subprocess.STDOUT)\n                self.changed = True\n                self.output.append(output)\n                self.save()\n            except subprocess.CalledProcessError as ex:\n                if '--line-numbers' in ex.cmd:\n                    raise IpTablesCreateJumpRuleError(\n                        chain=self.chain,\n                        msg=(\"Failed to query existing \" +\n                             self.jump_rule_chain +\n                             \" rules to determine jump rule location\"),\n                        cmd=ex.cmd, exit_code=ex.returncode,\n                        output=ex.output)\n                else:\n                    raise IpTablesCreateJumpRuleError(\n                        chain=self.chain,\n                        msg=(\"Failed to create jump rule for chain \" +\n                             self.chain),\n                        cmd=ex.cmd, exit_code=ex.returncode,\n                        output=ex.output)\n\n    def create_chain(self):\n        if self.check_mode:\n            self.changed = True\n            self.output.append(\"Create chain %s\" % self.chain)\n        else:\n            try:\n                cmd = self.cmd + ['-N', self.chain]\n                self.output.append(check_output(cmd,\n                                                stderr=subprocess.STDOUT))\n                self.changed = True\n                self.output.append(\"Successfully created chain %s\" %\n                                   self.chain)\n                self.save()\n            except subprocess.CalledProcessError as ex:\n                raise IpTablesCreateChainError(\n                    chain=self.chain,\n                    msg=\"Failed to create chain: %s\" % self.chain,\n                    cmd=ex.cmd, exit_code=ex.returncode, output=ex.output\n                    )\n\n    def jump_rule_exists(self):\n        cmd = self.cmd + ['-C', self.jump_rule_chain, '-j', self.chain]\n        return True if call(cmd) == 0 else False\n\n    def chain_exists(self):\n        cmd = self.cmd + ['-L', self.chain]\n        return True if call(cmd) == 0 else False\n\n    def gen_cmd(self):\n        cmd = 'iptables' if self.ip_version == 'ipv4' else 'ip6tables'\n        return [\"/usr/sbin/%s\" % cmd]\n\n    def gen_save_cmd(self): # pylint: disable=no-self-use\n        return ['/usr/libexec/iptables/iptables.init', 'save']\n\n\ndef main():\n    module = AnsibleModule(\n        argument_spec=dict(\n            name=dict(required=True),\n            action=dict(required=True, choices=['add', 'remove',\n                                                'verify_chain']),\n            chain=dict(required=False, default='OS_FIREWALL_ALLOW'),\n            create_jump_rule=dict(required=False, type='bool', default=True),\n            jump_rule_chain=dict(required=False, default='INPUT'),\n            protocol=dict(required=False, choices=['tcp', 'udp']),\n            port=dict(required=False, type='int'),\n            ip_version=dict(required=False, default='ipv4',\n                            choices=['ipv4', 'ipv6']),\n        ),\n        supports_check_mode=True\n    )\n\n    action = module.params['action']\n    protocol = module.params['protocol']\n    port = module.params['port']\n\n    if action in ['add', 'remove']:\n        if not protocol:\n            error = \"protocol is required when action is %s\" % action\n            module.fail_json(msg=error)\n        if not port:\n            error = \"port is required when action is %s\" % action\n            module.fail_json(msg=error)\n\n    iptables_manager = IpTablesManager(module)\n\n    try:\n        if action == 'add':\n            iptables_manager.add_rule(port, protocol)\n        elif action == 'remove':\n            iptables_manager.remove_rule(port, protocol)\n        elif action == 'verify_chain':\n            iptables_manager.verify_chain()\n    except IpTablesError as ex:\n        module.fail_json(msg=ex.msg)\n\n    return module.exit_json(changed=iptables_manager.changed,\n                            output=iptables_manager.output)\n\n\n# pylint: disable=redefined-builtin, unused-wildcard-import, wildcard-import\n# import module snippets\nfrom ansible.module_utils.basic import *\nif __name__ == '__main__':\n    main()\n", "path": "roles/os_firewall/library/os_firewall_manage_iptables.py"}]}