| #!/usr/bin/python |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| |
| |
| ''' |
| This reads a configuration file and checks for functioning |
| network links in /sys/class/net/*, then emits a ks.cfg network line. |
| ''' |
| |
| from __future__ import print_function |
| import os |
| import re |
| |
| |
| global TO_LOG |
| # This "logs" to stdout which is captured during kickstart |
| TO_LOG = True |
| |
| # This is the standard interface we install to. It is set to a speed value of |
| # 5 (vice 100,1000 or 10000) later on and any other interface will override it |
| # if you've got something faster installed. |
| standard_interface=['p4p1'] |
| |
| ## These are configuration settings: |
| # Name of Configuration file: |
| cfg_file = "network.cfg" |
| |
| # Where linux is putting the interface stuff: |
| iface_dir = '/sys/class/net/' |
| |
| ignore_interfaces = ['lo','bond'] |
| |
| # Where we kickstart mounts the ISO, and our config directory: |
| base_cfg_dir = '/mnt/stage2/ks_scripts/' |
| |
| # Remember the ? makes the match non-greedy. This is important. |
| cfg_line = re.compile("\s*(?P<key>.*?)=(?P<value>.*)\s*$") |
| |
| # Pick the interface speed for bonding, or "Auto". |
| # Auto assumes you want the fastest connections with more than 1 interface, |
| # Or if there's not 2 interfaces at the same speed you want the fastest. |
| # auto is expected to be a string, otherwise use integers: |
| # Speed is in megs. 1000 is 1 gig, 10000 is 10G. |
| |
| iface_speed = 'auto' |
| |
| restring = iface_dir + "(?P<iface>.*)/speed" |
| iface_search = re.compile(restring) |
| |
| |
| def read_config(config_file): |
| ''' Reads our network config file and hands back a dict of key:value |
| pairs ''' |
| |
| net_cfg = {} |
| with open(config_file,'r') as cfg: |
| network_lines = cfg.readlines() |
| for line in network_lines: |
| if cfg_line.match(line): |
| key = cfg_line.match(line).group('key') |
| value = cfg_line.match(line).group('value') |
| net_cfg[key] = value |
| return net_cfg |
| |
| |
| def find_usable_net_devs(location): |
| ''' Search through iface_dir looking for /speed files. |
| Build a dict keyed on speed (in otherwords the speed is the key with a list of |
| interfaces as teh value). ''' |
| # We "pre-seed" the dictionary with the standard interface names at a |
| # speed of 5 so that if there's nothing else we set that up. This |
| # makes it easier to reconfigure later. |
| ifaces = {5:standard_interface} |
| bad_ifaces={} |
| devs = os.listdir(location) |
| for dev in devs: |
| dev_path = os.path.join(location,dev,'speed') |
| add=True |
| if os.path.isfile(dev_path): |
| with open(dev_path,'r') as iface: |
| try: |
| speed = iface.readlines() |
| # speed should only have one line: |
| speed = int(speed[0]) |
| # if there is no link some drivers/cards/whatever will |
| # throw an IOError when you try to read the speed file. |
| except IOError: |
| speed = 0 |
| # Other cards will return a -1, which is fine, but *some* of them |
| # return a 65535. Those we set to 0 as well. |
| if speed == 65535: |
| speed = 0 |
| for i_face in ignore_interfaces: |
| if i_face in dev: |
| add = False |
| if speed <= 0: |
| add = False |
| if TO_LOG: |
| print(add, dev) |
| if add: |
| if speed in ifaces: |
| this_speed = ifaces[speed] |
| this_speed.append(dev) |
| ifaces[speed]=this_speed |
| else: |
| ifaces[speed]=[dev] |
| else: |
| bad_ifaces[dev] = speed |
| print("We find these interfaces have link and might be useful:", ifaces) |
| if TO_LOG: |
| print("And these aren't useful:", bad_ifaces) |
| return ifaces |
| |
| |
| def useable_interfaces(net_devs, nc, iface_speed): |
| ''' This takes a go at figuring out which interfaces to use.''' |
| iface_list = False |
| notes = False |
| if TO_LOG: |
| print("in usable interfaces") |
| |
| if "bond" not in nc['BOND_DEVICE'].lower(): |
| if TO_LOG: |
| print("useable interfaces if not", nc['BOND_DEVICE']) |
| # Not doing a bond, so we check to make sure the requested device, |
| # nc['BOND_DEVICE'], is in the list of devices with carrier: |
| if nc['BOND_DEVICE'] == '""': |
| # In this case we have no network interface in the configuration but we |
| # network settings. |
| # First we check how many net_devs we have: |
| if TO_LOG: |
| print("nc['BOND_DEVICE']=''", len(net_devs), net_devs) |
| if len(net_devs) == 1: # This is a dict of speed: devices |
| speeds = net_devs.keys() |
| speeds.sort(reverse=True) |
| speed = speeds[0] |
| possibles = net_devs[speed] |
| if TO_LOG: |
| print(possibles) |
| # At this point we have options, but no information, so: |
| notes = "No device in the configuration file and multiple devices found. Picking the first" |
| iface_list = [possibles[0]] |
| else: |
| if TO_LOG: |
| print("inner else") |
| for speed in net_devs: |
| if nc['BOND_DEVICE'] in net_devs[speed]: |
| iface_list = [nc['BOND_DEVICE']] |
| else: |
| iface_list = [nc['BOND_DEVICE']] |
| notes = "{0} did not have carrier at install time, and may not work".format(nc['BOND_DEVICE']) |
| elif iface_speed != 'auto': |
| if len(net_devs[iface_speed]) > 0: |
| iface_list = net_devs[iface_speed] |
| else: |
| notes = "no devices set to {0}".format(iface_speed) |
| else: # This SHOULD be iface_speed == auto, and nc['BOND_DEVCE'] containing bond. |
| # if not it is anyway. |
| # Thus we are doing a bond of some sort. |
| # This gives us the fastest interfaces first: |
| speeds = [k for k in sorted(net_devs.keys(), reverse=True)] |
| fastest = speeds[0] |
| # Walk through "speeds" and take the first one that has more than one |
| # interface. This will only set iface_list if there are 2 or more interfaces: |
| # previous_speed = 0 |
| for i in speeds: |
| if len(net_devs[i]) > 1: |
| iface_list = net_devs[i] |
| break |
| if TO_LOG: |
| print("iface list:", iface_list) |
| # if iface_list is still false, and we are requesting a bond, we will |
| # want the fastest interface with link: |
| if (iface_list == False) and ("bond" in nc['BOND_DEVICE'].lower()): |
| if TO_LOG: |
| print(len(net_devs), net_devs, i) |
| if len(net_devs) == 0: |
| iface_list = net_devs |
| notes = "no devices found for the bond. Will not have network after reboot" |
| else: |
| iface_list = net_devs[fastest] # This is assuming that we'll want to bond the fastest interaface. |
| if TO_LOG: |
| print("dev:", net_devs[fastest]) |
| if TO_LOG: |
| print(iface_list, notes) |
| return iface_list, notes |
| |
| |
| # Find our network configuration file: |
| if os.path.isfile(os.path.join(base_cfg_dir,cfg_file)): |
| cfg_path = os.path.join(base_cfg_dir,cfg_file) |
| elif os.path.isfile(cfg_file): |
| cfg_path = cfg_file |
| else: |
| cfg_path = '' |
| |
| if cfg_path: |
| nc = read_config(cfg_path) |
| else: |
| # if we don't have a working config file we use this |
| # The IPs and hostnames are bad. |
| nc = { IPADDR:"10.0.0.2", |
| NETMASK:"255.255.255.252", |
| GATEWAY:"10.0.0.1", |
| BOND_DEVICE:"bond0", |
| MTU:"9000", |
| NAMESERVER:"192.168.0.1", |
| HOSTNAME:"bad.example.com", |
| NETWORKING_IPV6:"yes", |
| IPV6ADDR:" 2001:0db8:0a0b:12f0:0000:0000:0000:0002/64", |
| IPV6_DEFAULTGW:" 2001:0db8:0a0b:12f0:0000:0000:0000:0001", |
| BONDING_OPTS:"miimon=100 mode=4 lacp_rate=fast xmit_hash_policy=layer3+4", |
| DHCP:"no" } |
| # This should be set to no in the config file, but that could change: |
| if "DHCP" not in nc: |
| nc['DHCP']='no' |
| |
| net_devs = find_usable_net_devs(iface_dir) |
| bondable_iface, iface_problems = useable_interfaces(net_devs, nc, iface_speed) |
| |
| # turn bondable_iface into a string for the network line: |
| if bondable_iface and len(bondable_iface) > 1: |
| dev_list = bondable_iface |
| dev_str = dev_list.pop() |
| for d in dev_list: |
| dev_str = dev_str + "," + d |
| else: |
| dev_str = bondable_iface[0] |
| |
| |
| if ('y' in nc['NETWORKING_IPV6'].lower()) and re.search(":",nc['IPV6ADDR']): |
| IPV6 = "--ipv6=" + nc["IPV6ADDR"] |
| else: |
| if 'y' in nc['NETWORKING_IPV6'].lower(): |
| if iface_problems is False: |
| iface_problems = "IPv6 enabled but no address provided" |
| else: |
| iface_problems = "{0} and IPv6 enabled but no address provided".format(iface_problems) |
| if re.search(":",nc['IPV6ADDR']): |
| if iface_problems is False: |
| iface_problems = "IPv6 is disabled, but IPV6ADDR was set to {0}".format(nc['IPV6ADDR']) |
| else: |
| iface_problems = "{0} and IPv6 is disabled, but IPV6ADDR was set to {1}".format(iface_problems, nc['IPV6ADDR']) |
| IPV6 = "--noipv6" |
| |
| if "bond" in nc['BOND_DEVICE'].lower(): |
| bond_stuff = "--device={BOND_DEVICE} --bondslaves={0} --bondopts={BONDING_OPTS}".format(dev_str, **nc) |
| elif nc['BOND_DEVICE'] in dev_str: |
| bond_stuff = "--device={0}".format(nc["BOND_DEVICE"]) |
| elif bondable_iface and nc['BOND_DEVICE'] == '""' : |
| print("**") |
| print("No device (BOND_DEVICE) specified it he config, found", bondable_iface, "with link, using it.") |
| print("**") |
| bond_stuff = "--device={0}".format(bondable_iface[0]) |
| else: |
| print("**") |
| print(nc["BOND_DEVICE"], "not found within $usable_devices, setting anyway, this probably won't work") |
| print("**") |
| bond_stuff = "--device={0}".format(nc["BOND_DEVICE"]) |
| |
| if 'yes' in nc['DHCP'].lower() or not bondable_iface: |
| network_line = "network --bootproto=dhcp --device={BOND_DEVICE} --hostname={HOSTNAME}".format(**nc) |
| else: |
| network_line = "network --bootproto=static {0} --activate {1} --ip={IPADDR} --netmask={NETMASK} --gateway={GATEWAY} --nameserver={NAMESERVER} --mtu={MTU} --hostname={HOSTNAME} \n".format( |
| bond_stuff, IPV6, **nc) |
| |
| if iface_problems: |
| network_line = "# Problems found: {0}\n{1}".format(iface_problems,network_line) |
| |
| with open('/tmp/network_line','w') as OUT: |
| OUT.write(network_line) |