├── LICENSE ├── README.md ├── changeInterface.awk ├── readInterfaces.awk └── testChangeInterface.py /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Joe Kuan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Network-Interfaces-Script (Ver 1.3) 2 | 3 | Read and update awk scripts for Ubuntu /etc/network/interfaces file. 4 | 5 | * **readInterfaces.awk** -- parse and output interface configuration 6 | * **changeInterface.awk** -- modify interfaces file 7 | 8 | ## Parse Interfaces Script 9 | Complete re-implement both scripts with much simpler logic, shorter code and pack with more flexible features. It now allows you to add/modify/delete interfaces, as well as add/modify/delete any settings within an interface. These scripts are tested with testChangeInterface.py python script. 10 | 11 | ### Update Interfaces Script (changeInterface.awk) 12 | Here is the standard usage for changeInterface.awk: 13 | 14 | ``` 15 | awk -f changeInterface.awk 16 | [action=add|remove] [version=ipv4|ipv6] 17 | [address= netmask= ...] 18 | ``` 19 | 20 | * `device=ethX` - target network interface to configure 21 | * `mode=(dhcp|static|manual)` - type of network interface to configure. This argument is not required when `action` is `remove` 22 | * `action=(add|remove)` - optional argument. Add or remove an interface entry. If this argument is not specified, it assumes `modify` operation 23 | * `version=(ipv4|ipv6)` - optional argument. Choose the ip protocol version. If this argument is not specified, it assumes `ipv4` operation 24 | * `name=value` - configuration for a network interface. The script doesn't limit what settings as long as in name and value syntax. For example: 25 | * `network=192.168.0.0` - modify (if already exists) or add the network setting 26 | * `network=` - specify without value means to remove the network setting if already exists 27 | * `'dns-nameservers=192.168.200.5 10.0.10.1'` - for settings with multiple values, enter with quote around it. Or you can use `dns=192.168.200.5` as short form. 28 | 29 | #### Usage Examples: 30 | **Configure network device _eth0_ to DHCP mode** 31 | ``` 32 | awk -f changeInterfaces.awk /etc/network/interfaces device=eth0 mode=dhcp 33 | ``` 34 | **Add a network device _p3p1_ with static settings** 35 | ``` 36 | awk -f changeInterfaces.awk /etc/network/interfaces device=p3p1 action=add mode=static address=192.168.202.1 netmask=255.255.255.0 gateway=192.168.202.254 37 | ``` 38 | If _p3p1_ already exists and configured as DHCP, it will automatically modify to static interface with all the input settings. 39 | 40 | If _p3p1_ already exists and configured as static, it will overwrite the existing field or add the field if it is new. 41 | 42 | **Delete an interface entry** 43 | ``` 44 | awk -f changeInterfaces.awk /etc/network/interfaces device=eth1 action=remove 45 | ``` 46 | 47 | **Remove `network` & `broadcast` fields and add (or modify if exists) `dns-nameservers` & `foo` fields** 48 | ``` 49 | awk -f changeInterfaces.awk /etc/network/interfaces device=eth1 mode=static network= broadcast= foo=bar 'dns-nameservers=10.0.10.1 192.168.200.5 192.168.202.254' 50 | ``` 51 | ### Read Interfaces Script (readInterfaces.awk) 52 | Here is the standard usage for readInterface.awk: 53 | 54 | ``` 55 | awk -f readInterfaces.awk [output=all] 56 | ``` 57 | * `output=all` - print out the full settings for an interface. Without this option, the script will just print out the basic address config in : `ipaddr netmask gateway` (separated with a single white space). With this option, all the settings are displayed, e.g. 58 | ``` 59 | 192.168.0.10 255.255.255.0 192.168.0.1 60 | bridge_maxage 12 61 | broadcast 192.168.0.255 62 | network 192.168.0.0 63 | bridge_stp off 64 | bridge_fd 9 65 | bridge_ports eth0 66 | bridge_hello 2 67 | ``` 68 | 69 | -------------------------------------------------------------------------------- /changeInterface.awk: -------------------------------------------------------------------------------- 1 | function writeStatic(device, fields, orders) { 2 | 3 | # Create the order as original 4 | for (o = 0; o in orders; o++) { 5 | field = orders[o]; 6 | value = fields[field]; 7 | delete fields[field]; 8 | if (length(value)) 9 | printf(" %s %s\n", field, value); 10 | } 11 | 12 | # additional items have no order 13 | for (f in fields) { 14 | value = fields[f]; 15 | if (length(value)) 16 | printf(" %s %s\n", f, value); 17 | } 18 | } 19 | 20 | function usage() { 21 | print "awk -f changeInterfaces.awk dev= \n" \ 22 | " [mode=dhcp|static|manual] [action=add|remove] \n" \ 23 | " [address= networkmask= ]\n" \ 24 | " [arg=debug]\n" 25 | } 26 | 27 | BEGIN { 28 | 29 | start = 0; 30 | order = 0 31 | version = "inet" 32 | 33 | if (ARGC < 3 || ARGC > 10) { 34 | usage(); 35 | exit 1; 36 | } 37 | 38 | for (i = 2; i < ARGC; i++) { 39 | num = split(ARGV[i], pair, "="); 40 | if (pair[1] == "arg" && pair[2] == "debug") 41 | debug = 1; 42 | else if (pair[1] == "mode") 43 | mode = pair[2]; 44 | else if (pair[1] == "action" && pair[2] == "remove") 45 | remove = 1; 46 | else if (pair[1] == "action" && pair[2] == "add") 47 | add = 1; 48 | else if (pair[1] == "version" && pair[2] == "ipv6") 49 | version = "inet6"; 50 | else if (pair[1] == "device" || pair[1] == "dev") 51 | device = pair[2]; 52 | else if (num == 2) { 53 | if (pair[1] == "dns") 54 | pair[1] = "dns-nameservers"; 55 | settings[pair[1]] = pair[2]; 56 | } 57 | else { 58 | usage(); 59 | exit 1; 60 | } 61 | } 62 | 63 | # Sort out the logic of argument 64 | if (mode == "dhcp" && (length(network) || length(gateway) || length(address) || length(netmask))) { 65 | print "Both DHCP and static properties are defined"; 66 | usage(); 67 | exit 1; 68 | } 69 | else if (!mode && !remove) { 70 | print "Missing mode input"; 71 | usage(); 72 | exit 1; 73 | } 74 | 75 | if (debug) { 76 | for (f in settings) { 77 | print f, ": ", settings[f]; 78 | } 79 | } 80 | } 81 | 82 | { 83 | # auto line 84 | if ($1 == "auto" || $1 == "allow-hotplug") { 85 | if ($2 != device) { 86 | # We come to different device 87 | # Good place to write all the settings 88 | if (targetDev) { 89 | targetDev = 0; 90 | if (!add && !remove) { 91 | if (mode == "static" || mode == "manual") 92 | writeStatic(device, settings, fieldOrders); 93 | print ""; 94 | } 95 | } 96 | print $0; 97 | next; 98 | } 99 | else if (!remove) { 100 | print $0; 101 | add = 0; 102 | next; 103 | } 104 | # Remove - don't print 105 | next; 106 | } 107 | # iface .. line 108 | else if ($1 == "iface") { 109 | 110 | if ($2 != device) { 111 | # We come to different device 112 | # Good place to write all the settings 113 | if (targetDev) { 114 | targetDev = 0; 115 | if (!add && !remove) { 116 | if (mode == "static" || mode == "manual") 117 | writeStatic(device, settings, fieldOrders); 118 | print ""; 119 | } 120 | } 121 | print $0; 122 | next; 123 | } 124 | else { 125 | # If already specified 'add' and found an existing entry 126 | # cancel it 127 | add = 0; 128 | 129 | # Go to different condition in next loop 130 | targetDev = 1; 131 | 132 | if (!remove) 133 | printf("iface %s %s %s\n", device, version, mode); 134 | next; 135 | } 136 | } 137 | # Matched device found - working through each line 138 | # until found a 'auto' line or end of file 139 | else if (targetDev) { 140 | 141 | # Comment line - leave it 142 | if (substr($1, 0, 1) == "#") { 143 | print $0; 144 | next; 145 | } 146 | 147 | field = $1; 148 | if (field in settings) { 149 | # It means we specify the argument in command line 150 | # as preference over the file content 151 | } 152 | else { 153 | # field not in the command line 154 | # copy it over 155 | settings[field] = substr($0, index($0, $2)) 156 | } 157 | fieldOrders[order] = field 158 | order++; 159 | next; 160 | 161 | # Other type of lines e.g. comment 162 | } 163 | else { 164 | print $0; 165 | next; 166 | } 167 | } 168 | 169 | END { 170 | # Come to the last line and we may not print out the 171 | # matched device settings 172 | if (!remove) { 173 | if (add || targetDev) { 174 | if (add) { 175 | printf("auto %s\n", device); 176 | printf("iface %s %s %s\n", device, version, mode); 177 | } 178 | if (mode != "dhcp") 179 | writeStatic(device, settings, fieldOrders); 180 | print ""; 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /readInterfaces.awk: -------------------------------------------------------------------------------- 1 | BEGIN { 2 | 3 | start = 0; 4 | 5 | if (ARGC < 3 || ARGC > 5) { 6 | print "awk -f readInterfaces.awk device= [output=all] [debug=1]" 7 | exit 1; 8 | } 9 | 10 | outAll = 0 11 | 12 | for (i = 2; i < ARGC; i++) { 13 | split(ARGV[i], arg, "="); 14 | if (arg[1] == "device") 15 | device = arg[2]; 16 | else if (arg[1] == "output" && arg[2] == "all") 17 | outAll = 1; 18 | else if (arg[1] == "debug" && arg[2] == "1") 19 | debug = 1; 20 | } 21 | 22 | if (!length(device)) { 23 | print "awk -f readInterfaces.awk device= [output=all] [debug=1]" 24 | exit 1; 25 | } 26 | } 27 | 28 | { 29 | # Look for iface line and if the interface comes with the device name 30 | # scan whether it is dhcp or static or manual 31 | # e.g. iface eth0 inet [static | dhcp | manual] 32 | if ($1 == "iface") { 33 | # Ethernet name matches - switch the line scanning on 34 | if ($2 == device) { 35 | if (debug) 36 | print $0; 37 | # It's a DHCP interface 38 | if (match($0, / dhcp/)) { 39 | print "dhcp"; 40 | gotTypeNoAddr = 1; 41 | exit 0; 42 | # It's a static network interface. We want to scan the 43 | # addresses after the static line 44 | } 45 | else if (match ($0, / static/)) { 46 | static = 1; 47 | next; 48 | } 49 | else if (match ($0, / manual/)) { 50 | print "manual"; 51 | gotTypeNoAddr = 1; 52 | exit 0; 53 | } 54 | # If it is other inteface line, switch it off 55 | # Go to the next line 56 | } 57 | else { 58 | static = 0; 59 | next; 60 | } 61 | } 62 | else if ($1 == "auto") { 63 | static = 0; 64 | next; 65 | } 66 | 67 | # At here, it means we are after the iface static line of 68 | # after the device we are searching for 69 | # Scan for the static content 70 | if (static) { 71 | # 2nd field to end of the line 72 | if (length($1)) { 73 | interface[$1] = substr($0, index($0, $2)); 74 | gotAddr = 1; 75 | } 76 | } 77 | } 78 | 79 | END { 80 | if (gotAddr) { 81 | printf("%s %s %s\n", interface["address"], interface["netmask"], interface["gateway"]); 82 | if (outAll) { 83 | delete interface["address"]; 84 | delete interface["netmask"]; 85 | delete interface["gateway"]; 86 | for (field in interface) { 87 | printf("%s %s\n", field, interface[field]); 88 | } 89 | } 90 | exit 0; 91 | } 92 | else { 93 | if (gotTypeNoAddr) 94 | exit 0; 95 | else 96 | exit 1; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /testChangeInterface.py: -------------------------------------------------------------------------------- 1 | # Author: Joe Kuan from iTrinegy (www.itrinegy.com). Email: kuan.joe@gmail.com 2 | # 3 | # Due to some contributions have broken this utility. 4 | # I have created this suite of unit tests to make sure none of existing 5 | # operations cannot be compromised 6 | # 7 | # Contributors are permitted to ADD automated tests for their own needs, 8 | # see before __main__ but NOT authorised to CHANGE OR DELETE 9 | # any of the existing tests. Any contribution can be only accepted by passing 10 | # the tests. 11 | import unittest, os, os.path, subprocess 12 | 13 | # This is the content for network interface tests 14 | # You are allowed to ADD but NOT allowed to CHANGE or REMOVE any 15 | # of the following interfaces 16 | network_test = """ 17 | auto lo 18 | iface lo inet loopback 19 | 20 | auto eth0 21 | iface eth0 inet static 22 | address 192.168.202.143 23 | broadcast 192.168.202.255 24 | netmask 255.255.255.0 25 | gateway 192.168.202.254 26 | dns-nameservers 192.168.202.254 192.168.203.254 192.168.200.254 8.8.8.8 27 | 28 | auto p3p1 29 | iface p3p1 inet dhcp 30 | 31 | auto eth3 32 | iface eth3 inet static 33 | address 192.168.3.3 34 | netmask 255.255.255.0 35 | gateway 192.168.3.1 36 | dns-search example.com sales.example.com dev.example.com 37 | dns-nameservers 192.168.3.45 192.168.8.10 38 | 39 | auto br0 40 | iface br0 inet static 41 | address 192.168.0.10 42 | network 192.168.0.0 43 | netmask 255.255.255.0 44 | broadcast 192.168.0.255 45 | gateway 192.168.0.1 46 | bridge_ports eth0 47 | bridge_fd 9 48 | bridge_hello 2 49 | bridge_maxage 12 50 | bridge_stp off 51 | 52 | """ 53 | 54 | testfile="./network_test" 55 | 56 | class NI_TestCase(unittest.TestCase): 57 | 58 | def setUp(self): 59 | self.assertTrue(os.path.isfile("network_test"), "Expect test sample file") 60 | self.testOutput = self.__class__.__name__ + ".output" 61 | self.testSource = testfile 62 | if os.path.isfile(self.testOutput): 63 | os.remove(self.testOutput) 64 | 65 | def tearDown(self): 66 | if os.path.isfile(self.testOutput): 67 | os.remove(self.testOutput) 68 | 69 | def numOfDiffLines(self, output=None): 70 | outfile = self.testOutput 71 | if output != None: 72 | outfile = output 73 | cmd = "diff -w -B -y --suppress-common-lines %s %s" % (outfile, testfile) 74 | # diff -B doesn't ignore the blank line 75 | diffCnt = 0 76 | try: 77 | lines = subprocess.check_output(cmd, shell=True) 78 | except subprocess.CalledProcessError as e: 79 | lines = e.output.splitlines() 80 | for ln in lines: 81 | if ln.strip() == '>' or ln.strip() == '<': 82 | continue 83 | diffCnt += 1 84 | return diffCnt 85 | 86 | def diffContent(self, output=None): 87 | outfile = self.testOutput 88 | if output != None: 89 | outfile = output 90 | diff = '' 91 | try: 92 | cmd = "diff -w -B -y --suppress-common-lines %s %s 2>&1" % (outfile, testfile) 93 | subprocess.check_output(cmd, shell=True) 94 | except subprocess.CalledProcessError as e: 95 | return e.output 96 | return diff 97 | 98 | # Match the line with header and tail 99 | def matchLine(self, lines, head, tail): 100 | found = False 101 | for ln in lines: 102 | h, t = ln.split(None, 1) 103 | if h == head: 104 | found = True 105 | self.assertTrue(t.strip() == tail.strip(), 106 | "Not matching: %s, %s" % (t.strip(), tail.strip())) 107 | self.assertTrue(found == True, "No output lines or matching with header %s" % head) 108 | 109 | # Opposite to above method 110 | def matchNoLine(self, lines, head): 111 | for ln in lines: 112 | h, t = ln.split(None, 1) 113 | self.assertTrue(h != head, "No suppose to contain the linewith %s" % (head)) 114 | 115 | class StaticChangeAddress(NI_TestCase): 116 | 117 | def runTest(self): 118 | """ Changing Static Address to Another IP Address """ 119 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=eth0 mode=static address=10.0.10.1 gateway=10.0.10.254 netmask=255.255.0.0 > %s" % (self.testSource, self.testOutput), shell=True) 120 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit status") 121 | output = subprocess.check_output("awk -f readInterfaces.awk %s device=eth0" % self.testOutput, shell=True) 122 | self.assertTrue(output.strip() == "10.0.10.1 255.255.0.0 10.0.10.254", 123 | "Actual output " + output.strip() + " but not 10.0.10.1 255.255.0.0 10.0.10.254"); 124 | # Make sure the rest of the content is not corrupted 125 | numLines = self.numOfDiffLines() 126 | self.assertTrue(numLines == 3, "Make sure the rest of the content is not corrupted") 127 | 128 | class StaticAddNetworkBroadcast(NI_TestCase): 129 | 130 | def runTest(self): 131 | """ Add network address on Static entry """ 132 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=eth0 mode=static network=192.168.0.0 broadcast=192.168.0.255 > %s" % (self.testSource, self.testOutput), shell=True) 133 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit status") 134 | output = subprocess.check_output("awk -f readInterfaces.awk %s device=eth0 output=all" % self.testOutput, 135 | shell=True) 136 | lines = output.splitlines() 137 | self.assertTrue(lines[0].strip() == "192.168.202.143 255.255.255.0 192.168.202.254", 138 | "Actual output " + output.strip() + " but not " + "192.168.202.143 255.255.255.0 192.168.202.254"); 139 | self.matchLine(lines, "network", "192.168.0.0") 140 | # Make sure the rest of the content is not corrupted 141 | numLines = self.numOfDiffLines() 142 | self.assertTrue(numLines == 2, "Make sure the rest of the content is not corrupted" + self.diffContent()) 143 | 144 | class StaticChangeNetwork(NI_TestCase): 145 | 146 | def runTest(self): 147 | """ Change network address on Static entry already with network address """ 148 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=br0 mode=static network=192.0.0.0 broadcast=192.168.202.0.128 > %s" % (self.testSource, self.testOutput), 149 | shell=True) 150 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit status") 151 | output = subprocess.check_output("awk -f readInterfaces.awk %s device=br0 output=all" % self.testOutput, 152 | shell=True) 153 | lines = output.splitlines() 154 | self.assertTrue(lines[0].strip() == "192.168.0.10 255.255.255.0 192.168.0.1", 155 | "Basic entry not the same for br0 " + lines[0]); 156 | self.matchLine(lines, "network", "192.0.0.0") 157 | # Make sure the rest of the content is not corrupted 158 | numLines = self.numOfDiffLines() 159 | self.assertTrue(numLines == 2, "Make sure the rest of the content is not corrupted" + self.diffContent()) 160 | 161 | class StaticDeleteNetworkBroadcast(NI_TestCase): 162 | def runTest(self): 163 | """ Delete network address on Static entry already with network entry """ 164 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=br0 mode=static network= broadcast= > %s" % (self.testSource, self.testOutput), shell=True) 165 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit status") 166 | output = subprocess.check_output("awk -f readInterfaces.awk %s device=br0 output=all" % self.testOutput, 167 | shell=True) 168 | lines = output.splitlines() 169 | self.assertTrue(lines[0].strip() == "192.168.0.10 255.255.255.0 192.168.0.1", 170 | "Basic entry not the same for br0 " + lines[0]); 171 | self.matchNoLine(lines, 'network'); 172 | # Make sure the rest of the content is not corrupted 173 | numLines = self.numOfDiffLines() 174 | self.assertTrue(numLines == 2, "Make sure the rest of the content is not corrupted") 175 | 176 | class StaticAddDns(NI_TestCase): 177 | 178 | def runTest(self): 179 | """ Add Dns address on Static entry """ 180 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=eth0 mode=static 'dns=192.168.0.1 192.168.0.2 192.168.0.3' > %s" 181 | % (self.testSource, self.testOutput), shell=True) 182 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit status") 183 | output = subprocess.check_output("awk -f readInterfaces.awk %s device=eth0 output=all" % self.testOutput, 184 | shell=True) 185 | lines = output.splitlines() 186 | self.matchLine(lines, 'dns-nameservers', '192.168.0.1 192.168.0.2 192.168.0.3') 187 | # Make sure the rest of the content is not corrupted 188 | numLines = self.numOfDiffLines() 189 | self.assertTrue(numLines == 1, "Make sure the rest of the content is not corrupted") 190 | 191 | class StaticChangeDns(NI_TestCase): 192 | 193 | def runTest(self): 194 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=eth0 mode=static 'dns=192.168.0.1 192.168.0.2 192.168.0.3' > %s" 195 | % (self.testSource, self.testOutput), shell=True) 196 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit status") 197 | # Need sort because the order of output is diff on Linux vs OSX 198 | output = subprocess.check_output("awk -f readInterfaces.awk %s device=eth0 output=all | sort" % self.testOutput, 199 | shell=True) 200 | lines = output.splitlines() 201 | ln = lines[2] 202 | head, tail = ln.split(None, 1) 203 | self.assertTrue(head == 'dns-nameservers', "Expect dns-nameservers output: " + head) 204 | self.assertTrue(tail == "192.168.0.1 192.168.0.2 192.168.0.3", 205 | "Actual output " + output.strip() + " but not " + 206 | "192.168.0.1 192.168.0.2 192.168.0.3"); 207 | # Make sure the rest of the content is not corrupted 208 | numLines = self.numOfDiffLines() 209 | self.assertTrue(numLines == 1, "Make sure the rest of the content is not corrupted") 210 | 211 | 212 | class StaticDeleteDns(NI_TestCase): 213 | def runTest(self): 214 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=eth0 mode=static 'dns=' > %s" 215 | % (self.testSource, self.testOutput), shell=True) 216 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit status") 217 | output = subprocess.check_output("awk -f readInterfaces.awk %s device=eth0 output=all" % self.testOutput, 218 | shell=True) 219 | lines = output.splitlines() 220 | for ln in lines: 221 | head, tail = ln.split(None, 1) 222 | self.assertTrue(head != 'dns-nameservers', "Expect no dns-nameservers entry") 223 | # Make sure the rest of the content is not corrupted 224 | numLines = self.numOfDiffLines() 225 | self.assertTrue(numLines == 1, "Make sure the rest of the content is not corrupted") 226 | 227 | class DhcpToStaticAddress(NI_TestCase): 228 | 229 | def runTest(self): 230 | """ Changing DHCP interface to Static IP Address """ 231 | rc = subprocess.check_call("awk -f changeInterface.awk network_test dev=p3p1 mode=static " + 232 | "address=10.0.10.1 gateway=10.0.10.254 netmask=255.255.0.0 > " + self.testOutput, 233 | shell=True) 234 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit status") 235 | output = subprocess.check_output("awk -f readInterfaces.awk %s device=p3p1" % self.testOutput, shell=True) 236 | self.assertTrue(output.strip() == "10.0.10.1 255.255.0.0 10.0.10.254", 237 | "Actual output " + output.strip() + " but not " + "10.0.10.1 255.255.0.0 10.0.10.254"); 238 | # Make sure the rest of the content is not corrupted 239 | numLines = self.numOfDiffLines() 240 | # Should be 4 lines diff: interface + address line for each 241 | self.assertTrue(numLines == 4, 242 | "Make sure the rest of the content is not corrupted\n" + self.diffContent()) 243 | 244 | class StaticToDhcpMode(NI_TestCase): 245 | def runTest(self): 246 | """ Changing Static Interface to DHCP """ 247 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=eth3 mode=dhcp > %s" 248 | % (self.testSource, self.testOutput), shell=True) 249 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit status") 250 | output = subprocess.check_output("awk -f readInterfaces.awk %s device=eth3" % self.testOutput, shell=True) 251 | self.assertTrue(output.strip() == "dhcp", 252 | "Expect dhcp mode"); 253 | # Make sure the rest of the content is not corrupted 254 | numLines = self.numOfDiffLines() 255 | self.assertTrue(numLines == 6, 256 | "Make sure the rest of the content is not corrupted\n" + self.diffContent()) 257 | 258 | class StaticToDhcpModeLastEntry(NI_TestCase): 259 | def runTest(self): 260 | """ Changing Static Interface to DHCP Last Entry """ 261 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=br0 mode=dhcp > %s" 262 | % (self.testSource, self.testOutput), shell=True) 263 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit status") 264 | output = subprocess.check_output("awk -f readInterfaces.awk %s device=br0" % self.testOutput, shell=True) 265 | self.assertTrue(output.strip() == "dhcp", 266 | "Expect dhcp mode"); 267 | # Make sure the rest of the content is not corrupted 268 | numLines = self.numOfDiffLines() 269 | self.assertTrue(numLines == 11, 270 | "Make sure the rest of the content is not corrupted\n" + self.diffContent()) 271 | 272 | class RemoveDhcp(NI_TestCase): 273 | def runTest(self): 274 | """ Remove DHCP Interface """ 275 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=p3p1 action=remove > %s" 276 | % (self.testSource, self.testOutput), shell=True) 277 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit status") 278 | with self.assertRaises(subprocess.CalledProcessError): 279 | subprocess.check_output("awk -f readInterfaces.awk %s device=p3p1" % self.testOutput, shell=True) 280 | 281 | # Make sure the rest of the content is not corrupted 282 | numLines = self.numOfDiffLines() 283 | self.assertTrue(numLines == 2, 284 | "Make sure the rest of the content is not corrupted\n" + self.diffContent()) 285 | 286 | class RemoveStatic(NI_TestCase): 287 | def runTest(self): 288 | """ Remove Static Interface """ 289 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=br0 action=remove > %s" 290 | % (self.testSource, self.testOutput), shell=True) 291 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit status") 292 | with self.assertRaises(subprocess.CalledProcessError): 293 | subprocess.check_output("awk -f readInterfaces.awk %s device=br0" % self.testOutput, shell=True) 294 | 295 | # Make sure the rest of the content is not corrupted 296 | numLines = self.numOfDiffLines() 297 | self.assertTrue(numLines == 12, 298 | "Make sure the rest of the content is not corrupted\n" + self.diffContent()) 299 | 300 | class RemoveNotExist(NI_TestCase): 301 | def runTest(self): 302 | """ Remove an a device not exist """ 303 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=abc action=remove > %s" 304 | % (self.testSource, self.testOutput), shell=True) 305 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit status") 306 | with self.assertRaises(subprocess.CalledProcessError): 307 | subprocess.check_output("awk -f readInterfaces.awk %s device=abc" % self.testOutput, shell=True) 308 | 309 | # Make sure the rest of the content is not corrupted 310 | numLines = self.numOfDiffLines() 311 | self.assertTrue(numLines == 0, 312 | "Make sure the rest of the content is not corrupted\n" + self.diffContent()) 313 | 314 | 315 | class RemoveStatic2(NI_TestCase): 316 | def runTest(self): 317 | """ Remove first static interface """ 318 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=eth0 action=remove > %s" 319 | % (self.testSource, self.testOutput), shell=True) 320 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit status") 321 | with self.assertRaises(subprocess.CalledProcessError): 322 | subprocess.check_output("awk -f readInterfaces.awk %s device=eth0" % self.testOutput, shell=True) 323 | 324 | # Make sure the rest of the content is not corrupted 325 | numLines = self.numOfDiffLines() 326 | self.assertTrue(numLines == 7, 327 | "Make sure the rest of the content is not corrupted\n" + self.diffContent()) 328 | 329 | class RemoveMultiple(NI_TestCase): 330 | 331 | def setup(self): 332 | super(RemoveMultiple, self).setup() 333 | outfiles = [ self.testOutput + '.1', self.testOutput + '.2', 334 | self.testOutput + '.3' ] 335 | 336 | for of in outfiles: 337 | if os.path.isfile(of): 338 | os.remove(of) 339 | 340 | def runTest(self): 341 | """ Remove multiple interfaces in sequence """ 342 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=eth3 action=remove > %s" 343 | % (self.testSource, self.testOutput), shell=True) 344 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit status") 345 | with self.assertRaises(subprocess.CalledProcessError): 346 | subprocess.check_output("awk -f readInterfaces.awk %s device=eth3" % self.testOutput, shell=True) 347 | 348 | # Make sure the rest of the content is not corrupted 349 | numLines = self.numOfDiffLines() 350 | self.assertTrue(numLines == 7, 351 | "Make sure the rest of the content is not corrupted\n" + self.diffContent()) 352 | 353 | outfile = self.testOutput + ".1" 354 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=p3p1 action=remove > %s" 355 | % (self.testOutput, outfile), shell=True) 356 | with self.assertRaises(subprocess.CalledProcessError): 357 | subprocess.check_output("awk -f readInterfaces.awk %s device=p3p1" % outfile, shell=True) 358 | numLines = self.numOfDiffLines(outfile) 359 | self.assertTrue(numLines == 9, 360 | "Make sure the rest of the content is not corrupted\n" + self.diffContent(outfile)) 361 | 362 | outfile2 = self.testOutput + ".2" 363 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=eth0 action=remove > %s" 364 | % (outfile, outfile2), shell=True) 365 | with self.assertRaises(subprocess.CalledProcessError): 366 | subprocess.check_output("awk -f readInterfaces.awk %s device=eth0" % outfile2, shell=True) 367 | numLines = self.numOfDiffLines(outfile2) 368 | self.assertTrue(numLines == 16, 369 | "Make sure the rest of the content is not corrupted\n" + self.diffContent(outfile2)) 370 | 371 | outfile3 = self.testOutput + ".3" 372 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=br0 action=remove > %s" 373 | % (outfile2, outfile3), shell=True) 374 | with self.assertRaises(subprocess.CalledProcessError): 375 | subprocess.check_output("awk -f readInterfaces.awk %s device=br0" % outfile3, shell=True) 376 | numLines = self.numOfDiffLines(outfile3) 377 | self.assertTrue(numLines == 28, 378 | "Make sure the rest of the content is not corrupted\n" + self.diffContent(outfile3)) 379 | 380 | def tearDown(self): 381 | super(RemoveMultiple, self).tearDown() 382 | os.remove(self.testOutput + ".1") 383 | os.remove(self.testOutput + ".2") 384 | os.remove(self.testOutput + ".3") 385 | 386 | class DhcpAdd(NI_TestCase): 387 | 388 | def runTest(self): 389 | """ Add a basic dhcp entry """ 390 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=eth2 action=add mode=dhcp > %s" 391 | % (self.testSource, self.testOutput), shell=True) 392 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit") 393 | out = subprocess.check_output("awk -f readInterfaces.awk %s device=eth2" % self.testOutput, shell=True) 394 | self.assertTrue(out.strip() == 'dhcp', 'Cannot find added dhcp entry: ' + out) 395 | numLines = self.numOfDiffLines() 396 | self.assertTrue(numLines == 2, 397 | "Make sure the rest of the content is not corrupted\n" + self.diffContent()) 398 | 399 | class DhcpAddExist(NI_TestCase): 400 | 401 | def runTest(self): 402 | """ Add a dhcp entry already exist """ 403 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=p3p1 action=add mode=dhcp > %s" 404 | % (self.testSource, self.testOutput), shell=True) 405 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit") 406 | out = subprocess.check_output("awk -f readInterfaces.awk %s device=p3p1" % self.testOutput, shell=True) 407 | self.assertTrue(out.strip() == 'dhcp', 'Cannot find added dhcp entry: ' + out) 408 | numLines = self.numOfDiffLines() 409 | self.assertTrue(numLines == 0, 410 | "Make sure the rest of the content is not corrupted\n" + self.diffContent()) 411 | 412 | class DhcpAddStaticExist(NI_TestCase): 413 | 414 | def runTest(self): 415 | """ Add a dhcp entry which already exist as a static """ 416 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=eth0 action=add mode=dhcp > %s" 417 | % (self.testSource, self.testOutput), shell=True) 418 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit") 419 | out = subprocess.check_output("awk -f readInterfaces.awk %s device=eth0" % self.testOutput, shell=True) 420 | self.assertTrue(out.strip() == 'dhcp', 'Cannot find added dhcp entry: ' + out) 421 | numLines = self.numOfDiffLines() 422 | self.assertTrue(numLines == 6, 423 | "Make sure the rest of the content is not corrupted\n" + self.diffContent()) 424 | 425 | class StaticAdd(NI_TestCase): 426 | 427 | def runTest(self): 428 | """ Add a basic static entry """ 429 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=eth2 action=add mode=static address=10.0.10.12 netmask=255.255.255.0 gateway=10.0.10.254 > %s" 430 | % (self.testSource, self.testOutput), shell=True) 431 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit") 432 | out = subprocess.check_output("awk -f readInterfaces.awk %s device=eth2" % self.testOutput, shell=True) 433 | self.assertTrue(out.strip() == '10.0.10.12 255.255.255.0 10.0.10.254', 'Cannot find added dhcp entry: ' + out) 434 | numLines = self.numOfDiffLines() 435 | self.assertTrue(numLines == 5, 436 | "Make sure the rest of the content is not corrupted\n" + self.diffContent()) 437 | 438 | class StaticAddFull(NI_TestCase): 439 | 440 | def runTest(self): 441 | """ Add a basic static full entry """ 442 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=eth2 action=add mode=static address=10.0.10.18 netmask=255.255.255.0 gateway=10.0.10.1 network=10.0.0.0 'dns=10.0.10.1 10.0.10.11 10.0.10.77' > %s" 443 | % (self.testSource, self.testOutput), shell=True) 444 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit") 445 | # Need sort because the order of output is diff on Linux vs OSX 446 | out = subprocess.check_output("awk -f readInterfaces.awk %s device=eth2 output=all | sort" % self.testOutput, shell=True) 447 | lines = out.splitlines() 448 | self.assertTrue(len(lines) == 3, "Expect full interface output") 449 | self.assertTrue(lines[0].strip() == '10.0.10.18 255.255.255.0 10.0.10.1', 'Cannot find added dhcp entry: ' + out) 450 | head, tail = lines[1].split(None, 1) 451 | self.assertTrue(head == 'dns-nameservers', "Expect dns output ") 452 | self.assertTrue(tail == '10.0.10.1 10.0.10.11 10.0.10.77', "Expect dns address") 453 | head, tail = lines[2].split(None, 1) 454 | self.assertTrue(head == 'network', "Expect network output ") 455 | self.assertTrue(tail == '10.0.0.0', "Expect network address") 456 | numLines = self.numOfDiffLines() 457 | self.assertTrue(numLines == 7, 458 | "Make sure the rest of the content is not corrupted\n" + self.diffContent()) 459 | 460 | class StaticAddDhcpExist(NI_TestCase): 461 | 462 | def runTest(self): 463 | """ Add a basic static entry which DHCP already exist """ 464 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=p3p1 action=add mode=static address=10.0.10.12 netmask=255.255.255.0 gateway=10.0.10.254 > %s" 465 | % (self.testSource, self.testOutput), shell=True) 466 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit") 467 | out = subprocess.check_output("awk -f readInterfaces.awk %s device=p3p1" % self.testOutput, shell=True) 468 | self.assertTrue(out.strip() == '10.0.10.12 255.255.255.0 10.0.10.254', 'Cannot find added dhcp entry: ' + out) 469 | numLines = self.numOfDiffLines() 470 | self.assertTrue(numLines == 4, 471 | "Make sure the rest of the content is not corrupted\n" + self.diffContent()) 472 | 473 | class StaticAddFullDhcpExist(NI_TestCase): 474 | 475 | def runTest(self): 476 | """ Add a full static entry which DHCP already exist """ 477 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=p3p1 action=add mode=static address=196.254.1.2 netmask=255.255.255.96 gateway=196.254.1.128 network=196.254.0.0 dns=196.254.1.128 > %s" 478 | % (self.testSource, self.testOutput), shell=True) 479 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit") 480 | out = subprocess.check_output("awk -f readInterfaces.awk %s device=p3p1 output=all" % self.testOutput, shell=True) 481 | lines = out.splitlines() 482 | self.assertTrue(lines[0].strip() == '196.254.1.2 255.255.255.96 196.254.1.128', 483 | 'Cannot find added dhcp entry: ' + lines[0]) 484 | self.matchLine(lines, 'network', '196.254.0.0') 485 | self.matchLine(lines, 'dns-nameservers', '196.254.1.128') 486 | 487 | numLines = self.numOfDiffLines() 488 | self.assertTrue(numLines == 6, 489 | "Make sure the rest of the content is not corrupted\n" + self.diffContent()) 490 | 491 | class StaticAddFullStaticExist(NI_TestCase): 492 | 493 | def runTest(self): 494 | """ Add a full static entry which static entry already exist """ 495 | rc = subprocess.check_call("awk -f changeInterface.awk %s dev=br0 action=add mode=static address=10.0.10.12 netmask=255.255.128.0 gateway=10.0.10.254 > %s" 496 | % (self.testSource, self.testOutput), shell=True) 497 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit") 498 | out = subprocess.check_output("awk -f readInterfaces.awk %s device=br0 output=all" % self.testOutput, shell=True) 499 | lines = out.splitlines() 500 | self.assertTrue(lines[0].strip() == '10.0.10.12 255.255.128.0 10.0.10.254', 501 | 'Cannot find br0 static entry: ' + lines[0]) 502 | self.matchLine(lines, 'network', '192.168.0.0') 503 | self.matchLine(lines, 'broadcast', '192.168.0.255') 504 | self.matchLine(lines, 'bridge_fd', '9') 505 | 506 | numLines = self.numOfDiffLines() 507 | self.assertTrue(numLines == 3, 508 | "Make sure the rest of the content is not corrupted\n" + self.diffContent()) 509 | 510 | class DhcpIpv4ToIpv6(NI_TestCase): 511 | 512 | def runTest(self): 513 | """ Change from Dhcp IPv4 to IPv6 """ 514 | rc = subprocess.check_call("awk -f changeInterface.awk %s device=p3p1 version=ipv6 mode=dhcp > %s" 515 | % (self.testSource, self.testOutput), shell=True) 516 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit") 517 | 518 | out = None 519 | try: 520 | subprocess.check_output("diff %s %s" % (self.testSource, self.testOutput), shell=True) 521 | self.fail('No difference after ip version command') 522 | except subprocess.CalledProcessError as err: 523 | self.assertEqual(err.returncode, 1, 524 | "Expected different in diff command") 525 | out = err.output 526 | 527 | lines = out.splitlines() 528 | self.assertTrue(lines[0].strip() == '14c14', 529 | 'Unexpected diff output ' + lines[0]) 530 | self.assertTrue(lines[1].strip() == '< iface p3p1 inet dhcp', 531 | 'Unexpected diff output ' + lines[1]) 532 | self.assertTrue(lines[3].strip() == '> iface p3p1 inet6 dhcp', 533 | 'Unexpected diff output ' + lines[3]) 534 | numLines = self.numOfDiffLines() 535 | self.assertTrue(numLines == 1, 536 | "Make sure the rest of the content is not corrupted\n" + self.diffContent()) 537 | 538 | class ChangeIpv4StaticToIpv6Static(NI_TestCase): 539 | 540 | def runTest(self): 541 | """ Change a IPv4 entry to IPv6 Static """ 542 | rc = subprocess.check_call("awk -f changeInterface.awk %s device=eth3 version=ipv6 mode=static address=2607:f0d0:2001:000a:0000:0000:0000:0010 netmask=64 gateway=2607:f0d0:2001:000a:0000:0000:0000:0001 > %s" 543 | % (self.testSource, self.testOutput), shell=True) 544 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit") 545 | out = subprocess.check_output("awk -f readInterfaces.awk %s device=eth3" % self.testOutput, shell=True) 546 | lines = out.splitlines() 547 | self.assertTrue(lines[0].strip() == '2607:f0d0:2001:000a:0000:0000:0000:0010 64 2607:f0d0:2001:000a:0000:0000:0000:0001', 548 | 'IPv6 address does not match ' + lines[0]) 549 | 550 | numLines = self.numOfDiffLines() 551 | self.assertTrue(numLines == 4, 552 | "Make sure the rest of the content is not corrupted\n" + self.diffContent()) 553 | 554 | class AddIpv6Static(NI_TestCase): 555 | 556 | def runTest(self): 557 | """ Add a IPv6 entry """ 558 | rc = subprocess.check_call("awk -f changeInterface.awk %s device=eth1 version=ipv6 action=add mode=static address=2001:470:801f::1 netmask=64 gateway=2001:470:1f05:15a::1 > %s" 559 | % (self.testSource, self.testOutput), shell=True) 560 | self.assertTrue(rc == 0, "changeInterface.awk non zero exit") 561 | out = subprocess.check_output("awk -f readInterfaces.awk %s device=eth1 output=all" % self.testOutput, shell=True) 562 | lines = out.splitlines() 563 | self.assertTrue(lines[0].strip() == '2001:470:801f::1 64 2001:470:1f05:15a::1', 564 | 'Cannot find eth1 static entry: ' + lines[0]) 565 | 566 | numLines = self.numOfDiffLines() 567 | self.assertTrue(numLines == 5, 568 | "Make sure the rest of the content is not corrupted\n" + self.diffContent()) 569 | 570 | 571 | ########################################### 572 | # ADD YOUR OWN TESTS HERE 573 | ########################################### 574 | 575 | if __name__ == "__main__": 576 | 577 | intf_file = open(testfile, 'w') 578 | intf_file.write(network_test) 579 | intf_file.close() 580 | 581 | suite = unittest.TestSuite() 582 | suite.addTest(StaticChangeAddress()) 583 | suite.addTest(DhcpToStaticAddress()) 584 | suite.addTest(StaticToDhcpMode()) 585 | suite.addTest(StaticToDhcpModeLastEntry()) 586 | 587 | suite.addTest(StaticAddNetworkBroadcast()) 588 | suite.addTest(StaticChangeNetwork()) 589 | suite.addTest(StaticDeleteNetworkBroadcast()) 590 | 591 | suite.addTest(StaticAddDns()) 592 | suite.addTest(StaticChangeDns()) 593 | suite.addTest(StaticDeleteDns()) 594 | 595 | suite.addTest(RemoveDhcp()) 596 | suite.addTest(RemoveStatic()) 597 | suite.addTest(RemoveStatic2()) 598 | suite.addTest(RemoveNotExist()) 599 | suite.addTest(RemoveMultiple()) 600 | 601 | suite.addTest(DhcpAdd()) 602 | suite.addTest(DhcpAddExist()) 603 | suite.addTest(DhcpAddStaticExist()) 604 | 605 | suite.addTest(StaticAdd()) 606 | suite.addTest(StaticAddFull()) 607 | suite.addTest(StaticAddDhcpExist()) 608 | suite.addTest(StaticAddFullDhcpExist()) 609 | suite.addTest(StaticAddFullStaticExist()) 610 | 611 | suite.addTest(DhcpIpv4ToIpv6()) 612 | suite.addTest(ChangeIpv4StaticToIpv6Static()) 613 | suite.addTest(AddIpv6Static()) 614 | 615 | unittest.TextTestRunner(verbosity=2).run(suite) 616 | 617 | os.remove(testfile) 618 | 619 | --------------------------------------------------------------------------------