Fix interfaces_file for proper file contents (#37818)
The generated file was completely unusable by the system therefore the fix which ensures that diffing the file prior to changes and after only shows diffs Furthermore the code did not work for Python 3.6 > f.writelines(to_bytes(lines, errors='surrogate_or_strict')) E TypeError: a bytes-like object is required, not 'int' The other modifications (lambda variable renaming) is to comply with default flake8 rules
This commit is contained in:
parent
710db82930
commit
612d0d6634
8 changed files with 248 additions and 6 deletions
|
@ -290,11 +290,11 @@ def setInterfaceOption(module, lines, iface, option, raw_value, state):
|
||||||
if option in ["pre-up", "up", "down", "post-up"] and value is not None and value != "None":
|
if option in ["pre-up", "up", "down", "post-up"] and value is not None and value != "None":
|
||||||
for target_option in filter(lambda i: i['value'] == value, target_options):
|
for target_option in filter(lambda i: i['value'] == value, target_options):
|
||||||
changed = True
|
changed = True
|
||||||
lines = list(filter(lambda l: l != target_option, lines))
|
lines = list(filter(lambda ln: ln != target_option, lines))
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
for target_option in target_options:
|
for target_option in target_options:
|
||||||
lines = list(filter(lambda l: l != target_option, lines))
|
lines = list(filter(lambda ln: ln != target_option, lines))
|
||||||
else:
|
else:
|
||||||
module.fail_json(msg="Error: unsupported state %s, has to be either present or absent" % state)
|
module.fail_json(msg="Error: unsupported state %s, has to be either present or absent" % state)
|
||||||
|
|
||||||
|
@ -322,7 +322,7 @@ def write_changes(module, lines, dest):
|
||||||
|
|
||||||
tmpfd, tmpfile = tempfile.mkstemp()
|
tmpfd, tmpfile = tempfile.mkstemp()
|
||||||
f = os.fdopen(tmpfd, 'wb')
|
f = os.fdopen(tmpfd, 'wb')
|
||||||
f.writelines(to_bytes(lines, errors='surrogate_or_strict'))
|
f.write(to_bytes(''.join(lines), errors='surrogate_or_strict'))
|
||||||
f.close()
|
f.close()
|
||||||
module.atomic_move(tmpfile, os.path.realpath(dest))
|
module.atomic_move(tmpfile, os.path.realpath(dest))
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
# The loopback network interface
|
||||||
|
auto lo eth0
|
||||||
|
iface lo inet loopback
|
||||||
|
|
||||||
|
# The primary network interface
|
||||||
|
iface eth0 inet dhcp
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"eth0": {
|
||||||
|
"address_family": "inet",
|
||||||
|
"down": [],
|
||||||
|
"method": "dhcp",
|
||||||
|
"post-up": [],
|
||||||
|
"pre-up": [],
|
||||||
|
"up": []
|
||||||
|
},
|
||||||
|
"lo": {
|
||||||
|
"address_family": "inet",
|
||||||
|
"down": [],
|
||||||
|
"method": "loopback",
|
||||||
|
"post-up": [],
|
||||||
|
"pre-up": [],
|
||||||
|
"up": []
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
auto aggi
|
||||||
|
iface aggi inet static
|
||||||
|
hwaddress ether 22:44:77:88:D5:96
|
||||||
|
address 10.44.15.196
|
||||||
|
netmask 255.255.255.248
|
||||||
|
mtu 1500
|
||||||
|
slaves int1 int2
|
||||||
|
bond_mode 4
|
||||||
|
bond_miimon 100
|
||||||
|
bond_downdelay 200
|
||||||
|
bond_updelay 200
|
||||||
|
bond_lacp_rate slow
|
||||||
|
bond_xmit_hash_policy layer3+4
|
||||||
|
post-up /sbin/ethtool -K aggi tx off tso off
|
||||||
|
|
||||||
|
auto agge
|
||||||
|
iface agge inet manual
|
||||||
|
|
||||||
|
auto br0
|
||||||
|
iface br0 inet static
|
||||||
|
bridge_ports agge
|
||||||
|
hwaddress ether 22:44:77:88:D5:98
|
||||||
|
address 188.44.133.76
|
||||||
|
netmask 255.255.255.248
|
||||||
|
gateway 188.44.133.75
|
||||||
|
slaves ext1 ext2
|
||||||
|
bond_mode 4
|
||||||
|
bond_miimon 100
|
||||||
|
bond_downdelay 200
|
||||||
|
bond_updelay 200
|
||||||
|
bond_lacp_rate slow
|
||||||
|
bond_xmit_hash_policy layer3+4
|
||||||
|
post-up /sbin/ethtool -K agge tx off tso off
|
||||||
|
|
||||||
|
up route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi
|
||||||
|
up route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi
|
||||||
|
up route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi
|
||||||
|
|
||||||
|
auto int1
|
||||||
|
iface int1 inet manual
|
||||||
|
bond-master aggi
|
||||||
|
|
||||||
|
auto int2
|
||||||
|
iface int2 inet manual
|
||||||
|
bond-master aggi
|
||||||
|
|
||||||
|
auto ext1
|
||||||
|
iface ext1 inet manual
|
||||||
|
bond-master agge
|
||||||
|
|
||||||
|
auto ext2
|
||||||
|
iface ext2 inet manual
|
||||||
|
bond-master agge
|
||||||
|
|
||||||
|
auto lo
|
||||||
|
iface lo inet loopback
|
||||||
|
|
||||||
|
source /etc/network/interfaces.d/*.cfg
|
|
@ -0,0 +1,8 @@
|
||||||
|
fail_json message: Error: interface eth0 not found
|
||||||
|
options:
|
||||||
|
{
|
||||||
|
"iface": "eth0",
|
||||||
|
"option": "mtu",
|
||||||
|
"state": "absent",
|
||||||
|
"value": "1350"
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
{
|
||||||
|
"agge": {
|
||||||
|
"address_family": "inet",
|
||||||
|
"down": [],
|
||||||
|
"method": "manual",
|
||||||
|
"post-up": [],
|
||||||
|
"pre-up": [],
|
||||||
|
"up": []
|
||||||
|
},
|
||||||
|
"aggi": {
|
||||||
|
"address": "10.44.15.196",
|
||||||
|
"address_family": "inet",
|
||||||
|
"bond_downdelay": "200",
|
||||||
|
"bond_lacp_rate": "slow",
|
||||||
|
"bond_miimon": "100",
|
||||||
|
"bond_mode": "4",
|
||||||
|
"bond_updelay": "200",
|
||||||
|
"bond_xmit_hash_policy": "layer3+4",
|
||||||
|
"down": [],
|
||||||
|
"hwaddress": "ether 22:44:77:88:D5:96",
|
||||||
|
"method": "static",
|
||||||
|
"mtu": "1500",
|
||||||
|
"netmask": "255.255.255.248",
|
||||||
|
"post-up": [
|
||||||
|
"/sbin/ethtool -K aggi tx off tso off"
|
||||||
|
],
|
||||||
|
"pre-up": [],
|
||||||
|
"slaves": "int1 int2",
|
||||||
|
"up": []
|
||||||
|
},
|
||||||
|
"br0": {
|
||||||
|
"address": "188.44.133.76",
|
||||||
|
"address_family": "inet",
|
||||||
|
"bond_downdelay": "200",
|
||||||
|
"bond_lacp_rate": "slow",
|
||||||
|
"bond_miimon": "100",
|
||||||
|
"bond_mode": "4",
|
||||||
|
"bond_updelay": "200",
|
||||||
|
"bond_xmit_hash_policy": "layer3+4",
|
||||||
|
"bridge_ports": "agge",
|
||||||
|
"down": [],
|
||||||
|
"gateway": "188.44.133.75",
|
||||||
|
"hwaddress": "ether 22:44:77:88:D5:98",
|
||||||
|
"method": "static",
|
||||||
|
"netmask": "255.255.255.248",
|
||||||
|
"post-up": [
|
||||||
|
"/sbin/ethtool -K agge tx off tso off"
|
||||||
|
],
|
||||||
|
"pre-up": [],
|
||||||
|
"slaves": "ext1 ext2",
|
||||||
|
"up": [
|
||||||
|
"route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi",
|
||||||
|
"route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi",
|
||||||
|
"route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ext1": {
|
||||||
|
"address_family": "inet",
|
||||||
|
"bond-master": "agge",
|
||||||
|
"down": [],
|
||||||
|
"method": "manual",
|
||||||
|
"post-up": [],
|
||||||
|
"pre-up": [],
|
||||||
|
"up": []
|
||||||
|
},
|
||||||
|
"ext2": {
|
||||||
|
"address_family": "inet",
|
||||||
|
"bond-master": "agge",
|
||||||
|
"down": [],
|
||||||
|
"method": "manual",
|
||||||
|
"post-up": [],
|
||||||
|
"pre-up": [],
|
||||||
|
"up": []
|
||||||
|
},
|
||||||
|
"int1": {
|
||||||
|
"address_family": "inet",
|
||||||
|
"bond-master": "aggi",
|
||||||
|
"down": [],
|
||||||
|
"method": "manual",
|
||||||
|
"post-up": [],
|
||||||
|
"pre-up": [],
|
||||||
|
"up": []
|
||||||
|
},
|
||||||
|
"int2": {
|
||||||
|
"address_family": "inet",
|
||||||
|
"bond-master": "aggi",
|
||||||
|
"down": [],
|
||||||
|
"method": "manual",
|
||||||
|
"post-up": [],
|
||||||
|
"pre-up": [],
|
||||||
|
"up": []
|
||||||
|
},
|
||||||
|
"lo": {
|
||||||
|
"address_family": "inet",
|
||||||
|
"down": [],
|
||||||
|
"method": "loopback",
|
||||||
|
"post-up": [],
|
||||||
|
"pre-up": [],
|
||||||
|
"up": []
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,14 +16,13 @@
|
||||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from ansible.compat.tests import unittest
|
from ansible.compat.tests import unittest
|
||||||
import ansible.module_utils.basic
|
|
||||||
from ansible.modules.system import interfaces_file
|
from ansible.modules.system import interfaces_file
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import sys
|
|
||||||
import io
|
import io
|
||||||
import inspect
|
import inspect
|
||||||
import json
|
from shutil import copyfile, move
|
||||||
|
import difflib
|
||||||
|
|
||||||
|
|
||||||
class AnsibleFailJson(Exception):
|
class AnsibleFailJson(Exception):
|
||||||
|
@ -31,6 +30,14 @@ class AnsibleFailJson(Exception):
|
||||||
|
|
||||||
|
|
||||||
class ModuleMocked():
|
class ModuleMocked():
|
||||||
|
def atomic_move(self, src, dst):
|
||||||
|
move(src, dst)
|
||||||
|
|
||||||
|
def backup_local(self, path):
|
||||||
|
backupp = os.path.join("/tmp", os.path.basename(path) + ".bak")
|
||||||
|
copyfile(path, backupp)
|
||||||
|
return backupp
|
||||||
|
|
||||||
def fail_json(self, msg):
|
def fail_json(self, msg):
|
||||||
raise AnsibleFailJson(msg)
|
raise AnsibleFailJson(msg)
|
||||||
|
|
||||||
|
@ -44,6 +51,18 @@ class TestInterfacesFileModule(unittest.TestCase):
|
||||||
def getTestFiles(self):
|
def getTestFiles(self):
|
||||||
return next(os.walk(fixture_path))[2]
|
return next(os.walk(fixture_path))[2]
|
||||||
|
|
||||||
|
def compareFileToBackup(self, path, backup):
|
||||||
|
with open(path) as f1:
|
||||||
|
with open(backup) as f2:
|
||||||
|
diffs = difflib.context_diff(f1.readlines(),
|
||||||
|
f2.readlines(),
|
||||||
|
fromfile=os.path.basename(path),
|
||||||
|
tofile=os.path.basename(backup))
|
||||||
|
# Restore backup
|
||||||
|
move(backup, path)
|
||||||
|
deltas = [d for d in diffs]
|
||||||
|
self.assertTrue(len(deltas) == 0)
|
||||||
|
|
||||||
def compareInterfacesLinesToFile(self, interfaces_lines, path, testname=None):
|
def compareInterfacesLinesToFile(self, interfaces_lines, path, testname=None):
|
||||||
if not testname:
|
if not testname:
|
||||||
testname = "%s.%s" % (path, inspect.stack()[1][3])
|
testname = "%s.%s" % (path, inspect.stack()[1][3])
|
||||||
|
@ -137,3 +156,35 @@ class TestInterfacesFileModule(unittest.TestCase):
|
||||||
|
|
||||||
self.compareInterfacesLinesToFile(lines, testfile, "%s_%s" % (testfile, testname))
|
self.compareInterfacesLinesToFile(lines, testfile, "%s_%s" % (testfile, testname))
|
||||||
self.compareInterfacesToFile(ifaces, testfile, "%s_%s.json" % (testfile, testname))
|
self.compareInterfacesToFile(ifaces, testfile, "%s_%s.json" % (testfile, testname))
|
||||||
|
|
||||||
|
def test_revert(self):
|
||||||
|
testcases = {
|
||||||
|
"revert": [
|
||||||
|
{
|
||||||
|
'iface': 'eth0',
|
||||||
|
'option': 'mtu',
|
||||||
|
'value': '1350',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
for testname, options_list in testcases.items():
|
||||||
|
for testfile in self.getTestFiles():
|
||||||
|
path = os.path.join(fixture_path, testfile)
|
||||||
|
lines, ifaces = interfaces_file.read_interfaces_file(module, path)
|
||||||
|
backupp = module.backup_local(path)
|
||||||
|
options = options_list[0]
|
||||||
|
for state in ['present', 'absent']:
|
||||||
|
fail_json_iterations = []
|
||||||
|
options['state'] = state
|
||||||
|
try:
|
||||||
|
_, lines = interfaces_file.setInterfaceOption(module, lines, options['iface'], options['option'], options['value'], options['state'])
|
||||||
|
except AnsibleFailJson as e:
|
||||||
|
fail_json_iterations.append("fail_json message: %s\noptions:\n%s" %
|
||||||
|
(str(e), json.dumps(options, sort_keys=True, indent=4, separators=(',', ': '))))
|
||||||
|
interfaces_file.write_changes(module, [d['line'] for d in lines if 'line' in d], path)
|
||||||
|
|
||||||
|
self.compareStringWithFile("\n=====\n".join(fail_json_iterations), "%s_%s.exceptions.txt" % (testfile, testname))
|
||||||
|
|
||||||
|
self.compareInterfacesLinesToFile(lines, testfile, "%s_%s" % (testfile, testname))
|
||||||
|
self.compareInterfacesToFile(ifaces, testfile, "%s_%s.json" % (testfile, testname))
|
||||||
|
self.compareFileToBackup(path, backupp)
|
||||||
|
|
Loading…
Reference in a new issue