Updating the ODL shim layer to port binding model
This patch modifies the ODl shim layer to comply with the revised port and interface binding model in Gluon. Change-Id: Ib4c7800203165474b4b72ac40ab5ef363d831d0c Signed-off-by: Georg Kunz <georg.kunz@ericsson.com>
This commit is contained in:
parent
ade1ed78e2
commit
617efa3616
@ -50,7 +50,7 @@ objects:
|
||||
attributes:
|
||||
service_id: # Override from base object for specific Service type
|
||||
type: VpnService
|
||||
interface_id: # Override from base object for specific Inteface type
|
||||
interface_id: # Override from base object for specific Interface type
|
||||
type: Interface
|
||||
ipaddress:
|
||||
type: string
|
||||
|
@ -45,10 +45,13 @@ class ApiNetL3VPN(ApiModelBase):
|
||||
statuses = shim_data.client.read(etcd_path)
|
||||
if statuses:
|
||||
for status in statuses.children:
|
||||
attributes = json.loads(status.value)
|
||||
self.handle_object_change(obj_name,
|
||||
os.path.basename(status.key),
|
||||
attributes, shim_data)
|
||||
if status.value is not None:
|
||||
attributes = json.loads(status.value)
|
||||
self.handle_object_change(
|
||||
obj_name,
|
||||
os.path.basename(status.key),
|
||||
attributes,
|
||||
shim_data)
|
||||
except Exception as e:
|
||||
LOG.error("reading %s keys failed: %s" % (obj_name, str(e)))
|
||||
pass
|
||||
|
@ -27,6 +27,7 @@ class OdlNetL3VPN(HandlerBase):
|
||||
self.odlclient = RestConfClient()
|
||||
self.l3vpn_network_id = None
|
||||
self.l3vpn_subnets = dict()
|
||||
self.l3vpn_ports = dict()
|
||||
|
||||
def bind_port(self, uuid, model, changes):
|
||||
"""Called to bind port to VM.
|
||||
@ -43,36 +44,6 @@ class OdlNetL3VPN(HandlerBase):
|
||||
LOG.error("Cannot find port")
|
||||
return dict()
|
||||
|
||||
# binding tap device on compute host by creating an IETF interface
|
||||
LOG.info("Updating IETF interface")
|
||||
parent_interface = 'tap%s' % uuid[:11]
|
||||
self.odlclient.update_ietf_interface(uuid, parent_interface)
|
||||
|
||||
# check if a valid service binding exists already in the model
|
||||
# if so, create or update it on the SDN controller
|
||||
service_binding = model.vpnbindings.get(uuid, None)
|
||||
if not service_binding:
|
||||
LOG.info("Port not bound to a service yet.")
|
||||
else:
|
||||
vpn_instance = model.vpn_instances.get(
|
||||
service_binding["service_id"],
|
||||
None)
|
||||
if not vpn_instance:
|
||||
LOG.warn("VPN instance not defined yet.")
|
||||
else:
|
||||
LOG.info("port: %s" % port)
|
||||
LOG.info("service: %s" % vpn_instance)
|
||||
|
||||
LOG.info("Creating or updating VPN instance")
|
||||
self._create_or_update_service(vpn_instance)
|
||||
|
||||
LOG.info("Creating or updating VPN Interface")
|
||||
adjacency = [{"ip_address": port.ipaddress,
|
||||
"mac_address": port.mac_address}]
|
||||
self.odlclient.update_vpn_interface(
|
||||
port.id,
|
||||
vpn_instance.id,
|
||||
adjacency)
|
||||
retval = {'vif_type': 'ovs',
|
||||
'vif_details': {'port_filter': False,
|
||||
'bridge_name': 'br-int'}}
|
||||
@ -88,10 +59,6 @@ class OdlNetL3VPN(HandlerBase):
|
||||
LOG.info("unbind_port: %s" % uuid)
|
||||
LOG.info(changes)
|
||||
|
||||
# we keep the service binding untouched for now
|
||||
LOG.info("Deleting IETF Interface")
|
||||
self.odlclient.delete_ietf_interface(uuid)
|
||||
|
||||
def modify_port(self, uuid, model, changes):
|
||||
"""Called when attributes change on a bound port.
|
||||
|
||||
@ -103,10 +70,6 @@ class OdlNetL3VPN(HandlerBase):
|
||||
LOG.info("modify_port: %s" % uuid)
|
||||
LOG.info(changes)
|
||||
|
||||
LOG.info("Updating IETF interface")
|
||||
parent_interface = 'tap%s' % uuid[:11]
|
||||
self.odlclient.update_ietf_interface(uuid, parent_interface)
|
||||
|
||||
def delete_port(self, uuid, model, port):
|
||||
"""Called when a bound port is deleted
|
||||
|
||||
@ -114,8 +77,7 @@ class OdlNetL3VPN(HandlerBase):
|
||||
:param model: Model object
|
||||
:returns: None
|
||||
"""
|
||||
LOG.info("Deleting IETF Interface")
|
||||
self.odlclient.delete_ietf_interface(uuid)
|
||||
pass
|
||||
|
||||
def modify_interface(self, uuid, model, changes):
|
||||
"""Called when attributes on an interface
|
||||
@ -128,6 +90,10 @@ class OdlNetL3VPN(HandlerBase):
|
||||
LOG.info("modify_interface: %s" % uuid)
|
||||
LOG.info(changes)
|
||||
|
||||
LOG.info("Updating IETF interface")
|
||||
parent_interface = 'tap%s' % uuid[:11]
|
||||
self.odlclient.update_ietf_interface(uuid, parent_interface)
|
||||
|
||||
def delete_interface(self, uuid, model, changes):
|
||||
"""Called when an interface is deleted
|
||||
|
||||
@ -137,6 +103,8 @@ class OdlNetL3VPN(HandlerBase):
|
||||
:returns: None
|
||||
"""
|
||||
LOG.info("delete_interface: %s" % uuid)
|
||||
LOG.info("Deleting IETF Interface")
|
||||
self.odlclient.delete_ietf_interface(uuid)
|
||||
|
||||
def modify_service(self, uuid, model, changes):
|
||||
"""Called when attributes change on a bound port's service
|
||||
@ -149,23 +117,6 @@ class OdlNetL3VPN(HandlerBase):
|
||||
LOG.info("modify_service: %s" % uuid)
|
||||
LOG.info(changes)
|
||||
|
||||
# create a L3VPN network if none exists yet
|
||||
if self.l3vpn_network_id is None:
|
||||
networks = self.odlclient.get_l3vpn_networks()
|
||||
for network in networks['networks']['network']:
|
||||
if network['name'] == 'GluonL3VPNNetwork':
|
||||
LOG.info('Caching UUID %s of Gluon L3VPN network %s' %
|
||||
(network['uuid'], network['name']))
|
||||
self.l3vpn_network_id = network['uuid']
|
||||
|
||||
# no existing L3VPN network found -> create one
|
||||
if self.l3vpn_network_id is None:
|
||||
id = UUID.uuid4()
|
||||
# TODO(egeokun): support multi-tenancy
|
||||
tenant_id = model.ports[model.ports.keys()[0]]['tenant_id']
|
||||
self.odlclient.update_l3vpn_network(id, UUID.UUID(tenant_id))
|
||||
self.l3vpn_network_id = id
|
||||
|
||||
LOG.info("Creating or updating VPN instance")
|
||||
vpn_instance = model.vpn_instances.get(uuid)
|
||||
if vpn_instance:
|
||||
@ -181,10 +132,6 @@ class OdlNetL3VPN(HandlerBase):
|
||||
:param changes: dictionary of changed attributes
|
||||
:returns: None
|
||||
"""
|
||||
LOG.info("Deleting L3VPN network")
|
||||
self.odlclient.delete_l3vpn_network(self.l3vpn_network_id)
|
||||
self.l3vpn_network_id = None
|
||||
|
||||
LOG.info("Deleting VPN Instance")
|
||||
self.odlclient.delete_l3_vpn_instance(uuid)
|
||||
|
||||
@ -199,43 +146,29 @@ class OdlNetL3VPN(HandlerBase):
|
||||
LOG.info("modify_service_binding: %s" % uuid)
|
||||
LOG.info(prev_binding)
|
||||
|
||||
vpn_instance = model.vpnbindings[uuid].vpn_instance
|
||||
vpn_binding = model.vpnbindings[uuid]
|
||||
vpn_instance_id = vpn_binding.service_id
|
||||
vpn_instance = model.vpn_instances[vpn_instance_id]
|
||||
interface = model.interfaces.get(uuid)
|
||||
port = model.ports.get(uuid)
|
||||
ip_address = port.ipaddress
|
||||
prefix = int(port.subnet_prefix)
|
||||
|
||||
network = utils.compute_network_addr(ip_address, prefix)
|
||||
network_name = "GluonL3VPNSubnet_" + network
|
||||
subnet_name = self._create_l3vpn_port(
|
||||
model,
|
||||
port,
|
||||
interface,
|
||||
vpn_binding)
|
||||
|
||||
# if no subnet for this port exists
|
||||
if network_name not in self.l3vpn_subnets:
|
||||
# try to refresh cache
|
||||
subnets = self.odlclient.get_l3vpn_subnets()
|
||||
for subnet in subnets['subnets']['subnet']:
|
||||
if subnet['name'] == network_name:
|
||||
LOG.info('Caching Gluon L3VPN subnet %s (%s)' %
|
||||
(network_name, subnet['uuid']))
|
||||
self.l3vpn_subnets[network_name] = subnet['uuid']
|
||||
|
||||
# no subnet exists yet -> create one
|
||||
if network_name not in self.l3vpn_subnets:
|
||||
id = UUID.uuid4()
|
||||
self.odlclient.update_l3vpn_subnet(
|
||||
id,
|
||||
self.l3vpn_network_id,
|
||||
UUID.UUID(port.tenant_id),
|
||||
network,
|
||||
prefix)
|
||||
self.l3vpn_subnets[network_name] = id
|
||||
|
||||
adjacency = [{"ip_address": port.ipaddress,
|
||||
adjacency = [{"ip_address": vpn_binding.ipaddress,
|
||||
"mac_address": port.mac_address,
|
||||
"primary-adjacency": "true",
|
||||
"subnet_id": self.l3vpn_subnets[network_name]
|
||||
"subnet_id": self.l3vpn_subnets[subnet_name]
|
||||
}]
|
||||
|
||||
LOG.info("Creating or updating VPN Interface")
|
||||
self.odlclient.update_vpn_interface(port.id, vpn_instance, adjacency)
|
||||
self.odlclient.update_vpn_interface(
|
||||
interface.id,
|
||||
vpn_instance,
|
||||
adjacency)
|
||||
|
||||
def delete_service_binding(self, model, prev_binding):
|
||||
"""Called when a service is disassociated with a bound port.
|
||||
@ -244,39 +177,11 @@ class OdlNetL3VPN(HandlerBase):
|
||||
:param prev_binding: dictionary of previous binding
|
||||
:returns: None
|
||||
"""
|
||||
port = model.ports.get(prev_binding.id)
|
||||
subnet = utils.compute_network_addr(port.ipaddress, port.subnet_prefix)
|
||||
|
||||
found_another_port = False
|
||||
for k in model.vpnbindings:
|
||||
vpnport = model.vpnbindings[k]
|
||||
p = model.ports.get(vpnport.id)
|
||||
sn = utils.compute_network_addr(p.ipaddress, p.subnet_prefix)
|
||||
if subnet == sn:
|
||||
found_another_port = True
|
||||
LOG.info("Found another port on the subnet. Keeping subnet.")
|
||||
break
|
||||
|
||||
if not found_another_port:
|
||||
subnet_name = "GluonL3VPNSubnet_" + subnet
|
||||
if subnet_name not in self.l3vpn_subnets:
|
||||
subnets = self.odlclient.get_l3vpn_subnets()
|
||||
for snet in subnets['subnets']['subnet']:
|
||||
if snet.name == subnet_name:
|
||||
LOG.info('Deleting Gluon L3VPN subnet %s (%s)' %
|
||||
(subnet_name, snet.uuid))
|
||||
self.odlclient.delete_l3vpn_subnet(snet.uuid)
|
||||
del self.l3vpn_subnets[snet.uuid]
|
||||
break
|
||||
else:
|
||||
LOG.info('Found L3VPN subnet %s (%s) in cache. Deleting.' %
|
||||
(subnet_name, self.l3vpn_subnets[subnet_name]))
|
||||
self.odlclient.delete_l3vpn_subnet(
|
||||
self.l3vpn_subnets[subnet_name])
|
||||
del self.l3vpn_subnets[subnet_name]
|
||||
LOG.info("Deleting L3VPN port")
|
||||
self._delete_l3vpn_port(model, prev_binding)
|
||||
|
||||
LOG.info("Deleting VPN Interface")
|
||||
self.odlclient.delete_vpn_interface(prev_binding.id)
|
||||
self.odlclient.delete_vpn_interface(prev_binding.interface_id)
|
||||
|
||||
def modify_subport_parent(self, uuid, model, prev_parent,
|
||||
prev_parent_type):
|
||||
@ -302,3 +207,145 @@ class OdlNetL3VPN(HandlerBase):
|
||||
vpn_instance.id,
|
||||
vpn_instance.get("route_distinguishers"),
|
||||
ipv4_vpnTargets)
|
||||
|
||||
def _create_l3vpn_port(self, model, port, interface, vpn_binding):
|
||||
# create network for port if needed
|
||||
self._create_l3vpn_network(model)
|
||||
|
||||
# create subnet for port if needed
|
||||
subnet_name = self._create_l3vpn_subnet(
|
||||
model,
|
||||
port,
|
||||
interface,
|
||||
vpn_binding)
|
||||
|
||||
# create a L3VPN port if needed
|
||||
if interface.id not in self.l3vpn_ports:
|
||||
p = self.odlclient.get_l3vpn_port(interface.id)
|
||||
if not p:
|
||||
# port does not exist yet -> create it
|
||||
self.odlclient.update_l3vpn_port(
|
||||
self.l3vpn_network_id,
|
||||
self.l3vpn_subnets[subnet_name],
|
||||
port,
|
||||
vpn_binding)
|
||||
|
||||
# add to port cache
|
||||
self.l3vpn_ports[interface.id] = interface
|
||||
return subnet_name
|
||||
|
||||
def _create_l3vpn_network(self, model):
|
||||
"""create a L3VPN network if needed """
|
||||
if self.l3vpn_network_id is None:
|
||||
networks = self.odlclient.get_l3vpn_networks()
|
||||
for network in networks['networks']['network']:
|
||||
if network['name'] == 'GluonL3VPNNetwork':
|
||||
LOG.info('Caching UUID %s of Gluon L3VPN network %s' %
|
||||
(network['uuid'], network['name']))
|
||||
self.l3vpn_network_id = network['uuid']
|
||||
|
||||
# no existing L3VPN network found -> create one
|
||||
if self.l3vpn_network_id is None:
|
||||
id = UUID.uuid4()
|
||||
# TODO(egeokun): support multi-tenancy
|
||||
tenant_id = model.ports[model.ports.keys()[0]]['tenant_id']
|
||||
self.odlclient.update_l3vpn_network(id, UUID.UUID(tenant_id))
|
||||
self.l3vpn_network_id = id
|
||||
|
||||
def _create_l3vpn_subnet(self, model, port, interface, vpn_binding):
|
||||
"""create a L3VPN subnet if needed """
|
||||
ip_address = vpn_binding.ipaddress
|
||||
prefix = int(vpn_binding.subnet_prefix)
|
||||
|
||||
network = utils.compute_network_addr(ip_address, prefix)
|
||||
subnet_name = "GluonL3VPNSubnet_" + network
|
||||
|
||||
if subnet_name not in self.l3vpn_subnets:
|
||||
# try to refresh cache
|
||||
subnets = self.odlclient.get_l3vpn_subnets()
|
||||
for subnet in subnets['subnets']['subnet']:
|
||||
if subnet['name'] == subnet_name:
|
||||
LOG.info('Caching Gluon L3VPN subnet %s (%s)' %
|
||||
(subnet_name, subnet['uuid']))
|
||||
self.l3vpn_subnets[subnet_name] = subnet['uuid']
|
||||
|
||||
# no subnet exists yet -> create one
|
||||
if subnet_name not in self.l3vpn_subnets:
|
||||
id = UUID.uuid4()
|
||||
self.odlclient.update_l3vpn_subnet(
|
||||
id,
|
||||
self.l3vpn_network_id,
|
||||
UUID.UUID(port.tenant_id),
|
||||
network,
|
||||
prefix)
|
||||
self.l3vpn_subnets[subnet_name] = id
|
||||
return subnet_name
|
||||
|
||||
def _delete_l3vpn_port(self, model, vpn_binding):
|
||||
uuid = vpn_binding.interface_id
|
||||
|
||||
# clean up L3VPN port
|
||||
if uuid not in self.l3vpn_ports:
|
||||
# check if port exists in ODL
|
||||
l3vpn_ports = self.odlclient.get_l3vpn_ports()
|
||||
for l3vpn_port in l3vpn_ports['ports']['port']:
|
||||
if l3vpn_port['uuid'] == uuid:
|
||||
LOG.info('Found L3VPN port %s on ODL. Deleting.' % uuid)
|
||||
self.odlclient.delete_l3vpn_port(uuid)
|
||||
else:
|
||||
del self.l3vpn_ports[uuid]
|
||||
LOG.info('Deleting L3VPN port %s.' % uuid)
|
||||
self.odlclient.delete_l3vpn_port(uuid)
|
||||
|
||||
subnet = utils.compute_network_addr(
|
||||
vpn_binding.ipaddress,
|
||||
int(vpn_binding.subnet_prefix))
|
||||
|
||||
# clean up L3VPN subnet
|
||||
found_another_port = False
|
||||
for k in model.vpnbindings:
|
||||
this_vpn_binding = model.vpnbindings[k]
|
||||
sn = utils.compute_network_addr(
|
||||
this_vpn_binding.ipaddress,
|
||||
this_vpn_binding.subnet_prefix)
|
||||
if subnet == sn:
|
||||
found_another_port = True
|
||||
LOG.info("Found another port on the subnet. Keeping subnet.")
|
||||
break
|
||||
|
||||
if not found_another_port:
|
||||
subnet_name = "GluonL3VPNSubnet_" + subnet
|
||||
if subnet_name not in self.l3vpn_subnets:
|
||||
subnets = self.odlclient.get_l3vpn_subnets()
|
||||
for snet in subnets['subnets']['subnet']:
|
||||
if snet.name == subnet_name:
|
||||
LOG.info('Deleting Gluon L3VPN subnet %s (%s)' %
|
||||
(subnet_name, snet.uuid))
|
||||
self.odlclient.delete_l3vpn_subnet(snet.uuid)
|
||||
del self.l3vpn_subnets[snet.uuid]
|
||||
break
|
||||
else:
|
||||
LOG.info('Found L3VPN subnet %s (%s) in cache. Deleting.' %
|
||||
(subnet_name, self.l3vpn_subnets[subnet_name]))
|
||||
self.odlclient.delete_l3vpn_subnet(
|
||||
self.l3vpn_subnets[subnet_name])
|
||||
del self.l3vpn_subnets[subnet_name]
|
||||
|
||||
# clean up L3VPN network
|
||||
if not self.l3vpn_ports:
|
||||
if self.l3vpn_network_id is None:
|
||||
l3vpn_networks = self.odlclient.get_l3vpn_networks()
|
||||
for l3vpn_network in l3vpn_networks['networks']['network']:
|
||||
if l3vpn_network['name'] == 'GluonL3VPNNetwork':
|
||||
LOG.info("Deleting L3VPN network %s."
|
||||
% self.l3vpn_network_id)
|
||||
self.odlclient.delete_l3vpn_network(
|
||||
l3vpn_network['uuid'])
|
||||
else:
|
||||
LOG.info("L3VPN network %s found in cache. Deleting."
|
||||
% self.l3vpn_network_id)
|
||||
self.odlclient.delete_l3vpn_network(self.l3vpn_network_id)
|
||||
self.l3vpn_network_id = None
|
||||
else:
|
||||
LOG.info('Still L3VPN ports present. '
|
||||
'Not deleting L3VPN network: %s' % self.l3vpn_ports)
|
||||
|
@ -16,6 +16,7 @@ from gluon.shim import utils
|
||||
import json
|
||||
from oslo_config import cfg
|
||||
import requests
|
||||
import uuid as UUID
|
||||
try:
|
||||
from neutron.openstack.common import jsonutils
|
||||
except ImportError:
|
||||
@ -134,6 +135,51 @@ class RestConfClient(ODL_Client):
|
||||
output = self.sendjson('get', urlpath)
|
||||
return output
|
||||
|
||||
def get_l3vpn_ports(self):
|
||||
return self.get('neutron:neutron/ports')
|
||||
|
||||
def get_l3vpn_port(self, uuid):
|
||||
return self.get('neutron:neutron/ports/port/' + uuid)
|
||||
|
||||
def update_l3vpn_port(self, network_id, subnet_id, port, vpn_binding):
|
||||
l3vpn_port = \
|
||||
{
|
||||
"uuid": port.id,
|
||||
"device-owner": "compute:None",
|
||||
"name": "",
|
||||
"fixed-ips": [
|
||||
{
|
||||
"subnet-id": subnet_id,
|
||||
"ip-address": vpn_binding.ipaddress
|
||||
}
|
||||
],
|
||||
"network-id": network_id,
|
||||
"admin-state-up": "true",
|
||||
"neutron-binding:vnic-type": "normal",
|
||||
"neutron-binding:host-id": port.host_id,
|
||||
"neutron-binding:vif-details": [
|
||||
{
|
||||
"details-key": "port_filter",
|
||||
"value": "true"
|
||||
}
|
||||
],
|
||||
"neutron-binding:vif-type": "ovs",
|
||||
"tenant-id": UUID.UUID(port.tenant_id),
|
||||
"mac-address": port.mac_address,
|
||||
"neutron-portsecurity:port-security-enabled": "false"
|
||||
}
|
||||
# "device-id": "c6ba1b66-6149-4b7a-ad20-05072058ea3b",
|
||||
# "security-groups": [
|
||||
# "e08d477a-1b78-47cd-b502-591e3f3a6213"
|
||||
# ],
|
||||
self._update(l3vpn_port,
|
||||
'neutron:neutron/ports',
|
||||
'uuid',
|
||||
'port')
|
||||
|
||||
def delete_l3vpn_port(self, uuid):
|
||||
self.delete('neutron:neutron/ports/port', id=uuid)
|
||||
|
||||
def get_l3vpn_networks(self):
|
||||
return self.get('neutron:neutron/networks')
|
||||
|
||||
@ -182,7 +228,8 @@ class RestConfClient(ODL_Client):
|
||||
'cidr': network + "/" + str(prefix),
|
||||
'enable-dhcp': 'true',
|
||||
'tenant-id': tenant_id,
|
||||
'allocation-pools': allocation_pool
|
||||
'allocation-pools': allocation_pool,
|
||||
'dns-nameservers': ['8.8.8.8']
|
||||
}
|
||||
self._update(subnet,
|
||||
'neutron:neutron/subnets/',
|
||||
@ -229,9 +276,9 @@ class RestConfClient(ODL_Client):
|
||||
def get_vpn_interfaces(self):
|
||||
return self.get('l3vpn:vpn-interfaces')
|
||||
|
||||
def update_vpn_interface(self, name, vpn_instance_name, adjacency):
|
||||
def update_vpn_interface(self, name, vpn_instance, adjacency):
|
||||
vpn_interface = {'name': name,
|
||||
'vpn-instance-name': vpn_instance_name,
|
||||
'vpn-instance-name': vpn_instance.id,
|
||||
"odl-l3vpn:adjacency": adjacency}
|
||||
self._update(vpn_interface,
|
||||
'l3vpn:vpn-interfaces',
|
||||
|
Loading…
Reference in New Issue
Block a user