aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcel Enguehard <mengueha+fdio@cisco.com>2017-05-15 16:26:27 +0200
committerMarcel Enguehard <mengueha+fdio@cisco.com>2017-05-15 16:26:27 +0200
commit895a6328d6e64948ed213e8fbbb3ab15aca0df43 (patch)
tree2c85049fcf73c445ea25097b4753299568061538
parent548479d60dafb24eddcec3762aa558f13581cdb2 (diff)
IPv6 dataplane support + Decentralized IPv4 prefix attribution + Bug fix for attribute default values handling + Various fixes
Change-Id: I0a26eda064d9e22d9d55fd568748076148f49645 Signed-off-by: Marcel Enguehard <mengueha+fdio@cisco.com>
-rw-r--r--examples/tutorial/tutorial01.json12
-rw-r--r--vicn/core/api.py2
-rw-r--r--vicn/core/resource.py8
-rw-r--r--vicn/core/resource_mgr.py10
-rw-r--r--vicn/resource/central.py248
-rw-r--r--vicn/resource/channel.py36
-rw-r--r--vicn/resource/ip/prefix_tree.py211
-rw-r--r--vicn/resource/ip/route.py3
-rw-r--r--vicn/resource/ip/routing_table.py16
-rw-r--r--vicn/resource/ip_assignment.py68
-rw-r--r--vicn/resource/linux/link.py4
-rw-r--r--vicn/resource/linux/net_device.py95
-rw-r--r--vicn/resource/linux/package_manager.py6
-rw-r--r--vicn/resource/linux/phy_interface.py3
-rw-r--r--vicn/resource/linux/tap_device.py28
-rw-r--r--vicn/resource/lxd/lxc_container.py17
-rw-r--r--vicn/resource/ns3/emulated_lte_channel.py2
-rw-r--r--vicn/resource/vpp/interface.py29
-rw-r--r--vicn/resource/vpp/vpp_bridge.py26
-rw-r--r--vicn/resource/vpp/vpp_commands.py2
20 files changed, 603 insertions, 223 deletions
diff --git a/examples/tutorial/tutorial01.json b/examples/tutorial/tutorial01.json
index d7c716c2..edc0e6da 100644
--- a/examples/tutorial/tutorial01.json
+++ b/examples/tutorial/tutorial01.json
@@ -3,7 +3,7 @@
{
"type": "Physical",
"name": "server",
- "hostname": "hostname"
+ "hostname": "localhost"
},
{
"type": "NetDevice",
@@ -117,14 +117,14 @@
},
{
"type": "CentralIP",
- "ip_routing_strategy": "spt"
+ "ip_routing_strategy": "spt",
+ "ip6_data_prefix": "2001::/50",
+ "ip4_data_prefix": "192.168.128.0/24",
+ "ip4_control_prefix": "192.168.140.0/24"
},
{
"type": "CentralICN",
"face_protocol": "udp4"
}
- ],
- "settings": {
- "network": "192.168.29.0/24"
- }
+ ]
}
diff --git a/vicn/core/api.py b/vicn/core/api.py
index 761177c9..09167aa0 100644
--- a/vicn/core/api.py
+++ b/vicn/core/api.py
@@ -63,7 +63,7 @@ class API(metaclass = Singleton):
# SETTING
settings = DEFAULT_SETTINGS
- settings.update(topology.get('settings'))
+ settings.update(topology.get('settings', dict()))
# VICN process-related initializations
nofile = settings.get('ulimit-n', None)
diff --git a/vicn/core/resource.py b/vicn/core/resource.py
index 53ad2181..9355cd07 100644
--- a/vicn/core/resource.py
+++ b/vicn/core/resource.py
@@ -196,8 +196,14 @@ class BaseResource(BaseType, ABC, metaclass=ResourceMetaclass):
if mandatory:
raise VICNException('Mandatory attributes not set: %r' % (mandatory,))
- # Check requirements
+ # Check requirements and default values
for attr in self.iter_attributes():
+ if attr.name not in kwargs:
+ default = self.get_default_collection(attr) if attr.is_collection else \
+ self.get_default(attr)
+ if vars(attr)['default'] != NEVER_SET:
+ #import pdb; pdb.set_trace()
+ self.set(attr.name, default, blocking=False)
if issubclass(attr.type, Resource) and attr.requirements:
for req in attr.requirements:
instance = self.get(attr.name)
diff --git a/vicn/core/resource_mgr.py b/vicn/core/resource_mgr.py
index b750f555..c6ce77ab 100644
--- a/vicn/core/resource_mgr.py
+++ b/vicn/core/resource_mgr.py
@@ -902,8 +902,8 @@ class ResourceManager(metaclass=Singleton):
new_state = AttributeState.INITIALIZED
elif pending_state == AttributeState.PENDING_UPDATE:
+ attrs = resource._state.attr_change_value[attribute.name]
if resource._state.attr_change_success[attribute.name] == True:
- attrs = resource._state.attr_change_value[attribute.name]
self.attr_log(resource, attribute,
'UPDATE success. Value = {}. Attribute is CLEAN'.format(attrs))
if attrs != NEVER_SET:
@@ -934,6 +934,8 @@ class ResourceManager(metaclass=Singleton):
resource.get_uuid(), attribute.name))
e = resource._state.attr_change_value[attribute.name]
new_state = AttributeState.ERROR
+ import traceback; traceback.print_tb(e.__traceback__)
+ import os; os._exit(1)
else:
raise RuntimeError
@@ -954,7 +956,7 @@ class ResourceManager(metaclass=Singleton):
return Query.from_dict(dic)
def _monitor_netmon(self, resource):
- ip = resource.node.host_interface.ip_address
+ ip = resource.node.host_interface.ip4_address
if not ip:
log.error('IP of monitored Node is None')
import os; os._exit(1)
@@ -1007,7 +1009,7 @@ class ResourceManager(metaclass=Singleton):
def _monitor_emulator(self, resource):
ns = resource
- ip = ns.node.bridge.ip_address # host_interface.ip_address
+ ip = ns.node.bridge.ip4_address # host_interface.ip_address
ws_ns = self._router.add_interface('websocketclient', address = ip,
port = ns.control_port,
@@ -1356,8 +1358,10 @@ class ResourceManager(metaclass=Singleton):
new_state = ResourceState.INITIALIZED
else:
e = resource._state.change_value
+ import traceback; traceback.print_tb(e.__traceback__)
log.error('Cannot setup resource {} : {}'.format(
resource.get_uuid(), e))
+ import os; os._exit(1)
elif pending_state == ResourceState.PENDING_GET:
if resource._state.change_success == True:
diff --git a/vicn/resource/central.py b/vicn/resource/central.py
index 6f3c680b..1013d1a5 100644
--- a/vicn/resource/central.py
+++ b/vicn/resource/central.py
@@ -22,7 +22,6 @@ import os
from netmodel.model.type import String
from netmodel.util.misc import pairwise
-from vicn.core.address_mgr import AddressManager
from vicn.core.attribute import Attribute
from vicn.core.exception import ResourceNotFound
from vicn.core.resource import Resource
@@ -34,9 +33,9 @@ from vicn.resource.icn.forwarder import Forwarder
from vicn.resource.icn.face import L2Face, L4Face, FaceProtocol
from vicn.resource.icn.producer import Producer
from vicn.resource.icn.route import Route as ICNRoute
-from vicn.resource.linux.file import TextFile
from vicn.resource.lxd.lxc_container import LxcContainer
from vicn.resource.node import Node
+from vicn.resource.ip_assignment import Ipv4Assignment, Ipv6Assignment
log = logging.getLogger(__name__)
@@ -47,7 +46,7 @@ CMD_CONTAINER_SET_DNS = 'echo "nameserver {ip_dns}" | ' \
# For host
CMD_NAT = '\n'.join([
- 'iptables -t nat -A POSTROUTING -o {bridge_name} -s {network} ' \
+ 'iptables -t nat -A POSTROUTING -o {interface_name} -s {network} ' \
'! -d {network} -j MASQUERADE',
'echo 1 > /proc/sys/net/ipv4/ip_forward'
])
@@ -208,90 +207,6 @@ def _get_icn_graph(manager):
#-------------------------------------------------------------------------------
-class IPAssignment(Resource):
- """
- Resource: IPAssignment
- """
-
- #--------------------------------------------------------------------------
- # Resource lifecycle
- #--------------------------------------------------------------------------
-
- def __after__(self):
- return ('Interface',)
-
- @inline_task
- def __get__(self):
- raise ResourceNotFound
-
- def __subresources__(self):
- basedir = os.path.dirname(self._state.manager._base)
- self.host_file = TextFile(node = None,
- filename = os.path.join(basedir, HOST_FILE),
- overwrite = True)
- return self.host_file
-
- def __create__(self):
- """
- IP assignment strategy /32: assign /32 IP address to each interface
-
- We might use different subnets for resources involved in experiment,
- and supporting resources, and to minimize routing tables.
- """
- log.info('Assigment of IP addresses')
- tasks = EmptyTask()
-
- # We sort nodes by names for IP assignment. This code ensures that
- # interfaces on the same channel get consecutive IP addresses. That
- # way, we can assign /31 on p2p channels.
- channels = sorted(self._state.manager.by_type(Channel),
- key = lambda x : x.get_sortable_name())
- channels.extend(sorted(self._state.manager.by_type(Node),
- key = lambda node : node.name))
-
- host_file_content = ""
-
- # Dummy code to start IP addressing on an even number for /31
- ip = AddressManager().get_ip(None)
- if int(ip[-1]) % 2 == 0:
- ip = AddressManager().get_ip("dummy object")
-
- for channel in channels:
- # Sort interfaces in a deterministic order to ensure consistent
- # addressing across restarts of the tool
- interfaces = sorted(channel.interfaces,
- key = lambda x : x.device_name)
-
- for interface in interfaces:
- if interface.ip_address is None:
- ip = AddressManager().get_ip(interface)
-
- @async_task
- async def set_ip(interface, ip):
- await interface.async_set('ip_address', ip)
-
- if interface.managed:
- tasks = tasks | set_ip(interface, ip)
- else:
- ip = interface.ip_address
-
- # Note: interface.ip_address should still be None at this stage
- # since we have not made the assignment yet
-
- if isinstance(channel, Node):
- host_file_content += '# {} {} {}\n'.format(
- interface.node.name, interface.device_name, ip)
- if interface == interface.node.host_interface:
- host_file_content += '{} {}\n'.format(ip,
- interface.node.name)
- self.host_file.content = host_file_content
-
- return tasks
-
- __delete__ = None
-
-#-------------------------------------------------------------------------------
-
class IPRoutes(Resource):
"""
Resource: IPRoutes
@@ -331,7 +246,9 @@ class IPRoutes(Resource):
if not node_uuid in origins:
origins[node_uuid] = list()
for interface in node.interfaces:
- origins[node_uuid].append(interface.ip_address)
+ origins[node_uuid].append(interface.ip4_address)
+ if interface.ip6_address: #Control interfaces have no v6 address
+ origins[node_uuid].append(interface.ip6_address)
return origins
def _get_ip_routes(self):
@@ -370,37 +287,44 @@ class IPRoutes(Resource):
if prefix in ip_routes[src_node]:
continue
- if prefix == next_hop_ingress.ip_address:
+ #FIXME: should test for IP format
+ ip_version = 6 if ":" in prefix else 4
+ next_hop_ingress_ip = (next_hop_ingress.ip6_address if ip_version is 6 else
+ next_hop_ingress.ip4_address)
+ if prefix == next_hop_ingress_ip:
# Direct route on src_node.name :
# route add [prefix] dev [next_hop_interface_.device_name]
- route = IPRoute(node = src_node,
+ route4 = IPRoute(node = src_node,
managed = False,
owner = self,
ip_address = prefix,
mac_address = mac_addr,
+ ip_version = ip_version,
interface = next_hop_interface)
else:
# We need to be sure we have a route to the gw from the node
- if not next_hop_ingress.ip_address in ip_routes[src_node]:
- pre_route = IPRoute(node = src_node,
- managed = False,
- owner = self,
- ip_address = next_hop_ingress.ip_address,
+ if not next_hop_ingress_ip in ip_routes[src_node]:
+ pre_route = IPRoute(node = src_node,
+ managed = False,
+ owner = self,
+ ip_address = next_hop_ingress_ip,
+ ip_version = ip_version,
mac_address = mac_addr,
- interface = next_hop_interface)
- ip_routes[src_node].append(next_hop_ingress.ip_address)
+ interface = next_hop_interface)
+ ip_routes[src_node].append(next_hop_ingress_ip)
pre_routes.append(pre_route)
# Route on src_node.name:
# route add [prefix] dev [next_hop_interface_.device_name]
# via [next_hop_ingress.ip_address]
- route = IPRoute(node = src_node,
- managed = False,
- owner = self,
- ip_address = prefix,
- interface = next_hop_interface,
+ route = IPRoute(node = src_node,
+ managed = False,
+ owner = self,
+ ip_address = prefix,
+ ip_version = ip_version,
+ interface = next_hop_interface,
mac_address = mac_addr,
- gateway = next_hop_ingress.ip_address)
+ gateway = next_hop_ingress_ip)
ip_routes[src_node].append(prefix)
routes.append(route)
return pre_routes, routes
@@ -420,16 +344,24 @@ class IPRoutes(Resource):
dst = self._state.manager.by_uuid(map_[dst_node_uuid])
log.debug('[IP ROUTE] NODES {}/{}/{} -> {}/{}/{}'.format(
- src_node.name, src.device_name, src.ip_address,
- dst_node.name, dst.device_name, dst.ip_address))
+ src_node.name, src.device_name, src.ip4_address,
+ dst_node.name, dst.device_name, dst.ip4_address))
log.debug('[IP ROUTE] NODES {}/{}/{} -> {}/{}/{}'.format(
- dst_node.name, dst.device_name, dst.ip_address,
- src_node.name, src.device_name, src.ip_address))
+ dst_node.name, dst.device_name, dst.ip4_address,
+ src_node.name, src.device_name, src.ip4_address))
route = IPRoute(node = src_node,
managed = False,
owner = self,
- ip_address = dst.ip_address,
+ ip_address = dst.ip4_address,
+ mac_address = dst.mac_address,
+ interface = src)
+ routes.append(route)
+ route = IPRoute(node = src_node,
+ managed = False,
+ owner = self,
+ ip_address = dst.ip6_address,
+ ip_version = 6,
mac_address = dst.mac_address,
interface = src)
routes.append(route)
@@ -437,7 +369,15 @@ class IPRoutes(Resource):
route = IPRoute(node = dst_node,
managed = False,
owner = self,
- ip_address = src.ip_address,
+ ip_address = src.ip4_address,
+ mac_address = src.mac_address,
+ interface = dst)
+ routes.append(route)
+ route = IPRoute(node = dst_node,
+ managed = False,
+ owner = self,
+ ip_address = src.ip6_address,
+ ip_version = 6,
mac_address = src.mac_address,
interface = dst)
routes.append(route)
@@ -513,15 +453,15 @@ class ICNFaces(Resource):
src_face = L4Face(node = src_node,
owner = self,
protocol = protocol,
- src_ip = src.ip_address,
- dst_ip = dst.ip_address,
+ src_ip = src.ip4_address,
+ dst_ip = dst.ip4_address,
src_port = TMP_DEFAULT_PORT,
dst_port = TMP_DEFAULT_PORT)
dst_face = L4Face(node = dst_node,
owner = self,
protocol = protocol,
- src_ip = dst.ip_address,
- dst_ip = src.ip_address,
+ src_ip = dst.ip4_address,
+ dst_ip = src.ip4_address,
src_port = TMP_DEFAULT_PORT,
dst_port = TMP_DEFAULT_PORT)
else:
@@ -649,38 +589,9 @@ class ContainerSetup(Resource):
def __subresources__(self):
- # a) routes: host -> container
- # . container interfaces
- # . container host (main) interface
- # route add -host {ip_address} dev {bridge_name}
- route = IPRoute(node = self.container.node,
- managed = False,
- owner = self,
- ip_address = self.container.host_interface.ip_address,
- interface = self.container.node.bridge)
- route.node.routing_table.routes << route
-
- # b) route: container -> host
- # route add {ip_gateway} dev {interface_name}
- # route add default gw {ip_gateway} dev {interface_name}
- route = IPRoute(node = self.container,
- owner = self,
- managed = False,
- ip_address = self.container.node.bridge.ip_address,
- interface = self.container.host_interface)
- route.node.routing_table.routes << route
- route_gw = IPRoute(node = self.container,
- managed = False,
- owner = self,
- ip_address = 'default',
- interface = self.container.host_interface,
- gateway = self.container.node.bridge.ip_address)
- route_gw.node.routing_table.routes << route_gw
-
- # c) dns
dns_server_entry = DnsServerEntry(node = self.container,
owner = self,
- ip_address = self.container.node.bridge.ip_address,
+ ip_address = self.container.node.bridge.ip4_address,
interface_name = self.container.host_interface.device_name)
return dns_server_entry
@@ -690,6 +601,44 @@ class ContainerSetup(Resource):
raise ResourceNotFound
def __create__(self):
+ #If no IP has been given on the host interface (e.g., through DHCP)
+ #We need to assign one
+ if not self.container.host_interface.ip4_address:
+ # a) get the IP
+ assign=self._state.manager.by_type(Ipv4Assignment)[0]
+ ip4_addr = assign.get_control_address(self.container.host_interface)
+ self.container.host_interface.ip4_address = ip4_addr
+
+
+ # a) routes: host -> container
+ # . container interfaces
+ # . container host (main) interface
+ # route add -host {ip_address} dev {bridge_name}
+ route = IPRoute(node = self.container.node,
+ managed = False,
+ owner = self,
+ ip_address = ip4_addr,
+ interface = self.container.node.bridge)
+ route.node.routing_table.routes << route
+
+ # b) route: container -> host
+ # route add {ip_gateway} dev {interface_name}
+ # route add default gw {ip_gateway} dev {interface_name}
+ route = IPRoute(node = self.container,
+ owner = self,
+ managed = False,
+ ip_address = self.container.node.bridge.ip4_address,
+ interface = self.container.host_interface)
+ route.node.routing_table.routes << route
+ route_gw = IPRoute(node = self.container,
+ managed = False,
+ owner = self,
+ ip_address = 'default',
+ interface = self.container.host_interface,
+ gateway = self.container.node.bridge.ip4_address)
+ route_gw.node.routing_table.routes << route_gw
+
+
return BashTask(self.container.node, CMD_IP_FORWARD)
#------------------------------------------------------------------------------
@@ -729,21 +678,26 @@ class CentralIP(Resource):
ip_routing_strategy = Attribute(String, description = 'IP routing strategy',
default = 'pair') # spt, pair
+ ip6_data_prefix = Attribute(String, description="Prefix for IPv6 forwarding", mandatory=True)
+ ip4_data_prefix = Attribute(String, description="Prefix for IPv4 forwarding", mandatory=True)
+ ip4_control_prefix = Attribute(String, description="Prefix for IPv4 control", mandatory=True)
#--------------------------------------------------------------------------
# Resource lifecycle
#--------------------------------------------------------------------------
- def __after_init__(self):
- return ('Node', 'Channel', 'Interface')
+ #def __after_init__(self):
+ # return ('Node', 'Channel', 'Interface')
def __subresources__(self):
- ip_assign = IPAssignment(owner=self)
+ ip4_assign = Ipv4Assignment(prefix=self.ip4_data_prefix,
+ control_prefix=self.ip4_control_prefix)
+ ip6_assign = Ipv6Assignment(prefix=self.ip6_data_prefix)
containers_setup = ContainersSetup(owner=self)
ip_routes = IPRoutes(owner = self,
routing_strategy = self.ip_routing_strategy)
- return ip_assign > (ip_routes | containers_setup)
+ return (ip4_assign | ip6_assign) > (ip_routes | containers_setup)
@inline_task
def __get__(self):
diff --git a/vicn/resource/channel.py b/vicn/resource/channel.py
index cd64b641..d91bebcf 100644
--- a/vicn/resource/channel.py
+++ b/vicn/resource/channel.py
@@ -16,9 +16,13 @@
# limitations under the License.
#
-from netmodel.model.type import String
-from vicn.core.resource import Resource
-from vicn.core.attribute import Attribute
+from netmodel.model.type import String
+from vicn.core.resource import Resource
+from vicn.core.attribute import Attribute
+from vicn.core.task import EmptyTask
+from vicn.resource.ip_assignment import Ipv6Assignment, Ipv4Assignment
+
+from math import log, ceil
class Channel(Resource):
"""
@@ -29,6 +33,9 @@ class Channel(Resource):
# Public API
#--------------------------------------------------------------------------
+ def __after_init__(self):
+ return ("IpAssignment",)
+
def get_remote_name(self, name):
if len(self._interfaces) != 2:
return None
@@ -42,3 +49,26 @@ class Channel(Resource):
ret = "{:03}".format(len(self.interfaces))
ret = ret + ''.join(sorted(map(lambda x : x.node.name, self.interfaces)))
return ret
+
+ def __create__(self):
+ interfaces = sorted(self.interfaces, key = lambda x : x.device_name)
+ if interfaces:
+ #IPv6
+ central6 = self._state.manager.by_type(Ipv6Assignment)[0]
+ prefix6_size = min(64, 128 - ceil(log(len(self.interfaces), 2)))
+ prefix6 = iter(central6.get_prefix(self, prefix6_size))
+
+ #IPv4
+ central4 = self._state.manager.by_type(Ipv4Assignment)[0]
+ prefix4_size = 32 - ceil(log(len(self.interfaces), 2))
+ prefix4 = iter(central4.get_prefix(self, prefix4_size))
+
+ for interface in interfaces:
+ try:
+ interface.ip4_address = next(prefix4)
+ except StopIteration as e:
+ import pdb; pdb.set_trace()
+ interface.ip6_address = next(prefix6)
+ interface.ip6_prefix = prefix6_size
+
+ return EmptyTask()
diff --git a/vicn/resource/ip/prefix_tree.py b/vicn/resource/ip/prefix_tree.py
new file mode 100644
index 00000000..d3a8139a
--- /dev/null
+++ b/vicn/resource/ip/prefix_tree.py
@@ -0,0 +1,211 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017 Cisco and/or its affiliates.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+from socket import inet_pton, inet_ntop, AF_INET6
+from struct import unpack, pack
+from abc import ABCMeta
+
+class NotEnoughAddresses(Exception):
+ pass
+
+class Prefix(metaclass=ABCMeta):
+
+ def __init__(self, ip_address, prefix_size=None):
+ if not prefix_size:
+ ip_address, prefix_size = ip_address.split('/')
+ prefix_size = int(prefix_size)
+ if isinstance(ip_address, str):
+ ip_address = self.aton(ip_address)
+ self.ip_address = ip_address
+ self.prefix_size = prefix_size
+ self._range = self.limits()
+
+ def __contains__(self, obj):
+ #it can be an IP as a integer
+ if isinstance(obj, int):
+ obj = type(self)(obj, self.MAX_PREFIX_SIZE)
+ #Or it's an IP string
+ if isinstance(obj, str):
+ #It's a prefix as 'IP/prefix'
+ if '/' in obj:
+ split_obj = obj.split('/')
+ obj = type(self)(split_obj[0], int(split_obj[1]))
+ else:
+ obj = type(self)(obj, self.MAX_PREFIX_SIZE)
+
+ return self._contains_prefix(obj)
+
+ def _contains_prefix(self, prefix):
+ assert isinstance(prefix, type(self))
+ return (prefix.prefix_size >= self.prefix_size and
+ prefix.ip_address >= self.first_prefix_address() and
+ prefix.ip_address <= self.last_prefix_address())
+
+ #Returns the first address of a prefix
+ def first_prefix_address(self):
+ return self.ip_address & (self.MASK << (self.MAX_PREFIX_SIZE-self.prefix_size))
+
+ def last_prefix_address(self):
+ return self.ip_address | (self.MASK >> self.prefix_size)
+
+ def limits(self):
+ return self.first_prefix_address(), self.last_prefix_address()
+
+ def __str__(self):
+ return "{}/{}".format(self.ntoa(self.first_prefix_address()), self.prefix_size)
+
+ def __eq__(self, other):
+ return (self.first_prefix_address() == other.first_prefix_address() and
+ self.prefix_size == other.prefix_size)
+
+ def __hash__(self):
+ return hash(str(self))
+
+ def __iter__(self):
+ for i in range(self._range[0], self._range[1]+1):
+ yield self.ntoa(i)
+
+class Inet4Prefix(Prefix):
+
+ MASK = 0xffffffff
+ MAX_PREFIX_SIZE = 32
+
+ @classmethod
+ def aton(cls, address):
+ ret = 0
+ components = address.split('.')
+ for comp in components:
+ ret = (ret << 8) + int(comp)
+ return ret
+
+ @classmethod
+ def ntoa(cls, address):
+ components = []
+ for _ in range(0,4):
+ components.insert(0,'{}'.format(address % 256))
+ address = address >> 8
+ return '.'.join(components)
+
+class Inet6Prefix(Prefix):
+
+ MASK = 0xffffffffffffffff
+ MAX_PREFIX_SIZE = 64
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self._range = self.limits(True)
+
+ @classmethod
+ def aton (cls, address, with_suffix=False):
+ ret, suffix = unpack(">QQ", inet_pton(AF_INET6, address))
+ if with_suffix:
+ ret = (ret << 64) | suffix
+ return ret
+
+ @classmethod
+ def ntoa (cls, address, with_suffix=False):
+ ret = None
+ if with_suffix:
+ ret = inet_ntop(AF_INET6, pack(">QQ", address >> 64, address & ((1 << 64) -1)))
+ else:
+ ret = inet_ntop(AF_INET6, pack(">QQ", address, 0))
+ return ret
+
+ def limits(self, with_suffix=False):
+ ret = super().limits()
+ if with_suffix:
+ ret = ret[0] << 64, ret[1] << 64 | self.MASK
+ return ret
+
+ def __iter__(self):
+ for i in range(*self._range):
+ yield self.ntoa(i, True)
+
+###### PREFIX TREE ######
+
+class PrefixTree:
+ def __init__(self, prefix):
+ self.prefix = prefix
+ self.prefix_cls = type(prefix)
+ self.left = None
+ self.right = None
+ #When the full prefix is assigned
+ self.full = False
+
+ def find_prefix(self, prefix_size):
+ ret, lret, rret = [None]*3
+ if prefix_size > self.prefix.prefix_size and not self.full:
+ if self.left is None:
+ lret = self.prefix_cls(self.prefix.first_prefix_address(), self.prefix.prefix_size+1)
+ else:
+ lret = self.left.find_prefix(prefix_size)
+
+ if self.right is None:
+ rret = self.prefix_cls(self.prefix.last_prefix_address(), self.prefix.prefix_size+1)
+ else:
+ rret = self.right.find_prefix(prefix_size)
+
+ #Now we look for the longer prefix to assign
+ if not lret or (rret and rret.prefix_size > lret.prefix_size):
+ ret = rret
+ else:
+ ret = lret
+ return ret
+
+
+ def assign_prefix(self, prefix):
+ assert prefix in self.prefix
+ if prefix.prefix_size > self.prefix.prefix_size:
+ #Existing prefix on the left
+ lp = self.prefix_cls(self.prefix.first_prefix_address(), self.prefix.prefix_size+1)
+ #It's on the left branch
+ if prefix in lp:
+ if not self.left:
+ self.left = PrefixTree(lp)
+ self.left.assign_prefix(prefix)
+ #It's on the right branch
+ else:
+ rp = self.prefix_cls(self.prefix.last_prefix_address(), self.prefix.prefix_size+1)
+ if not self.right:
+ self.right = PrefixTree(rp)
+ self.right.assign_prefix(prefix)
+ elif self.prefix == prefix:
+ self.full = True
+ else:
+ raise RuntimeError("And you may ask yourself, well, how did I get here?")
+
+ def get_prefix(self, prefix_size):
+ ret = self.find_prefix(prefix_size)
+ if not ret:
+ raise NotEnoughAddresses
+ #find_prefix returns the size of the largest available prefix in our space
+ #not necessarily the one we asked for
+ ret.prefix_size = prefix_size
+ self.assign_prefix(ret)
+ return ret
+
+ def get_assigned_prefixes(self):
+ ret = []
+ if not self.right and not self.left and self.full:
+ ret.append(self.prefix)
+ else:
+ if self.right:
+ ret.extend(self.right.get_assigned_prefixes())
+ if self.left:
+ ret.extend(self.left.get_assigned_prefixes())
+ return ret
diff --git a/vicn/resource/ip/route.py b/vicn/resource/ip/route.py
index f073e426..b9f82960 100644
--- a/vicn/resource/ip/route.py
+++ b/vicn/resource/ip/route.py
@@ -16,7 +16,7 @@
# limitations under the License.
#
-from netmodel.model.type import String
+from netmodel.model.type import String, Integer
from vicn.resource.node import Node
from vicn.core.attribute import Attribute
from vicn.core.resource import Resource
@@ -27,6 +27,7 @@ class IPRoute(Resource):
ip_address = Attribute(String, mandatory = True)
interface = Attribute(Interface, mandatory = True)
gateway = Attribute(String)
+ ip_version = Attribute(Integer, default=4)
# FIXME Temp hack for VPP, migrate this to an ARP table resource
mac_address = Attribute(String)
diff --git a/vicn/resource/ip/routing_table.py b/vicn/resource/ip/routing_table.py
index 52b81794..e45793cc 100644
--- a/vicn/resource/ip/routing_table.py
+++ b/vicn/resource/ip/routing_table.py
@@ -25,15 +25,17 @@ from vicn.resource.node import Node
from vicn.resource.vpp.vpp_commands import CMD_VPP_ADD_ROUTE
from vicn.resource.vpp.vpp_commands import CMD_VPP_ADD_ROUTE_GW
-CMD_ADD_ROUTE = ('ip route add {route.ip_address} '
+CMD_ADD_ROUTE = ('ip -{route.ip_version} route add {route.ip_address} '
'dev {route.interface.device_name} || true')
-CMD_ADD_ROUTE_GW = ('ip route add {route.ip_address} '
+CMD_ADD_ROUTE_GW = ('ip -{route.ip_version} route add {route.ip_address} '
'dev {route.interface.device_name} via {route.gateway} || true')
-CMD_DEL_ROUTE = ('ip route del {route.ip_address} '
+CMD_DEL_ROUTE = ('ip -{route.ip_version} route del {route.ip_address} '
'dev {route.interface.device_name}')
CMD_SHOW_ROUTES = 'ip route show'
CMD_ADD_ARP_ENTRY = 'arp -s {route.ip_address} {route.mac_address}'
+CMD_ADD_NDB_ENTRY = ('ip -6 neigh add {route.ip_address} lladr {route.mac_address} '
+ 'dev {route.interface.device_name}')
# Populate arp table too. The current configuration with one single bridge
# connecting every container and vpp nodes seem to create loops that prevent
@@ -93,7 +95,7 @@ class RoutingTable(Resource):
#--------------------------------------------------------------------------
def __after__(self):
- return ('CentralIP', 'VPPInterface')
+ return ('CentralIP', 'VPPInterface', 'ContainerSetup')
def __get__(self):
def cache(rv):
@@ -125,10 +127,10 @@ class RoutingTable(Resource):
if route.ip_address in done:
continue
done.add(route.ip_address)
-
+
# TODO VPP should provide its own implementation of routing table
# on the node
- if not route.interface.has_vpp_child:
+ if not route.interface.has_vpp_child:
if route.gateway is None:
cmd = CMD_ADD_ROUTE.format(route = route)
routes_cmd.append(cmd)
@@ -148,7 +150,7 @@ class RoutingTable(Resource):
cmd = CMD_VPP_ADD_ROUTE_GW.format(route = route)
routes_via_cmd.append(cmd)
routes_via_lock = route.node.vpp.vppctl_lock
-
+
# TODO: checks
clean_routes_task = EmptyTask()
diff --git a/vicn/resource/ip_assignment.py b/vicn/resource/ip_assignment.py
new file mode 100644
index 00000000..7553f4ff
--- /dev/null
+++ b/vicn/resource/ip_assignment.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017 Cisco and/or its affiliates.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+from vicn.core.resource import Resource
+from netmodel.model.type import String
+from vicn.core.attribute import Attribute
+from vicn.resource.ip.prefix_tree import Inet6Prefix, PrefixTree, Inet4Prefix
+from vicn.core.task import inline_task
+from vicn.core.exception import ResourceNotFound
+
+class IpAssignment(Resource):
+ prefix = Attribute(String, mandatory=True)
+ control_prefix = Attribute(String, description="prefix for control plane")
+
+ PrefixClass = None
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self._prefix = self.PrefixClass(self.prefix)
+ self._prefix_tree = PrefixTree(self._prefix)
+ self._assigned_prefixes = {}
+ if self.control_prefix:
+ self._ctrl_prefix = self.PrefixClass(self.control_prefix)
+ self._ctrl_prefix_it = iter(self._ctrl_prefix)
+ next(self._ctrl_prefix_it) #Removes internet address
+ self._assigned_addresses = {}
+
+ def get_prefix(self, obj, prefix_size):
+ ret = None
+ if obj in self._assigned_prefixes:
+ ret = self._assigned_prefixes[obj]
+ else:
+ ret = self._prefix_tree.get_prefix(prefix_size)
+ self._assigned_prefixes[obj] = ret
+ return ret
+
+ def get_control_address(self, obj):
+ ret = None
+ if not self.control_prefix:
+ raise RuntimeError("No control prefix given")
+ try:
+ ret = self._assigned_addresses[obj]
+ except KeyError:
+ ret = next(self._ctrl_prefix_it)
+ self._assigned_addresses[obj] = ret
+ return ret
+
+class Ipv6Assignment(IpAssignment):
+ PrefixClass = Inet6Prefix
+
+
+class Ipv4Assignment(IpAssignment):
+ PrefixClass = Inet4Prefix
diff --git a/vicn/resource/linux/link.py b/vicn/resource/linux/link.py
index a930b8a3..ad77bfb9 100644
--- a/vicn/resource/linux/link.py
+++ b/vicn/resource/linux/link.py
@@ -120,14 +120,14 @@ class Link(Channel):
if hasattr(self.src_node, 'vpp') and not self.src_node.vpp is None:
vpp_src = VPPInterface(parent = self._src,
vpp = self.src_node.vpp,
- ip_address = Reference(self._src, 'ip_address'),
+ ip4_address = Reference(self._src, 'ip4_address'),
device_name = 'vpp' + self._src.device_name)
manager.commit_resource(vpp_src)
if hasattr(self.dst_node, 'vpp') and not self.dst_node.vpp is None:
vpp_dst = VPPInterface(parent = self._dst,
vpp = self.dst_node.vpp,
- ip_address = Reference(self._dst, 'ip_address'),
+ ip4_address = Reference(self._dst, 'ip4_address'),
device_name = 'vpp' + self._dst.device_name)
manager.commit_resource(vpp_dst)
diff --git a/vicn/resource/linux/net_device.py b/vicn/resource/linux/net_device.py
index a6f7614e..1ce7e4d5 100644
--- a/vicn/resource/linux/net_device.py
+++ b/vicn/resource/linux/net_device.py
@@ -38,7 +38,10 @@ LXD_FIX = lambda cmd: 'sleep 1 && {}'.format(cmd)
MAX_DEVICE_NAME_SIZE = 15
-CMD_FLUSH_IP = 'ip addr flush dev {device_name}'
+IPV4=4
+IPV6=6
+
+CMD_FLUSH_IP = 'ip -{ip_version} addr flush dev {device_name}'
CMD_INTERFACE_LIST = 'ip link show | grep -A 1 @{}'
RX_INTERFACE_LIST = '.*?(?P<ifname>[^ ]*)@{}:'
@@ -57,8 +60,10 @@ CMD_DELETE = 'ip link delete {netdevice.device_name}'
CMD_SET_MAC_ADDRESS = 'ip link set dev {netdevice.device_name} ' \
'address {netdevice.mac_address}'
CMD_GET_IP_ADDRESS = 'ip addr show {netdevice.device_name}'
-CMD_SET_IP_ADDRESS = 'ip addr add dev {netdevice.device_name} ' \
- '{netdevice.ip_address} brd + || true'
+CMD_SET_IP4_ADDRESS = 'ip addr add dev {netdevice.device_name} ' \
+ '{netdevice.ip4_address} brd + || true'
+CMD_SET_IP6_ADDRESS = 'ip addr add dev {netdevice.device_name} ' \
+ '{netdevice.ip6_address}/{netdevice.ip6_prefix} || true'
CMD_SET_PROMISC = 'ip link set dev {netdevice.device_name} promisc {on_off}'
CMD_SET_UP = 'ip link set {netdevice.device_name} {up_down}'
CMD_SET_CAPACITY='\n'.join([
@@ -85,6 +90,11 @@ sysctl net.ipv4.conf.all.rp_filter
sysctl net.ipv4.conf.{netdevice.device_name}.rp_filter
'''
+CMD_UNSET_IP6_FWD = 'sysctl -w net.ipv6.conf.{netdevice.device_name}.forwarding=0'
+CMD_SET_IP6_FWD = 'sysctl -w net.ipv6.conf.{netdevice.device_name}.forwarding=1'
+CMD_GET_IP6_FWD = 'sysctl -n net.ipv6.conf.{netdevice.device_name}.forwarding'
+
+
#-------------------------------------------------------------------------------
# FIXME GPL code
@@ -269,7 +279,10 @@ class BaseNetDevice(Interface, Application):
capacity = Attribute(Integer,
description = 'Capacity for interface shaping (Mb/s)')
mac_address = Attribute(String, description = 'Mac address of the device')
- ip_address = Attribute(String, description = 'IP address of the device')
+ ip4_address = Attribute(String, description = 'IP address of the device')
+ ip6_address = Attribute(String, description = 'IPv6 address of the device')
+ ip6_prefix = Attribute(Integer, description = 'Prefix for the IPv6 link', default=64)
+ ip6_forwarding = Attribute(Bool, description = 'IPv6 forwarding', default = True)
pci_address = Attribute(String,
description = 'PCI bus address of the device',
ro = True)
@@ -329,7 +342,7 @@ class BaseNetDevice(Interface, Application):
def _set_mac_address(self):
return BashTask(self.node, CMD_SET_MAC_ADDRESS, {'netdevice': self})
- def _get_ip_address(self):
+ def _get_ip4_address(self):
"""
NOTE: Incidently, this will also give the MAC address, as well as other
attributes...
@@ -357,21 +370,79 @@ class BaseNetDevice(Interface, Application):
if len(ips) > 1:
log.warning('Keeping only first of many IP addresses...')
ip = ips[0]
- attrs['ip_address'] = ip['ip-address']
+ attrs['ip4_address'] = ip['ip-address']
+ else:
+ attrs['ip4_address'] = None
+ return attrs
+
+ return BashTask(self.node, CMD_GET_IP_ADDRESS,
+ {'netdevice': self}, parse=parse)
+
+ def _set_ip4_address(self):
+ if self.ip4_address is None:
+ # Unset IP
+ return BashTask(self.node, CMD_FLUSH_IP,
+ {'device_name': self.device_name, 'ip_version': IPV4})
+ return BashTask(self.node, CMD_SET_IP4_ADDRESS, {'netdevice': self})
+
+ def _get_ip6_address(self):
+ """
+ NOTE: Incidently, this will also give the MAC address, as well as other
+ attributes...
+ """
+ def parse(rv):
+ attrs = dict()
+
+ assert rv is not None
+
+ nds = list(parse_ip_addr(rv.stdout))
+
+ assert nds
+ assert len(nds) <= 1
+
+ nd = nds[0]
+
+ assert nd['name'] == self.device_name
+
+ attrs['mac_address'] = nd['hardware-address']
+
+ # We assume a single IPv4 address for now...
+ ips = [ip for ip in nd['ip-addresses']
+ #We remove the link-local address that starts with fe80
+ if ip['ip-address-type'] == 'ipv6' and ip['ip-address'][:4] != 'fe80']
+ if len(ips) >= 1:
+ if len(ips) > 1:
+ log.warning('Keeping only first of many IPv6 addresses...')
+ ip = ips[0]
+ attrs['ip6_address'] = ip['ip-address']
+ attrs['ip6_prefix'] = ip['prefix']
else:
- attrs['ip_address'] = None
+ attrs['ip6_address'] = None
return attrs
return BashTask(self.node, CMD_GET_IP_ADDRESS,
{'netdevice': self}, parse=parse)
- def _set_ip_address(self):
- if self.ip_address is None:
+ _get_ip6_prefix = _get_ip6_address
+
+ def _set_ip6_address(self):
+ if self.ip6_address is None:
# Unset IP
return BashTask(self.node, CMD_FLUSH_IP,
- {'device_name': self.device_name})
- return BashTask(self.node, CMD_SET_IP_ADDRESS,
- {'netdevice': self})
+ {'device_name': self.device_name, 'ip_version': IPV6})
+ return BashTask(self.node, CMD_SET_IP6_ADDRESS, {'netdevice': self})
+
+ def _get_ip6_forwarding(self):
+ def parse(rv):
+ ret = {"ip6_forwarding" : False}
+ if rv.stdout == "1":
+ ret["ip6_forwarding"] = True
+ return ret
+ return BashTask(self.node, CMD_GET_IP6_FWD, {'netdevice': self}, parse=parse)
+
+ def _set_ip6_forwarding(self):
+ cmd = CMD_SET_IP6_FWD if self.ip6_forwarding else CMD_UNSET_IP6_FWD
+ return BashTask(self.node, cmd, {'netdevice': self})
@task
def _get_promiscuous(self):
diff --git a/vicn/resource/linux/package_manager.py b/vicn/resource/linux/package_manager.py
index cb149ac6..86b7057a 100644
--- a/vicn/resource/linux/package_manager.py
+++ b/vicn/resource/linux/package_manager.py
@@ -96,9 +96,9 @@ class PackageManager(Resource):
def __after__(self):
if self.node.__class__.__name__ == 'Physical':
# UGLY : This blocking code is currently needed
- task = self.node.host_interface._get_ip_address()
+ task = self.node.host_interface._get_ip4_address()
ip_dict = task.execute_blocking()
- self.node.host_interface.ip_address = ip_dict['ip_address']
+ self.node.host_interface.ip4_address = ip_dict['ip4_address']
return ('Repository',)
else:
return ('Repository', 'CentralIP', 'RoutingTable')
@@ -158,7 +158,7 @@ class PackageManager(Resource):
return '/etc/apt/sources.list.d/{}.list'.format(repository.repo_name)
def _get_deb_source(self, repository):
- path = repository.node.host_interface.ip_address + '/'
+ path = repository.node.host_interface.ip4_address + '/'
if repository.directory:
path += repository.directory + '/'
return 'deb http://{} {}/'.format(path, self.node.dist)
diff --git a/vicn/resource/linux/phy_interface.py b/vicn/resource/linux/phy_interface.py
index c1aef27e..81d2950c 100644
--- a/vicn/resource/linux/phy_interface.py
+++ b/vicn/resource/linux/phy_interface.py
@@ -36,7 +36,8 @@ class PhyInterface(Interface):
mandatory = True)
mac_address = Attribute(String, description = "Device's MAC address",
mandatory=True)
- ip_address = Attribute(String, description = "Device's IP address")
+ ip4_address = Attribute(String, description = "Device's IP address")
+ ip6_address = Attribute(String, description = "Device's IP address")
#--------------------------------------------------------------------------
# Constructor and Accessors
diff --git a/vicn/resource/linux/tap_device.py b/vicn/resource/linux/tap_device.py
index 68c0b9c7..b7c9f967 100644
--- a/vicn/resource/linux/tap_device.py
+++ b/vicn/resource/linux/tap_device.py
@@ -19,11 +19,10 @@
from netmodel.model.type import String
from vicn.core.attribute import Attribute
from vicn.core.task import BashTask
-from vicn.resource.linux.net_device import BaseNetDevice
+from vicn.resource.linux.net_device import BaseNetDevice, IPV4, IPV6, CMD_FLUSH_IP
CMD_CREATE='ip tuntap add name {netdevice.device_name} mode tap'
-CMD_FLUSH_IP='ip addr flush dev {device_name}'
-CMD_SET_IP_ADDRESS='ip addr add dev {netdevice.device_name} 0.0.0.0'
+#CMD_SET_IP_ADDRESS='ip -{version} addr add dev {netdevice.device_name} 0.0.0.0'
class TapDevice(BaseNetDevice):
@@ -35,13 +34,22 @@ class TapDevice(BaseNetDevice):
def __create__(self):
return BashTask(self.node, CMD_CREATE, {'netdevice': self})
- def _set_ip_address(self):
- if self.ip_address is None:
- # Unset IP
- return BashTask(self.node, CMD_FLUSH_IP,
- {'device_name': self.device_name})
- return BashTask(self.node, CMD_SET_IP_ADDRESS,
- {'netdevice': self})
+##mengueha: do we actually need that?
+# def _set_ip4_address(self):
+# if self.ip4_address is None:
+# # Unset IP
+# return BashTask(self.node, CMD_FLUSH_IP,
+# {'device_name': self.device_name})
+# return BashTask(self.node, CMD_SET_IP_ADDRESS,
+# {'netdevice': self})
+#
+# def _set_ip6_address(self):
+# if self.ip6_address is None:
+# # Unset IP
+# return BashTask(self.node, CMD_FLUSH_IP,
+# {'ip_version': IPV6, 'device_name': self.device_name})
+# return BashTask(self.node, CMD_SET_IP_ADDRESS,
+# {'netdevice': self})
class TapChannel(TapDevice):
station_name = Attribute(String)
diff --git a/vicn/resource/lxd/lxc_container.py b/vicn/resource/lxd/lxc_container.py
index 7c678e57..9daaffb7 100644
--- a/vicn/resource/lxd/lxc_container.py
+++ b/vicn/resource/lxd/lxc_container.py
@@ -52,6 +52,10 @@ DEFAULT_SOURCE_PROTOCOL = 'simplestreams'
# Commands used to interact with LXD (in addition to pylxd bindings)
CMD_GET_PID='lxc info {container.name} | grep Pid | cut -d " " -f 2'
+CMD_UNSET_IP6_FWD = 'sysctl -w net.ipv6.conf.all.forwarding=0'
+CMD_SET_IP6_FWD = 'sysctl -w net.ipv6.conf.all.forwarding=1'
+CMD_GET_IP6_FWD = 'sysctl -n net.ipv6.conf.all.forwarding'
+
# Type: ContainerName
ContainerName = String(max_size = 64, ascii = True,
forbidden = ('/', ',', ':'))
@@ -94,6 +98,7 @@ class LxcContainer(Node):
image = Attribute(String, description = 'image', default = None)
is_image = Attribute(Bool, defaut = False)
pid = Attribute(Integer, description = 'PID of the container')
+ ip6_forwarding = Attribute(Bool, default=True)
#--------------------------------------------------------------------------
# Constructor / Accessors
@@ -317,3 +322,15 @@ class LxcContainer(Node):
if output:
args += (ret.stdout, ret.stderr)
return ReturnValue(*args)
+
+ def _get_ip6_forwarding(self):
+ def parse(rv):
+ ret = {"ip6_forwarding" : False}
+ if rv.stdout == "1":
+ ret["ip6_forwarding"] = True
+ return ret
+ return BashTask(self, CMD_GET_IP6_FWD, parse=parse)
+
+ def _set_ip6_forwarding(self):
+ cmd = CMD_SET_IP6_FWD if self.ip6_forwarding else CMD_UNSET_IP6_FWD
+ return BashTask(self, cmd)
diff --git a/vicn/resource/ns3/emulated_lte_channel.py b/vicn/resource/ns3/emulated_lte_channel.py
index 39370228..bf0f7097 100644
--- a/vicn/resource/ns3/emulated_lte_channel.py
+++ b/vicn/resource/ns3/emulated_lte_channel.py
@@ -137,7 +137,7 @@ class EmulatedLteChannel(EmulatedChannel):
sta_list.append(interface.name)
sta_macs.append(interface.mac_address)
- sta_ips.append(interface.ip_address + '/24')
+ sta_ips.append(interface.ip4_address + '/24')
else:
identifier = self._sta_ifs[station]._state.uuid._uuid
sta_list.append(identifier)
diff --git a/vicn/resource/vpp/interface.py b/vicn/resource/vpp/interface.py
index efe4fe5a..5f8f5018 100644
--- a/vicn/resource/vpp/interface.py
+++ b/vicn/resource/vpp/interface.py
@@ -35,15 +35,16 @@ class VPPInterface(Resource):
An interface representation in VPP
"""
- vpp = Attribute(VPP,
+ vpp = Attribute(VPP,
description = 'Forwarder to which this interface belong to',
mandatory = True,
multiplicity = Multiplicity.ManyToOne,
key = True,
- reverse_name = 'interfaces')
- parent = Attribute(Interface, description = 'parent',
- mandatory = True, reverse_name = 'vppinterface')
- ip_address = Attribute(String)
+ reverse_name = 'interfaces')
+ parent = Attribute(Interface, description = 'parent',
+ mandatory = True, reverse_name = 'vppinterface')
+ ip4_address = Attribute(String)
+ ip6_address = Attribute(String)
prefix_len = Attribute(Integer, default = 31)
up = Attribute(Bool, description = 'Interface up/down status')
monitored = Attribute(Bool, default = True)
@@ -73,7 +74,8 @@ class VPPInterface(Resource):
# belongs to vpp
self.parent.has_vpp_child = True
- self.ip_address = self.parent.ip_address
+ self.ip4_address = self.parent.ip4_address
+ self.ip6_address = self.parent.ip6_address
self.up = True
if isinstance(self.parent,NonTapBaseNetDevice):
@@ -86,7 +88,8 @@ class VPPInterface(Resource):
{'vpp_interface': self},
lock = self.vpp.vppctl_lock)
- self.parent.set('ip_address', None)
+ self.parent.set('ip4_address', None)
+ self.parent.set('ip6_address', None)
self.parent.set('offload', False)
self.parent.remote.set('offload', False)
@@ -107,8 +110,8 @@ class VPPInterface(Resource):
# Attributes
#--------------------------------------------------------------------------
- def _set_ip_address(self):
- if self.ip_address:
+ def _set_ip4_address(self):
+ if self.ip4_address:
return BashTask(self.vpp.node, CMD_VPP_SET_IP, {'netdevice': self},
lock = self.vpp.vppctl_lock)
@@ -121,5 +124,9 @@ class VPPInterface(Resource):
return {'up' : False}
@task
- def _get_ip_address(self):
- return {'ip_address' : None}
+ def _get_ip4_address(self):
+ return {'ip4_address' : None}
+
+ @task
+ def _get_ip6_address(self):
+ return {'ip6_address' : None}
diff --git a/vicn/resource/vpp/vpp_bridge.py b/vicn/resource/vpp/vpp_bridge.py
index c7a70c02..612145d9 100644
--- a/vicn/resource/vpp/vpp_bridge.py
+++ b/vicn/resource/vpp/vpp_bridge.py
@@ -40,8 +40,8 @@ class VPPBridge(Channel, LinuxApplication):
"""
Resource: VPPBridge
- VPPBridge instantiate a vpp resource and set it as a vpp bridge.
-
+ VPPBridge instantiate a vpp resource and set it as a vpp bridge.
+
This resource requires to be run within a LxcContainer which will have VPP.
Every interface in the lxc_container (i.e., the ones contained in
self.node.interfaces) will be added to the vpp bridge. To connect other vpp
@@ -64,7 +64,7 @@ class VPPBridge(Channel, LinuxApplication):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._vpp_interfaces = list()
-
+
#--------------------------------------------------------------------------
# Resource lifecycle
#--------------------------------------------------------------------------
@@ -72,7 +72,7 @@ class VPPBridge(Channel, LinuxApplication):
def __subresources__ (self):
# We don't need any reference to the list of SymVethPair because each
# side of a veth will be included in the node.interfaces list
- self._veths = [SymVethPair(node1 = self.node, node2 = node,
+ self._veths = [SymVethPair(node1 = self.node, node2 = node,
owner = self) for node in self.connected_nodes]
return Resource.__concurrent__(*self._veths)
@@ -82,7 +82,7 @@ class VPPBridge(Channel, LinuxApplication):
# Add the veth side on the connected_nodes to the set of interfaces of
# the channel
self.interfaces.extend([veth.side2 for veth in self._veths])
-
+
@task
def __get__(self):
# Forces creation
@@ -90,10 +90,10 @@ class VPPBridge(Channel, LinuxApplication):
# Nothing to do
__delete__ = None
-
+
def __create__(self):
manager = self._state.manager
-
+
# Create a VPPInterface for each interface in the node. These will be
# the interfaces we will connect to the vpp bridge process
vpp_interfaces = list()
@@ -102,9 +102,9 @@ class VPPBridge(Channel, LinuxApplication):
if interface.device_name == 'eth0':
continue
- vpp_interface = VPPInterface(vpp = self.node.vpp,
- parent = interface,
- ip_address = Reference(interface, 'ip_address'),
+ vpp_interface = VPPInterface(vpp = self.node.vpp,
+ parent = interface,
+ ip4_address = Reference(interface, 'ip4_address'),
device_name = 'host-' + interface.device_name)
vpp_interfaces.append(vpp_interface)
manager.commit_resource(vpp_interface)
@@ -112,17 +112,17 @@ class VPPBridge(Channel, LinuxApplication):
tasks = EmptyTask()
for vpp_interface in vpp_interfaces:
- tasks = tasks > (wait_resource_task(vpp_interface) >
+ tasks = tasks > (wait_resource_task(vpp_interface) >
self._add_interface(vpp_interface,0))
- return wait_resource_task(self.node.vpp) > tasks
+ return wait_resource_task(self.node.vpp) > tasks
#--------------------------------------------------------------------------
# Internal methods
#--------------------------------------------------------------------------
def _add_interface(self, interface, br_domain):
- return BashTask(self.node, CMD_ADD_INTERFACE_TO_BR,
+ return BashTask(self.node, CMD_ADD_INTERFACE_TO_BR,
{'interface': interface, 'br_domain': br_domain})
def _del_interface(self, interface, br_domain):
diff --git a/vicn/resource/vpp/vpp_commands.py b/vicn/resource/vpp/vpp_commands.py
index 63c74808..40315c19 100644
--- a/vicn/resource/vpp/vpp_commands.py
+++ b/vicn/resource/vpp/vpp_commands.py
@@ -21,7 +21,7 @@ CMD_VPP_CREATE_IFACE = '''
vppctl create host-interface name {vpp_interface.parent.device_name} hw-addr {vpp_interface.parent.mac_address}
vppctl set interface state {vpp_interface.device_name} up
'''
-CMD_VPP_SET_IP = 'vppctl set int ip address {netdevice.device_name} {netdevice.ip_address}/{netdevice.prefix_len}'
+CMD_VPP_SET_IP = 'vppctl set int ip address {netdevice.device_name} {netdevice.ip4_address}/{netdevice.prefix_len}'
CMD_VPP_SET_UP = 'vppctl set int state {netdevice.device_name} up'
##### VPP IP ROUTING #####