def _setup_bridge(bridge, ip=None, prefix=None, ifc=None): """ Sets up a bridge. If the bridge does not exist, it is created. If an ip address and prefix are given it will be added to the bridge. If an interface name is specified it will be attached to the bridge Args: bridge (str) bridge name ip (str) ipv4 address (ie '1.2.3.4') prefix (int or str) Network mask length (ie 24) ifc (str) An interface name """ LOG.debug('Setting up bridge {} with ifc {} and address {}' .format(bridge, ifc, ip)) if not IPR.link_lookup(ifname=bridge): IPR.link("add", ifname=bridge, kind="bridge") IPR.link("set", ifname=bridge, state="up") if ip and prefix: if not _is_addr_on_link(ip, bridge): IPR.addr( 'add', index=IPR.link_lookup(ifname=bridge)[0], address=ip, mask=int(prefix)) if ifc: IPR.link( 'set', index=IPR.link_lookup(ifname=ifc)[0], master=IPR.link_lookup(ifname=bridge)[0]) if not _wait_for_ifc_up(ifc): LOG.error('Failed to bring up interface {}'.format(ifc)) raise UserCriticalException('Failed to bring up interface {}'.format(ifc)) if not _wait_for_ifc_up(bridge): LOG.error('Failed to bring up bridge {}'.format(bridge)) raise UserCriticalException('Failed to bring up bridge {}'.format(bridge))
def validate_config_schema(self): """Config schema validation Exception: If schema validation fails """ schema = SchemaDefinition.get_schema(ordered=True) try: validate(self.config, schema, format_checker=jsonschema.FormatChecker()) except jsonschema.exceptions.ValidationError as error: if error.cause is None: path = None for index, element in enumerate(error.path): if isinstance(element, int): path += '[{}]'.format(element) else: if index == 0: path = '{}'.format(element) else: path += '.{}'.format(element) exc = 'Schema validation failed - {} - {}'.format( path, error.message) else: exc = 'Schema validation failed - {} - {}'.format( error.cause, error.message) if 'Additional properties are not allowed' in error.message: raise UserException(exc) else: raise UserCriticalException(exc)
def _config_file(self): from lib.inv_items import InventoryNodes print(COL.scroll_ten, COL.up_ten) print('{}Validating cluster configuration file{}\n'.format( COL.header1, COL.endc)) dbase = DatabaseConfig(self.config_file_path) inv_path = gen.GEN_LOGS_PATH + gen.INV_FILE_NAME nodes = InventoryNodes(inv_path, self.config_file_path) try: dbase.validate_config() nodes.create_nodes() rc, stdout = _run_playbook("validate_bootstrap_vars.yml", self.config_file_path, extra_vars=self.args.extra_vars, display=False, load_config_vars=True) if rc != 0: raise UserCriticalException(stdout) except UserCriticalException as exc: message = 'Failure: Config file validation.\n' + str(exc) print('{}{}{}'.format(COL.red, message, COL.endc)) sys.exit(1) except UserException as exc: message = 'Warning: Config file validation.\n' + str(exc) print('{}{}{}'.format(COL.yellow, message, COL.endc)) else: print('Successfully completed config file validation.\n')
def validate_config_logic(self): """Config logic validation""" self._validate_version() self._validate_physical_interfaces() self._validate_deployer_networks() self._validate_dhcp_lease_time() if self.exc: raise UserCriticalException(self.exc)
def validate_config_logic(self): """Config logic validation""" self._validate_version() self._validate_physical_interfaces() self._validate_deployer_networks() self._validate_dhcp_lease_time() self._validate_labels() self._validate_software_bootstrap() self._validate_os_profiles() if self.exc: raise UserCriticalException(self.exc)
def validate_pxe(self, bootdev='default', persist=True): # if self.inv.check_all_nodes_pxe_macs(): # self.log.info("Inventory exists with PXE MACs populated.") # if not self.ran_ipmi: # return # print("\nPress Enter to continue cluster deployment without " # "running PXE hardware validation.") # print("Type 'C' to validate cluster nodes defined in current " # "'config.yml'") # resp = input("Type 'T' to terminate Power-Up ") # if resp == 'T': # resp = input("Type 'y' to confirm ") # if resp == 'y': # self.log.info("'{}' entered. Terminating Power-Up at user " # "request".format(resp)) # sys.exit(1) # elif resp == 'C': # self.log.info("'{}' entered. Continuing with hardware " # "validation".format(resp)) # else: # print() # return # if not self.ran_ipmi: # return if not self.node_table_ipmi: raise UserCriticalException('No BMCs discovered') self.log.debug("Checking PXE networks and client PXE" " ports ________\n") self.log.debug('Boot device: {}'.format(bootdev)) ipmi_cnt, pxe_cnt = self._get_port_cnts() pxe_addr, bridge_addr, pxe_prefix, pxe_vlan = self._get_network('pxe') pxe_network = pxe_addr + '/' + str(pxe_prefix) addr = IPNetwork(bridge_addr + '/' + str(pxe_prefix)) netmask = str(addr.netmask) addr.value += NAME_SPACE_OFFSET_ADDR addr = str(addr) foundall = False dhcp_st = get_dhcp_pool_start() pxe_ns = NetNameSpace('pxe-ns-', 'br-pxe-' + str(pxe_vlan), addr) # setup DHCP. save start and end addr raw numeric values self.log.debug('Installing DHCP server in network namespace') addr_st = self._add_offset_to_address(pxe_network, dhcp_st) addr_end = self._add_offset_to_address(pxe_network, dhcp_st + pxe_cnt + 2) dns_list, stderr, rc = sub_proc_exec('pgrep dnsmasq') dns_list = dns_list.splitlines() if os.path.exists(self.dhcp_pxe_leases_file): os.remove(self.dhcp_pxe_leases_file) # delete any remnant dnsmasq processes for pid in dns_list: ns_name, stderr, rc = sub_proc_exec( 'ip netns identify {}'.format(pid)) if pxe_ns._get_name_sp_name() in ns_name: self.log.debug('Killing dnsmasq. pid {}'.format(pid)) stdout, stderr, rc = sub_proc_exec('kill -15 ' + pid) cmd = (f'dnsmasq --dhcp-leasefile={self.dhcp_pxe_leases_file} ' f'--interface={pxe_ns._get_name_sp_ifc_name()} ' f'--dhcp-range={addr_st},{addr_end},{netmask},3600') stdout, stderr, rc = pxe_ns._exec_cmd(cmd) if rc != 0: self.log.warning(f'Error configuring dnsmasq. rc: {rc}') if os.path.exists(self.tcp_dump_file): os.remove(self.tcp_dump_file) tcpdump_list, stderr, rc = sub_proc_exec('pgrep tcpdump') tcpdump_list = tcpdump_list.splitlines() # delete any remnant tcpdump processes for pid in tcpdump_list: ns_name, stderr, rc = sub_proc_exec('ip netns identify ' + pid) if pxe_ns._get_name_sp_name() in ns_name: self.log.debug('Killing tcpdump. pid {}'.format(pid)) stdout, stderr, rc = sub_proc_exec('kill -15 ' + pid) cmd = (f'sudo tcpdump -X -U -i {pxe_ns._get_name_sp_ifc_name()} ' f'-w {self.tcp_dump_file} --immediate-mode port 67') proc = pxe_ns._launch_cmd(cmd) if not isinstance(proc, object): self.log.error( f'Failure to launch process of tcpdump monitor {proc}') # Scan up to 25 times. Delay 10 seconds between scans # Allow infinite number of retries self.log.info('Scanning pxe network on 10 s intervals.') cnt = 0 cnt_prev = 0 cnt_down = 25 mac_list = [] dump = '' while cnt < pxe_cnt: print() cmd = 'sudo tcpdump -r {} -xx'.format(self.tcp_dump_file) for i in range(cnt_down): print('\r{} of {} nodes requesting PXE boot. Scan cnt: {} '. format(cnt, pxe_cnt, cnt_down - i), end="") sys.stdout.flush() time.sleep(10) # read the tcpdump file if size is not 0 if os.path.exists(self.tcp_dump_file) and os.path.getsize( self.tcp_dump_file): dump, stderr, rc = sub_proc_exec(cmd) if rc != 0: self.log.warning( f'Error reading tcpdump file. rc: {rc}') if 'reading' not in stderr: self.log.warning( f'Failure reading tcpdump file - {stderr}') mac_list = self._get_macs(mac_list, dump) cnt = len(mac_list) if cnt > cnt_prev: cnt_prev = cnt # Pause briefly for in flight DHCP to complete and lease file to update time.sleep(5) self._build_port_table_pxe(mac_list) if cnt >= pxe_cnt: foundall = True print( '\r{} of {} nodes requesting PXE boot. Scan count: {} ' .format(cnt, pxe_cnt, cnt_down - i), end="") break self.log.debug('Table of found PXE ports: {}'.format( self.node_table_pxe)) for switch in self.node_table_pxe: print('\n\nSwitch: {}'.format(switch)) print( tabulate(self.node_table_pxe[switch], headers=('port', 'MAC address', 'IP address'))) print() if cnt >= pxe_cnt: break print('\n\nPress Enter to continue scanning for cluster nodes.') print( "Or enter 'C' to continue cluster deployment with a subset of nodes" ) print("Or enter 'R' to cycle power to missing nodes") resp = input("Or enter 'T' to terminate Power-Up ") if resp == 'T': resp = input("Enter 'y' to confirm ") if resp == 'y': self.log.info("'{}' entered. Terminating Power-Up at user" " request".format(resp)) self._teardown_ns(self.ipmi_ns) self._teardown_ns(pxe_ns) sys.exit(1) elif resp == 'R': self._reset_unfound_nodes() elif resp == 'C': print('\nNot all nodes have been discovered') resp = input("Enter 'y' to confirm continuation of" " deployment without all nodes ") if resp == 'y': self.log.info( "'{}' entered. Continuing Power-Up".format(resp)) break if cnt < pxe_cnt: self.log.warning('Failed to validate expected number of nodes') self._teardown_ns(pxe_ns) # Cycle power on all discovered nodes if bootdev set to 'network' if bootdev == 'network': self.log.debug('\nCycling power to discovered nodes.\n') set_power_clients('off', clients=self.bmc_ai) set_power_clients('on', clients=self.bmc_ai) set_bootdev_clients('network', clients=self.bmc_ai) self._teardown_ns(self.ipmi_ns) # Reset BMCs to insure they acquire a new address from container # during inv_add_ports. Avoids conflicting addresses during redeploy self._reset_existing_bmcs(self.node_list, self._get_cred_list()) self.log.info('Cluster nodes validation complete') if not foundall: raise UserException('Not all node PXE ports validated')
def validate_mgmt_switches(self): self.log.info('Verifying management switches') sw_cnt = self.cfg.get_sw_mgmt_cnt() self.log.debug( 'Number of management switches defined in config file: {}'.format( sw_cnt)) for index, switch_label in enumerate(self.cfg.yield_sw_mgmt_label()): print('.', end="") sys.stdout.flush() label = self.cfg.get_sw_mgmt_label(index) self.log.debug('switch_label: {}'.format(switch_label)) switch_class = self.cfg.get_sw_mgmt_class(index) if not switch_class: self.log.error('No switch class found') return False userid = None password = None rc = True try: userid = self.cfg.get_sw_mgmt_userid(index) except AttributeError: self.log.debug('Passive switch mode specified') return rc try: password = self.cfg.get_sw_mgmt_password(index) except AttributeError: try: self.cfg.get_sw_mgmt_ssh_key(index) except AttributeError: return rc else: self.log.error( 'Switch authentication via ssh keys not yet supported') rc = False # Verify communication on each defined interface for ip in self.cfg.yield_sw_mgmt_interfaces_ip(index): self.log.debug('Verifying switch communication on ip address:' ' {}'.format(ip)) sw = SwitchFactory.factory(switch_class, ip, userid, password, 'active') if sw.is_pingable(): self.log.debug( 'Successfully pinged management switch \"%s\" at %s' % (label, ip)) else: self.log.warning( 'Failed to ping management switch \"%s\" at %s' % (label, ip)) rc = False try: vlans = sw.show_vlans() except (SwitchException, SSH_Exception): self.log.error( 'Failed communicating with management switch' ' at address {}'.format(ip)) rc = False if vlans and len(vlans) > 1: self.log.debug( 'Successfully communicated with management switch \"%s\"' ' at %s' % (label, ip)) else: self.log.warning( 'Failed to communicate with data switch \"%s\"' 'at %s' % (label, ip)) rc = False print() if rc: self.log.debug(' OK - All management switches verified') else: raise UserCriticalException( 'Failed verification of management switches')
def configure_mgmt_switches(config_file=None): LOG = logger.getlogger() cfg = Config(config_file) LOG.debug( '------------------- configure_mgmt_switches -------------------') for index, switch_label in enumerate(cfg.yield_sw_mgmt_label()): mode = ACTIVE label = cfg.get_sw_mgmt_label(index) LOG.info('Configuring switch: {}'.format(switch_label)) switch_class = cfg.get_sw_mgmt_class(index) if not switch_class: LOG.error('Unrecognized switch class') raise UserCriticalException('Unrecognized switch class') userid = None password = None switch_ip = None if cfg.is_passive_mgmt_switches(): mode = PASSIVE try: userid = cfg.get_sw_mgmt_userid(index) except AttributeError: pass try: password = cfg.get_sw_mgmt_password(index) except AttributeError: try: cfg.get_sw_mgmt_ssh_key(index) except AttributeError: pass else: LOG.error( 'Switch authentication via ssh keys not yet supported') raise UserCriticalException( 'Switch authentication via ssh keys not yet supported') if mode == PASSIVE: sw = SwitchFactory.factory(switch_class, mode) elif mode == ACTIVE: # Try all ipaddrs in switches.interfaces for ip in cfg.yield_sw_mgmt_interfaces_ip(index): sw = SwitchFactory.factory(switch_class, ip, userid, password) # Get the enumerations needed to call set_switchport_mode() and # allowed_vlans_port() port_mode, allow_op = sw.get_enums() if sw.is_pingable(): LOG.debug( 'Sucessfully pinged management switch \"%s\" at %s' % (label, ip)) switch_ip = ip break LOG.debug('Failed to ping management switch \"%s\" at %s' % (label, ip)) else: LOG.error('Management switch is not responding to pings') raise UserCriticalException( 'Management switch at address {} is not responding to ' 'pings'.format(ip)) LOG.debug('%d: \"%s\" (%s) %s %s/%s' % (index, switch_label, mode, switch_ip, userid, password)) ports_cfg = sw.show_ports(format='std') sw_vlans = [] for port in ports_cfg: if int(ports_cfg[str(port)]['nvlan']) not in sw_vlans: sw_vlans.append(int(port)) avlans = ports_cfg[str(port)]['avlans'].split(', ') for avlan in avlans: if avlan and int(avlan) not in sw_vlans: sw_vlans.append(int(avlan)) vlan_mgmt = cfg.get_depl_netw_mgmt_vlan() vlan_mgmt = [x for x in vlan_mgmt if x is not None] LOG.debug('Management vlans: {}'.format(vlan_mgmt)) vlan_client = cfg.get_depl_netw_client_vlan() LOG.debug('vlan_mgmt: {} , vlan_client: {}'.format( vlan_mgmt, vlan_client)) for vlan in vlan_mgmt + vlan_client: if vlan and vlan not in sw_vlans: print('.', end="") sys.stdout.flush() sw.create_vlan(vlan) for intf_i, ip in enumerate(cfg.yield_sw_mgmt_interfaces_ip(index)): if ip != switch_ip: vlan = cfg.get_sw_mgmt_interfaces_vlan(index, intf_i) if vlan is None: vlan = vlan_mgmt[0] netmask = cfg.get_sw_mgmt_interfaces_netmask(index, intf_i) try: LOG.debug( "Configuring mgmt switch \"%s\" inband interface. " "(ip=%s netmask=%s vlan=%s)" % (label, ip, netmask, vlan)) # sw.configure_interface(ip, netmask, vlan, port) sw.configure_interface(ip, netmask, vlan) except SwitchException as exc: LOG.warning(exc) for target_i, target in (enumerate( cfg.yield_sw_mgmt_links_target(index))): port = cfg.get_sw_mgmt_links_port(index, target_i) if target.lower() == 'deployer': vlans = vlan_mgmt + vlan_client if ports_cfg[str(port)]['mode'] != 'trunk': try: print('.', end="") sys.stdout.flush() LOG.debug('Adding vlans {} to port {}'.format( vlans, port)) sw.set_switchport_mode(port, port_mode.TRUNK) except SwitchException as exc: LOG.error(exc) else: LOG.debug('Port {} already in trunk mode'.format(port)) port_vlans = ports_cfg[str(port)]['avlans'].split(', ') add_vlans = False for vlan in vlan_client: if str(vlan) not in port_vlans: add_vlans = True if (vlan_mgmt and str(vlan_mgmt) not in port_vlans) or add_vlans: try: sw.allowed_vlans_port(port, allow_op.ADD, vlans) except SwitchException as exc: LOG.error(exc) else: vlan = cfg.get_sw_mgmt_links_vlan(index, target_i) if vlan is None: if not vlan_mgmt: vlan = 1 else: vlan = vlan_mgmt[0] if ports_cfg[str(port)]['mode'] != 'trunk' or \ str(vlan) not in ports_cfg[str(port)]['avlans'].split(', '): try: sw.set_switchport_mode(port, port_mode.TRUNK) sw.allowed_vlans_port(port, allow_op.NONE) sw.allowed_vlans_port(port, allow_op.ADD, vlan) sw.set_switchport_mode(port, port_mode.TRUNK, vlan) except SwitchException as exc: LOG.error(exc) for if_type in ['ipmi', 'pxe']: vlan = cfg.get_depl_netw_client_vlan(if_type=if_type)[0] for port in cfg.yield_client_switch_ports(switch_label, if_type): if mode == 'passive': LOG.debug( 'Set switchport mode - switch is in passive mode.') else: print('.', end="") sys.stdout.flush() if vlan != int(ports_cfg[str(port)]['nvlan']): try: LOG.debug( 'Setting port {} into {} mode with access ' 'vlan {}'.format(port, port_mode.ACCESS, vlan)) sw.set_switchport_mode(port, port_mode.ACCESS, vlan) except SwitchException as exc: LOG.error(exc) else: LOG.debug( '\n{} port {} access vlan already configured.'. format(if_type, port)) # Remove (optionally) access vlan from ports in pxe or ipmi vlan that # are not listed in the config file. ports = cfg.get_client_switch_ports(switch_label, if_type) resp = 'y' for port in ports_cfg: if int(port) not in ports and ports_cfg[port]['nvlan'] == str( vlan): msg = ( 'Port {} on switch {} configured with vlan {} but is ' 'not specified in the {} network in your cluster config ' 'file '.format(port, switch_label, vlan, if_type)) print() LOG.warning(msg) if resp not in ('yta', 'nta'): resp = rlinput( '\nOK to remove port {} from {} vlan ' '(y/yta/n/nta)? '.format(port, if_type), 'y') if resp in ('y', 'yta'): sw.set_switchport_mode(port, port_mode.ACCESS, 1)
def configure_mgmt_switches(config_file=None): LOG = logger.getlogger() cfg = Config(config_file) LOG.debug( '------------------- configure_mgmt_switches -------------------') for index, switch_label in enumerate(cfg.yield_sw_mgmt_label()): mode = ACTIVE label = cfg.get_sw_mgmt_label(index) LOG.info('Configuring switch: {}'.format(switch_label)) switch_class = cfg.get_sw_mgmt_class(index) if not switch_class: LOG.error('Unrecognized switch class') raise UserCriticalException('Unrecognized switch class') userid = None password = None switch_ip = None if cfg.is_passive_mgmt_switches(): mode = PASSIVE try: userid = cfg.get_sw_mgmt_userid(index) except AttributeError: pass try: password = cfg.get_sw_mgmt_password(index) except AttributeError: try: cfg.get_sw_mgmt_ssh_key(index) except AttributeError: pass else: LOG.error( 'Switch authentication via ssh keys not yet supported') raise UserCriticalException( 'Switch authentication via ssh keys not yet supported') if mode == PASSIVE: sw = SwitchFactory.factory(switch_class, mode) elif mode == ACTIVE: # Try all ipaddrs in switches.interfaces for ip in cfg.yield_sw_mgmt_interfaces_ip(index): sw = SwitchFactory.factory(switch_class, ip, userid, password) # Get the enumerations needed to call set_switchport_mode() and # allowed_vlans_port() port_mode, allow_op = sw.get_enums() if sw.is_pingable(): LOG.debug( 'Sucessfully pinged management switch \"%s\" at %s' % (label, ip)) switch_ip = ip break LOG.debug('Failed to ping management switch \"%s\" at %s' % (label, ip)) else: LOG.error('Management switch is not responding to pings') raise UserCriticalException( 'Management switch at address {} is not responding to ' 'pings'.format(ip)) LOG.debug('%d: \"%s\" (%s) %s %s/%s' % (index, switch_label, mode, switch_ip, userid, password)) vlan_mgmt = cfg.get_depl_netw_mgmt_vlan() vlan_mgmt = [x for x in vlan_mgmt if x is not None] LOG.debug('Management vlans: {}'.format(vlan_mgmt)) vlan_client = cfg.get_depl_netw_client_vlan() LOG.debug('vlan_mgmt: {} , vlan_client: {}'.format( vlan_mgmt, vlan_client)) for vlan in vlan_mgmt + vlan_client: if vlan: print('.', end="") sys.stdout.flush() sw.create_vlan(vlan) for intf_i, ip in enumerate(cfg.yield_sw_mgmt_interfaces_ip(index)): if ip != switch_ip: vlan = cfg.get_sw_mgmt_interfaces_vlan(index, intf_i) if vlan is None: vlan = vlan_mgmt[0] netmask = cfg.get_sw_mgmt_interfaces_netmask(index, intf_i) try: LOG.debug( "Configuring mgmt switch \"%s\" inband interface. " "(ip=%s netmask=%s vlan=%s)" % (label, ip, netmask, vlan)) # sw.configure_interface(ip, netmask, vlan, port) sw.configure_interface(ip, netmask, vlan) except SwitchException as exc: LOG.warning(exc) for target_i, target in (enumerate( cfg.yield_sw_mgmt_links_target(index))): port = cfg.get_sw_mgmt_links_port(index, target_i) if target.lower() == 'deployer': try: print('.', end="") sys.stdout.flush() vlans = vlan_mgmt + vlan_client LOG.debug('Adding vlans {} to port {}'.format(vlans, port)) sw.set_switchport_mode(port, port_mode.TRUNK) except SwitchException as exc: LOG.error(exc) try: sw.allowed_vlans_port(port, allow_op.ADD, vlans) except SwitchException as exc: LOG.error(exc) else: vlan = cfg.get_sw_mgmt_links_vlan(index, target_i) if vlan is None: if not vlan_mgmt: vlan = 1 else: vlan = vlan_mgmt[0] try: sw.set_switchport_mode(port, port_mode.TRUNK) sw.allowed_vlans_port(port, allow_op.NONE) sw.allowed_vlans_port(port, allow_op.ADD, vlan) sw.set_switchport_mode(port, port_mode.TRUNK, vlan) except SwitchException as exc: LOG.error(exc) for if_type in ['ipmi', 'pxe']: vlan = cfg.get_depl_netw_client_vlan(if_type=if_type)[0] for port in cfg.yield_client_switch_ports(switch_label, if_type): if mode == 'passive': LOG.debug( 'Set switchport mode - switch is in passive mode.') else: print('.', end="") sys.stdout.flush() try: LOG.debug('Setting port {} into {} mode with access ' 'vlan {}'.format(port, port_mode.ACCESS, vlan)) sw.set_switchport_mode(port, port_mode.ACCESS, vlan) except SwitchException as exc: LOG.error(exc)
def _create_network( dev_label, interface_ipaddr, netprefix, container_ipaddr=None, bridge_ipaddr=None, vlan=None, _type='mgmt'): """ Creates a network between the container interface and a physical interface. If no bridge ip address is specified, the connection is via the default lxc bridge. If a bridge ip address and vlan are specified, a bridge is created. Inputs: dev_label (str): Name of the physical device providing external connectivity for the network. interface_ipaddr (str): ipv4 address of the phsical device specified by dev_label. If the address does not already exist on the interface it is added. netprefix (int): Size in bits of the network address. container_ipaddr (str): ip address of the management interface in the PowerUp container. bridge_ipaddr (str): ip address of the bridge connecting the container and the physical device. vlan (int): type (str): Type of interface being served by the network (mgmt, pxe, ipmi) to be created. """ ifc_addresses = _get_ifc_addresses() if not IPR.link_lookup(ifname=dev_label): LOG.error('External interface {} not found'.format(dev_label)) raise UserCriticalException('External interface {} not found.' .format(dev_label)) # if no bridge_ipaddr is specied (ie None), then a bridge will not be created. if not bridge_ipaddr: # if address not already on device, then add it. if not interface_ipaddr + '/' + str(netprefix) in ifc_addresses[dev_label]: LOG.debug('Adding address {} to link {}'. format(interface_ipaddr, dev_label)) index = IPR.link_lookup(ifname=dev_label) IPR.addr('add', index=index, address=interface_ipaddr, mask=netprefix) else: LOG.debug('Address {} already exists on link {}'. format(interface_ipaddr, dev_label)) # Check to see if the device and address is configured in any interface # definition file. If not, then write a definition file. ifc_file_list = _get_ifcs_file_list() for filename in ifc_file_list: ifc_cfgd, addr_cfgd = _is_ifc_configured(filename, dev_label, interface_ipaddr) if ifc_cfgd: break broadcast = None netmask = str(IPNetwork('255.255.255.255/' + str(netprefix)).netmask) if not addr_cfgd: _write_ifc_cfg_file( dev_label, ip=interface_ipaddr, mask=netmask, broadcast=broadcast, ifc_cfgd=ifc_cfgd) # bridge else: # Check for existing addresses on the external interface and # remove any that lie within the mgmt subnet. You only need to remove # the first address found in the subnet since any additional ones are # secondary and removed when the first (primary) is removed. Note that # this does not remove addresses from network config files. cidr = IPNetwork(bridge_ipaddr + '/' + str(netprefix)) network = IPNetwork(cidr) network_addr = str(network.network) for addr in IPR.get_addr(label=dev_label): pfx = int(addr['prefixlen']) adr = addr.get_attr('IFA_ADDRESS') existing_network = IPNetwork( addr.get_attr('IFA_ADDRESS') + '/' + str(addr['prefixlen'])) existing_network_addr = str(existing_network.network) if existing_network_addr == network_addr: LOG.debug( 'Removing address {}/{} from interface {}' .format(adr, pfx, dev_label)) IPR.addr( 'delete', index=IPR.link_lookup(ifname=dev_label), address=adr, mask=pfx) # Prepare to setup the bridge br_label = 'br-' + _type if vlan and vlan != 4095: br_label = br_label + '-' + str(vlan) link = dev_label # if a vlan other than 1 or 4095 is specified, create the vlan link if vlan and vlan != 4095: link = dev_label + '.{}'.format(vlan) # if the vlan link already exists on another bridge then display warning if _is_ifc_attached_elsewhere(link, br_label): LOG.warning('Link {} already in use on another bridge.'.format(link)) print('Warning: Link {} already in use on another bridge.'.format(link)) if not IPR.link_lookup(ifname=link): LOG.debug('creating vlan interface: {}'.format(link)) IPR.link( "add", ifname=link, kind="vlan", link=IPR.link_lookup(ifname=dev_label)[0], vlan_id=vlan) IPR.link("set", index=IPR.link_lookup(ifname=link)[0], state="up") if not _wait_for_ifc_up(link): raise UserCriticalException('Failed to bring up interface {} '. format(link)) # set bridge file write mode to 'w' (write) or 'a' (append) mode = 'a' if _type == 'mgmt' else 'w' if IPR.link_lookup(ifname=br_label): LOG.info('{}NOTE: bridge {} is already configured.{}'.format(Color.bold, br_label, Color.endc)) print("Enter to continue, or 'T' to terminate deployment") resp = input("\nEnter or 'T': ") if resp == 'T': sys.exit('POWER-Up stopped at user request') _write_br_cfg_file( br_label, ip=bridge_ipaddr, prefix=netprefix, ifc=link, mode=mode) _setup_bridge(br_label, bridge_ipaddr, netprefix, link) _update_firewall(br_label)
def _write_br_cfg_file(bridge, ip=None, prefix=None, ifc=None, mode='w'): """ Writes the config file for the specified bridge. If the specified interface is not configured, a config file is created for it also. If mode is set to 'a' (append) and the bridge config file exists, the specified interface is added to the bridge config file. If mode is unspecified or set to 'w' (write), the config file is created or overwritten. The interface specified by 'ifc' is attached to the bridge. Args: bridge (str) bridge name ip (str) ipv4 address to be added to the bridge prefix (int or str) network prefix length. ie the length of the network portion of the ip address ifc (str) name of the interface to be added to the bridge. """ LOG.debug('OS: ' + OPSYS) if OPSYS not in ('debian', 'Ubuntu', 'redhat'): LOG.error('Unsupported Operating System') raise UserCriticalException('Unsupported Operating System') network = IPNetwork(ip + '/' + str(prefix)) network_addr = str(network.network) broadcast = str(network.broadcast) netmask = str(network.netmask) if OPSYS in ('debian', 'Ubuntu'): if mode == 'a' and os.path.exists('/etc/network/interfaces.d/' + bridge): LOG.debug('Appending to bridge config file {} IP addr {}'. format(bridge, ip)) os.system( 'cp /etc/network/interfaces.d/{} {}{} ' .format(bridge, GEN_PATH, bridge)) f = open(GEN_PATH + bridge, 'r') data = f.read() f.close() data = data.split('auto') f = open(GEN_PATH + bridge, 'w') # Write the specified interface if it is a vlan interface # If the interface already exists in the file it will be replaced f.write(data[0]) if '.' in ifc: f.write('auto {}\n'.format(ifc)) f.write('iface {} inet manual\n'.format(ifc)) f.write(' vlan-raw-device {}\n\n'.format(ifc[:ifc.find('.')])) for item in data[1:len(data) - 1]: if not ('iface ' + ifc + ' ') in item: f.write('auto' + item) # rewrite the bridge config data = ('auto' + data[len(data) - 1]).splitlines() for item in data: item = item + ' ' if 'bridge_ports' in item and not ifc + ' ' in item: item = item + ifc f.write(item.rstrip(' ') + '\n') f.close() os.system( 'sudo cp {}{} /etc/network/interfaces.d/{}' .format(GEN_PATH, bridge, bridge)) os.system('rm ' + GEN_PATH + bridge) else: LOG.debug('Wrting bridge configuration file: {} IP addr: {}'. format(bridge, ip)) f = open(GEN_PATH + bridge, 'w') f.write("# This file should not be edited manually\n") f.write('auto {}\n'.format(ifc)) f.write('iface {} inet manual\n'.format(ifc)) f.write(' vlan-raw-device {}\n\n'.format(ifc[:ifc.find('.')])) f.write('auto {}\n'.format(bridge)) f.write('iface {} inet static\n'.format(bridge)) f.write(' address {}\n'.format(ip)) f.write(' netmask {}\n'.format(netmask)) f.write(' broadcast {}\n'.format(broadcast)) f.write(' network {}\n'.format(network_addr)) f.write(' bridge_ports {}\n'.format(ifc)) f.write(' bridge fd 0\n') f.close() os.system( 'sudo cp {}{} /etc/network/interfaces.d/{}' .format(GEN_PATH, bridge, bridge)) os.system('rm ' + GEN_PATH + bridge) return elif OPSYS == 'redhat': # Create the ifc config file # non vlan ifc if '.' not in ifc: _write_ifc_cfg_file(ifc, bridge=bridge) # vlan ifc else: file_path = IFCFG_PATH + f'ifcfg-{ifc}' with open(file_path, 'w') as f: LOG.debug(f'Writing vlan config file:\n{file_path}') f.write(f'DEVICE={ifc}\n') f.write('ONBOOT=yes\n') f.write('BOOTPROTO=none\n') f.write('NM_CONTROLLED=no\n') f.write('VLAN=yes\n') f.write(f'BRIDGE={bridge}') # Create the bridge config file file_path = f'{IFCFG_PATH}ifcfg-{bridge}' if mode == 'a' and os.path.isfile(file_path): LOG.debug(f'Appending to bridge config file {bridge} IP addr {ip}') ifc_cfgd, addr_cfgd = _is_ifc_configured(file_path, ifc, ip) if not addr_cfgd: ipaddr_num = _get_ip_addr_num(file_path) with open(file_path, 'a') as f: f.write(f'IPADDR{ipaddr_num}={ip}\n') f.write(f'PREFIX{ipaddr_num}={prefix}') else: with open(file_path, 'w') as f: f.write(f'DEVICE={bridge}\n') f.write('ONBOOT=yes\n') f.write('TYPE=Bridge\n') f.write(f'IPADDR={ip}\n') f.write(f'PREFIX={prefix}\n') f.write('BOOTPROTO=none\n') f.write('NM_CONTROLLED=no\n') f.write('DELAY=0')
def _create_network( dev_label, interface_ipaddr, netprefix, container_ipaddr=None, bridge_ipaddr=None, vlan=None, type_='mgmt'): ifc_addresses = _get_ifc_addresses() if not IPR.link_lookup(ifname=dev_label): LOG.error('External interface {} not found'.format(dev_label)) raise UserCriticalException('External interface {} not found.' .format(dev_label)) # if a bridge_ipaddr is not specied, then a bridge will not be created. if not bridge_ipaddr: # if address not already on device, then add it. if not interface_ipaddr + '/' + str(netprefix) in ifc_addresses[dev_label]: LOG.debug('Adding address {} to link {}'. format(interface_ipaddr, dev_label)) index = IPR.link_lookup(ifname=dev_label) IPR.addr('add', index=index, address=interface_ipaddr, mask=netprefix) else: LOG.debug('Address {} already exists on link {}'. format(interface_ipaddr, dev_label)) # Check to see if the device and address is configured in any interface # definition file. If not, then write a definition file. ifc_file_list = _get_ifcs_file_list() ifc_cfgd = False addr_cfgd = False for filename in ifc_file_list: f = open(filename, 'r') interfaces = f.read() f.close() if re.findall(r'^ *auto\s+' + dev_label + '\s', interfaces, re.MULTILINE): ifc_cfgd = True LOG.debug('Device {} already configured in network configuration file {}'. format(dev_label, filename)) interfaces = interfaces.split('iface') for line in interfaces: if dev_label in line: if re.findall(r'^ *address\s+' + interface_ipaddr, line, re.MULTILINE): addr_cfgd = True LOG.debug('Address {} configured in {}'. format(interface_ipaddr, filename)) broadcast = None netmask = str(IPNetwork('255.255.255.255/' + str(netprefix)).netmask) if not addr_cfgd: _write_ifc_cfg_file( dev_label, ip=interface_ipaddr, mask=netmask, broadcast=broadcast, ifc_cfgd=ifc_cfgd) # bridge else: # Check for existing addresses on the external interface and # remove any that lie within the mgmt subnet. You only need to remove # the first address found in the subnet since any additional ones are # secondary and removed when the first (primary) is removed. cidr = IPNetwork(bridge_ipaddr + '/' + str(netprefix)) network = IPNetwork(cidr) network_addr = str(network.network) for addr in IPR.get_addr(label=dev_label): pfx = int(addr['prefixlen']) adr = addr.get_attr('IFA_ADDRESS') existing_network = IPNetwork( addr.get_attr('IFA_ADDRESS') + '/' + str(addr['prefixlen'])) existing_network_addr = str(existing_network.network) if existing_network_addr == network_addr: LOG.debug( 'Removing address {}/{} from interface {}' .format(adr, pfx, dev_label)) IPR.addr( 'delete', index=IPR.link_lookup(ifname=dev_label), address=adr, mask=pfx) # Prepare to setup the bridge br_label = 'br-' + type_ if vlan and vlan != 4095: br_label = br_label + '-' + str(vlan) link = dev_label # if a vlan other than 1 or 4095 is specified, create the vlan link if vlan and vlan != 4095: link = dev_label + '.{}'.format(vlan) # if the vlan link already exists on another bridge then display warning if _is_ifc_attached_elsewhere(link, br_label): LOG.warning('Link {} already in use on another bridge.'.format(link)) print('Warning: Link {} already in use on another bridge.'.format(link)) if not IPR.link_lookup(ifname=link): LOG.debug('creating vlan interface: {}'.format(link)) IPR.link( "add", ifname=link, kind="vlan", link=IPR.link_lookup(ifname=dev_label)[0], vlan_id=vlan) IPR.link("set", index=IPR.link_lookup(ifname=link)[0], state="up") if not _wait_for_ifc_up(link): raise UserCriticalException('Failed to bring up interface {} '. format(link)) # set bridge file write mode to 'w' (write) or 'a' (add) if type_ == 'mgmt': mode = 'a' else: mode = 'w' if IPR.link_lookup(ifname=br_label): LOG.info('{}NOTE: bridge {} is already configured.{}'.format(Color.bold, br_label, Color.endc)) print("Enter to continue, or 'T' to terminate deployment") resp = raw_input("\nEnter or 'T': ") if resp == 'T': sys.exit('POWER-Up stopped at user request') _write_br_cfg_file( br_label, ip=bridge_ipaddr, prefix=netprefix, ifc=link, mode=mode) _setup_bridge(br_label, bridge_ipaddr, netprefix, link)
def _write_br_cfg_file(bridge, ip=None, prefix=None, ifc=None, mode='w'): """ Writes the config file for the specified bridge. If the specified interface is not configured, a config file is created for it also. If mode is set to 'a' (append) and the bridge config file exists, the specified interface is added to the bridge config file. If mode is unspecified or set to 'w' (write), the config file is created or overwritten. Args: bridge (str) bridge name ip (str) ipv4 address to be added to the bridge prefix (int or str) network prefix length. ie the length of the network portion of the ip address ifc (str) name of the interface to be added to the bridge. """ opsys = platform.dist()[0] LOG.debug('OS: ' + opsys) if opsys not in ('Ubuntu', 'redhat'): LOG.error('Unsupported Operating System') raise UserCriticalException('Unsupported Operating System') network = IPNetwork(ip + '/' + str(prefix)) network_addr = str(network.network) broadcast = str(network.broadcast) netmask = str(network.netmask) if opsys == 'Ubuntu': if mode == 'a' and os.path.exists('/etc/network/interfaces.d/' + bridge): LOG.debug('Appending to bridge config file {} IP addr {}'. format(bridge, ip)) os.system( 'cp /etc/network/interfaces.d/{} {}{} ' .format(bridge, GEN_PATH, bridge)) f = open(GEN_PATH + bridge, 'r') data = f.read() f.close() data = data.split('auto') f = open(GEN_PATH + bridge, 'w') # Write the specified interface if it is a vlan interface # If the interface already exists in the file it will be replaced f.write(data[0]) if '.' in ifc: f.write('auto {}\n'.format(ifc)) f.write('iface {} inet manual\n'.format(ifc)) f.write(' vlan-raw-device {}\n\n'.format(ifc[:ifc.find('.')])) for item in data[1:len(data) - 1]: if not ('iface ' + ifc + ' ') in item: f.write('auto' + item) # rewrite the bridge config data = ('auto' + data[len(data) - 1]).splitlines() for item in data: item = item + ' ' if 'bridge_ports' in item and not ifc + ' ' in item: item = item + ifc f.write(item.rstrip(' ') + '\n') f.close() os.system( 'sudo cp {}{} /etc/network/interfaces.d/{}' .format(GEN_PATH, bridge, bridge)) os.system('rm ' + GEN_PATH + bridge) else: LOG.debug('Wrting bridge configuration file: {} IP addr: {}'. format(bridge, ip)) f = open(GEN_PATH + bridge, 'w') f.write("# This file should not be edited manually\n") f.write('auto {}\n'.format(ifc)) f.write('iface {} inet manual\n'.format(ifc)) f.write(' vlan-raw-device {}\n\n'.format(ifc[:ifc.find('.')])) f.write('auto {}\n'.format(bridge)) f.write('iface {} inet static\n'.format(bridge)) f.write(' address {}\n'.format(ip)) f.write(' netmask {}\n'.format(netmask)) f.write(' broadcast {}\n'.format(broadcast)) f.write(' network {}\n'.format(network_addr)) f.write(' bridge_ports {}\n'.format(ifc)) f.write(' bridge fd 0\n') f.close() os.system( 'sudo cp {}{} /etc/network/interfaces.d/{}' .format(GEN_PATH, bridge, bridge)) os.system('rm ' + GEN_PATH + bridge) os.system('cp /etc/lxc/lxc-usernet ' + GEN_PATH) f = open(GEN_PATH + 'lxc-usernet', 'r') data = f.read() f.close() username = os.getlogin() perm = re.findall(username + r'\s+veth\s+' + bridge, data, re.MULTILINE) permlxcbr0 = re.findall(username + r'\s+veth\s+lxcbr0', data, re.MULTILINE) if not perm or not permlxcbr0: LOG.debug('Updating lxc user network permissions') f = open(GEN_PATH + 'lxc-usernet', 'a') if not permlxcbr0: f.write(username + ' veth lxcbr0 10\n') if not perm: f.write(username + ' veth ' + bridge + ' 10\n') f.close() os.system('sudo cp ' + GEN_PATH + 'lxc-usernet /etc/lxc/lxc-usernet') return LOG.error('Support for Red Hat not yet implemented') raise UserCriticalException('Support for Red Hat not yet implemented')