aboutsummaryrefslogtreecommitdiffstats
path: root/vicn/resource/vpp
diff options
context:
space:
mode:
Diffstat (limited to 'vicn/resource/vpp')
-rw-r--r--vicn/resource/vpp/cicn.py138
-rw-r--r--vicn/resource/vpp/dpdk_device.py2
-rw-r--r--vicn/resource/vpp/interface.py103
-rw-r--r--vicn/resource/vpp/memif_device.py77
-rw-r--r--vicn/resource/vpp/memif_link.py144
-rw-r--r--vicn/resource/vpp/scripts.py2
-rw-r--r--vicn/resource/vpp/vpp.py96
-rw-r--r--vicn/resource/vpp/vpp_bridge.py7
-rw-r--r--vicn/resource/vpp/vpp_commands.py50
-rw-r--r--vicn/resource/vpp/vpp_host.py10
10 files changed, 410 insertions, 219 deletions
diff --git a/vicn/resource/vpp/cicn.py b/vicn/resource/vpp/cicn.py
deleted file mode 100644
index 1a68f11f..00000000
--- a/vicn/resource/vpp/cicn.py
+++ /dev/null
@@ -1,138 +0,0 @@
-#!/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.
-#
-
-import re
-
-from netmodel.model.type import Integer, Bool
-from vicn.core.attribute import Attribute
-from vicn.core.exception import ResourceNotFound
-from vicn.core.requirement import Requirement
-from vicn.core.resource_mgr import wait_resource_task
-from vicn.core.task import async_task, task, BashTask, EmptyTask
-from vicn.resource.icn.forwarder import Forwarder
-from vicn.resource.node import Node
-from vicn.resource.vpp.vpp_commands import CMD_VPP_ENABLE_PLUGIN
-from vicn.resource.vpp.vpp_commands import CMD_VPP_CICN_GET
-from vicn.resource.vpp.vpp_commands import CMD_VPP_ADD_ICN_FACE
-from vicn.resource.vpp.vpp_commands import CMD_VPP_ADD_ICN_ROUTE
-from vicn.resource.vpp.vpp_commands import CMD_VPP_CICN_GET_CACHE_SIZE
-from vicn.resource.vpp.vpp_commands import CMD_VPP_CICN_SET_CACHE_SIZE
-
-_ADD_FACE_RETURN_FORMAT = "Face ID: [0-9]+"
-
-def check_face_id_return_format(data):
- prog = re.compile(_ADD_FACE_RETURN_FORMAT)
- return prog.match(data)
-
-def parse_face_id(data):
- return data.partition(':')[2]
-
-class CICNForwarder(Forwarder):
- """
- NOTE: Based on the Vagrantfile, we recommend a node with mem=4096, cpu=4
- """
-
- node = Attribute(Node,
- mandatory=True,
- requirements = [Requirement('vpp')],
- reverse_name='cicn')
- numa_node = Attribute(Integer,
- description = 'Numa node on which vpp will run',
- default = None)
- core = Attribute(Integer,
- description = 'Core belonging the numa node on which vpp will run',
- default = None)
- enable_worker = Attribute(Bool,
- description = 'Enable one worker for packet processing',
- default = False)
-
- #__packages__ = ['vpp-plugin-cicn']
-
- def __after__(self):
- return ['CentralICN']
-
- def __get__(self):
- def parse(rv):
- if rv.return_value > 0 or 'cicn: not enabled' in rv.stdout:
- raise ResourceNotFound
- return BashTask(self.node, CMD_VPP_CICN_GET,
- lock = self.node.vpp.vppctl_lock, parse=parse)
-
- def __create__(self):
-
- #self.node.vpp.plugins.append("cicn")
- lock = self.node.vpp.vppctl_lock
- create_task = BashTask(self.node, CMD_VPP_ENABLE_PLUGIN,
- {'plugin' : 'cicn'}, lock = lock)
-
- face_task = EmptyTask()
- route_task = EmptyTask()
-
- def parse_face(rv, face):
- if check_face_id_return_format(rv.stdout):
- face.id = parse_face_id(rv.stdout)
- return {}
-
- for face in self.faces:
- face_task = face_task > BashTask(self.node, CMD_VPP_ADD_ICN_FACE,
- {'face':face},
- parse = (lambda x : parse_face(x, face)), lock = lock)
-
- if not self.routes:
- from vicn.resource.icn.route import Route
- for route in self._state.manager.by_type(Route):
- if route.node is self.node:
- self.routes.append(route)
- for route in self.routes:
- route_task = route_task > BashTask(self.node,
- CMD_VPP_ADD_ICN_ROUTE, {'route' : route}, lock = lock)
-
- return (wait_resource_task(self.node.vpp) > create_task) > (face_task > route_task)
-
- # Nothing to do
- __delete__ = None
-
- #--------------------------------------------------------------------------
- # Attributes
- #--------------------------------------------------------------------------
-
- # Force local update
-
- _add_faces = None
- _remove_faces = None
- _get_faces = None
- _set_faces = None
-
- _add_routes = None
- _remove_routes = None
- _get_routes = None
- _set_routes = None
-
- #--------------------------------------------------------------------------
- # Internal methods
- #--------------------------------------------------------------------------
-
- def _set_cache_size(self):
- return BashTask(self.node, CMD_VPP_CICN_SET_CACHE_SIZE, {'self': self},
- lock = self.node.vpp.vppctl_lock)
-
- def _get_cache_size(self):
- def parse(rv):
- return int(rv.stdout)
- return BashTask(self.node, CMD_VPP_CICN_GET_CACHE_SIZE, parse=parse,
- lock = self.node.vpp.vppctl_lock)
diff --git a/vicn/resource/vpp/dpdk_device.py b/vicn/resource/vpp/dpdk_device.py
index 472ee26f..76659129 100644
--- a/vicn/resource/vpp/dpdk_device.py
+++ b/vicn/resource/vpp/dpdk_device.py
@@ -19,6 +19,7 @@
from netmodel.model.type import Integer, String
from vicn.core.attribute import Attribute
from vicn.resource.linux.phy_interface import PhyInterface
+from vicn.core.task import BashTask, task
class DpdkDevice(PhyInterface):
"""
@@ -32,4 +33,3 @@ class DpdkDevice(PhyInterface):
socket_mem = Attribute(Integer,
description = 'Memory used by the vpp forwarder',
default = 512)
- mac_address = Attribute(String)
diff --git a/vicn/resource/vpp/interface.py b/vicn/resource/vpp/interface.py
index 5f8f5018..d0538661 100644
--- a/vicn/resource/vpp/interface.py
+++ b/vicn/resource/vpp/interface.py
@@ -16,17 +16,63 @@
# limitations under the License.
#
+import pyparsing as pp
+
+from netmodel.model.key import Key
from netmodel.model.type import Integer, String, Bool
+from netmodel.model.type import Inet4Address, Inet6Address
from vicn.core.resource import Resource
from vicn.core.attribute import Attribute, Multiplicity
from vicn.core.exception import ResourceNotFound
from vicn.core.task import inline_task, BashTask, task
+from vicn.core.task import inherit_parent
from vicn.core.task import EmptyTask
from vicn.resource.interface import Interface
from vicn.resource.linux.net_device import NonTapBaseNetDevice
from vicn.resource.vpp.vpp import VPP
-from vicn.resource.vpp.vpp_commands import CMD_VPP_CREATE_IFACE
+from vicn.resource.vpp.vpp_commands import CMD_VPP_CREATE_IFACE, CMD_VPP_CREATE_MEMIFACE
from vicn.resource.vpp.vpp_commands import CMD_VPP_SET_IP, CMD_VPP_SET_UP
+from vicn.resource.vpp.memif_device import MemifDevice
+
+GREP_MEMIF_INFO = 'vppctl_wrapper show memif | grep interface --no-group-separator -A 1'
+
+def parse_memif(rv, vppinterface):
+ kw_interface = pp.CaselessKeyword('interface')
+ kw_key = pp.CaselessKeyword('key')
+ kw_file = pp.CaselessKeyword('file')
+ kw_listener = pp.CaselessKeyword('listener')
+ kw_connfd = pp.CaselessKeyword('conn-fd')
+ kw_intfd = pp.CaselessKeyword('int-fd')
+ kw_ringsize = pp.CaselessKeyword('ring-size')
+ kw_numc2srings = pp.CaselessKeyword('num-c2s-rings')
+ kw_nums2crings = pp.CaselessKeyword('num-s2c-rings')
+ kw_buffersize = pp.CaselessKeyword('buffer_size')
+
+ r_path = ' *(/[a-zA-Z0-9_\-]*)*\.[a-zA-Z0-9_\-]*'
+ r_id = ' *-+[0-9]*'
+
+ single = kw_interface.suppress() + pp.Word(pp.alphanums).setResultsName('interface') + \
+ kw_key.suppress() + pp.Word(pp.alphanums).setResultsName('key') + \
+ kw_file.suppress() + pp.Regex(r_path).setResultsName('path') # + \
+ # kw_listener.suppress() + pp.Word(pp.alphanums).setResultsName('listener') + \
+ # kw_connfd.suppress() + pp.Regex(r_id).setResultsName('conn-fd') + \
+ # kw_intfd.suppress() + pp.Regex(r_id).setResultsName('int-fd') + \
+ # kw_ringsize.suppress() + pp.Word(pp.nums).setResultsName('ring-size') + \
+ # kw_numc2srings.suppress() + pp.Word(pp.nums).setResultsName('num-c2s-rings') + \
+ # kw_nums2crings.suppress() + pp.Word(pp.nums).setResultsName('num-s2c-rings') + \
+ # kw_buffersize.suppress() + pp.Word(pp.nums).setResultsName('buffer-size')
+
+ multiple = pp.OneOrMore(pp.Group(single))
+
+ results = multiple.parseString(rv.stdout)
+
+ for interface in results:
+ if interface['path'] == vppinterface.parent.path_unix_socket + vppinterface.parent.socket_name:
+ vppinterface.device_name = interface['interface']
+ vppinterface.parent.device_name = interface['interface']
+
+ return vppinterface.device_name
+
class VPPInterface(Resource):
"""
@@ -39,18 +85,21 @@ class VPPInterface(Resource):
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')
- ip4_address = Attribute(String)
- ip6_address = Attribute(String)
- prefix_len = Attribute(Integer, default = 31)
+ ip4_address = Attribute(Inet4Address)
+ ip6_address = Attribute(Inet6Address)
up = Attribute(Bool, description = 'Interface up/down status')
monitored = Attribute(Bool, default = True)
device_name = Attribute(String)
+ __key__ = Key(vpp, Resource.name)
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
#--------------------------------------------------------------------------
# Resource lifecycle
#--------------------------------------------------------------------------
@@ -61,10 +110,12 @@ class VPPInterface(Resource):
"""
return ['CentralIP']
+ @inherit_parent
@inline_task
def __get__(self):
raise ResourceNotFound
+ @inherit_parent
def __create__(self):
# We must control what is the type of the parent netDevice (currently
# supported only veths, physical nics are coming)
@@ -73,15 +124,27 @@ class VPPInterface(Resource):
# We must let the routing algorithm know that the parent interface
# belongs to vpp
self.parent.has_vpp_child = True
-
- self.ip4_address = self.parent.ip4_address
- self.ip6_address = self.parent.ip6_address
self.up = True
- if isinstance(self.parent,NonTapBaseNetDevice):
+ if isinstance(self.parent, MemifDevice):
+ #TODO: add output parsing to get the interface name
+ create_task = BashTask(self.vpp.node, CMD_VPP_CREATE_MEMIFACE, {
+ 'key': hex(self.parent.key),
+ 'vpp_interface': self,
+ 'master_slave': 'master' if self.parent.master else 'slave'},
+ lock = self.vpp.vppctl_lock)
+ fill_name = BashTask(self.vpp.node, GREP_MEMIF_INFO,
+ parse = (lambda x, y=self : parse_memif(x, y)),
+ lock = self.vpp.vppctl_lock)
+
+ create_task = create_task > fill_name
+
+ elif isinstance(self.parent,NonTapBaseNetDevice):
# Remove ip address in the parent device, it must only be set in
# the vpp interface otherwise vpp and the linux kernel will reply
# to non-icn request (e.g., ARP replies, port ureachable etc)
+ self.ip4_address = self.parent.ip4_address
+ self.ip6_address = self.parent.ip6_address
self.device_name = 'host-' + self.parent.device_name
create_task = BashTask(self.vpp.node, CMD_VPP_CREATE_IFACE,
@@ -94,6 +157,8 @@ class VPPInterface(Resource):
self.parent.remote.set('offload', False)
elif self.parent.get_type() == 'dpdkdevice':
+ self.ip4_address = self.parent.ip4_address
+ self.ip6_address = self.parent.ip6_address
self.device_name = self.parent.device_name
else :
# Currently assume naively that everything else will be a physical
@@ -112,13 +177,27 @@ class VPPInterface(Resource):
def _set_ip4_address(self):
if self.ip4_address:
- return BashTask(self.vpp.node, CMD_VPP_SET_IP, {'netdevice': self},
+ return BashTask(self.vpp.node, CMD_VPP_SET_IP, {
+ 'device_name': self.device_name,
+ 'ip_address': str(self.ip4_address),
+ 'prefix_len': self.ip4_address.prefix_len},
lock = self.vpp.vppctl_lock)
- def _set_up(self):
- return BashTask(self.vpp.node, CMD_VPP_SET_UP, {'netdevice': self},
+ def _set_ip6_address(self):
+ if self.ip6_address:
+ return BashTask(self.vpp.node, CMD_VPP_SET_IP, {
+ 'device_name': self.device_name,
+ 'ip_address': str(self.ip6_address),
+ 'prefix_len': self.ip6_address.prefix_len},
lock = self.vpp.vppctl_lock)
+ def _set_up(self):
+ state = 'up' if self.up else 'down'
+ return BashTask(self.vpp.node, CMD_VPP_SET_UP, {
+ 'netdevice': self,
+ 'state': state},
+ lock = self.vpp.vppctl_lock)
+
@task
def _get_up(self):
return {'up' : False}
diff --git a/vicn/resource/vpp/memif_device.py b/vicn/resource/vpp/memif_device.py
new file mode 100644
index 00000000..a114900a
--- /dev/null
+++ b/vicn/resource/vpp/memif_device.py
@@ -0,0 +1,77 @@
+#!/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 netmodel.model.key import Key
+from netmodel.model.type import Bool, String, Integer
+from netmodel.model.type import Inet4Address, Inet6Address
+from vicn.core.attribute import Attribute
+from vicn.resource.interface import Interface
+from vicn.resource.lxd.lxc_container import CMD_MOUNT_FOLDER
+from vicn.resource.linux.folder import Folder
+from vicn.core.task import inline_task, BashTask
+from vicn.core.task import inherit_parent
+from vicn.core.exception import ResourceNotFound
+from vicn.core.address_mgr import AddressManager
+
+class MemifDevice(Interface):
+ """
+ Resource: Memory interface device
+
+ A MemifDevice is device build on top of a the memory interface provided by vpp.
+ It uses a unix socket to connect the two vpp-s (one master and one slave).
+ The unix socket must be shared between the two vpp-s.
+ """
+ path_unix_socket = Attribute(String,
+ mandatory = True,
+ description = 'Path to the shared folder holding the unix socket')
+ socket_name = Attribute(String,
+ mandatory = True,
+ description = 'Path to the shared folder holding the unix socket')
+ folder_host = Attribute(Folder,
+ mandatory = True,
+ description = 'Folder in the host to be mounted in the container ih path_unix_socket')
+ master= Attribute(Bool,
+ description = 'True if this interface is connected to the master vpp',
+ default = False)
+ # We need to automatically assign a mac address to the memif so that we can
+ # recreate it after vpp reboots thanks to a config file or bash script.
+ # Just reading the actual value would not work since we need to use an
+ # external script and this mac address would thus not be prevent in the list
+ # of executed commands.
+ mac_address = Attribute(String, description = 'Mac address of the device',
+ default = lambda self: AddressManager().get_mac(self))
+ ip4_address = Attribute(Inet4Address, description = "Device's IP address")
+ ip6_address = Attribute(Inet6Address, description = "Device's IP address")
+ device_name = Attribute(String)
+ key = Attribute(Integer)
+
+ __key__ = Key(folder_host)
+
+ @inline_task
+ def __get__(self):
+ raise ResourceNotFound
+
+ @inherit_parent
+ def __create__(self):
+ return BashTask(self.node.node_with_kernel, CMD_MOUNT_FOLDER, {
+ 'container': self.node,
+ 'device-name': self.socket_name,
+ 'host_path': self.folder_host.foldername,
+ 'container_path': self.path_unix_socket}, output=True,
+ lock=self.node.vpp.memif_lock)
+
diff --git a/vicn/resource/vpp/memif_link.py b/vicn/resource/vpp/memif_link.py
new file mode 100644
index 00000000..62de03c6
--- /dev/null
+++ b/vicn/resource/vpp/memif_link.py
@@ -0,0 +1,144 @@
+#!/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.
+#
+
+import random
+import string
+import logging
+import asyncio
+
+from netmodel.model.type import Integer, String
+from netmodel.model.key import Key
+from vicn.core.attribute import Attribute, Reference
+from vicn.core.exception import ResourceNotFound
+from vicn.core.state import ResourceState, AttributeState
+from vicn.core.task import inline_task, async_task, run_task
+from vicn.core.task import get_attributes_task, BashTask
+from vicn.core.task import inherit_parent
+from vicn.resource.channel import Channel
+from vicn.resource.interface import Interface
+from vicn.resource.linux.net_device import NonTapBaseNetDevice
+from vicn.resource.node import Node
+from vicn.resource.lxd.lxc_container import CMD_MOUNT_FOLDER, LxcContainer
+from vicn.resource.vpp.memif_device import MemifDevice
+from vicn.resource.linux.folder import Folder
+
+# FIXME remove VPP specific code
+from vicn.resource.vpp.interface import VPPInterface
+
+log = logging.getLogger(__name__)
+
+CONTAINER_SOCKET_PATH='/root/{}'
+SHARED_FOLDER_PATH='/tmp/{}'
+
+class MemifLink(Channel):
+ """
+ Resource: MemifLink
+
+ Implements a virtual wired link between containers. It is made
+ with a pair of MemifDevice which are created in the two containers.
+
+ Because of this, the resource only supports passing source and destination
+ containers, and not interfaces. It also explains the relative complexity of
+ the current implementation.
+ """
+
+ src_node = Attribute(LxcContainer, description = 'Source node',
+ mandatory = True)
+ dst_node = Attribute(LxcContainer, description = 'Destination node',
+ mandatory = True)
+ folder = Attribute(Folder, description = 'Shared folder holding the socket used by the MemifDevices')
+ key = Attribute(Integer)
+
+ __key__ = Key(src_node, dst_node)
+
+ def __init__(self, *args, **kwargs):
+ assert 'src_node' in kwargs and 'dst_node' in kwargs
+ self._src = None
+ self._dst = None
+ self._folder = None
+ super().__init__(*args, **kwargs)
+
+ @inherit_parent
+ def __subresources__(self):
+ assert self.src_node.node_with_kernel == self.dst_node.node_with_kernel
+
+ host = self.src_node.node_with_kernel
+ # We create two managed net devices that are pre-setup
+ # but the resource manager has to take over for IP addresses etc.
+ # Being done in initialize, those attributes won't be considered as
+ # dependencies and will thus not block the resource state machine.
+
+ socket_name = 'socket-' + ''.join(random.choice(string.ascii_uppercase
+ + string.digits) for _ in range(5)) +'.file'
+
+ _folder = Folder(node = host,
+ foldername = SHARED_FOLDER_PATH.format(self.src_node.name + '-' + self.dst_node.name),
+ permission = 777)
+
+ self.key = random.randint(0, 2**64)
+
+ self._src = MemifDevice(node = self.src_node,
+ channel = self,
+ owner = self,
+ path_unix_socket = CONTAINER_SOCKET_PATH.format(self.src_node.name + '-'
+ + self.dst_node.name) + '/',
+ folder_host = _folder,
+ socket_name = socket_name,
+ device_name = 'memif-'+self.dst_node.name,
+ key = Reference(self, 'key'),
+ master = False)
+ self._dst = MemifDevice(node = self.dst_node,
+ channel = self,
+ owner = self,
+ path_unix_socket = CONTAINER_SOCKET_PATH.format(self.src_node.name + '-'
+ + self.dst_node.name) + '/',
+ socket_name = socket_name,
+ folder_host = _folder,
+ device_name = 'memif-'+self.src_node.name,
+ key = Reference(self, 'key'),
+ master = True)
+ self._dst.remote = self._src
+ self._src.remote = self._dst
+
+ return _folder | (self._src | self._dst)
+
+
+ #--------------------------------------------------------------------------
+ # Internal methods
+ #--------------------------------------------------------------------------
+
+ async def _commit(self):
+ manager = self._state.manager
+
+ 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,
+ ip4_address = Reference(self._src, 'ip4_address'),
+ ip6_address = Reference(self._src, 'ip6_address'))
+ 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,
+ ip4_address = Reference(self._dst, 'ip4_address'),
+ ip6_address = Reference(self._dst, 'ip6_address'))
+ manager.commit_resource(vpp_dst)
+
+ @inherit_parent
+ def __get__(self):
+ return async_task(self._commit)()
diff --git a/vicn/resource/vpp/scripts.py b/vicn/resource/vpp/scripts.py
index 3a3d5e8f..d5130212 100644
--- a/vicn/resource/vpp/scripts.py
+++ b/vicn/resource/vpp/scripts.py
@@ -282,6 +282,6 @@ api-segment {
'''
APPARMOR_VPP_PROFILE = '''
-lxc.aa_profile = lxc-dpdk
+lxc.aa_profile = lxc-dpdk
lxc.mount.entry = hugetlbfs dev/hugepages hugetlbfs rw,relatime,create=dir 0 0
lxc.mount.auto = sys:rw'''
diff --git a/vicn/resource/vpp/vpp.py b/vicn/resource/vpp/vpp.py
index 9edcfea3..8250f99a 100644
--- a/vicn/resource/vpp/vpp.py
+++ b/vicn/resource/vpp/vpp.py
@@ -23,6 +23,7 @@ from vicn.core.attribute import Attribute, Multiplicity
from vicn.core.exception import ResourceNotFound
from vicn.core.resource import Resource
from vicn.core.task import BashTask, task, inline_task
+from vicn.core.task import inherit_parent, EmptyTask
from vicn.resource.lxd.lxc_container import LxcContainer
from vicn.resource.node import Node
from vicn.resource.linux.application import LinuxApplication
@@ -33,6 +34,7 @@ from vicn.resource.vpp.scripts import TPL_VPP_DPDK_DAEMON_SCRIPT
from vicn.resource.vpp.vpp_commands import CMD_VPP_DISABLE, CMD_VPP_STOP
from vicn.resource.vpp.vpp_commands import CMD_VPP_START
from vicn.resource.vpp.vpp_commands import CMD_VPP_ENABLE_PLUGIN
+from vicn.resource.vpp.vpp_commands import CMD_REMOVE_DPDK_PLUGIN
from vicn.resource.vpp.vpp_host import VPPHost
#------------------------------------------------------------------------------
@@ -50,7 +52,7 @@ class VPP(LinuxApplication):
start and stop commands
"""
- __package_names__ = ['vpp', 'vpp-dbg', 'vpp-dpdk-dev']
+ __package_names__ = ['vpp', 'vpp-dpdk-dev']
plugins = Attribute(String,
multiplicity = Multiplicity.OneToMany)
@@ -72,12 +74,15 @@ class VPP(LinuxApplication):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
- self.vppctl_lock = asyncio.Lock()
+ self.vppctl_lock = asyncio.Lock()
+
+ # needed by "lxc config device add"
+ self.memif_lock = asyncio.Lock()
- self.dpdk_setup_file = None
if isinstance(self.node, LxcContainer):
if not 'vpp' in self.node.profiles:
self.node.profiles.append('vpp')
+ self.dpdk_setup_file = None
#--------------------------------------------------------------------------
# Resource lifecycle
@@ -86,16 +91,14 @@ class VPP(LinuxApplication):
def __after__(self):
return ['BaseNetDevice']
+ @inherit_parent
+ @task
def __get__(self):
+ raise ResourceNotFound
return BashTask(self.node, CMD_GET)
+ @inherit_parent
def __subresources__(self):
- self.dpdk_setup_file = TextFile(node = self.node,
- filename = FN_VPP_DPDK_SCRIPT,
- overwrite = True)
- return self.dpdk_setup_file
-
- def __create__(self):
socket_mem = dict()
numa_mgr = self.node.node_with_kernel.numa_mgr
@@ -117,25 +120,6 @@ class VPP(LinuxApplication):
self.numa_node, self.core = \
numa_mgr.get_numa_core(numa_node = self.numa_node)
- dpdk_list = list()
-
- # On numa architecture socket-mem requires to set the amount of memory
- # to be reserved on each numa node
- socket_mem_str = 'socket-mem '
- for numa in range (0,numa_mgr.get_number_of_numa()):
- if numa in socket_mem:
- socket_mem_str = socket_mem_str + str(socket_mem[numa])
- else:
- socket_mem_str = socket_mem_str + '0'
-
- if numa < numa_mgr.get_number_of_numa()-1:
- socket_mem_str = socket_mem_str + ','
-
- dpdk_list.append(socket_mem_str)
-
- for interface in self.node.interfaces:
- if isinstance(interface, DpdkDevice):
- dpdk_list.append('dev ' + interface.pci_address)
# Add the core on which running vpp and the dpdk parameters
setup = TPL_VPP_DPDK_DAEMON_SCRIPT + 'cpu {'
@@ -146,19 +130,42 @@ class VPP(LinuxApplication):
self.numa_node, cpu_worker =numa_mgr.get_numa_core(self.numa_node)
setup = setup + '''\n corelist-workers ''' + str(cpu_worker)
- setup = setup + '''\n}\n\n dpdk { '''
-
- for dpdk_dev in dpdk_list:
- setup = setup + ''' \n ''' + dpdk_dev
-
- setup = setup + '\n}'
+ dpdk_list = list()
+ for interface in self.node.interfaces:
+ if isinstance(interface, DpdkDevice):
+ dpdk_list.append('dev ' + interface.pci_address)
+ setup = setup + '''\n}\n\n'''
+ if dpdk_list:
+ setup = setup + 'dpdk {'
+ # add socket_mem
+ # On numa architecture socket-mem requires to set the amount of memory
+ # to be reserved on each numa node
+ socket_mem_str = 'socket-mem '
+ for numa in range (0,numa_mgr.get_number_of_numa()):
+ if numa in socket_mem:
+ socket_mem_str = socket_mem_str + str(socket_mem[numa])
+ else:
+ socket_mem_str = socket_mem_str + '0'
+
+ if numa < numa_mgr.get_number_of_numa()-1:
+ socket_mem_str = socket_mem_str + ','
+
+ dpdk_list.append(socket_mem_str)
+
+ for dpdk_dev in dpdk_list:
+ setup = setup + ''' \n ''' + dpdk_dev
+ setup = setup + '''\n}'''
+
+ dpdk_setup_file = TextFile(node = self.node,
+ filename = FN_VPP_DPDK_SCRIPT,
+ content = setup,
+ overwrite = True)
- if any([isinstance(interface,DpdkDevice) for interface in self.node.interfaces]):
- self.dpdk_setup_file.content = setup
- else:
- self.dpdk_setup_file.content = TPL_VPP_DPDK_DAEMON_SCRIPT
+ return dpdk_setup_file
+ @inherit_parent
+ def __create__(self):
lock = self.node.node_with_kernel.vpp_host.vppstart_lock
vpp_disable = BashTask(self.node, CMD_VPP_DISABLE, lock = lock)
@@ -166,8 +173,19 @@ class VPP(LinuxApplication):
enable_ip_forward = BashTask(self.node, CMD_DISABLE_IP_FORWARD)
start_vpp = BashTask(self.node, CMD_VPP_START, lock = lock)
- return ((vpp_disable > vpp_stop) | enable_ip_forward) > start_vpp
+ found = False
+ for iface in self.interfaces:
+ if isinstance(iface.parent, DpdkDevice):
+ found = True
+ break
+
+ remove_dpdk_plugin = EmptyTask()
+ if (not found):
+ remove_dpdk_plugin = BashTask(self.node, CMD_REMOVE_DPDK_PLUGIN, lock = lock)
+
+ return (((vpp_disable > vpp_stop) | enable_ip_forward) > (remove_dpdk_plugin > start_vpp))
+ @inherit_parent
def __delete__(self):
return BashTask(self.node, CMD_VPP_STOP)
diff --git a/vicn/resource/vpp/vpp_bridge.py b/vicn/resource/vpp/vpp_bridge.py
index 612145d9..53523c17 100644
--- a/vicn/resource/vpp/vpp_bridge.py
+++ b/vicn/resource/vpp/vpp_bridge.py
@@ -24,6 +24,7 @@ from vicn.core.requirement import Requirement
from vicn.core.resource_mgr import wait_resource_task
from vicn.core.resource import Resource
from vicn.core.task import task, BashTask, EmptyTask
+from vicn.core.task import inherit_parent
from vicn.resource.channel import Channel
from vicn.resource.linux.application import LinuxApplication
from vicn.resource.linux.sym_veth_pair import SymVethPair
@@ -33,7 +34,7 @@ from vicn.resource.vpp.dpdk_device import DpdkDevice
from vicn.resource.vpp.interface import VPPInterface
from vicn.resource.vpp.vpp import VPP
-CMD_ADD_INTERFACE_TO_BR = ('vppctl set interface l2 bridge '
+CMD_ADD_INTERFACE_TO_BR = ('vppctl_wrapper set interface l2 bridge '
'{interface.device_name} {br_domain}')
class VPPBridge(Channel, LinuxApplication):
@@ -69,6 +70,7 @@ class VPPBridge(Channel, LinuxApplication):
# Resource lifecycle
#--------------------------------------------------------------------------
+ @inherit_parent
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
@@ -77,12 +79,14 @@ class VPPBridge(Channel, LinuxApplication):
return Resource.__concurrent__(*self._veths)
+ @inherit_parent
@task
def __initialize__ (self):
# 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])
+ @inherit_parent
@task
def __get__(self):
# Forces creation
@@ -91,6 +95,7 @@ class VPPBridge(Channel, LinuxApplication):
# Nothing to do
__delete__ = None
+ @inherit_parent
def __create__(self):
manager = self._state.manager
diff --git a/vicn/resource/vpp/vpp_commands.py b/vicn/resource/vpp/vpp_commands.py
index 40315c19..30898eae 100644
--- a/vicn/resource/vpp/vpp_commands.py
+++ b/vicn/resource/vpp/vpp_commands.py
@@ -5,37 +5,41 @@ CMD_VPP_DISABLE = 'systemctl disable vpp.service'
# 'sleep 1' ensures that VPP has enough time to start
CMD_VPP_START = '''
-systemctl start vpp
-sleep 1
+flock /tmp/vppctl.lock -c "systemctl start vpp"
'''
CMD_VPP_STOP = '''
-systemctl stop vpp
-killall -9 vpp_main || true
+flock /tmp/vppctl.lock -c "systemctl stop vpp"
+'''
+#killall -9 vpp_main || true
+CMD_VPP_ENABLE_PLUGIN = 'vppctl_wrapper {plugin} control start'
+
+CMD_REMOVE_DPDK_PLUGIN = '''
+rm /usr/lib/vpp_api_test_plugins/dpdk_test_plugin.so
+rm /usr/lib/vpp_plugins/dpdk_plugin.so
'''
-CMD_VPP_ENABLE_PLUGIN = 'vppctl {plugin} enable'
##### VPP INTERFACES #####
CMD_VPP_CREATE_IFACE = '''
-# Create vpp interface from netmodel.network.interface.device_name} with mac {self.parent.mac_address}
-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
+# Create vpp interface from {vpp_interface.parent.device_name} with mac {vpp_interface.parent.mac_address}
+vppctl_wrapper create host-interface name {vpp_interface.parent.device_name} hw-addr {vpp_interface.parent.mac_address}
+vppctl_wrapper set interface state {vpp_interface.device_name} up
'''
-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 #####
-CMD_VPP_ADD_ROUTE = 'vppctl set ip arp static {route.interface.vppinterface.device_name} {route.ip_address} {route.mac_address}'
-CMD_VPP_DEL_ROUTE = 'vppctl set ip arp del static {route.interface.vppinterface.device_name} {route.ip_address} {route.mac_address}'
-CMD_VPP_ADD_ROUTE_GW = 'vppctl ip route add {route.ip_address}/32 via {route.gateway} {route.interface.vppinterface.device_name}'
-CMD_VPP_DEL_ROUTE_GW = 'vppctl ip route del {route.ip_address}/32 via {route.gateway} {route.interface.vppinterface.device_name}'
-
-##### VPP CICN PLUGIN #####
+# It is important to pass the mac address so that it does not get randomly
+# generated by VPP, preventing any reboot of VPP and recreation of commands
+CMD_VPP_CREATE_MEMIFACE = '''
+# Create vpp interface from shared_memory
+vppctl_wrapper create memif key {key} socket {vpp_interface.parent.path_unix_socket}{vpp_interface.parent.socket_name} hw-addr {vpp_interface.parent.mac_address} {master_slave}
+'''
+CMD_VPP_SET_IP = 'vppctl_wrapper set int ip address {device_name} {ip_address}/{prefix_len}'
+CMD_VPP_SET_UP = 'vppctl_wrapper set int state {netdevice.device_name} {state}'
-CMD_VPP_CICN_GET = "timeout 1 vppctl cicn show" #We timeout if vpp is not started
-CMD_VPP_ADD_ICN_ROUTE = 'vppctl cicn cfg fib add prefix {route.prefix} face {route.face.id}'
-CMD_VPP_ADD_ICN_FACE = 'vppctl cicn cfg face add local {face.src_ip}:{face.src_port} remote {face.dst_ip}:{face.dst_port}'
+##### VPP IP ROUTING #####
-CMD_VPP_CICN_GET_CACHE_SIZE = 'vppctl cicn show | grep "CS entries" | grep -Eo "[0-9]+"'
-CMD_VPP_CICN_SET_CACHE_SIZE = 'vppctl cicn control param cs size {self.cache_size}'
+CMD_VPP_ADD_ARP = 'vppctl_wrapper set ip arp static {route.interface.vppinterface.device_name} {route.ip_address} {route.mac_address}'
+CMD_VPP_DEL_ARP = 'vppctl_wrapper set ip arp del static {route.interface.vppinterface.device_name} {route.ip_address} {route.mac_address}'
+CMD_VPP_ADD_ROUTE = 'vppctl_wrapper ip route add {route.ip_address}/{route.ip_address.prefix_len} via {route.interface.vppinterface.device_name}'
+CMD_VPP_DEL_ROUTE = 'vppctl_wrapper ip route del {route.ip_address}/{route.ip_address.prefix_len} via {route.interface.vppinterface.device_name}'
+CMD_VPP_ADD_ROUTE_GW = 'vppctl_wrapper ip route add {route.ip_address}/{route.ip_address.prefix_len} via {route.gateway} {route.interface.vppinterface.device_name}'
+CMD_VPP_DEL_ROUTE_GW = 'vppctl_wrapper ip route del {route.ip_address}/{route.ip_address.prefix_len} via {route.gateway} {route.interface.vppinterface.device_name}'
diff --git a/vicn/resource/vpp/vpp_host.py b/vicn/resource/vpp/vpp_host.py
index 954d1d32..29094451 100644
--- a/vicn/resource/vpp/vpp_host.py
+++ b/vicn/resource/vpp/vpp_host.py
@@ -23,6 +23,7 @@ from vicn.core.attribute import Attribute, Multiplicity
from vicn.core.exception import ResourceNotFound
from vicn.core.requirement import Requirement
from vicn.core.task import BashTask, task, EmptyTask
+from vicn.core.task import inherit_parent
from vicn.resource.linux.application import LinuxApplication
from vicn.resource.linux.file import TextFile
from vicn.resource.node import Node
@@ -41,7 +42,7 @@ CMD_APP_ARMOR_RELOAD = '''
CMD_SYSCTL_HUGEPAGES = 'sysctl -w vm.nr_hugepages={nb_hp}'
DEFAULT_NB_HUGEPAGES = 1024
CMD_GREP_UIO_DEV = 'ls /dev | grep uio'
-CMD_CREATE_UIO_DEVICES = "dpdk_nic_bind --bind=igb_uio {pci_address}"
+CMD_CREATE_UIO_DEVICES = "dpdk-devbind --bind=igb_uio {pci_address}"
class VPPHost(LinuxApplication):
"""
@@ -75,7 +76,7 @@ class VPPHost(LinuxApplication):
description = 'Dpdk devices on the node',
multiplicity = Multiplicity.OneToMany)
- __package_names__ = ['dpdk', 'vpp-dpdk-dkms']
+ __package_names__ = ['vpp-dpdk-dkms', 'vpp-dpdk-dev']
#--------------------------------------------------------------------------
# Constructor and Accessors
@@ -89,6 +90,7 @@ class VPPHost(LinuxApplication):
# Resource lifecycle
#--------------------------------------------------------------------------
+ @inherit_parent
def __subresources__(self):
app_armor_file = TextFile(node = self.node,
filename = FN_APPARMOR_DPDK_SCRIPT,
@@ -100,6 +102,7 @@ class VPPHost(LinuxApplication):
overwrite = True)
return app_armor_file | startup_conf
+ @inherit_parent
@task
def __get__(self):
"""
@@ -108,6 +111,7 @@ class VPPHost(LinuxApplication):
"""
raise ResourceNotFound
+ @inherit_parent
def __create__(self):
modules = BashTask(self.node, CMD_INSERT_MODULES)
app_armor_reload = BashTask(self.node, CMD_APP_ARMOR_RELOAD)
@@ -132,8 +136,6 @@ class VPPHost(LinuxApplication):
return ((modules | app_armor_reload) | sysctl_hugepages) > \
(disable_vpp > create_uio)
- __delete__ = None
-
#--------------------------------------------------------------------------
# Attributes
#--------------------------------------------------------------------------