#!/usr/bin/env python3
import unittest
from scapy.layers.l2 import Ether
from framework import VppTestCase
from asfframework import VppTestRunner
from util import Host
class TestL2LearnLimit(VppTestCase):
"""L2 Learn no limit Test Case"""
@classmethod
def setUpClass(self):
super(TestL2LearnLimit, self).setUpClass()
self.create_pg_interfaces(range(3))
@classmethod
def tearDownClass(cls):
super(TestL2LearnLimit, cls).tearDownClass()
def create_hosts(self, pg_if, n_hosts_per_if
@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
#!/usr/bin/env python3
import inspect
import os
import reprlib
import unittest
from framework import VppTestCase
from multiprocessing import Process, Pipe
from pickle import dumps
from enum import IntEnum, IntFlag
class SerializableClassCopy:
"""
Empty class used as a basis for a serializable copy of another class.
"""
pass
def __repr__(self):
return "<SerializableClassCopy dict=%s>" % self.__dict__
class RemoteClassAttr:
"""
Wrapper around attribute of a remotely executed class.
"""
def __init__(self, remote, attr):
self._path = [attr] if attr else []
self._remote = remote
def path_to_str(self):
return ".".join(self._path)
def get_remote_value(self):
return self._remote._remote_exec(RemoteClass.GET, self.path_to_str())
def __repr__(self):
return self._remote._remote_exec(RemoteClass.REPR, self.path_to_str())
def __str__(self):
return self._remote._remote_exec(RemoteClass.STR, self.path_to_str())
def __getattr__(self, attr):
if attr[0] == "_":
if not (attr.startswith("__") and attr.endswith("__")):
raise AttributeError("tried to get private attribute: %s ", attr)
self._path.append(attr)
return self
def __setattr__(self, attr, val):
if attr[0] == "_":
if not (attr.startswith("__") and attr.endswith("__")):
super(RemoteClassAttr, self).__setattr__(attr, val)
return
self._path.append(attr)
self._remote._remote_exec(RemoteClass.SETATTR, self.path_to_str(), value=val)
def __call__(self, *args, **kwargs):
return self._remote._remote_exec(
RemoteClass.CALL, self.path_to_str(), *args, **kwargs
)
class RemoteClass(Process):
"""
This class can wrap around and adapt the interface of another class,
and then delegate its execution to a newly forked child process.
Usage:
#. Create a remotely executed instance of MyClass. ::
object = RemoteClass(MyClass, arg1='foo', arg2='bar')
object.start_remote()
#. Access the object normally as if it was an instance of your
class. ::
object.my_attribute = 20
print object.my_attribute
print object.my_method(object.my_attribute)
object.my_attribute.nested_attribute = 'test'
#. If you need the value of a remote attribute, use .get_remote_value
method. This method is automatically called when needed in the
context of a remotely executed class. E.g. ::
if (object.my_attribute.get_remote_value() > 20):
object.my_attribute2 = object.my_attribute
#. Destroy the instance. ::
object.quit_remote()
object.terminate()
"""
GET = 0 # Get attribute remotely
CALL = 1 # Call method remotely
SETATTR = 2 # Set attribute remotely
REPR = 3 # Get representation of a remote object
STR = 4 # Get string representation of a remote object
QUIT = 5 # Quit remote execution
PIPE_PARENT = 0 # Parent end of the pipe
PIPE_CHILD = 1 # Child end of the pipe
DEFAULT_TIMEOUT = 2 # default timeout for an operation to execute
def __init__(self, cls, *args, **kwargs):
super(RemoteClass, self).__init__()
self._cls = cls
self._args = args
self._kwargs = kwargs
self._timeout = RemoteClass.DEFAULT_TIMEOUT
self._pipe = Pipe() # pipe for input/output arguments
def __repr__(self):
return reprlib.repr(RemoteClassAttr(self, None))
def __str__(self):
return str(RemoteClassAttr(self, None))
def __call__(self, *args, **kwargs):
return self.RemoteClassAttr(self, None)()
def __getattr__(self, attr):
if attr[0] == "_" or not self.is_alive():
if not (attr.startswith("__") and attr.endswith("__")):
if hasattr(super(RemoteClass, self), "__getattr__"):
return super(RemoteClass, self).__getattr__(attr)
raise AttributeError("missing: %s", attr)
return RemoteClassAttr(self, attr)
def __setattr__(self, attr, val):
if attr[0] == "_" or not self.is_alive():
if not (attr.startswith("__") and attr.endswith("__")):
super(RemoteClass, self).__setattr__(attr, val)
return
setattr(RemoteClassAttr(self, None), attr, val)
def _remote_exec(self, op, path=None, *args, **kwargs):
"""
Execute given operation on a given, possibly nested, member remotely.
"""
# automatically resolve remote objects in the arguments
mutable_args = list(args)
for i, val in enumerate(mutable_args):
if isinstance(val, RemoteClass) or isinstance(val, RemoteClassAttr):
mutable_args[i] = val.get_remote_value()
args = tuple(mutable_args)
for key, val in kwargs.items():
if isinstance(val, RemoteClass) or isinstance(val, RemoteClassAttr):
kwargs[key] = val.get_remote_value()
# send request
args = self._make_serializable(args)
kwargs = self._make_serializable(kwargs)
self._pipe[RemoteClass.PIPE_PARENT].send((op, path, args, kwargs))
timeout = self._timeout
# adjust timeout specifically for the .sleep method
if path is not None and path.split(".")[-1] == "sleep":
if args and isinstance(args[0], (long, int)):
timeout += args[0]
elif "timeout" in kwargs:
timeout += kwargs["timeout"]
if not self._pipe[RemoteClass.PIPE_PARENT].poll(timeout):
return None
try:
rv = self._pipe[RemoteClass.PIPE_PARENT].recv()
rv = self._deserialize(rv)
return rv
except EOFError:
return None
def _get_local_object(self, path):
"""
Follow the path to obtain a reference on the addressed nested attribute
"""
obj = self._instance
for attr in path:
obj = getattr(obj, attr)
return obj
def _get_local_value(self, path):
try:
return self._get_local_object(path)
except AttributeError:
return None
def _call_local_method(self, path, *args, **kwargs):
try:
method = self._get_local_object(path)
return method(*args, **kwargs)
except AttributeError:
return None
def _set_local_attr(self, path, value):
try:
obj = self._get_local_object(path[:-1])
setattr(obj, path[-1], value)
except AttributeError:
pass
return None
def _get_local_repr(self, path):
try:
obj = self._get_local_object(path)
return reprlib.repr(obj)
except AttributeError:
return None
def _get_local_str(self, path):
try:
obj = self._get_local_object(path)
return str(obj)
except AttributeError:
return None
def _serializable(self, obj):
"""Test if the given object is serializable"""
try:
dumps(obj)
return True
except:
return False
def _make_obj_serializable(self, obj):
"""
Make a serializable copy of an object.
Members which are difficult/impossible to serialize are stripped.
"""
if self._serializable(obj):
return obj # already serializable
copy = SerializableClassCopy()
"""
Dictionaries can hold complex values, so we split keys and values into
separate lists and serialize them individually.
"""
if type(obj) is dict:
copy.type = type(obj)
copy.k_list = list()
copy.v_list = list()
for k, v in obj.items():
copy.k_list.append(self._make_serializable(k))
copy.v_list.append(self._make_serializable(v))
return copy
# copy at least serializable attributes and properties
for name, member in inspect.getmembers(obj):
# skip private members and non-writable dunder methods.
if name[0] == "_":
if name in ["__weakref__"]:
continue
if name in ["__dict__"]:
continue
if not (name.startswith("__") and name.endswith("__")):
continue
if callable(member) and not isinstance(member, property):
continue
if not self._serializable(member):
member = self._make_serializable(member)
setattr(copy, name, member)
return copy
def _make_serializable(self, obj):
"""
Make a serializable copy of an object or a list/tuple of objects.
Members which are difficult/impossible to serialize are stripped.
"""
if (type(obj) is list) or (type(obj) is tuple):
rv = []
for item in obj:
rv.append(self._make_serializable(item))
if type(obj) is tuple:
rv = tuple(rv)
return rv
elif isinstance(obj, IntEnum) or isinstance(obj, IntFlag):
return obj.value
else:
return self._make_obj_serializable(obj)
def _deserialize_obj(self, obj):
if hasattr(obj, "type"):
if obj.type is dict:
_obj = dict()
for k, v in zip(obj.k_list, obj.v_list):
_obj[self._deserialize(k)] = self._deserialize(v)
return _obj
return obj
def _deserialize(self, obj):
if (type(obj) is list) or (type(obj) is tuple):
rv = []
for item in obj:
rv.append(self._deserialize(item))
if type(obj) is tuple:
rv = tuple(rv)
return rv
else:
return self._deserialize_obj(obj)
def start_remote(self):
"""Start remote execution"""
self.start()
def quit_remote(self):
"""Quit remote execution"""
self._remote_exec(RemoteClass.QUIT, None)
def get_remote_value(self):
"""Get value of a remotely held object"""
return RemoteClassAttr(self, None).get_remote_value()
def set_request_timeout(self, timeout):
"""Change request timeout"""
self._timeout = timeout
def run(self):
"""
Create instance of the wrapped class and execute operations
on it as requested by the parent process.
"""
self._instance = self._cls(*self._args, **self._kwargs)
while True:
try:
rv = None
# get request from the parent process
(op, path, args, kwargs) = self._pipe[RemoteClass.PIPE_CHILD].recv()
args = self._deserialize(args)
kwargs = self._deserialize(kwargs)
path = path.split(".") if path else []
if op == RemoteClass.GET:
rv = self._get_local_value(path)
elif op == RemoteClass.CALL:
rv = self._call_local_method(path, *args, **kwargs)
elif op == RemoteClass.SETATTR and "value" in kwargs:
self._set_local_attr(path, kwargs["value"])
elif op == RemoteClass.REPR:
rv = self._get_local_repr(path)
elif op == RemoteClass.STR:
rv = self._get_local_str(path)
elif op == RemoteClass.QUIT:
break
else:
continue
# send return value
if not self._serializable(rv):
rv = self._make_serializable(rv)
self._pipe[RemoteClass.PIPE_CHILD].send(rv)
except EOFError:
break
self._instance = None # destroy the instance
@unittest.skip("Remote Vpp Test Case Class")
class RemoteVppTestCase(VppTestCase):
"""Re-use VppTestCase to create remote VPP segment
In your test case::
@classmethod
def setUpClass(cls):
# fork new process before client connects to VPP
cls.remote_test = RemoteClass(RemoteVppTestCase)
# start remote process
cls.remote_test.start_remote()
# set up your test case
super(MyTestCase, cls).setUpClass()
# set up remote test
cls.remote_test.setUpClass(cls.tempdir)
@classmethod
def tearDownClass(cls):
# tear down remote test
cls.remote_test.tearDownClass()
# stop remote process
cls.remote_test.quit_remote()
# tear down your test case
super(MyTestCase, cls).tearDownClass()
"""
def __init__(self):
super(RemoteVppTestCase, self).__init__("emptyTest")
# Note: __del__ is a 'Finalizer" not a 'Destructor'.
# https://docs.python.org/3/reference/datamodel.html#object.__del__
def __del__(self):
if hasattr(self, "vpp"):
self.vpp.poll()
if self.vpp.returncode is None:
self.vpp.terminate()
self.vpp.communicate()
@classmethod
def setUpClass(cls, tempdir):
# disable features unsupported in remote VPP
orig_env = dict(os.environ)
if "STEP" in os.environ:
del os.environ["STEP"]
if "DEBUG" in os.environ:
del os.environ["DEBUG"]
cls.tempdir_prefix = os.path.basename(tempdir) + "/"
super(RemoteVppTestCase, cls).setUpClass()
os.environ = orig_env
@classmethod
def tearDownClass(cls):
super(RemoteVppTestCase, cls).tearDownClass()
@unittest.skip("Empty test")
def emptyTest(self):
"""Do nothing"""
pass
def setTestFunctionInfo(self, name, doc):
"""
Store the name and documentation string of currently executed test
in the main VPP for logging purposes.
"""
self._testMethodName = name
self._testMethodDoc = doc