def configure_management(): parser = argparse.ArgumentParser( description='Configure Management Interface' ) parser.add_argument('mac_address', metavar='lladdr', type=str) parser.add_argument('ip_address', metavar='ipaddr', type=str) args = parser.parse_args() ip_addr = netaddr.IPNetwork(args.ip_address) mgr = ip.IPManager() for intf in mgr.get_interfaces(): if args.mac_address == intf.lladdr: if not intf.is_up: mgr.up(intf) if ip_addr not in intf.addresses: if ip_addr.version == 6: real_ifname = mgr.generic_to_host(intf.ifname) utils.execute([ 'sysctl', '-w', 'net.ipv6.conf.%s.accept_dad=0' % real_ifname ]) intf.addresses.append(ip_addr) mgr.update_interface(intf) configure_ssh(ip_addr.ip) configure_gunicorn(ip_addr.ip) break
def restart(self): try: execute(['/etc/rc.d/bird', 'stop'], self.root_helper) except: # failure is ok here pass execute(['/etc/rc.d/bird', 'start'], self.root_helper)
def save_config(self, config, interface_map): """ Save iptables-persistent firewall rules to disk. :param config: The akanda configuration to save to disk :type config: akanda.rug.models.Configuration :param interface_map: A mapping of virtual ('ge0') to physical ('eth0') interface names :type interface_map: dict """ rules = itertools.chain( self._build_filter_table(config), self._build_nat_table(config), self._build_raw_table(config) ) for version, rules in zip((4, 6), itertools.tee(rules)): data = "\n".join(map(str, [r for r in rules if getattr(r, "for_v%s" % version)])) # Map virtual interface names real_name = interface_map.get("ge0")[:-1] ifname_re = "\-(?P<flag>i|o)(?P<ws>[\s!])(?P<not>!?)(?P<if>ge)(?P<no>\d+)" # noqa ifname_sub = r"-\g<flag>\g<ws>\g<not>%s\g<no>" % real_name data = re.sub(ifname_re, ifname_sub, data) + "\n" utils.replace_file("/tmp/ip%stables.rules" % version, data) utils.execute( ["mv", "/tmp/ip%stables.rules" % version, "/etc/iptables/rules.v%s" % version], self.root_helper )
def save_config(self, config, interface_map): ''' Save iptables-persistent firewall rules to disk. :param config: The akanda configuration to save to disk :type config: akanda.rug.models.Configuration :param interface_map: A mapping of virtual ('ge0') to physical ('eth0') interface names :type interface_map: dict ''' rules = itertools.chain(self._build_filter_table(config), self._build_nat_table(config), self._build_raw_table(config)) for version, rules in zip((4, 6), itertools.tee(rules)): data = '\n'.join( map(str, [r for r in rules if getattr(r, 'for_v%s' % version)])) # Map virtual interface names real_name = interface_map.get('ge0')[:-1] ifname_re = '\-(?P<flag>i|o)(?P<ws>[\s!])(?P<not>!?)(?P<if>ge)(?P<no>\d+)' # noqa ifname_sub = r'-\g<flag>\g<ws>\g<not>%s\g<no>' % real_name data = re.sub(ifname_re, ifname_sub, data) + '\n' utils.replace_file('/tmp/ip%stables.rules' % version, data) utils.execute([ 'mv', '/tmp/ip%stables.rules' % version, '/etc/iptables/rules.v%s' % version ], self.root_helper)
def save_config(self, config): config_data = build_config(config) replace_file( '/tmp/metadata.conf', json.dumps(config_data, sort_keys=True) ) execute(['mv', '/tmp/metadata.conf', CONF_PATH], self.root_helper)
def restart(self): ''' Reload firewall rules via iptables-persistent ''' utils.execute( ['/etc/init.d/iptables-persistent', 'restart'], self.root_helper )
def ensure_started(self): """ Checks if the metadata service is started and starts it if it is determined to be stopped. """ try: execute(['/etc/init.d/metadata', 'status'], self.root_helper) except: execute(['/etc/init.d/metadata', 'start'], self.root_helper)
def update_network_dhcp_config(self, ifname, network): if network.is_tenant_network: config_data = self._build_dhcp_config(ifname, network) else: config_data = self._build_disabled_config(ifname) file_path = os.path.join(CONF_DIR, '%s.conf' % ifname) utils.replace_file('/tmp/dnsmasq.conf', config_data) utils.execute(['mv', '/tmp/dnsmasq.conf', file_path], self.root_helper)
def restart(self): """ Restarts the metadata service using the init script. """ try: execute(['/etc/init.d/metadata', 'stop'], self.root_helper) except: # failure is ok here pass execute(['/etc/init.d/metadata', 'start'], self.root_helper)
def update_hosts(self, config): mgr = ip.IPManager() listen_ip = mgr.get_management_address() config_data = [ '127.0.0.1 localhost', '::1 localhost ip6-localhost ip6-loopback', '%s %s' % (listen_ip, config.hostname) ] utils.replace_file('/tmp/hosts', '\n'.join(config_data)) utils.execute(['mv', '/tmp/hosts', '/etc/hosts'], self.root_helper)
def test_execute_exception(self): with mock.patch('subprocess.check_output') as co: co.side_effect = subprocess.CalledProcessError( 1, ['command', 'with', 'args'], output='output text', ) try: utils.execute(['command', 'with', 'args']) except RuntimeError as e: self.assertIn('output text', str(e))
def get_rules(self): """ Return the output of `iptables` and `ip6tables`. This function is used by akanda-rug -> HTTP as a test for "router aliveness". :rtype: str """ v4 = utils.execute(["iptables", "-L", "-n"]) v6 = utils.execute(["ip6tables", "-L", "-n"]) return v4 + v6
def get_rules(self): ''' Return the output of `iptables` and `ip6tables`. This function is used by akanda-rug -> HTTP as a test for "router aliveness". :rtype: str ''' v4 = utils.execute(['iptables', '-L', '-n']) v6 = utils.execute(['ip6tables', '-L', '-n']) return v4 + v6
def save_config(self, config): """ Writes <config> to the metadata configuration file (<CONF_PATH>). :type config: akanda.router.models.Configuration :param config: An akanda.router.models.Configuration object containing the configuration of metadata service. """ config_data = build_config(config) replace_file('/tmp/metadata.conf', json.dumps(config_data, sort_keys=True)) execute(['mv', '/tmp/metadata.conf', CONF_PATH], self.root_helper)
def update_hosts(self, config): mgt_addr = config.management_address if not mgt_addr: return config_data = [ '127.0.0.1 localhost', '::1 localhost ip6-localhost ip6-loopback', '%s %s' % (mgt_addr, config.hostname) ] utils.replace_file('/tmp/hosts', '\n'.join(config_data)) utils.execute(['mv', '/tmp/hosts', '/etc/hosts'], self.root_helper)
def save_config(self, config, if_map): """ Writes config file for bird daemon. :type config: akanda.router.models.Configuration :param config: :type if_map: dict :param if_map: A (dict) mapping of generic to physical hostname, e.g.: {'ge0': 'eth0', 'ge1': 'eth1'} """ config_data = build_config(config, if_map) utils.replace_file('/tmp/bird6.conf', config_data) utils.execute(['mv', '/tmp/bird6.conf', CONF_PATH], self.root_helper)
def save_config(self, config): """ Writes <config> to the metadata configuration file (<CONF_PATH>). :type config: akanda.router.models.Configuration :param config: An akanda.router.models.Configuration object containing the configuration of metadata service. """ config_data = build_config(config) replace_file( '/tmp/metadata.conf', json.dumps(config_data, sort_keys=True) ) execute(['mv', '/tmp/metadata.conf', CONF_PATH], self.root_helper)
def do(self, ip): """ Sends a single ICMP packet to <ip> using the systems ping utility. :type ip: str :param ip: The IP address to send ICMP packets to. :rtype: bool. If <ip> responds to the ICMP packet, returns True else, returns False """ version = netaddr.IPAddress(ip).version args = ['-c', '1', ip] try: utils.execute([self.exe_map.get(version)] + args) return True except RuntimeError: return False
def do(self, ip): """ Sends a single ICMP packet to <ip> using the systems ping utility. :type ip: str :param ip: The IP address to send ICMP packets to. :rtype: bool. If <ip> responds to the ICMP packet, returns True else, returns False """ version = netaddr.IPAddress(ip).version args = ["-c", "1", ip] try: utils.execute([self.exe_map.get(version)] + args) return True except RuntimeError: return False
def disable_duplicate_address_detection(self, network): """ Disabled duplicate address detection for a specific interface. :type network: akanda.models.Network """ # For non-external networks, duplicate address detection isn't # necessary (and it sometimes results in race conditions for services # that attempt to bind to addresses before they're ready). if network.network_type != network.TYPE_EXTERNAL: real_ifname = self.generic_to_host(network.interface.ifname) try: utils.execute(["sysctl", "-w", "net.ipv6.conf.%s.accept_dad=0" % real_ifname], self.root_helper) except RuntimeError: LOG.debug("Failed to disable v6 dad on %s" % real_ifname)
def restart(self): try: utils.execute(['/etc/rc.d/dnsmasq', 'stop'], self.root_helper) except: pass # dnsmasq can get confused on startup remaining = 5 while remaining: remaining -= 1 try: utils.execute(['/etc/rc.d/dnsmasq', 'start'], self.root_helper) return except Exception: if remaining <= 0: raise time.sleep(1)
def restart(self): ''' Reload firewall rules via [netfilter/iptables]-persistent Note that at some point iptables-persistent merged into netfilter-persistent as a plugin, so use that instead if it is available ''' _init = '/etc/init.d/%s-persistent' if os.path.isfile(_init % 'netfilter'): init = _init % 'netfilter' else: init = _init % 'iptables' utils.execute( [init, 'restart'], self.root_helper )
def _delete_conntrack_state(self, ip): """ Explicitly remove an IP from in-kernel connection tracking. :param ip: The IP address to remove :type ip: netaddr.IPAddress """ # If no flow entries are deleted, `conntrack -D` will return 1 try: utils.execute(['conntrack', '-D', '-d', str(ip)], self.root_helper) except RuntimeError: LOG.debug('Failed deleting ingress connection state of %s' % ip) try: utils.execute(['conntrack', '-D', '-q', str(ip)], self.root_helper) except RuntimeError: LOG.debug('Failed deleting egress connection state of %s' % ip)
def _delete_conntrack_state(self, ip): """ Explicitly remove an IP from in-kernel connection tracking. :param ip: The IP address to remove :type ip: netaddr.IPAddress """ # If no flow entries are deleted, `conntrack -D` will return 1 try: utils.execute(["conntrack", "-D", "-d", str(ip)], self.root_helper) except RuntimeError: LOG.debug("Failed deleting ingress connection state of %s" % ip) try: utils.execute(["conntrack", "-D", "-q", str(ip)], self.root_helper) except RuntimeError: LOG.debug("Failed deleting egress connection state of %s" % ip)
def do(self, *args): """ Executes command <args> with specified flags and without escalated privileges. :type args: tuple :param args: A command, and flags, to execute. :rtype: tuple """ return utils.execute([self.EXECUTABLE] + list(args))
def sudo(self, *args): """ Executes command <args> with the specified flags through the root_helper facility (i.e. escalated privileges). :type args: tuple :param args: A command, and flags, to execute. :rtype: tuple """ return utils.execute([self.EXECUTABLE] + list(args), self.root_helper)
def disable_duplicate_address_detection(self, network): """ Disabled duplicate address detection for a specific interface. :type network: akanda.models.Network """ # For non-external networks, duplicate address detection isn't # necessary (and it sometimes results in race conditions for services # that attempt to bind to addresses before they're ready). if network.network_type != network.TYPE_EXTERNAL: real_ifname = self.generic_to_host(network.interface.ifname) try: utils.execute([ 'sysctl', '-w', 'net.ipv6.conf.%s.accept_dad=0' % real_ifname ], self.root_helper) except RuntimeError: LOG.debug('Failed to disable v6 dad on %s' % real_ifname)
def update_network_dhcp_config(self, ifname, network): """ Updates the dnsmasq.conf config, enabling dhcp configuration for nova networks that are mapped to tenants and disabling networks that do not map to tenants. :type ifname: str :param ifname: :type network: :param network: """ if network.is_tenant_network: config_data = self._build_dhcp_config(ifname, network) else: config_data = self._build_disabled_config(ifname) file_path = os.path.join(CONF_DIR, '%s.conf' % ifname) utils.replace_file('/tmp/dnsmasq.conf', config_data) utils.execute(['mv', '/tmp/dnsmasq.conf', file_path], self.root_helper)
def restart(self): """ Restarts dnsmasq service using the system provided init script. """ try: utils.execute(['/etc/init.d/dnsmasq', 'stop'], self.root_helper) except: pass # dnsmasq can get confused on startup remaining = 5 while remaining: remaining -= 1 try: utils.execute(['/etc/init.d/dnsmasq', 'start'], self.root_helper) return except Exception: if remaining <= 0: raise time.sleep(1)
def restart(self): try: utils.execute(['/etc/rc.d/bird6', 'check'], self.root_helper) except: # pragma no cover utils.execute(['/etc/rc.d/bird6', 'start'], self.root_helper) else: # pragma no cover utils.execute(['/etc/rc.d/bird6', 'reload'], self.root_helper)
def restart(self): """ Restarts dnsmasq service using the system provided init script. """ try: utils.execute(['/etc/init.d/dnsmasq', 'stop'], self.root_helper) except: pass # dnsmasq can get confused on startup remaining = 5 while remaining: remaining -= 1 try: utils.execute( ['/etc/init.d/dnsmasq', 'start'], self.root_helper ) return except Exception: if remaining <= 0: raise time.sleep(1)
def send_gratuitous_arp_for_floating_ips(self, config, generic_to_host): """ Send a gratuitous ARP for every Floating IP. :type config: akanda.router.models.Configuration :param config: An akanda.router.models.Configuration object containing configuration information for the system's network setup. :type generic_to_host: callable :param generic_to_host: A callable which translates a generic interface name (e.g., "ge0") to a physical name (e.g., "eth0") """ external_nets = filter( lambda n: n.network_type == Network.TYPE_EXTERNAL, config.networks ) for net in external_nets: for fip in net.floating_ips: utils.execute([ 'akanda-gratuitous-arp', generic_to_host(net.interface.ifname), str(fip.floating_ip) ], self.root_helper)
def restart(self): """ Restart the BIRD daemon using the system provided init scripts. """ try: utils.execute(['/etc/init.d/bird6', 'status'], self.root_helper) except: # pragma no cover utils.execute(['/etc/init.d/bird6', 'start'], self.root_helper) else: # pragma no cover utils.execute(['/etc/init.d/bird6', 'reload'], self.root_helper)
def update_allocations(self, allocations): """Rebuilds the dnsmasq config and signal the dnsmasq to reload.""" self.allocations = allocations self._output_hosts_file() execute(['kill', '-HUP', self.pid], self.root_helper) LOG.debug('Reloading allocations')
def __del__(self): #FIXME: ensure the pid is actually dnsmasq execute(['kill', '-9', self.pid], self.root_helper)
def save_config(self, config, if_map): config_data = build_config(config, if_map) replace_file('/tmp/bird6.conf', config_data) execute(['mv', '/tmp/bird6.conf', CONF_PATH], self.root_helper)
def test_execute_exception_real(self): try: utils.execute(['/bin/ls', '/no-such-directory']) except RuntimeError as e: self.assertIn('cannot access', str(e))
def restart(self): ''' Reload firewall rules via iptables-persistent ''' utils.execute(['/etc/init.d/iptables-persistent', 'restart'], self.root_helper)
def update_hostname(self, config): self.sudo(config.hostname) utils.replace_file('/tmp/hostname', config.hostname) utils.execute(['mv', '/tmp/hostname', '/etc/hostname'], self.root_helper)