def run(self, args): host = args[0] rows = self.owner.call('list.host.interface', [host, 'expanded=True']) udev_output = "" for row in rows: mac = row['mac'] ip = row['ip'] device = row['interface'] netmask = row['mask'] subnet = row['network'] module = row['module'] options = row['options'] channel = row['channel'] gateway = row['gateway'] vlanid = row['vlan'] default = row['default'] if ip and not subnet: Warn( f'WARNING: skipping interface "{device}" on host "{host}" - ' 'interface has an IP but no network') continue # If we don't have a device, we don't need a config file if not device: continue mtu = None if subnet: subnetInfo = self.owner.call('list.network', [subnet]) mtu = subnetInfo[0]['mtu'] # Host attributes can override the subnets tables # definition of the netmask. x = self.owner.getHostAttr(host, 'network.%s.netmask' % subnet) if x: netmask = x optionlist = [] if options: optionlist = shlex.split(options) if 'noreport' in optionlist: continue # don't do anything if noreport set if device == 'ipmi': self.owner.addOutput( host, '<stack:file stack:name="/etc/sysconfig/ipmi" stack:perms="500">' ) self.owner.writeIPMI(host, ip, channel, netmask, gateway, vlanid) self.owner.addOutput(host, '</stack:file>') # ipmi is special, skip the standard stuff continue if device and device[0:4] != 'vlan': # # output a script to update modprobe.conf # self.writeModprobe(host, device, module, optionlist) if self.owner.interface: if self.owner.interface == device: self.writeConfig(host, mac, ip, device, netmask, vlanid, mtu, optionlist, channel, default) else: s = '<stack:file stack:name="' s += '/etc/sysconfig/network-scripts/ifcfg-' s += '%s">' % (device) self.owner.addOutput(host, s) self.owner.addOutput(host, '# AUTHENTIC STACKI') self.writeConfig(host, mac, ip, device, netmask, vlanid, mtu, optionlist, channel, default) self.owner.addOutput(host, '</stack:file>') ib_re = re.compile('^ib[0-9]+$') if not ib_re.match(device): udev_output += 'SUBSYSTEM=="net", ' udev_output += 'ACTION=="add", ' udev_output += 'DRIVERS=="?*", ' udev_output += 'ATTR{address}=="%s", ' % mac udev_output += 'ATTR{type}=="1", ' udev_output += 'KERNEL=="eth*", ' udev_output += 'NAME="%s"\n\n' % device if udev_output: self.owner.addOutput( host, '<stack:file stack:name="/etc/udev/rules.d/70-persistent-net.rules">' ) self.owner.addOutput(host, udev_output) self.owner.addOutput(host, '</stack:file>')
def run(self, args): host = args[0] bond_reg = re.compile('bond[0-9]+') udev_output = "" result = self.owner.call('list.host.interface', ['expanded=true', host]) for o in result: interface = o['interface'] default = str2bool(o['default']) ip = o['ip'] netname = o['network'] vlanid = o['vlan'] mac = o['mac'] if mac: mac = mac.lower() channel = o['channel'] options = o['options'] netmask = o['mask'] gateway = o['gateway'] startmode = None bootproto = 'static' if ip and not netname: Warn( f'WARNING: skipping interface "{interface}" on host "{o["host"]}" - ' 'interface has an IP but no network') continue if netname and ip and netmask: net = ipaddress.IPv4Network('%s/%s' % (ip, netmask), strict=False) broadcast = str(net.broadcast_address) network = str(net.network_address) else: broadcast = None network = None if options: options = shlex.split(o['options']) else: options = [] if 'noreport' in options: continue # don't do anything if noreport set ib_re = re.compile('^ib[0-9]+$') if mac: if not ib_re.match(interface) and interface != 'ipmi': udev_output += 'SUBSYSTEM=="net", ' udev_output += 'ACTION=="add", ' udev_output += 'DRIVERS=="?*", ' udev_output += 'ATTR{address}=="%s", ' % mac udev_output += 'ATTR{type}=="1", ' udev_output += 'KERNEL=="eth*", ' udev_output += 'NAME="%s"\n\n' % interface if not interface: continue if interface == 'ipmi': ipmisetup = '/tmp/ipmisetup' self.owner.addOutput( host, '<stack:file stack:name="%s">' % ipmisetup) self.owner.writeIPMI(host, ip, channel, netmask, gateway, vlanid) self.owner.addOutput(host, '</stack:file>') self.owner.addOutput(host, 'chmod 500 %s' % ipmisetup) continue if len(interface.split(':')) == 2: # # virtual interface configuration # self.owner.addOutput( host, '<stack:file stack:mode="append" stack:name="/etc/sysconfig/network/ifcfg-%s">' % interface.split(':')[0]) self.owner.addOutput(host, '# AUTHENTIC STACKI') vnum = interface.split(':')[1] if ip: self.owner.addOutput(host, 'IPADDR%s=%s' % (vnum, ip)) if netmask: self.owner.addOutput(host, 'NETMASK%s=%s' % (vnum, netmask)) if network: self.owner.addOutput(host, 'NETWORK%s=%s' % (vnum, network)) if broadcast: self.owner.addOutput(host, 'BROADCAST%s=%s' % (vnum, broadcast)) self.owner.addOutput(host, 'LABEL%s=%s' % (vnum, vnum)) else: self.owner.addOutput( host, '<stack:file stack:name="/etc/sysconfig/network/ifcfg-%s">' % interface) self.owner.addOutput(host, '# AUTHENTIC STACKI') if vlanid and self.owner.host_based_routing( host, interface, vlanid): parent_device = interface.strip().split('.')[0] self.owner.addOutput(host, 'ETHERDEVICE=%s' % parent_device) self.owner.addOutput(host, 'VLAN=yes') startmode = 'auto' else: self.owner.addOutput(host, 'USERCONTROL=no') dhcp = 'dhcp' in options if dhcp: bootproto = 'dhcp' if default: self.owner.addOutput(host, 'DHCLIENT_SET_HOSTNAME="yes"') self.owner.addOutput( host, 'DHCLIENT_SET_DEFAULT_ROUTE="yes"') else: self.owner.addOutput(host, 'DHCLIENT_SET_HOSTNAME="no"') self.owner.addOutput( host, 'DHCLIENT_SET_DEFAULT_ROUTE="no"') if 'onboot=no' in options: startmode = 'manual' elif ip or dhcp or channel or 'bridge' in options: # # if there is an IP address, or this # interface should DHCP, or anything in # the 'channel' field (e.g., this is a # bridged or bonded interface), or if 'bridge' # is in the options, then turn this interface on # startmode = 'auto' if not dhcp: if ip: self.owner.addOutput(host, 'IPADDR=%s' % ip) if netmask: self.owner.addOutput(host, 'NETMASK=%s' % netmask) if network: self.owner.addOutput(host, 'NETWORK=%s' % network) if broadcast: self.owner.addOutput(host, 'BROADCAST=%s' % broadcast) if mac: self.owner.addOutput(host, 'HWADDR=%s' % mac.strip()) # # bonded interface, e.g., 'bond0' # if bond_reg.match(interface): # # if a 'bond*' device is present, then always make # sure it is enabled on boot. # startmode = 'auto' self.owner.addOutput(host, 'BONDING_MASTER=yes') # # find the interfaces that are part of this bond # i = 0 for p in result: if p['channel'] == interface: self.owner.addOutput( host, 'BONDING_SLAVE%d="%s"' % (i, p['interface'])) i = i + 1 # # Check if there are bonding options set # for opt in options: if opt.startswith('bonding-opts='): i = opt.find('=') bo = opt[i + 1:] self.owner.addOutput( host, 'BONDING_MODULE_OPTS="%s"' % bo) break # # check if this is part of a bonded channel # if channel and bond_reg.match(channel): startmode = 'auto' bootproto = 'none' if not startmode: startmode = 'off' self.owner.addOutput(host, 'STARTMODE=%s' % startmode) self.owner.addOutput(host, 'BOOTPROTO=%s' % bootproto) # # if this is a bridged interface, then go look for the # physical interface this bridge is associated with # if 'bridge' in options: for p in result: if p['channel'] == interface: self.owner.addOutput(host, 'BRIDGE=yes') self.owner.addOutput(host, 'BRIDGE_FORWARDDELAY=0') self.owner.addOutput(host, 'BRIDGE_STP=off') self.owner.addOutput( host, 'BRIDGE_PORTS=%s' % p['interface']) break self.owner.addOutput(host, '\n') self.owner.addOutput(host, '</stack:file>') if udev_output: self.owner.addOutput( host, '<stack:file stack:name="/etc/udev/rules.d/70-persistent-net.rules">' ) self.owner.addOutput(host, udev_output) self.owner.addOutput(host, '</stack:file>')
def run(self, param, args): text = [] self.beginOutput() text.append('<stack:file stack:name="/etc/hosts">') text.append(stack.text.DoNotEdit()) text.append('# Site additions go in /etc/hosts.local\n') text.append('127.0.0.1\tlocalhost.localdomain\tlocalhost\n') zones = {} aliases = {} # Populate the zone map : network->zone for row in self.call('list.network'): zones[row['network']] = row['zone'] # Populate the host -> interface -> aliases map for row in self.call('list.host.interface.alias'): host = row['host'] interface = row['interface'] if host not in aliases: aliases[host] = {} if interface not in aliases[host]: aliases[host][interface] = [] aliases[host][interface].append(row['alias']) hosts = {} # Get a list of all interfaces and populate the host map # host -> interfaces. # {"hostname":[ # {"ip":"1.2.3.4", "interface":"eth0", "zone":"domain.com","default":True/None, "shortname":True/False}, # {"ip":"2.3.4.5", "interface":"eth1", "zone":"domain2.com","default":True/None, "shortname":True/False}, # ]} interfaces = self.call('list.host.interface') for row in interfaces: if not row['ip']: continue if not row['network']: Warn( f'WARNING: skipping interface "{row["interface"]}" on host "{row["host"]}" - ' 'interface has an IP but no network') continue # Each interface dict contains interface name, # zone, whether the interface is the default one, # and whether the shortname should be assigned # to that interface host = row['host'] if host not in hosts: hosts[host] = [] h = {} h['ip'] = row['ip'] h['name'] = row['name'] h['interface'] = row['interface'] h['zone'] = zones[row['network']] h['default'] = row['default'] h['shortname'] = False if 'options' in row and row['options']: options = shlex.split(row['options']) for option in options: if option.strip() == 'shortname': h['shortname'] = True if self.validateHostInterface(host, h, aliases): hosts[host].append(h) processed = {} for host in hosts: # Check if any interface for the host has # shortname set to true shortname_exists = False l = list(filter(lambda x: x['shortname'], hosts[host])) if len(l): shortname_exists = True # For each interface in the host, get ip, zone, and names, # default, and shortname info for row in hosts[host]: ip = row['ip'] zone = row['zone'] default = row['default'] interface = row['interface'] shortname = row['shortname'] names = [] # Get the FQDN if zone: names.append('%s.%s' % (host, zone)) if row['name']: name_fqdn = f"{row['name']}.{zone}" if name_fqdn not in names: names.append(name_fqdn) # If shortname for an interface is set to true, # set this interface to have the shortname if shortname_exists and shortname: names.append(host) if row['name'] and row['name'] not in names: names.append(row['name']) # If shortname is not set for any interface # set the default interface to have the shortname if default and not shortname_exists: names.append(host) if row['name'] and row['name'] not in names: names.append(row['name']) # Add any interface specific aliases if host in aliases: if interface in aliases[host]: for alias in aliases[host].get(interface): names.append(alias) # check if this is duplicate entry: if ip in processed: if processed[ip]['names'] == ' '.join(names): continue # Write it all text.append('%s\t%s' % (ip, ' '.join(names))) if ip not in processed: processed[ip] = {} processed[ip]['names'] = '\t'.join(names) # Finally, add the hosts.local file to the list hostlocal = '/etc/hosts.local' if os.path.exists(hostlocal): f = open(hostlocal, 'r') text.append('\n# Imported from /etc/hosts.local\n') h = f.read() text.append(h) f.close() text.append('</stack:file>') self.addOutput(None, '\n'.join(text)) self.endOutput(padChar='', trimOwner=True)
def writeDhcpDotConf(self): self.addOutput('', '<stack:file stack:name="/etc/dhcp/dhcpd.conf">') self.addOutput('', stack.text.DoNotEdit()) self.addOutput('', '%s' % header) # Build a dictionary of DHCPD server addresses # for each subnet that serves PXE (DHCP). servers = {} for row in self.db.select( """ s.name, n.ip from nodes nd, subnets s, networks n where s.pxe=TRUE and nd.name=%s and n.node=nd.id and s.id=n.subnet """, (self.db.getHostname(), )): servers[row[0]] = row[1] servers['default'] = row[1] if len(servers) > 2: del servers['default'] shared_networks = {} for (netname, network, netmask, gateway, zone, device) in self.db.select( """ s.name, s.address, s.mask, s.gateway, s.zone, n.device from subnets s, networks n where pxe=TRUE and node=(select id from nodes where name=%s) and subnet=s.id """, (self.db.getHostname(), )): if self.os == 'sles': device = device.split('.')[0].split(':')[0] dhchp_settings = (netname, network, netmask, gateway, zone) if device in shared_networks: if dhchp_settings not in shared_networks[device]: shared_networks[device].append(dhchp_settings) else: shared_networks[device] = [dhchp_settings] else: self.addOutput('', '\nsubnet %s netmask %s {' % (network, netmask)) self.addOutput('', '\tdefault-lease-time\t\t1200;') self.addOutput('', '\tmax-lease-time\t\t\t1200;') ipnetwork = ipaddress.IPv4Network(network + '/' + netmask) self.addOutput('', '\toption routers\t\t\t%s;' % gateway) self.addOutput('', '\toption subnet-mask\t\t%s;' % netmask) self.addOutput( '', '\toption broadcast-address\t%s;' % ipnetwork.broadcast_address) self.addOutput('', '}\n') # # if sles, add shared_network block to interfaces with multiple subnets # if self.os == 'sles': for device in shared_networks.keys(): sn = shared_networks[device] if len(sn) == 1: for (netname, network, netmask, gateway, zone) in sn: self.addOutput( '', '\nsubnet %s netmask %s {' % (network, netmask)) self.addOutput('', '\tdefault-lease-time\t\t1200;') self.addOutput('', '\tmax-lease-time\t\t\t1200;') ipnetwork = ipaddress.IPv4Network(network + '/' + netmask) self.addOutput('', '\toption routers\t\t\t%s;' % gateway) self.addOutput('', '\toption subnet-mask\t\t%s;' % netmask) self.addOutput( '', '\toption broadcast-address\t%s;' % ipnetwork.broadcast_address) self.addOutput('', '}\n') else: self.addOutput('', '\nshared-network %s {' % device) for (netname, network, netmask, gateway, zone) in sn: self.addOutput( '', '\n\tsubnet %s netmask %s {' % (network, netmask)) self.addOutput('', '\t\tdefault-lease-time\t\t1200;') self.addOutput('', '\t\tmax-lease-time\t\t\t1200;') ipnetwork = ipaddress.IPv4Network(network + '/' + netmask) self.addOutput('', '\t\toption routers\t\t\t%s;' % gateway) self.addOutput( '', '\t\toption subnet-mask\t\t%s;' % netmask) self.addOutput( '', '\t\toption broadcast-address\t%s;' % ipnetwork.broadcast_address) self.addOutput('', '\t}\n') self.addOutput('', '}\n') data = {} for host in self.call('list.host'): data[host['host']] = [] host_devices = {} interfaces = self.call('list.host.interface') for interface in interfaces: host = interface['host'] mac = interface['mac'] ip = interface['ip'] device = interface['interface'] channel = interface['channel'] other_interfaces = [ iface['interface'] for iface in interfaces if iface['host'] == host and iface['interface'] != device ] if channel: if device == 'ipmi' and not ip: Warn( f'WARNING: skipping IPMI interface on host "{host}" - interface has a channel but no IP' ) continue elif device != 'ipmi' and channel not in other_interfaces: Warn( f'WARNING: skipping interface "{device}" on host "{host}" - ' f'interface has channel "{channel}" that does not match any other interface on the host' ) continue if host in host_devices: if device in host_devices[host]: Warn( f'WARNING: skipping interface "{device}" on host "{host}" - duplicate interface detected' ) continue else: host_devices[host].append(device) elif host: host_devices[host] = [device] if host and mac: data[host].append((mac, ip, device)) for name in data.keys(): kickstartable = self.str2bool( self.getHostAttr(name, 'kickstartable')) aws = self.str2bool(self.getHostAttr(name, 'aws')) mac = None ip = None dev = None # # look for a physical private interface that has an # IP address assigned to it. # for (mac, ip, dev) in data[name]: if not ip: try: ip = self.resolve_ip(name, dev) except IndexError: Warn( f'WARNING: skipping interface "{device}" on host "{host}" - duplicate interface detected' ) continue netname = None if ip: r = self.db.select( """ s.name from subnets s, networks nt, nodes n where nt.node=n.id and n.name=%s and nt.subnet=s.id and s.pxe=TRUE and nt.ip=%s """, (name, ip)) if r: (netname, ) = r[0] if ip and mac and dev and netname and not aws: self.addOutput('', '\nhost %s.%s.%s {' % (name, netname, dev)) self.addOutput('', '\toption host-name\t"%s";' % name) self.addOutput('', '\thardware ethernet\t%s;' % mac) self.addOutput('', '\tfixed-address\t\t%s;' % ip) if kickstartable: self.addOutput('', filename) server = servers.get(netname) if not server: server = servers.get('default') self.addOutput('', '\tserver-name\t\t"%s";' % server) self.addOutput('', '\tnext-server\t\t%s;' % server) self.addOutput('', '}') self.addOutput('', '</stack:file>')