def test_get_network_interface_file(self): dir = '/etc/network/interfaces.d' file = '/etc/network/interfaces' interface = 'eth0' cfg.CONF.set_override('agent_server_network_dir', dir, group='amphora_agent') path = util.get_network_interface_file(interface) expected_path = os.path.join(dir, interface + '.cfg') self.assertEqual(expected_path, path) cfg.CONF.set_override('agent_server_network_file', file, group='amphora_agent') path = util.get_network_interface_file(interface) self.assertEqual(file, path)
def test_get_network_interface_file(self): interface = 'eth0' self.conf.config(group="amphora_agent", agent_server_network_file=None) path = util.get_network_interface_file(interface) expected_path = os.path.join(self.dir, interface + '.cfg') self.assertEqual(expected_path, path) self.conf.config(group="amphora_agent", agent_server_network_file=self.file) path = util.get_network_interface_file(interface) self.assertEqual(self.file, path)
def test_get_network_interface_file(self): dir = '/etc/network/interfaces.d' file = '/etc/network/interfaces' interface = 'eth0' cfg.CONF.set_override('agent_server_network_dir', dir, group='amphora_agent') path = util.get_network_interface_file(interface) expected_path = os.path.join(dir, interface + '.cfg') self.assertEqual(expected_path, path) cfg.CONF.set_override('agent_server_network_file', file, group='amphora_agent') path = util.get_network_interface_file(interface) self.assertEqual(file, path)
def plug_network(mac_address): interface = _interface_by_mac(mac_address) # write interface file with open(util.get_network_interface_file(interface), 'w') as text_file: text = template_port.render(interface=interface) text_file.write(text) _bring_if_down(interface) _bring_if_up(interface, 'network') return flask.make_response(flask.jsonify(dict( message="OK", details="Plugged on interface {interface}".format( interface=interface))), 202)
def plug_network(mac_address): interface = _interface_by_mac(mac_address) # write interface file with open(util.get_network_interface_file(interface), 'w') as text_file: text = template_port.render(interface=interface) text_file.write(text) _bring_if_down(interface) _bring_if_up(interface, 'network') return flask.make_response(flask.jsonify(dict( message="OK", details="Plugged on interface {interface}".format( interface=interface))), 202)
def plug_network(mac_address): # This is the interface as it was initially plugged into the # default network namespace, this will likely always be eth1 default_netns_interface = _interface_by_mac(mac_address) # We need to determine the interface name when inside the namespace # to avoid name conflicts with pyroute2.NetNS(consts.AMPHORA_NAMESPACE, flags=os.O_CREAT) as netns: # 1 means just loopback, but we should already have a VIP # This works for the add/delete/add case as we don't delete interfaces # Note, eth0 is skipped because that is the VIP interface netns_interface = 'eth{0}'.format(len(netns.get_links())) LOG.info(_LI('Plugged interface {0} will become {1} in the ' 'namespace {2}').format(default_netns_interface, netns_interface, consts.AMPHORA_NAMESPACE)) interface_file_path = util.get_network_interface_file(netns_interface) # write interface file flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC # mode 00644 mode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH with os.fdopen(os.open(interface_file_path, flags, mode), 'w') as text_file: text = template_port.render(interface=netns_interface) text_file.write(text) # Update the list of interfaces to add to the namespace _update_plugged_interfaces_file(netns_interface, mac_address) with pyroute2.IPRoute() as ipr: # Move the interfaces into the namespace idx = ipr.link_lookup(ifname=default_netns_interface)[0] ipr.link('set', index=idx, net_ns_fd=consts.AMPHORA_NAMESPACE, IFLA_IFNAME=netns_interface) _bring_if_down(netns_interface) _bring_if_up(netns_interface, 'network') return flask.make_response(flask.jsonify(dict( message="OK", details="Plugged on interface {interface}".format( interface=netns_interface))), 202)
def plug_vip(vip, subnet_cidr, gateway, mac_address): # validate vip try: socket.inet_aton(vip) except socket.error: return flask.make_response(flask.jsonify(dict( message="Invalid VIP")), 400) interface = _interface_by_mac(mac_address) # assume for now only a fixed subnet size sections = vip.split('.')[:3] sections.append('255') broadcast = '.'.join(sections) # write interface file with open(util.get_network_interface_file(interface), 'w') as text_file: text = template_vip.render( interface=interface, vip=vip, broadcast=broadcast, # assume for now only a fixed subnet size netmask='255.255.255.0') text_file.write(text) # bring interfaces up _bring_if_down("{interface}".format(interface=interface)) _bring_if_down("{interface}:0".format(interface=interface)) _bring_if_up("{interface}".format(interface=interface), 'VIP') _bring_if_up("{interface}:0".format(interface=interface), 'VIP') # Setup policy based routes for the amphora ip = pyroute2.IPRoute() cidr_split = subnet_cidr.split('/') num_interface = ip.link_lookup(ifname=interface) ip.route('add', dst=cidr_split[0], mask=int(cidr_split[1]), oif=num_interface, table=1, rtproto='RTPROT_BOOT', rtscope='RT_SCOPE_LINK') ip.route('add', dst='0.0.0.0', gateway=gateway, oif=num_interface, table=1, rtproto='RTPROT_BOOT') ip.rule('add', table=1, action='FR_ACT_TO_TBL', src=cidr_split[0], src_len=int(cidr_split[1])) ip.rule('add', table=1, action='FR_ACT_TO_TBL', dst=cidr_split[0], dst_len=int(cidr_split[1])) return flask.make_response(flask.jsonify(dict( message="OK", details="VIP {vip} plugged on interface {interface}".format( vip=vip, interface=interface))), 202)
def plug_vip(self, vip, subnet_cidr, gateway, mac_address, mtu=None, vrrp_ip=None, host_routes=None): # Validate vip and subnet_cidr, calculate broadcast address and netmask try: render_host_routes = [] ip = ipaddress.ip_address( vip if six.text_type == type(vip) else six.u(vip)) network = ipaddress.ip_network( subnet_cidr if six.text_type == type(subnet_cidr) else six.u(subnet_cidr)) vip = ip.exploded broadcast = network.broadcast_address.exploded netmask = (network.prefixlen if ip.version is 6 else network.netmask.exploded) vrrp_version = None if vrrp_ip: vrrp_ip_obj = ipaddress.ip_address( vrrp_ip if six.text_type == type(vrrp_ip) else six.u(vrrp_ip) ) vrrp_version = vrrp_ip_obj.version if host_routes: for hr in host_routes: network = ipaddress.ip_network( hr['destination'] if isinstance( hr['destination'], six.text_type) else six.u(hr['destination'])) render_host_routes.append({'network': network, 'gw': hr['nexthop']}) except ValueError: return flask.make_response(flask.jsonify(dict( message="Invalid VIP")), 400) # Check if the interface is already in the network namespace # Do not attempt to re-plug the VIP if it is already in the # network namespace if self._netns_interface_exists(mac_address): return flask.make_response(flask.jsonify(dict( message="Interface already exists")), 409) # This is the interface prior to moving into the netns default_netns_interface = self._interface_by_mac(mac_address) # Always put the VIP interface as eth1 primary_interface = consts.NETNS_PRIMARY_INTERFACE secondary_interface = "{interface}:0".format( interface=primary_interface) # We need to setup the netns network directory so that the ifup # commands used here and in the startup scripts "sees" the right # interfaces and scripts. interface_file_path = util.get_network_interface_file( primary_interface) os.makedirs('/etc/netns/' + consts.AMPHORA_NAMESPACE) shutil.copytree( '/etc/network', '/etc/netns/{}/network'.format(consts.AMPHORA_NAMESPACE), symlinks=True, ignore=shutil.ignore_patterns('eth0*', 'openssh*')) name = '/etc/netns/{}/network/interfaces'.format( consts.AMPHORA_NAMESPACE) flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC # mode 00644 mode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH with os.fdopen(os.open(name, flags, mode), 'w') as int_file: int_file.write('auto lo\n') int_file.write('iface lo inet loopback\n') if not CONF.amphora_agent.agent_server_network_file: int_file.write('source /etc/netns/{}/network/' 'interfaces.d/*.cfg\n'.format( consts.AMPHORA_NAMESPACE)) # write interface file mode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH # If we are using a consolidated interfaces file, just append # otherwise clear the per interface file as we are rewriting it # TODO(johnsom): We need a way to clean out old interfaces records if CONF.amphora_agent.agent_server_network_file: flags = os.O_WRONLY | os.O_CREAT | os.O_APPEND else: flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC with os.fdopen(os.open(interface_file_path, flags, mode), 'w') as text_file: text = template_vip.render( interface=primary_interface, vip=vip, vip_ipv6=ip.version is 6, broadcast=broadcast, netmask=netmask, gateway=gateway, mtu=mtu, vrrp_ip=vrrp_ip, vrrp_ipv6=vrrp_version is 6, host_routes=render_host_routes, ) text_file.write(text) # Update the list of interfaces to add to the namespace # This is used in the amphora reboot case to re-establish the namespace self._update_plugged_interfaces_file(primary_interface, mac_address) # Create the namespace netns = pyroute2.NetNS(consts.AMPHORA_NAMESPACE, flags=os.O_CREAT) netns.close() # Load sysctl in new namespace sysctl = pyroute2.NSPopen(consts.AMPHORA_NAMESPACE, [consts.SYSCTL_CMD, '--system'], stdout=subprocess.PIPE) sysctl.communicate() sysctl.wait() sysctl.release() with pyroute2.IPRoute() as ipr: # Move the interfaces into the namespace idx = ipr.link_lookup(ifname=default_netns_interface)[0] ipr.link('set', index=idx, net_ns_fd=consts.AMPHORA_NAMESPACE, IFLA_IFNAME=primary_interface) # bring interfaces up self._bring_if_down(primary_interface) self._bring_if_down(secondary_interface) self._bring_if_up(primary_interface, 'VIP') self._bring_if_up(secondary_interface, 'VIP') return flask.make_response(flask.jsonify(dict( message="OK", details="VIP {vip} plugged on interface {interface}".format( vip=vip, interface=primary_interface))), 202)
def plug_network(self, mac_address, fixed_ips, mtu=None): # Check if the interface is already in the network namespace # Do not attempt to re-plug the network if it is already in the # network namespace if self._netns_interface_exists(mac_address): return flask.make_response(flask.jsonify(dict( message="Interface already exists")), 409) # This is the interface as it was initially plugged into the # default network namespace, this will likely always be eth1 try: self._check_ip_addresses(fixed_ips=fixed_ips) except socket.error: return flask.make_response(flask.jsonify(dict( message="Invalid network port")), 400) default_netns_interface = self._interface_by_mac(mac_address) # We need to determine the interface name when inside the namespace # to avoid name conflicts with pyroute2.NetNS(consts.AMPHORA_NAMESPACE, flags=os.O_CREAT) as netns: # 1 means just loopback, but we should already have a VIP. This # works for the add/delete/add case as we don't delete interfaces # Note, eth0 is skipped because that is the VIP interface netns_interface = 'eth{0}'.format(len(netns.get_links())) LOG.info(_LI('Plugged interface {0} will become {1} in the ' 'namespace {2}').format(default_netns_interface, netns_interface, consts.AMPHORA_NAMESPACE)) interface_file_path = util.get_network_interface_file(netns_interface) # write interface file # If we are using a consolidated interfaces file, just append # otherwise clear the per interface file as we are rewriting it # TODO(johnsom): We need a way to clean out old interfaces records if CONF.amphora_agent.agent_server_network_file: flags = os.O_WRONLY | os.O_CREAT | os.O_APPEND else: flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC # mode 00644 mode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH with os.fdopen(os.open(interface_file_path, flags, mode), 'w') as text_file: text = self._generate_network_file_text(netns_interface, fixed_ips, mtu) text_file.write(text) # Update the list of interfaces to add to the namespace self._update_plugged_interfaces_file(netns_interface, mac_address) with pyroute2.IPRoute() as ipr: # Move the interfaces into the namespace idx = ipr.link_lookup(ifname=default_netns_interface)[0] ipr.link('set', index=idx, net_ns_fd=consts.AMPHORA_NAMESPACE, IFLA_IFNAME=netns_interface) self._bring_if_down(netns_interface) self._bring_if_up(netns_interface, 'network') return flask.make_response(flask.jsonify(dict( message="OK", details="Plugged on interface {interface}".format( interface=netns_interface))), 202)
def plug_vip(vip, subnet_cidr, gateway, mac_address): # validate vip try: socket.inet_aton(vip) except socket.error: return flask.make_response(flask.jsonify(dict( message="Invalid VIP")), 400) interface = _interface_by_mac(mac_address) # assume for now only a fixed subnet size sections = vip.split('.')[:3] sections.append('255') broadcast = '.'.join(sections) # write interface file with open(util.get_network_interface_file(interface), 'w') as text_file: text = template_vip.render( interface=interface, vip=vip, broadcast=broadcast, # assume for now only a fixed subnet size netmask='255.255.255.0') text_file.write(text) # bring interfaces up _bring_if_down("{interface}".format(interface=interface)) _bring_if_down("{interface}:0".format(interface=interface)) _bring_if_up("{interface}".format(interface=interface), 'VIP') _bring_if_up("{interface}:0".format(interface=interface), 'VIP') # Setup policy based routes for the amphora ip = pyroute2.IPRoute() cidr_split = subnet_cidr.split('/') num_interface = ip.link_lookup(ifname=interface) ip.route('add', dst=cidr_split[0], mask=int(cidr_split[1]), oif=num_interface, table=1, rtproto='RTPROT_BOOT', rtscope='RT_SCOPE_LINK') ip.route('add', dst='0.0.0.0', gateway=gateway, oif=num_interface, table=1, rtproto='RTPROT_BOOT') ip.rule('add', table=1, action='FR_ACT_TO_TBL', src=cidr_split[0], src_len=int(cidr_split[1])) ip.rule('add', table=1, action='FR_ACT_TO_TBL', dst=cidr_split[0], dst_len=int(cidr_split[1])) return flask.make_response(flask.jsonify(dict( message="OK", details="VIP {vip} plugged on interface {interface}".format( vip=vip, interface=interface))), 202)
def plug_vip(vip, subnet_cidr, gateway, mac_address): # validate vip try: socket.inet_aton(vip) except socket.error: return flask.make_response(flask.jsonify(dict( message="Invalid VIP")), 400) interface = _interface_by_mac(mac_address) primary_interface = "{interface}".format(interface=interface) secondary_interface = "{interface}:0".format(interface=interface) # assume for now only a fixed subnet size sections = vip.split('.')[:3] sections.append('255') broadcast = '.'.join(sections) # We need to setup the netns network directory so that the ifup # commands used here and in the startup scripts "sees" the right # interfaces and scripts. interface_file_path = util.get_network_interface_file(interface) os.makedirs('/etc/netns/' + consts.AMPHORA_NAMESPACE) shutil.copytree('/etc/network', '/etc/netns/{}/network'.format(consts.AMPHORA_NAMESPACE), symlinks=True, ignore=shutil.ignore_patterns('eth0*', 'openssh*')) name = '/etc/netns/{}/network/interfaces'.format(consts.AMPHORA_NAMESPACE) flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC # mode 00644 mode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH with os.fdopen(os.open(name, flags, mode), 'w') as file: file.write('auto lo\n') file.write('iface lo inet loopback\n') file.write('source /etc/netns/{}/network/interfaces.d/*.cfg\n'.format( consts.AMPHORA_NAMESPACE)) # write interface file mode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC with os.fdopen(os.open(interface_file_path, flags, mode), 'w') as text_file: text = template_vip.render( interface=interface, vip=vip, broadcast=broadcast, # assume for now only a fixed subnet size netmask='255.255.255.0') text_file.write(text) # Update the list of interfaces to add to the namespace # This is used in the amphora reboot case to re-establish the namespace _update_plugged_interfaces_file(interface, mac_address) # Create the namespace netns = pyroute2.NetNS(consts.AMPHORA_NAMESPACE, flags=os.O_CREAT) netns.close() with pyroute2.IPRoute() as ipr: # Move the interfaces into the namespace idx = ipr.link_lookup(ifname=primary_interface)[0] ipr.link('set', index=idx, net_ns_fd=consts.AMPHORA_NAMESPACE) # bring interfaces up _bring_if_down(primary_interface) _bring_if_down(secondary_interface) _bring_if_up(primary_interface, 'VIP') _bring_if_up(secondary_interface, 'VIP') return flask.make_response(flask.jsonify(dict( message="OK", details="VIP {vip} plugged on interface {interface}".format( vip=vip, interface=interface))), 202)