Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 126 additions & 45 deletions opflexagent/as_metadata_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from oslo_utils import encodeutils

from opflexagent._i18n import _
from opflexagent import constants as ofcst
from opflexagent import config as oscfg # noqa
from opflexagent.utils import utils as opflexagent_utils

Expand Down Expand Up @@ -71,6 +72,10 @@
SVC_IP_SIZE = 1000
SVC_IP_CIDR = 16
SVC_NEXTHOP = "169.254.1.1"
SVC_V6_IP_DEFAULT = "fd00::a9fe:102"
SVC_V6_IP_BASE = int(netaddr.IPAddress("fd00::a9fe:f003"))
SVC_V6_IP_CIDR = 64
SVC_V6_NEXTHOP = "fd00::a9fe:101"
SVC_NS = "of-svc"
SVC_NS_PORT = "of-svc-nsport"
SVC_OVS_PORT = "of-svc-ovsport"
Expand Down Expand Up @@ -106,6 +111,21 @@ def write_jsonfile(name, data):
LOG.warning("Exception in writing file: %s", str(e))


def normalize_ipv6_next_hop(ipaddr):
if not ipaddr:
return ipaddr
addr = netaddr.IPAddress(ipaddr)
if not addr.is_link_local():
return ipaddr
words = addr.words
words[0] = 0xfd00
words[1] = 0
words[2] = 0
words[3] = 0
return str(netaddr.IPAddress(sum(word << (16 * (7 - idx))
for idx, word in enumerate(words))))


class AddressPool(object):
def __init__(self, base, size):
self.base = base
Expand Down Expand Up @@ -337,9 +357,15 @@ def process(self, files):

curr_svc = read_jsonfile(self.svcfile)
ip_pool = AddressPool(SVC_IP_BASE, SVC_IP_SIZE)
ip6_pool = AddressPool(SVC_V6_IP_BASE, SVC_IP_SIZE)
for domain_uuid in curr_svc:
thisip = netaddr.IPAddress(curr_svc[domain_uuid]['next-hop-ip'])
ip_pool.reserve(int(thisip))
thisip6 = curr_svc[domain_uuid].get('next-hop-ipv6')
if thisip6:
thisip6 = netaddr.IPAddress(normalize_ipv6_next_hop(thisip6))
if not thisip6.is_link_local():
ip6_pool.reserve(int(thisip6))

new_svc = {}
new_nets = {}
Expand Down Expand Up @@ -376,13 +402,25 @@ def process(self, files):
as_uuid = domain_uuid
as_addr = netaddr.IPAddress(ip_pool.get_addr())
as_addr = str(as_addr)
as_addr_v6 = netaddr.IPAddress(ip6_pool.get_addr())
as_addr_v6 = str(as_addr_v6)
new_svc[domain_uuid] = {
'domain-name': domain_name,
'domain-policy-space': domain_tenant,
'next-hop-ip': as_addr,
'next-hop-ipv6': as_addr_v6,
'uuid': as_uuid,
}
else:
thisip6 = curr_svc[domain_uuid].get('next-hop-ipv6')
thisip6 = normalize_ipv6_next_hop(thisip6)
if not thisip6:
updated = True
thisip6 = str(netaddr.IPAddress(ip6_pool.get_addr()))
elif thisip6 != curr_svc[domain_uuid].get(
'next-hop-ipv6'):
updated = True
curr_svc[domain_uuid]['next-hop-ipv6'] = thisip6
new_svc[domain_uuid] = curr_svc[domain_uuid]
del curr_svc[domain_uuid]

Expand Down Expand Up @@ -463,16 +501,27 @@ def as_equal(self, asvc, alloc):
for idx in ["uuid", "domain-name", "domain-policy-space"]:
if asvc[idx] != alloc[idx]:
return False
if asvc["service-mapping"][0]["next-hop-ip"] != alloc["next-hop-ip"]:
return False
return True
service_map = {
svc["service-ip"]: (
normalize_ipv6_next_hop(svc["next-hop-ip"])
if svc["service-ip"] == ofcst.METADATA_DEFAULT_IPV6
else svc["next-hop-ip"])
for svc in asvc.get("service-mapping", [])
}
alloc_ipv6 = normalize_ipv6_next_hop(alloc.get("next-hop-ipv6"))
return (
service_map.get(ofcst.METADATA_DEFAULT_IP) ==
alloc["next-hop-ip"] and
service_map.get(ofcst.METADATA_DEFAULT_IPV6) ==
alloc_ipv6)

def as_del(self, filename, asvc):
try:
self.mgr.del_ip(asvc["service-mapping"][0]["next-hop-ip"])
except Exception as e:
LOG.warning("EPwatcher: Exception in deleting IP: %s",
str(e))
for svc in asvc.get("service-mapping", []):
try:
self.mgr.del_ip(svc["next-hop-ip"])
except Exception as e:
LOG.warning("EPwatcher: Exception in deleting IP: %s",
str(e))

proxyfilename = PROXY_FILE_NAME_FORMAT % asvc["uuid"]
proxyfilename = "%s/%s" % (MD_DIR, proxyfilename)
Expand All @@ -483,6 +532,8 @@ def as_del(self, filename, asvc):
LOG.warning("EPwatcher: Exception in deleting file: %s", str(e))

def as_create(self, alloc):
alloc_ipv6 = normalize_ipv6_next_hop(alloc["next-hop-ipv6"])
alloc["next-hop-ipv6"] = alloc_ipv6
asvc = {
"uuid": alloc["uuid"],
"interface-name": SVC_OVS_PORT,
Expand All @@ -491,18 +542,24 @@ def as_create(self, alloc):
"domain-name": alloc["domain-name"],
"service-mapping": [
{
"service-ip": "169.254.169.254",
"gateway-ip": "169.254.1.1",
"service-ip": ofcst.METADATA_DEFAULT_IP,
"gateway-ip": SVC_NEXTHOP,
"next-hop-ip": alloc["next-hop-ip"],
},
{
"service-ip": ofcst.METADATA_DEFAULT_IPV6,
"gateway-ip": SVC_V6_NEXTHOP,
"next-hop-ip": alloc_ipv6,
},
],
}

try:
self.mgr.add_ip(alloc["next-hop-ip"])
except Exception as e:
LOG.warning("EPwatcher: Exception in adding IP: %s",
str(e))
for addr in [alloc["next-hop-ip"], alloc_ipv6]:
try:
self.mgr.add_ip(addr)
except Exception as e:
LOG.warning("EPwatcher: Exception in adding IP: %s",
str(e))

asfilename = AS_FILE_NAME_FORMAT % asvc["uuid"]
asfilename = "%s/%s" % (AS_MAPPING_DIR, asfilename)
Expand All @@ -516,33 +573,38 @@ def as_create(self, alloc):
with open(proxyfilename, "w") as f:
f.write(proxystr)
pidfile = PID_FILE_NAME_FORMAT % asvc["uuid"]
self.mgr.sh("rm -f %s" % pidfile)
self.mgr.sh("rm -f %s %s-v4.pid %s-v6.pid" % (
pidfile, pidfile[:-4], pidfile[:-4]))
except Exception as e:
LOG.warning("EPwatcher: Exception in writing proxy file: %s",
str(e))

def proxyconfig(self, alloc):
duuid = alloc["uuid"]
ipaddr = alloc["next-hop-ip"]
proxystr = "\n".join([
"[program:opflex-ns-proxy-%s]" % duuid,
"command=ip netns exec of-svc "
"/usr/bin/opflex-ns-proxy "
"--metadata_proxy_socket=/var/lib/neutron/metadata_proxy "
"--state_path=/var/lib/neutron "
"--pid_file=/var/lib/neutron/external/pids/%s.pid "
"--domain_id=%s --metadata_host %s --metadata_port=80 "
"--log-dir=/var/log/neutron --log-file=opflex-ns-proxy-%s.log" % (
duuid, duuid, ipaddr, duuid[:8]),
"exitcodes=0,2",
"stopasgroup=true",
"startsecs=10",
"startretries=3",
"stopwaitsecs=10",
"stdout_logfile=NONE",
"stderr_logfile=NONE",
])
return proxystr
proxy_configs = []
for family, ipaddr in [("v4", alloc["next-hop-ip"]),
("v6", normalize_ipv6_next_hop(
alloc["next-hop-ipv6"]))]:
proxy_configs.append("\n".join([
"[program:opflex-ns-proxy-%s-%s]" % (duuid, family),
"command=ip netns exec of-svc "
"/usr/bin/opflex-ns-proxy "
"--metadata_proxy_socket=/var/lib/neutron/metadata_proxy "
"--state_path=/var/lib/neutron "
"--pid_file=/var/lib/neutron/external/pids/%s-%s.pid "
"--domain_id=%s --metadata_host %s --metadata_port=80 "
"--log-dir=/var/log/neutron "
"--log-file=opflex-ns-proxy-%s-%s.log" % (
duuid, family, duuid, ipaddr, duuid[:8], family),
"exitcodes=0,2",
"stopasgroup=true",
"startsecs=10",
"startretries=3",
"stopwaitsecs=10",
"stdout_logfile=NONE",
"stderr_logfile=NONE",
]))
return "\n".join(proxy_configs)


class SnatConnTrackHandler(object):
Expand Down Expand Up @@ -695,26 +757,43 @@ def stop_supervisor(self):
self.sh("supervisorctl -c %s shutdown" % self.md_filename)
time.sleep(30)

def add_default_route(self, nexthop):
self.sh("ip netns exec %s ip route add default via %s" %
(SVC_NS, nexthop))
def add_default_route(self, nexthop, ip_version=4):
if ip_version == 6:
self.sh("ip netns exec %s ip -6 route add default via %s "
"dev %s" % (SVC_NS, nexthop, SVC_NS_PORT))
else:
self.sh("ip netns exec %s ip route add default via %s" %
(SVC_NS, nexthop))

def has_ip(self, ipaddr):
outp = self.sh("ip netns exec %s ip addr show dev %s" %
(SVC_NS, SVC_NS_PORT))
ip_version = netaddr.IPAddress(ipaddr).version
cmd = "ip netns exec %s ip addr show dev %s"
if ip_version == 6:
cmd = "ip netns exec %s ip -6 addr show dev %s"
outp = self.sh(cmd % (SVC_NS, SVC_NS_PORT))
return 'net %s/' % (ipaddr, ) in outp

def add_ip(self, ipaddr):
if self.has_ip(ipaddr):
return
self.sh("ip netns exec %s ip addr add %s/%s dev %s" %
(SVC_NS, ipaddr, SVC_IP_CIDR, SVC_NS_PORT))
ip_version = netaddr.IPAddress(ipaddr).version
if ip_version == 6:
self.sh("ip netns exec %s ip -6 addr add %s/%s dev %s" %
(SVC_NS, ipaddr, SVC_V6_IP_CIDR, SVC_NS_PORT))
else:
self.sh("ip netns exec %s ip addr add %s/%s dev %s" %
(SVC_NS, ipaddr, SVC_IP_CIDR, SVC_NS_PORT))

def del_ip(self, ipaddr):
if not self.has_ip(ipaddr):
return
self.sh("ip netns exec %s ip addr del %s/%s dev %s" %
(SVC_NS, ipaddr, SVC_IP_CIDR, SVC_NS_PORT))
ip_version = netaddr.IPAddress(ipaddr).version
if ip_version == 6:
self.sh("ip netns exec %s ip -6 addr del %s/%s dev %s" %
(SVC_NS, ipaddr, SVC_V6_IP_CIDR, SVC_NS_PORT))
else:
self.sh("ip netns exec %s ip addr del %s/%s dev %s" %
(SVC_NS, ipaddr, SVC_IP_CIDR, SVC_NS_PORT))

def get_asport_mac(self):
return self.sh(
Expand Down Expand Up @@ -747,7 +826,9 @@ def init_host(self):
self.sh("ip netns exec %s ip link set dev %s up" %
(SVC_NS, SVC_NS_PORT))
self.add_ip(SVC_IP_DEFAULT)
self.add_ip(SVC_V6_IP_DEFAULT)
self.add_default_route(SVC_NEXTHOP)
self.add_default_route(SVC_V6_NEXTHOP, ip_version=6)
self.sh("ethtool --offload %s tx off" % SVC_OVS_PORT)
self.sh("ip netns exec %s ethtool --offload %s tx off" %
(SVC_NS, SVC_NS_PORT))
Expand Down
2 changes: 2 additions & 0 deletions opflexagent/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,7 @@
TYPE_OPFLEX = 'opflex'
VHOST_USER_VPP_PLUG = 'vhostuser_vpp_plug'
METADATA_DEFAULT_IP = '169.254.169.254'
METADATA_DEFAULT_IPV6 = 'fe80::a9fe:a9fe'
METADATA_SUBNET = '169.254.0.0/16'
METADATA_SUBNET_V6 = 'fe80::a9fe:a9fe/128'
VPCMODULE_NAME = 'vpc-%s-%s'
Loading