├── .coveragerc ├── .github └── workflows │ └── pythonpublish.yml ├── .gitignore ├── .scrutinizer.yml ├── .travis.yml ├── AUTHORS.rst ├── CHANGELOG.rst ├── LICENSE ├── Makefile ├── README.rst ├── docs ├── .gitignore ├── AUTHORS.rst ├── LICENSE.rst ├── Makefile ├── _static │ └── .keep ├── conf.py ├── examples │ ├── index.rst │ ├── packing.rst │ └── unpacking.rst ├── index.rst └── references.rst ├── pyof ├── __init__.py ├── foundation │ ├── __init__.py │ ├── base.py │ ├── basic_types.py │ ├── constants.py │ ├── exceptions.py │ └── network_types.py ├── utils.py ├── v0x01 │ ├── __init__.py │ ├── asynchronous │ │ ├── __init__.py │ │ ├── error_msg.py │ │ ├── flow_removed.py │ │ ├── packet_in.py │ │ └── port_status.py │ ├── common │ │ ├── __init__.py │ │ ├── action.py │ │ ├── constants.py │ │ ├── flow_match.py │ │ ├── header.py │ │ ├── phy_port.py │ │ ├── queue.py │ │ └── utils.py │ ├── controller2switch │ │ ├── __init__.py │ │ ├── barrier_reply.py │ │ ├── barrier_request.py │ │ ├── common.py │ │ ├── features_reply.py │ │ ├── features_request.py │ │ ├── flow_mod.py │ │ ├── get_config_reply.py │ │ ├── get_config_request.py │ │ ├── packet_out.py │ │ ├── port_mod.py │ │ ├── queue_get_config_reply.py │ │ ├── queue_get_config_request.py │ │ ├── set_config.py │ │ ├── stats_reply.py │ │ └── stats_request.py │ └── symmetric │ │ ├── __init__.py │ │ ├── echo_reply.py │ │ ├── echo_request.py │ │ ├── hello.py │ │ └── vendor_header.py └── v0x04 │ ├── __init__.py │ ├── asynchronous │ ├── __init__.py │ ├── error_msg.py │ ├── flow_removed.py │ ├── packet_in.py │ └── port_status.py │ ├── common │ ├── __init__.py │ ├── action.py │ ├── constants.py │ ├── flow_instructions.py │ ├── flow_match.py │ ├── header.py │ ├── port.py │ ├── queue.py │ └── utils.py │ ├── controller2switch │ ├── __init__.py │ ├── barrier_reply.py │ ├── barrier_request.py │ ├── common.py │ ├── features_reply.py │ ├── features_request.py │ ├── flow_mod.py │ ├── get_async_reply.py │ ├── get_async_request.py │ ├── get_config_reply.py │ ├── get_config_request.py │ ├── group_mod.py │ ├── meter_mod.py │ ├── multipart_reply.py │ ├── multipart_request.py │ ├── packet_out.py │ ├── port_mod.py │ ├── queue_get_config_reply.py │ ├── queue_get_config_request.py │ ├── role_reply.py │ ├── role_request.py │ ├── set_async.py │ ├── set_config.py │ └── table_mod.py │ └── symmetric │ ├── __init__.py │ ├── echo_reply.py │ ├── echo_request.py │ ├── experimenter.py │ └── hello.py ├── raw ├── README.rst ├── v0x01 │ ├── ofpt_aggregate_stats_reply.dat │ ├── ofpt_aggregate_stats_request.dat │ ├── ofpt_barrier_reply.dat │ ├── ofpt_barrier_request.dat │ ├── ofpt_desc_stats_reply.dat │ ├── ofpt_echo_reply.dat │ ├── ofpt_echo_request.dat │ ├── ofpt_error_msg.dat │ ├── ofpt_features_reply.dat │ ├── ofpt_features_request.dat │ ├── ofpt_flow_add.dat │ ├── ofpt_flow_delete.dat │ ├── ofpt_flow_removed.dat │ ├── ofpt_flow_stats_reply.dat │ ├── ofpt_flow_stats_request.dat │ ├── ofpt_get_config_reply.dat │ ├── ofpt_get_config_request.dat │ ├── ofpt_hello.dat │ ├── ofpt_packet_in.dat │ ├── ofpt_packet_out.dat │ ├── ofpt_port_mod.dat │ ├── ofpt_port_stats.dat │ ├── ofpt_port_stats_request.dat │ ├── ofpt_port_status.dat │ ├── ofpt_queue_get_config_reply.dat │ ├── ofpt_queue_get_config_request.dat │ ├── ofpt_queue_stats.dat │ ├── ofpt_queue_stats_request.dat │ ├── ofpt_set_config.dat │ ├── ofpt_stats_reply.dat │ ├── ofpt_stats_request.dat │ ├── ofpt_table_stats.dat │ ├── ofpt_vendor_header.dat │ └── ofpt_vendor_stats_reply.dat └── v0x04 │ ├── ofpt_aggregate_stats.dat │ ├── ofpt_aggregate_stats_request.dat │ ├── ofpt_barrier_reply.dat │ ├── ofpt_barrier_request.dat │ ├── ofpt_echo_reply.dat │ ├── ofpt_echo_request.dat │ ├── ofpt_error.dat │ ├── ofpt_features_reply.dat │ ├── ofpt_features_request.dat │ ├── ofpt_flow_mod.dat │ ├── ofpt_flow_removed.dat │ ├── ofpt_flow_stats.dat │ ├── ofpt_flow_stats_request.dat │ ├── ofpt_group_stats.dat │ ├── ofpt_group_stats_request.dat │ ├── ofpt_hello.dat │ ├── ofpt_meter_multipart_request.dat │ ├── ofpt_packet_in.dat │ ├── ofpt_packet_out.dat │ ├── ofpt_port_desc.dat │ ├── ofpt_port_status.dat │ └── ofpt_set_config.dat ├── requirements ├── dev.in └── dev.txt ├── setup.cfg ├── setup.py ├── tests ├── __init__.py └── unit │ ├── __init__.py │ ├── raw_dump.py │ ├── test_class_inheritance │ ├── __init__.py │ └── test_inheritance.py │ ├── test_foundation │ ├── __init__.py │ ├── test_base.py │ ├── test_basic_types.py │ └── test_network_types.py │ ├── test_struct.py │ ├── test_utils.py │ ├── v0x01 │ ├── __init__.py │ ├── test_asynchronous │ │ ├── __init__.py │ │ ├── test_error_msg.py │ │ ├── test_flow_removed.py │ │ ├── test_packet_in.py │ │ └── test_port_status.py │ ├── test_common │ │ ├── __init__.py │ │ ├── test_action.py │ │ ├── test_flow_match.py │ │ ├── test_header.py │ │ ├── test_phy_port.py │ │ └── test_queue.py │ ├── test_controller2switch │ │ ├── __init__.py │ │ ├── test_aggregate_stats_reply.py │ │ ├── test_aggregate_stats_request.py │ │ ├── test_barrier_reply.py │ │ ├── test_barrier_request.py │ │ ├── test_desc_stats.py │ │ ├── test_features_reply.py │ │ ├── test_features_request.py │ │ ├── test_flow_mod.py │ │ ├── test_flow_stats.py │ │ ├── test_flow_stats_request.py │ │ ├── test_get_config_reply.py │ │ ├── test_get_config_request.py │ │ ├── test_packet_out.py │ │ ├── test_port_mod.py │ │ ├── test_port_stats.py │ │ ├── test_port_stats_request.py │ │ ├── test_queue_get_config_reply.py │ │ ├── test_queue_get_config_request.py │ │ ├── test_queue_stats.py │ │ ├── test_queue_stats_request.py │ │ ├── test_set_config.py │ │ ├── test_stats_reply.py │ │ ├── test_stats_request.py │ │ ├── test_table_stats.py │ │ └── test_vendor_stats.py │ └── test_symmetric │ │ ├── __init__.py │ │ ├── test_echo_reply.py │ │ ├── test_echo_request.py │ │ ├── test_hello.py │ │ └── test_vendor_header.py │ └── v0x04 │ ├── __init__.py │ ├── test_asynchronous │ ├── __init__.py │ ├── test_error_msg.py │ ├── test_flow_removed.py │ ├── test_packet_in.py │ └── test_port_status.py │ ├── test_common │ ├── __init__.py │ ├── test_flow_match.py │ ├── test_port.py │ └── test_queue.py │ ├── test_controller2switch │ ├── __init__.py │ ├── test_aggregate_stats.py │ ├── test_aggregate_stats_request.py │ ├── test_barrier_reply.py │ ├── test_barrier_request.py │ ├── test_features_reply.py │ ├── test_features_request.py │ ├── test_flow_mod.py │ ├── test_flow_stats.py │ ├── test_flow_stats_request.py │ ├── test_get_async_reply.py │ ├── test_get_async_request.py │ ├── test_get_config_reply.py │ ├── test_get_config_request.py │ ├── test_group_mod.py │ ├── test_group_stats.py │ ├── test_group_stats_request.py │ ├── test_meter_mod.py │ ├── test_meter_multipart_request.py │ ├── test_multipart_reply.py │ ├── test_multipart_request.py │ ├── test_packet_out.py │ ├── test_port_desc.py │ ├── test_port_mod.py │ ├── test_port_stats.py │ ├── test_port_stats_request.py │ ├── test_queue_get_config_reply.py │ ├── test_queue_get_config_request.py │ ├── test_queue_stats.py │ ├── test_queue_stats_request.py │ ├── test_role_reply.py │ ├── test_role_request.py │ ├── test_set_async.py │ ├── test_set_config.py │ ├── test_table_mod.py │ └── test_table_stats.py │ ├── test_struct.py │ └── test_symmetric │ ├── __init__.py │ ├── test_echo_reply.py │ ├── test_echo_request.py │ ├── test_hello.py │ └── test_vendor_header.py └── tox.ini /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source = pyof 3 | omit = .eggs/*,.tox/*,*tests*,setup.py 4 | -------------------------------------------------------------------------------- /.github/workflows/pythonpublish.yml: -------------------------------------------------------------------------------- 1 | name: Upload Python Package 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | deploy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Set up Python 13 | uses: actions/setup-python@v1 14 | with: 15 | python-version: '3.6' 16 | - name: Install dependencies 17 | run: | 18 | python -m pip install --upgrade pip 19 | pip install setuptools wheel twine 20 | - name: Build and publish 21 | env: 22 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} 23 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 24 | run: | 25 | python setup.py sdist bdist_wheel 26 | twine upload dist/* 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | *.egg-info 7 | # C extensions 8 | *.so 9 | 10 | *.swp 11 | 12 | 13 | # Distribution / packaging 14 | .Python 15 | env/ 16 | build/ 17 | develop-eggs/ 18 | dist/ 19 | downloads/ 20 | eggs/ 21 | .eggs/ 22 | lib/ 23 | lib64/ 24 | parts/ 25 | sdist/ 26 | var/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *,cover 50 | .hypothesis/ 51 | 52 | # Translations 53 | *.mo 54 | *.pot 55 | 56 | # Django stuff: 57 | *.log 58 | local_settings.py 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | docs/pyof*.rst 70 | 71 | # PyBuilder 72 | target/ 73 | 74 | # IPython Notebook 75 | .ipynb_checkpoints 76 | 77 | # pyenv 78 | .python-version 79 | 80 | # celery beat schedule file 81 | celerybeat-schedule 82 | 83 | # dotenv 84 | .env 85 | 86 | # virtualenv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | 93 | # Rope project settings 94 | .ropeproject 95 | 96 | # PyCharm project files 97 | .idea 98 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | checks: 2 | python: 3 | code_rating: true 4 | duplicate_code: true 5 | filter: 6 | paths: ["pyof/*", "tests/*"] 7 | build: 8 | environment: 9 | python: 3.6 10 | postgresql: false 11 | redis: false 12 | dependencies: 13 | override: 14 | - true 15 | tests: 16 | before: 17 | - pip install coverage 18 | override: 19 | - 20 | command: 'tox' 21 | coverage: 22 | file: '.coverage' 23 | config_file: '.coveragerc' 24 | format: 'py-cc' 25 | - py-scrutinizer-run 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.6" 4 | install: 5 | - pip install tox 6 | script: 7 | - tox 8 | -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | ####### 2 | Authors 3 | ####### 4 | 5 | - Beraldo Leal 6 | - Carlos Eduardo Moreira dos Santos 7 | - Diego Rabatone Oliveira 8 | - Gleyberson Andrade 9 | - Lucas Felix 10 | - Macártur de Sousa Carvalho 11 | - Renan Rodrigo 12 | 13 | 14 | Contributors 15 | ------------ 16 | 17 | - Artur Baruchi 18 | - Carlos Magno 19 | - Erick Vermot 20 | - Humberto Diógenes 21 | - Jose Mauro 22 | - Kenia Chang He 23 | - Raphael Cobe 24 | - Daniel Bruce 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Kytos Team 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: clean prepare 2 | python3 setup.py sdist 3 | ls -l dist/ 4 | 5 | clean: 6 | rm -rf build/ dist/ *.egg-info/ 7 | 8 | prepare: 9 | pip3 install --upgrade pip setuptools wheel twine 10 | 11 | testupload: build 12 | twine upload -r pypitest dist/* 13 | 14 | upload: build 15 | twine upload dist/* 16 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _rebuild 2 | python.inv 3 | -------------------------------------------------------------------------------- /docs/AUTHORS.rst: -------------------------------------------------------------------------------- 1 | ####### 2 | Authors 3 | ####### 4 | 5 | - Beraldo Leal 6 | - Artur Baruchi 7 | - Carlos Eduardo Moreira dos Santos 8 | - Diego Rabatone Oliveira 9 | - Macártur de Sousa Carvalho 10 | - Raphael Cóbe 11 | - André Tadeu 12 | -------------------------------------------------------------------------------- /docs/LICENSE.rst: -------------------------------------------------------------------------------- 1 | License 2 | ####### 3 | 4 | .. include:: ../LICENSE 5 | -------------------------------------------------------------------------------- /docs/_static/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/docs/_static/.keep -------------------------------------------------------------------------------- /docs/examples/index.rst: -------------------------------------------------------------------------------- 1 | ######## 2 | Examples 3 | ######## 4 | 5 | Here you will find examples on how to use python-openflow for both unpack raw 6 | binary openflow messages and create new openflow messages and pack them as 7 | binary data to be sent throughtout your network. 8 | 9 | .. include:: packing.rst 10 | 11 | .. include:: unpacking.rst 12 | -------------------------------------------------------------------------------- /docs/examples/packing.rst: -------------------------------------------------------------------------------- 1 | Packing examples 2 | **************** 3 | 4 | .. todo:: Write a full list of examples, with simples messages and also more 5 | complex ones 6 | -------------------------------------------------------------------------------- /docs/examples/unpacking.rst: -------------------------------------------------------------------------------- 1 | Unpacking examples 2 | ****************** 3 | 4 | .. todo:: Write unpacking examples, showing how the library behaves on simple 5 | and complex messages. 6 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../README.rst 2 | 3 | .. |kytos-site| replace:: Kytos web site 4 | .. _kytos-site: http://kytos.io/ 5 | .. |contrib| replace:: contributing instructions 6 | .. _contrib: https://github.com/kytos/python-openflow/blob/develop/docs/contributing/guidelines.rst 7 | -------------------------------------------------------------------------------- /docs/references.rst: -------------------------------------------------------------------------------- 1 | .. toctree:: 2 | :caption: Table of contents 3 | :maxdepth: 4 4 | :hidden: 5 | 6 | index 7 | examples/index 8 | API Reference 9 | AUTHORS 10 | LICENSE 11 | -------------------------------------------------------------------------------- /pyof/__init__.py: -------------------------------------------------------------------------------- 1 | """The ofx parser package. A package to parse OpenFlow messages. 2 | 3 | This package is a library that parses and creates OpenFlow Messages. 4 | It contains all implemented versions of OpenFlow protocol 5 | """ 6 | __version__ = '2021.1' 7 | -------------------------------------------------------------------------------- /pyof/foundation/__init__.py: -------------------------------------------------------------------------------- 1 | """Foundation package is the base of python-openflow. 2 | 3 | It provides modules used throughout the whole library. 4 | """ 5 | -------------------------------------------------------------------------------- /pyof/foundation/constants.py: -------------------------------------------------------------------------------- 1 | """General constants from python-openflow library.""" 2 | 3 | # Max values of each basic type 4 | UBINT8_MAX_VALUE = 255 5 | UBINT16_MAX_VALUE = 65535 6 | UBINT32_MAX_VALUE = 4294967295 7 | UBINT64_MAX_VALUE = 18446744073709551615 8 | 9 | OFP_ETH_ALEN = 6 10 | OFP_MAX_PORT_NAME_LEN = 16 11 | OFP_MAX_TABLE_NAME_LEN = 32 12 | SERIAL_NUM_LEN = 32 13 | DESC_STR_LEN = 256 14 | -------------------------------------------------------------------------------- /pyof/foundation/exceptions.py: -------------------------------------------------------------------------------- 1 | """Exceptions raised by this library.""" 2 | 3 | 4 | class ValidationError(Exception): 5 | """Can be used directly or inherited by mpre specific validation errors.""" 6 | 7 | def __str__(self): 8 | return "Validation error: " + super().__str__() 9 | 10 | 11 | class MethodNotImplemented(Exception): 12 | """Exception to be raised when a method is not implemented.""" 13 | 14 | def __str__(self): 15 | return "Method not yet implemented: " + super().__str__() 16 | 17 | 18 | class BadValueException(Exception): 19 | """Attribute has an unexpected value.""" 20 | 21 | def __str__(self): 22 | return "BadValue error: " + super().__str__() 23 | 24 | 25 | class WrongListItemType(Exception): 26 | """When an item of a wrong type is inserted into a list. 27 | 28 | Exception used by :class:`.FixedTypeList` and :class:`.ConstantTypeList` 29 | when the user tries to insert an item that does not match the expected 30 | type. 31 | """ 32 | 33 | def __init__(self, item_class, expected_class): 34 | """Take the parameters to inform the user about the error. 35 | 36 | Args: 37 | item_class (:obj:`type`): The class of the item that was being 38 | inserted in the list when the exception was raised. 39 | expected_class (:obj:`type`): The expected type that didn't match 40 | against the item to be inserted. 41 | """ 42 | super().__init__() 43 | self.item_class = item_class 44 | self.expected_class = expected_class 45 | 46 | def __str__(self): 47 | return "'{}' is not an instance of {}".format(self.item_class, 48 | self.expected_class) 49 | 50 | 51 | class UnpackException(Exception): 52 | """Error while unpacking.""" 53 | 54 | 55 | class PackException(Exception): 56 | """Error while unpacking.""" 57 | -------------------------------------------------------------------------------- /pyof/utils.py: -------------------------------------------------------------------------------- 1 | """General Unpack utils for python-openflow. 2 | 3 | This package was moved from kytos/of_core for the purpose of creating a generic 4 | method to perform package unpack independent of the OpenFlow version. 5 | """ 6 | from pyof import v0x01, v0x04 7 | from pyof.foundation.exceptions import UnpackException 8 | from pyof.v0x01.common import utils as u_v0x01 # pylint: disable=unused-import 9 | from pyof.v0x04.common import utils as u_v0x04 # pylint: disable=unused-import 10 | 11 | PYOF_VERSION_LIBS = {0x01: v0x01, 12 | 0x04: v0x04} 13 | 14 | 15 | def validate_packet(packet): 16 | """Check if packet is valid OF packet. 17 | 18 | Raises: 19 | UnpackException: If the packet is invalid. 20 | 21 | """ 22 | if not isinstance(packet, bytes): 23 | raise UnpackException('invalid packet') 24 | 25 | packet_length = len(packet) 26 | 27 | if packet_length < 8 or packet_length > 2**16: 28 | raise UnpackException('invalid packet') 29 | 30 | if packet_length != int.from_bytes(packet[2:4], byteorder='big'): 31 | raise UnpackException('invalid packet') 32 | 33 | version = packet[0] 34 | if version == 0 or version >= 128: 35 | raise UnpackException('invalid packet') 36 | 37 | 38 | def unpack(packet): 39 | """Unpack the OpenFlow Packet and returns a message. 40 | 41 | Args: 42 | packet: buffer with the openflow packet. 43 | 44 | Returns: 45 | GenericMessage: Message unpacked based on openflow packet. 46 | 47 | Raises: 48 | UnpackException: if the packet can't be unpacked. 49 | 50 | """ 51 | validate_packet(packet) 52 | 53 | version = packet[0] 54 | try: 55 | pyof_lib = PYOF_VERSION_LIBS[version] 56 | except KeyError: 57 | raise UnpackException('Version not supported') 58 | 59 | try: 60 | message = pyof_lib.common.utils.unpack_message(packet) 61 | return message 62 | except (UnpackException, ValueError) as exception: 63 | raise UnpackException(exception) 64 | -------------------------------------------------------------------------------- /pyof/v0x01/__init__.py: -------------------------------------------------------------------------------- 1 | """The ofx parser package - spec version 0x01 (1.0.0).""" 2 | -------------------------------------------------------------------------------- /pyof/v0x01/asynchronous/__init__.py: -------------------------------------------------------------------------------- 1 | """Asynchronous messages.""" 2 | -------------------------------------------------------------------------------- /pyof/v0x01/asynchronous/flow_removed.py: -------------------------------------------------------------------------------- 1 | """The controller has requested to be notified when flows time out.""" 2 | 3 | # System imports 4 | from enum import IntEnum 5 | 6 | from pyof.foundation.base import GenericMessage 7 | from pyof.foundation.basic_types import Pad, UBInt8, UBInt16, UBInt32, UBInt64 8 | # Local source tree imports 9 | from pyof.v0x01.common.flow_match import Match 10 | from pyof.v0x01.common.header import Header, Type 11 | 12 | __all__ = ('FlowRemoved', 'FlowRemovedReason') 13 | 14 | # Enums 15 | 16 | 17 | class FlowRemovedReason(IntEnum): 18 | """Why the flow was removed.""" 19 | 20 | #: Flow idle time exceeded idle_timeout 21 | OFPRR_IDLE_TIMEOUT = 0 22 | #: Time exceeded hard_timeout 23 | OFPRR_HARD_TIMEOUT = 1 24 | #: Evicted by a DELETE flow mod 25 | OFPRR_DELETE = 2 26 | 27 | 28 | # Classes 29 | class FlowRemoved(GenericMessage): 30 | """Flow removed (datapath -> controller).""" 31 | 32 | #: :class:`~pyof.v0x01.common.header.Header`: OpenFlow Header 33 | header = Header(message_type=Type.OFPT_FLOW_REMOVED) 34 | #: :class:`~pyof.v0x01.common.flow_match.Match`: OpenFlow Header 35 | match = Match() 36 | cookie = UBInt64() 37 | 38 | priority = UBInt16() 39 | reason = UBInt8(enum_ref=FlowRemovedReason) 40 | #: Align to 32-bits. 41 | pad = Pad(1) 42 | 43 | duration_sec = UBInt32() 44 | duration_nsec = UBInt32() 45 | 46 | idle_timeout = UBInt16() 47 | #: Align to 64-bits. 48 | pad2 = Pad(2) 49 | packet_count = UBInt64() 50 | byte_count = UBInt64() 51 | 52 | def __init__(self, xid=None, match=None, cookie=None, priority=None, 53 | reason=None, duration_sec=None, duration_nsec=None, 54 | idle_timeout=None, packet_count=None, byte_count=None): 55 | """Assign parameters to object attributes. 56 | 57 | Args: 58 | xid (int): OpenFlow Header's xid. 59 | match (~pyof.v0x01.common.flow_match.Match): Fields' description. 60 | cookie (int): Opaque controller-issued identifier. 61 | priority (int): Priority level of flow entry. 62 | reason (~pyof.v0x01.asynchronous.flow_removed.FlowRemovedReason): 63 | Why the flow was removed. 64 | duration_sec (int): Time the flow was alive in seconds. 65 | duration_nsec (int): Time the flow was alive in nanoseconds in 66 | addition to duration_sec. 67 | idle_timeout (int): Idle timeout from original flow mod. 68 | packet_count (int): Number of packets. 69 | byte_count (int): Byte count. 70 | """ 71 | super().__init__(xid) 72 | self.match = match 73 | self.cookie = cookie 74 | self.priority = priority 75 | self.reason = reason 76 | self.duration_sec = duration_sec 77 | self.duration_nsec = duration_nsec 78 | self.idle_timeout = idle_timeout 79 | self.packet_count = packet_count 80 | self.byte_count = byte_count 81 | -------------------------------------------------------------------------------- /pyof/v0x01/asynchronous/packet_in.py: -------------------------------------------------------------------------------- 1 | """For packets received by the datapath and sent to the controller.""" 2 | 3 | # System imports 4 | from enum import IntEnum 5 | 6 | from pyof.foundation.base import GenericMessage 7 | from pyof.foundation.basic_types import ( 8 | BinaryData, Pad, UBInt8, UBInt16, UBInt32) 9 | from pyof.v0x01.common.constants import NO_BUFFER 10 | from pyof.v0x01.common.header import Header, Type 11 | 12 | # Third-party imports 13 | 14 | 15 | __all__ = ('PacketIn', 'PacketInReason') 16 | 17 | # Enums 18 | 19 | 20 | class PacketInReason(IntEnum): 21 | """Reason why this packet is being sent to the controller.""" 22 | 23 | #: No matching flow 24 | OFPR_NO_MATCH = 0 25 | #: Action explicitly output to controller 26 | OFPR_ACTION = 1 27 | 28 | 29 | # Classes 30 | 31 | 32 | class PacketIn(GenericMessage): 33 | """Packet received on port (datapath -> controller).""" 34 | 35 | #: :class:`~pyof.v0x01.common.header.Header`: OpenFlow Header 36 | header = Header(message_type=Type.OFPT_PACKET_IN) 37 | buffer_id = UBInt32() 38 | total_len = UBInt16() 39 | in_port = UBInt16() 40 | reason = UBInt8(enum_ref=PacketInReason) 41 | #: Align to 32-bits. 42 | pad = Pad(1) 43 | data = BinaryData() 44 | 45 | def __init__(self, xid=None, buffer_id=NO_BUFFER, total_len=None, 46 | in_port=None, reason=None, data=b''): 47 | """Assign parameters to object attributes. 48 | 49 | Args: 50 | xid (int): Header's xid. 51 | buffer_id (int): ID assigned by datapath. 52 | total_len (int): Full length of frame. 53 | in_port (int): Port on which frame was received. 54 | reason (~pyof.v0x01.asynchronous.packet_in.PacketInReason): 55 | The reason why the packet is being sent 56 | data (bytes): Ethernet frame, halfway through 32-bit word, so the 57 | IP header is 32-bit aligned. The amount of data is inferred 58 | from the length field in the header. Because of padding, 59 | offsetof(struct ofp_packet_in, data) == 60 | sizeof(struct ofp_packet_in) - 2. 61 | """ 62 | super().__init__(xid) 63 | self.buffer_id = buffer_id 64 | self.total_len = total_len 65 | self.in_port = in_port 66 | self.reason = reason 67 | self.data = data 68 | -------------------------------------------------------------------------------- /pyof/v0x01/asynchronous/port_status.py: -------------------------------------------------------------------------------- 1 | """Defines an Error Message.""" 2 | 3 | # System imports 4 | from enum import IntEnum 5 | 6 | from pyof.foundation.base import GenericMessage 7 | from pyof.foundation.basic_types import Pad, UBInt8 8 | # Local source tree imports 9 | from pyof.v0x01.common.header import Header, Type 10 | from pyof.v0x01.common.phy_port import PhyPort 11 | 12 | # Third-party imports 13 | 14 | __all__ = ('PortStatus', 'PortReason') 15 | 16 | # Enums 17 | 18 | 19 | class PortReason(IntEnum): 20 | """What changed about the physical port.""" 21 | 22 | #: The port was added 23 | OFPPR_ADD = 0 24 | #: The port was removed 25 | OFPPR_DELETE = 1 26 | #: Some attribute of the port has changed 27 | OFPPR_MODIFY = 2 28 | 29 | 30 | # Classes 31 | 32 | class PortStatus(GenericMessage): 33 | """A physical port has changed in the datapath.""" 34 | 35 | #: :class:`~pyof.v0x01.common.header.Header`: OpenFlow Header 36 | header = Header(message_type=Type.OFPT_PORT_STATUS) 37 | reason = UBInt8(enum_ref=PortReason) 38 | #: Align to 32-bits. 39 | pad = Pad(7) 40 | desc = PhyPort() 41 | 42 | def __init__(self, xid=None, reason=None, desc=None): 43 | """Assign parameters to object attributes. 44 | 45 | Args: 46 | xid (int): Header's xid. 47 | reason (~pyof.v0x01.asynchronous.port_status.PortReason): 48 | Addition, deletion or modification. 49 | desc (PhyPort): Port description. 50 | """ 51 | super().__init__(xid) 52 | self.reason = reason 53 | self.desc = desc 54 | -------------------------------------------------------------------------------- /pyof/v0x01/common/__init__.py: -------------------------------------------------------------------------------- 1 | """Common structures used on OpenFlow Protocol.""" 2 | -------------------------------------------------------------------------------- /pyof/v0x01/common/constants.py: -------------------------------------------------------------------------------- 1 | """Here we have the constants related to v0x01 version.""" 2 | OFP_VERSION = 0x01 3 | 4 | #: This value represents the constant -1. This value is used when no buffer is 5 | #: specified 6 | NO_BUFFER = 2**32-1 7 | -------------------------------------------------------------------------------- /pyof/v0x01/common/header.py: -------------------------------------------------------------------------------- 1 | """Defines Header classes and related items.""" 2 | 3 | # System imports 4 | from enum import IntEnum 5 | 6 | # Local source tree imports 7 | from pyof.foundation.base import GenericStruct 8 | from pyof.foundation.basic_types import UBInt8, UBInt16, UBInt32 9 | from pyof.v0x01.common.constants import OFP_VERSION 10 | 11 | # Third-party imports 12 | 13 | __all__ = ('Header', 'Type') 14 | 15 | # Enums 16 | 17 | 18 | class Type(IntEnum): 19 | """Enumeration of Message Types.""" 20 | 21 | # Symetric/Immutable messages 22 | OFPT_HELLO = 0 23 | OFPT_ERROR = 1 24 | OFPT_ECHO_REQUEST = 2 25 | OFPT_ECHO_REPLY = 3 26 | OFPT_VENDOR = 4 27 | 28 | # Switch configuration messages 29 | # Controller/Switch messages 30 | OFPT_FEATURES_REQUEST = 5 31 | OFPT_FEATURES_REPLY = 6 32 | OFPT_GET_CONFIG_REQUEST = 7 33 | OFPT_GET_CONFIG_REPLY = 8 34 | OFPT_SET_CONFIG = 9 35 | 36 | # Async messages 37 | OFPT_PACKET_IN = 10 38 | OFPT_FLOW_REMOVED = 11 39 | OFPT_PORT_STATUS = 12 40 | 41 | # Controller command messages 42 | # Controller/switch message 43 | OFPT_PACKET_OUT = 13 44 | OFPT_FLOW_MOD = 14 45 | OFPT_PORT_MOD = 15 46 | 47 | # Statistics messages 48 | # Controller/Switch message 49 | OFPT_STATS_REQUEST = 16 50 | OFPT_STATS_REPLY = 17 51 | 52 | # Barrier messages 53 | # Controller/Switch message 54 | OFPT_BARRIER_REQUEST = 18 55 | OFPT_BARRIER_REPLY = 19 56 | 57 | # Queue Configuration messages 58 | # Controller/Switch message 59 | OFPT_QUEUE_GET_CONFIG_REQUEST = 20 60 | OFPT_QUEUE_GET_CONFIG_REPLY = 21 61 | 62 | 63 | # Classes 64 | 65 | 66 | class Header(GenericStruct): 67 | """Representation of an OpenFlow message Header.""" 68 | 69 | version = UBInt8(OFP_VERSION) 70 | message_type = UBInt8(enum_ref=Type) 71 | length = UBInt16() 72 | xid = UBInt32() 73 | 74 | def __init__(self, message_type=None, length=None, xid=None): 75 | """Create a Header with the optional parameters below. 76 | 77 | Args: 78 | message_type (~pyof.v0x01.common.header.Type): Type of the message. 79 | xid (int): ID of the message. Defaults to a random integer. 80 | length (int): Length of the message, including the header itself. 81 | """ 82 | super().__init__() 83 | self.message_type = message_type 84 | self.length = length 85 | self.xid = xid 86 | 87 | def __str__(self): 88 | """Get just the header type. Eg.: 'OFPT_SET_CONFIG'.""" 89 | return self.message_type.name 90 | 91 | def __repr__(self): 92 | """Show a full representation of the Header, including version.""" 93 | return "%s(message_type=%s, length=%r, xid=%r, version=%r)" \ 94 | % (self.__class__.__name__, self.message_type, self.length, 95 | self.xid, self.version) 96 | -------------------------------------------------------------------------------- /pyof/v0x01/controller2switch/__init__.py: -------------------------------------------------------------------------------- 1 | """Controller to Switch and Switch to Controller Messages.""" 2 | -------------------------------------------------------------------------------- /pyof/v0x01/controller2switch/barrier_reply.py: -------------------------------------------------------------------------------- 1 | """Defines Barrier Reply message.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | from pyof.foundation.base import GenericMessage 8 | from pyof.v0x01.common.header import Header, Type 9 | 10 | __all__ = ('BarrierReply',) 11 | 12 | # Classes 13 | 14 | 15 | class BarrierReply(GenericMessage): 16 | """OpenFlow Barrier Reply Message. 17 | 18 | This message does not contain a body beyond the OpenFlow Header. 19 | """ 20 | 21 | header = Header(message_type=Type.OFPT_BARRIER_REPLY) 22 | -------------------------------------------------------------------------------- /pyof/v0x01/controller2switch/barrier_request.py: -------------------------------------------------------------------------------- 1 | """Defines Barrier Request message.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | from pyof.foundation.base import GenericMessage 8 | from pyof.v0x01.common.header import Header, Type 9 | 10 | __all__ = ('BarrierRequest',) 11 | 12 | # Classes 13 | 14 | 15 | class BarrierRequest(GenericMessage): 16 | """OpenFlow Barrier Request Message. 17 | 18 | This message does not contain a body in addition to the OpenFlow Header. 19 | """ 20 | 21 | header = Header(message_type=Type.OFPT_BARRIER_REQUEST) 22 | -------------------------------------------------------------------------------- /pyof/v0x01/controller2switch/features_reply.py: -------------------------------------------------------------------------------- 1 | """Define Features Reply classes and related items.""" 2 | 3 | # Local source tree imports 4 | from pyof.foundation.base import GenericBitMask, GenericMessage 5 | from pyof.foundation.basic_types import DPID, Pad, UBInt8, UBInt32 6 | from pyof.v0x01.common.action import ActionType 7 | from pyof.v0x01.common.header import Header, Type 8 | from pyof.v0x01.common.phy_port import ListOfPhyPorts 9 | 10 | __all__ = ('FeaturesReply', 'Capabilities', 'SwitchFeatures') 11 | 12 | 13 | class Capabilities(GenericBitMask): 14 | """Capabilities supported by the datapath.""" 15 | 16 | #: Flow statistics 17 | OFPC_FLOW_STATS = 1 << 0 18 | #: Table statistics 19 | OFPC_TABLE_STATS = 1 << 1 20 | #: Port statistics 21 | OFPC_PORT_STATS = 1 << 2 22 | #: 802.1d spanning tree 23 | OFPC_STP = 1 << 3 24 | #: Reserved, must be zero 25 | OFPC_RESERVED = 1 << 4 26 | #: Can reassembe IP fragments 27 | OFPC_IP_REASM = 1 << 5 28 | #: Queue statistics 29 | OFPC_QUEUE_STATS = 1 << 6 30 | #: Match IP addresses in ARP pkts 31 | OFPC_ARP_MATCH_IP = 1 << 7 32 | 33 | 34 | # Classes 35 | 36 | class SwitchFeatures(GenericMessage): 37 | """Message sent by the switch device to the controller. 38 | 39 | This message is the response for a features_request message, sent by the 40 | controller to the switch device. The 'OFPT_FEATURES_REPLY' message inherits 41 | from this class, despite the strange name. 42 | """ 43 | 44 | header = Header(message_type=Type.OFPT_FEATURES_REPLY) 45 | datapath_id = DPID() 46 | n_buffers = UBInt32() 47 | n_tables = UBInt8() 48 | #: Align to 64-bits. 49 | pad = Pad(3) 50 | # Features 51 | capabilities = UBInt32(enum_ref=Capabilities) 52 | actions = UBInt32(enum_ref=ActionType) 53 | ports = ListOfPhyPorts() 54 | 55 | def __init__(self, xid=None, datapath_id=None, n_buffers=None, 56 | n_tables=None, capabilities=None, actions=None, ports=None): 57 | """Create a SwitchFeatures with the optional parameters below. 58 | 59 | Args: 60 | xid (int): xid to be used on the message header. 61 | datapath_id (:class:`str` or :class:`.DPID`): datapath unique ID. 62 | The lower 48-bits are for MAC address, while 63 | the upper 16-bits are implementer-defined. 64 | n_buffers (int): UBInt32 max packets buffered at once. 65 | n_tables (int): UBInt8 number of tables supported by datapath. 66 | capabilities (int): UBInt32 bitmap of supported capabilities. 67 | actions (int): UBInt32 Bitmap of supported "action_type"s. 68 | ports (int): Port definitions. 69 | """ 70 | super().__init__(xid) 71 | self.datapath_id = datapath_id 72 | self.n_buffers = n_buffers 73 | self.n_tables = n_tables 74 | self.capabilities = capabilities 75 | self.actions = actions 76 | self.ports = [] if ports is None else ports 77 | 78 | 79 | class FeaturesReply(SwitchFeatures): 80 | """'OFPT_FEATURES_REPLY' message.""" 81 | -------------------------------------------------------------------------------- /pyof/v0x01/controller2switch/features_request.py: -------------------------------------------------------------------------------- 1 | """Defines Features Request classes and related items.""" 2 | 3 | from pyof.foundation.base import GenericMessage 4 | from pyof.v0x01.common.header import Header, Type 5 | 6 | __all__ = ('FeaturesRequest',) 7 | 8 | # Classes 9 | 10 | 11 | class FeaturesRequest(GenericMessage): 12 | """Features request message. 13 | 14 | This message does not contain a body in addition to the OpenFlow Header. 15 | """ 16 | 17 | header = Header( 18 | message_type=Type.OFPT_FEATURES_REQUEST) 19 | -------------------------------------------------------------------------------- /pyof/v0x01/controller2switch/get_config_reply.py: -------------------------------------------------------------------------------- 1 | """Defines Get Config Reply message.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | from pyof.v0x01.common.header import Type 8 | from pyof.v0x01.controller2switch.common import SwitchConfig 9 | 10 | __all__ = ('GetConfigReply',) 11 | 12 | 13 | class GetConfigReply(SwitchConfig): 14 | """Get Config Reply message.""" 15 | 16 | def __init__(self, xid=None, flags=None, miss_send_len=None): 17 | """Create a GetConfigReply with the optional parameters below. 18 | 19 | Args: 20 | xid (int): xid to be used on the message header. 21 | flags (~pyof.v0x01.controller2switch.common.ConfigFlag): 22 | OFPC_* flags. 23 | miss_send_len (int): UBInt16 max bytes of new flow that the 24 | datapath should send to the controller. 25 | """ 26 | super().__init__(xid, flags, miss_send_len) 27 | self.header.message_type = Type.OFPT_GET_CONFIG_REPLY 28 | -------------------------------------------------------------------------------- /pyof/v0x01/controller2switch/get_config_request.py: -------------------------------------------------------------------------------- 1 | """Defines Get Config Request classes and related items.""" 2 | 3 | from pyof.foundation.base import GenericMessage 4 | from pyof.v0x01.common.header import Header, Type 5 | 6 | __all__ = ('GetConfigRequest',) 7 | 8 | # Classe 9 | 10 | 11 | class GetConfigRequest(GenericMessage): 12 | """Get Config Request message.""" 13 | 14 | header = Header(message_type=Type.OFPT_GET_CONFIG_REQUEST) 15 | -------------------------------------------------------------------------------- /pyof/v0x01/controller2switch/port_mod.py: -------------------------------------------------------------------------------- 1 | """Modifications to the port from the controller.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | from pyof.foundation.base import GenericMessage 8 | from pyof.foundation.basic_types import HWAddress, Pad, UBInt16, UBInt32 9 | # Local source tree imports 10 | from pyof.v0x01.common.header import Header, Type 11 | from pyof.v0x01.common.phy_port import PortConfig, PortFeatures 12 | 13 | __all__ = ('PortMod',) 14 | 15 | # Classes 16 | 17 | 18 | class PortMod(GenericMessage): 19 | """Implement messages to modify the physical port behavior.""" 20 | 21 | header = Header(message_type=Type.OFPT_PORT_MOD) 22 | port_no = UBInt16() 23 | hw_addr = HWAddress() 24 | config = UBInt32(enum_ref=PortConfig) 25 | mask = UBInt32(enum_ref=PortConfig) 26 | advertise = UBInt32(enum_ref=PortFeatures) 27 | #: Pad to 64-bits. 28 | pad = Pad(4) 29 | 30 | def __init__(self, xid=None, port_no=None, hw_addr=None, config=None, 31 | mask=None, advertise=None): 32 | """Create a PortMod with the optional parameters below. 33 | 34 | Args: 35 | xid (int): OpenFlow xid to the header. 36 | port_no (int): Physical port number. 37 | hw_addr (HWAddress): The hardware address is not configurable. 38 | This is used to sanity-check the request, 39 | so it must be the same as returned in an ofp_phy_port struct. 40 | config (~pyof.v0x01.common.phy_port.PortConfig): 41 | Bitmap of OFPPC_* flags 42 | mask (~pyof.v0x01.common.phy_port.PortConfig): 43 | Bitmap of OFPPC_* flags to be changed 44 | advertise (~pyof.v0x01.common.phy_port.PortFeatures): 45 | Bitmap of "ofp_port_features"s 46 | """ 47 | super().__init__(xid) 48 | self.port_no = port_no 49 | self.hw_addr = hw_addr 50 | self.config = config 51 | self.mask = mask 52 | self.advertise = advertise 53 | -------------------------------------------------------------------------------- /pyof/v0x01/controller2switch/queue_get_config_reply.py: -------------------------------------------------------------------------------- 1 | """Switch replies to controller.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | from pyof.foundation.base import GenericMessage 8 | from pyof.foundation.basic_types import Pad, UBInt16 9 | # Local source tree imports 10 | from pyof.v0x01.common.header import Header, Type 11 | from pyof.v0x01.common.phy_port import Port 12 | from pyof.v0x01.common.queue import ListOfQueues 13 | 14 | __all__ = ('QueueGetConfigReply',) 15 | 16 | 17 | class QueueGetConfigReply(GenericMessage): 18 | """Class implements the response to the config request.""" 19 | 20 | header = Header(message_type=Type.OFPT_QUEUE_GET_CONFIG_REPLY) 21 | port = UBInt16(enum_ref=Port) 22 | #: Pad to 64-bits. 23 | pad = Pad(6) 24 | queues = ListOfQueues() 25 | 26 | def __init__(self, xid=None, port=None, queues=None): 27 | """Create a QueueGetConfigReply with the optional parameters below. 28 | 29 | Args: 30 | xid (int): xid of OpenFlow header. 31 | port (~pyof.v0x01.common.phy_port.Port): 32 | Target port for the query. 33 | queue (~pyof.v0x01.common.queue.ListOfQueues): 34 | List of configured queues. 35 | """ 36 | super().__init__(xid) 37 | self.port = port 38 | self.queues = [] if queues is None else queues 39 | -------------------------------------------------------------------------------- /pyof/v0x01/controller2switch/queue_get_config_request.py: -------------------------------------------------------------------------------- 1 | """Query the switch for configured queues on a port.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | from pyof.foundation.base import GenericMessage 8 | from pyof.foundation.basic_types import Pad, UBInt16 9 | # Local source tree imports 10 | from pyof.v0x01.common.header import Header, Type 11 | from pyof.v0x01.common.phy_port import Port 12 | 13 | __all__ = ('QueueGetConfigRequest',) 14 | 15 | 16 | class QueueGetConfigRequest(GenericMessage): 17 | """Query structure for configured queues on a port.""" 18 | 19 | header = Header(message_type=Type.OFPT_QUEUE_GET_CONFIG_REQUEST) 20 | port = UBInt16(enum_ref=Port) 21 | #: Pad to 64-bits 22 | pad = Pad(2) 23 | 24 | def __init__(self, xid=None, port=None): 25 | """Create a QueueGetConfigRequest with the optional parameters below. 26 | 27 | Args: 28 | xid (int): xid of OpenFlow header 29 | port (~pyof.v0x01.common.phy_port.Port): Target port for the query 30 | """ 31 | super().__init__(xid) 32 | self.port = port 33 | -------------------------------------------------------------------------------- /pyof/v0x01/controller2switch/set_config.py: -------------------------------------------------------------------------------- 1 | """Define SetConfig message.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | # Local imports 8 | from pyof.v0x01.common.header import Type 9 | from pyof.v0x01.controller2switch.common import SwitchConfig 10 | 11 | __all__ = ('SetConfig',) 12 | 13 | 14 | class SetConfig(SwitchConfig): 15 | """Set config message.""" 16 | 17 | def __init__(self, xid=None, flags=None, miss_send_len=None): 18 | """Create a SetConfig with the optional parameters below. 19 | 20 | Args: 21 | xid (int): xid to be used on the message header. 22 | flags (~pyof.v0x01.controller2switch.common.ConfigFlag): 23 | OFPC_* flags. 24 | miss_send_len (int): UBInt16 max bytes of new flow that the 25 | datapath should send to the controller. 26 | """ 27 | super().__init__(xid, flags, miss_send_len) 28 | self.header.message_type = Type.OFPT_SET_CONFIG 29 | -------------------------------------------------------------------------------- /pyof/v0x01/controller2switch/stats_request.py: -------------------------------------------------------------------------------- 1 | """Query the datapath about its current state.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | from importlib import import_module 8 | 9 | from pyof.foundation.base import GenericMessage 10 | from pyof.foundation.basic_types import BinaryData, FixedTypeList, UBInt16 11 | # Local imports 12 | from pyof.v0x01.common.header import Header, Type 13 | from pyof.v0x01.controller2switch.common import StatsType 14 | 15 | __all__ = ('StatsRequest',) 16 | 17 | 18 | class StatsRequest(GenericMessage): 19 | """Request statistics to switch.""" 20 | 21 | #: OpenFlow :class:`~pyof.v0x01.common.header.Header` 22 | header = Header(message_type=Type.OFPT_STATS_REQUEST) 23 | body_type = UBInt16(enum_ref=StatsType) 24 | flags = UBInt16() 25 | body = BinaryData() 26 | 27 | def __init__(self, xid=None, body_type=None, flags=0, body=b''): 28 | """Create a StatsRequest with the optional parameters below. 29 | 30 | Args: 31 | xid (int): xid to be used on the message header. 32 | body_type (StatsType): One of the OFPST_* constants. 33 | flags (int): OFPSF_REQ_* flags (none yet defined). 34 | body (BinaryData): Body of the request. 35 | """ 36 | super().__init__(xid) 37 | self.body_type = body_type 38 | self.flags = flags 39 | self.body = body 40 | 41 | def pack(self, value=None): 42 | """Pack according to :attr:`body_type`. 43 | 44 | Make `body` a binary pack before packing this object. Then, restore 45 | body. 46 | """ 47 | backup = self.body 48 | if not value: 49 | value = self.body 50 | 51 | if hasattr(value, 'pack'): 52 | self.body = value.pack() 53 | stats_request_packed = super().pack() 54 | 55 | self.body = backup 56 | return stats_request_packed 57 | 58 | def unpack(self, buff, offset=0): 59 | """Unpack according to :attr:`body_type`.""" 60 | super().unpack(buff) 61 | 62 | class_name = self._get_body_class() 63 | buff = self.body.value 64 | self.body = FixedTypeList(pyof_class=class_name) 65 | self.body.unpack(buff) 66 | 67 | def _get_body_class(self): 68 | if isinstance(self.body_type, (int, UBInt16)): 69 | self.body_type = self.body_type.enum_ref(self.body_type.value) 70 | 71 | module = import_module('pyof.v0x01.controller2switch.common') 72 | body_name = self.body_type.name.replace('OFPST_', '').title() 73 | 74 | for class_name in module.__all__: 75 | if 'Request' in class_name and body_name in class_name: 76 | return getattr(module, class_name) 77 | return None 78 | -------------------------------------------------------------------------------- /pyof/v0x01/symmetric/__init__.py: -------------------------------------------------------------------------------- 1 | """Symmetric Messages.""" 2 | -------------------------------------------------------------------------------- /pyof/v0x01/symmetric/echo_reply.py: -------------------------------------------------------------------------------- 1 | """Defines Echo Reply message during the handshake.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | from pyof.foundation.base import GenericMessage 8 | from pyof.foundation.basic_types import BinaryData 9 | from pyof.v0x01.common.header import Header, Type 10 | 11 | __all__ = ('EchoReply',) 12 | 13 | # Classes 14 | 15 | 16 | class EchoReply(GenericMessage): 17 | """OpenFlow Reply message. 18 | 19 | This message does not contain a body beyond the OpenFlow Header. 20 | """ 21 | 22 | header = Header(message_type=Type.OFPT_ECHO_REPLY, length=8) 23 | data = BinaryData() 24 | 25 | def __init__(self, xid=None, data=None): 26 | """Create an EchoReply with the optional parameters below. 27 | 28 | Args: 29 | xid (int): xid to be used on the message header. 30 | data (bytes): arbitrary-length data field. 31 | """ 32 | super().__init__(xid) 33 | self.data = data 34 | -------------------------------------------------------------------------------- /pyof/v0x01/symmetric/echo_request.py: -------------------------------------------------------------------------------- 1 | """Defines Echo Request message during the handshake.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | from pyof.foundation.base import GenericMessage 8 | from pyof.foundation.basic_types import BinaryData 9 | from pyof.v0x01.common.header import Header, Type 10 | 11 | __all__ = ('EchoRequest',) 12 | 13 | # Classes 14 | 15 | 16 | class EchoRequest(GenericMessage): 17 | """OpenFlow Reply message. 18 | 19 | This message does not contain a body after the OpenFlow Header. 20 | """ 21 | 22 | header = Header(message_type=Type.OFPT_ECHO_REQUEST, length=8) 23 | data = BinaryData() 24 | 25 | def __init__(self, xid=None, data=None): 26 | """Create a EchoRequest with the optional parameters below. 27 | 28 | Args: 29 | xid (int): xid to be used on the message header. 30 | data (bytes): arbitrary-length data field. 31 | """ 32 | super().__init__(xid) 33 | self.data = data 34 | -------------------------------------------------------------------------------- /pyof/v0x01/symmetric/hello.py: -------------------------------------------------------------------------------- 1 | """Defines Hello message.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | from pyof.foundation.base import GenericMessage 8 | from pyof.foundation.basic_types import BinaryData 9 | from pyof.v0x01.common.header import Header, Type 10 | 11 | __all__ = ('Hello',) 12 | 13 | # Classes 14 | 15 | 16 | class Hello(GenericMessage): 17 | """OpenFlow Hello Message. 18 | 19 | This message does not contain a body beyond the OpenFlow Header. 20 | """ 21 | 22 | header = Header(message_type=Type.OFPT_HELLO, length=8) 23 | elements = BinaryData() 24 | -------------------------------------------------------------------------------- /pyof/v0x01/symmetric/vendor_header.py: -------------------------------------------------------------------------------- 1 | """Defines Vendor message.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | from pyof.foundation.base import GenericMessage 8 | from pyof.foundation.basic_types import BinaryData, UBInt32 9 | from pyof.v0x01.common.header import Header, Type 10 | 11 | __all__ = ('VendorHeader',) 12 | 13 | # Classes 14 | 15 | 16 | class VendorHeader(GenericMessage): 17 | """OpenFlow Vendor message. 18 | 19 | This message does not contain a body beyond the OpenFlow Header. 20 | """ 21 | 22 | header = Header(message_type=Type.OFPT_VENDOR) 23 | vendor = UBInt32() 24 | data = BinaryData() 25 | 26 | def __init__(self, xid=None, vendor=None, data=None): 27 | """Create a VendorHeader with the options parameters below. 28 | 29 | Args: 30 | xid (int): xid to be used on the message header. 31 | vendor (int): Vendor ID: 32 | MSB 0: low-order bytes are IEEE OUI. 33 | MSB != 0: defined by OpenFlow consortium. 34 | data (BinaryData): Vendor-defined arbitrary additional data. 35 | """ 36 | super().__init__(xid) 37 | self.vendor = vendor 38 | self.data = data 39 | -------------------------------------------------------------------------------- /pyof/v0x04/__init__.py: -------------------------------------------------------------------------------- 1 | """The ofx parser package - spec version 0x04 (1.3.0).""" 2 | -------------------------------------------------------------------------------- /pyof/v0x04/asynchronous/__init__.py: -------------------------------------------------------------------------------- 1 | """Asynchronous messages.""" 2 | -------------------------------------------------------------------------------- /pyof/v0x04/asynchronous/flow_removed.py: -------------------------------------------------------------------------------- 1 | """The controller has requested to be notified when flows time out.""" 2 | # System imports 3 | from enum import IntEnum 4 | 5 | from pyof.foundation.base import GenericMessage 6 | from pyof.foundation.basic_types import UBInt8, UBInt16, UBInt32, UBInt64 7 | # Local source tree imports 8 | from pyof.v0x04.common.flow_match import Match 9 | from pyof.v0x04.common.header import Header, Type 10 | 11 | __all__ = ('FlowRemoved', 'FlowRemovedReason') 12 | 13 | # Enums 14 | 15 | 16 | class FlowRemovedReason(IntEnum): 17 | """Why the flow was removed.""" 18 | 19 | #: Flow idle time exceeded idle_timeout 20 | OFPRR_IDLE_TIMEOUT = 0 21 | #: Time exceeded hard_timeout 22 | OFPRR_HARD_TIMEOUT = 1 23 | #: Evicted by a DELETE flow mod 24 | OFPRR_DELETE = 2 25 | #: Group was removed. 26 | OFPRR_GROUP_DELETE = 3 27 | 28 | 29 | # Classes 30 | class FlowRemoved(GenericMessage): 31 | """Flow removed (datapath -> controller). 32 | 33 | If the controller has requested to be notified when flow entries time out 34 | or are deleted from tables, the datapath does this with the 35 | OFPT_FLOW_REMOVED message. 36 | """ 37 | 38 | #: :class:`~pyof.v0x04.common.header.Header`: OpenFlow Header 39 | header = Header(message_type=Type.OFPT_FLOW_REMOVED) 40 | #: Opaque controller-issued identifier. 41 | cookie = UBInt64() 42 | #: Priority level of flow entry. 43 | priority = UBInt16() 44 | #: One of OFPRR_*. 45 | reason = UBInt8(enum_ref=FlowRemovedReason) 46 | #: ID of the table 47 | table_id = UBInt8() 48 | #: Time flow was alive in seconds. 49 | duration_sec = UBInt32() 50 | #: Time flow was alive in nanoseconds beyond duration_sec. 51 | duration_nsec = UBInt32() 52 | #: Idle timeout from original flow mod. 53 | idle_timeout = UBInt16() 54 | #: Hard timeout from original flow mod. 55 | hard_timeout = UBInt16() 56 | packet_count = UBInt64() 57 | byte_count = UBInt64() 58 | #: Description of fields. Variable size. 59 | #: :class:`~pyof.v0x04.common.flow_match.Match` 60 | match = Match() 61 | 62 | def __init__(self, xid=None, cookie=None, priority=None, reason=None, 63 | table_id=None, duration_sec=None, duration_nsec=None, 64 | idle_timeout=None, hard_timeout=None, packet_count=None, 65 | byte_count=None, match=None): 66 | """Assign parameters to object attributes. 67 | 68 | Args: 69 | xid (int): OpenFlow Header's xid. 70 | cookie (int): Opaque controller-issued identifier. 71 | priority (int): Priority level of flow entry. 72 | reason (~pyof.v0x04.asynchronous.flow_removed.FlowRemovedReason): 73 | Why the flow was removed. 74 | table_id (int): ID of the table. 75 | duration_sec (int): Time the flow was alive in seconds. 76 | duration_nsec (int): Time the flow was alive in nanoseconds in 77 | addition to duration_sec. 78 | idle_timeout (int): Idle timeout from original flow mod. 79 | hard_timeout (int): Hard timeout from original flow mod. 80 | packet_count (int): Number of packets. 81 | byte_count (int): Byte count. 82 | match (~pyof.v0x04.common.flow_match.Match): Fields' description. 83 | """ 84 | super().__init__(xid) 85 | self.cookie = cookie 86 | self.priority = priority 87 | self.reason = reason 88 | self.table_id = table_id 89 | self.duration_sec = duration_sec 90 | self.duration_nsec = duration_nsec 91 | self.idle_timeout = idle_timeout 92 | self.hard_timeout = hard_timeout 93 | self.packet_count = packet_count 94 | self.byte_count = byte_count 95 | self.match = match 96 | -------------------------------------------------------------------------------- /pyof/v0x04/asynchronous/port_status.py: -------------------------------------------------------------------------------- 1 | """Defines an PortStatus Message.""" 2 | # System imports 3 | from enum import IntEnum 4 | 5 | # Local source tree imports 6 | from pyof.foundation.base import GenericMessage 7 | from pyof.foundation.basic_types import Pad, UBInt8 8 | from pyof.v0x04.common.header import Header, Type 9 | from pyof.v0x04.common.port import Port 10 | 11 | # Third-party imports 12 | 13 | __all__ = ('PortStatus', 'PortReason') 14 | 15 | # Enums 16 | 17 | 18 | class PortReason(IntEnum): 19 | """What changed about the physical port.""" 20 | 21 | #: The port was added 22 | OFPPR_ADD = 0 23 | #: The port was removed 24 | OFPPR_DELETE = 1 25 | #: Some attribute of the port has changed 26 | OFPPR_MODIFY = 2 27 | 28 | 29 | # Classes 30 | 31 | class PortStatus(GenericMessage): 32 | """A physical port has changed in the datapath.""" 33 | 34 | #: :class:`~pyof.v0x04.common.action.ActionHeader`: OpenFlow Header 35 | header = Header(message_type=Type.OFPT_PORT_STATUS) 36 | #: One of OFPPR_*. 37 | reason = UBInt8(enum_ref=PortReason) 38 | #: Align to 32-bits. 39 | pad = Pad(7) 40 | #: :class:`~pyof.v0x04.common.port.Port` 41 | desc = Port() 42 | 43 | def __init__(self, xid=None, reason=None, desc=None): 44 | """Assign parameters to object attributes. 45 | 46 | Args: 47 | xid (int): Header's xid. 48 | reason (~pyof.v0x04.asynchronous.port_status.PortReason): 49 | Addition, deletion or modification. 50 | desc (~pyof.v0x04.common.port.Port): Port description. 51 | """ 52 | super().__init__(xid) 53 | self.reason = reason 54 | self.desc = desc 55 | -------------------------------------------------------------------------------- /pyof/v0x04/common/__init__.py: -------------------------------------------------------------------------------- 1 | """Common structures used on OpenFlow Protocol.""" 2 | -------------------------------------------------------------------------------- /pyof/v0x04/common/constants.py: -------------------------------------------------------------------------------- 1 | """Here we have the constants related to v0x04 version.""" 2 | OFP_VERSION = 0x04 3 | OFP_NO_BUFFER = 0xffffffff 4 | -------------------------------------------------------------------------------- /pyof/v0x04/common/header.py: -------------------------------------------------------------------------------- 1 | """Defines Header classes and related items.""" 2 | 3 | # System imports 4 | from enum import IntEnum 5 | 6 | # Local source tree imports 7 | from pyof.foundation.base import GenericStruct 8 | from pyof.foundation.basic_types import UBInt8, UBInt16, UBInt32 9 | from pyof.v0x04.common.constants import OFP_VERSION 10 | 11 | # Third-party imports 12 | 13 | __all__ = ('Header', 'Type') 14 | 15 | # Enums 16 | 17 | 18 | class Type(IntEnum): 19 | """Enumeration of Message Types.""" 20 | 21 | # Symetric/Immutable messages 22 | OFPT_HELLO = 0 23 | OFPT_ERROR = 1 24 | OFPT_ECHO_REQUEST = 2 25 | OFPT_ECHO_REPLY = 3 26 | OFPT_EXPERIMENTER = 4 27 | 28 | # Switch configuration messages 29 | # Controller/Switch messages 30 | OFPT_FEATURES_REQUEST = 5 31 | OFPT_FEATURES_REPLY = 6 32 | OFPT_GET_CONFIG_REQUEST = 7 33 | OFPT_GET_CONFIG_REPLY = 8 34 | OFPT_SET_CONFIG = 9 35 | 36 | # Async messages 37 | OFPT_PACKET_IN = 10 38 | OFPT_FLOW_REMOVED = 11 39 | OFPT_PORT_STATUS = 12 40 | 41 | # Controller command messages 42 | # Controller/Switch message 43 | OFPT_PACKET_OUT = 13 44 | OFPT_FLOW_MOD = 14 45 | OFPT_GROUP_MOD = 15 46 | OFPT_PORT_MOD = 16 47 | OFPT_TABLE_MOD = 17 48 | 49 | # Multipart messages. 50 | # Controller/Switch message 51 | OFPT_MULTIPART_REQUEST = 18 52 | OFPT_MULTIPART_REPLY = 19 53 | 54 | # Barrier messages 55 | # Controller/Switch message 56 | OFPT_BARRIER_REQUEST = 20 57 | OFPT_BARRIER_REPLY = 21 58 | 59 | # Queue Configuration messages 60 | # Controller/Switch message 61 | OFPT_QUEUE_GET_CONFIG_REQUEST = 22 62 | OFPT_QUEUE_GET_CONFIG_REPLY = 23 63 | 64 | # Controller role change request message 65 | # Controller/Switch message 66 | OFPT_ROLE_REQUEST = 24 67 | OFPT_ROLE_REPLY = 25 68 | 69 | # Asynchronous message configuration 70 | # Controller/Switch message 71 | OFPT_GET_ASYNC_REQUEST = 26 72 | OFPT_GET_ASYNC_REPLY = 27 73 | OFPT_SET_ASYNC = 28 74 | 75 | # Meters and rate limiters configuration messages 76 | # Controller/Switch message 77 | OFPT_METER_MOD = 29 78 | 79 | 80 | # Classes 81 | 82 | 83 | class Header(GenericStruct): 84 | """Representation of an OpenFlow message Header.""" 85 | 86 | version = UBInt8(OFP_VERSION) 87 | message_type = UBInt8(enum_ref=Type) 88 | length = UBInt16() 89 | xid = UBInt32() 90 | 91 | def __init__(self, message_type=None, length=None, xid=None): 92 | """Create a Header with the optional parameters below. 93 | 94 | Args: 95 | message_type (~pyof.v0x04.common.header.Type): 96 | One of the OFPT_* constants. 97 | length (int): Length including this ofp_header. 98 | xid (int): Transaction id associated with this packet. Replies use 99 | the same id as was in the request to facilitate pairing. 100 | """ 101 | super().__init__() 102 | self.message_type = message_type 103 | self.length = length 104 | self.xid = xid 105 | 106 | def __repr__(self): 107 | return (f"{type(self).__name__}(version={self.version}, " 108 | f"xid={self.xid}, {self.message_type!s})") 109 | -------------------------------------------------------------------------------- /pyof/v0x04/controller2switch/__init__.py: -------------------------------------------------------------------------------- 1 | """Controller to Switch and Switch to Controller Messages.""" 2 | -------------------------------------------------------------------------------- /pyof/v0x04/controller2switch/barrier_reply.py: -------------------------------------------------------------------------------- 1 | """Defines Barrier Reply message.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | from pyof.foundation.base import GenericMessage 8 | from pyof.v0x04.common.header import Header, Type 9 | 10 | __all__ = ('BarrierReply',) 11 | 12 | # Classes 13 | 14 | 15 | class BarrierReply(GenericMessage): 16 | """OpenFlow Barrier Reply Message. 17 | 18 | This message does not contain a body beyond the OpenFlow Header. 19 | """ 20 | 21 | header = Header(message_type=Type.OFPT_BARRIER_REPLY) 22 | -------------------------------------------------------------------------------- /pyof/v0x04/controller2switch/barrier_request.py: -------------------------------------------------------------------------------- 1 | """Defines Barrier Request message.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | from pyof.foundation.base import GenericMessage 8 | from pyof.v0x04.common.header import Header, Type 9 | 10 | __all__ = ('BarrierRequest',) 11 | 12 | # Classes 13 | 14 | 15 | class BarrierRequest(GenericMessage): 16 | """OpenFlow Barrier Request Message. 17 | 18 | This message does not contain a body in addition to the OpenFlow Header. 19 | """ 20 | 21 | header = Header(message_type=Type.OFPT_BARRIER_REQUEST) 22 | -------------------------------------------------------------------------------- /pyof/v0x04/controller2switch/features_reply.py: -------------------------------------------------------------------------------- 1 | """Defines Features Reply classes and related items.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | # Local source tree imports 8 | from pyof.foundation.base import GenericBitMask, GenericMessage 9 | from pyof.foundation.basic_types import DPID, Pad, UBInt8, UBInt32 10 | from pyof.v0x04.common.header import Header, Type 11 | 12 | __all__ = ('FeaturesReply', 'Capabilities', 'SwitchFeatures') 13 | 14 | 15 | class Capabilities(GenericBitMask): 16 | """Capabilities supported by the datapath.""" 17 | 18 | #: Flow statistics 19 | OFPC_FLOW_STATS = 1 << 0 20 | #: Table statistics 21 | OFPC_TABLE_STATS = 1 << 1 22 | #: Port statistics 23 | OFPC_PORT_STATS = 1 << 2 24 | #: Group statistics. 25 | OFPC_GROUP_STATS = 1 << 3 26 | #: Can reassembe IP fragments 27 | OFPC_IP_REASM = 1 << 5 28 | #: Queue statistics 29 | OFPC_QUEUE_STATS = 1 << 6 30 | #: Switch will block looping ports. 31 | OFPC_PORT_BLOCKED = 1 << 8 32 | 33 | 34 | # Classes 35 | 36 | class SwitchFeatures(GenericMessage): 37 | """Message sent by the switch device to the controller. 38 | 39 | This message is the response for a features_request message, sent by the 40 | controller to the switch device. The 'OFPT_FEATURES_REPLY' message inherits 41 | from this class, despite the strange name. 42 | """ 43 | 44 | header = Header(message_type=Type.OFPT_FEATURES_REPLY) 45 | datapath_id = DPID() 46 | 47 | n_buffers = UBInt32() 48 | 49 | n_tables = UBInt8() 50 | auxiliary_id = UBInt8() 51 | #: Align to 64-bits. 52 | pad = Pad(2) 53 | 54 | # Features 55 | capabilities = UBInt32(enum_ref=Capabilities) 56 | reserved = UBInt32() 57 | 58 | def __init__(self, xid=None, datapath_id=None, n_buffers=None, 59 | n_tables=None, auxiliary_id=None, capabilities=None, 60 | reserved=None): 61 | """Create a SwitchFeatures with the optional parameters below. 62 | 63 | Args: 64 | xid (int): xid to be used on the message header. 65 | datapath_id (int): Datapath unique ID. 66 | The lower 48-bits are for MAC address, while 67 | the upper 16-bits are implementer-defined. 68 | n_buffers (int): Max packets buffered at once. 69 | n_tables (int): Number of tables supported by datapath. 70 | auxiliary_id (int): Identify auxiliary connections. 71 | capabilities (int): bitmap of supported capabilities. 72 | reserved (int): Reserved. 73 | """ 74 | super().__init__(xid) 75 | self.datapath_id = datapath_id 76 | self.n_buffers = n_buffers 77 | self.n_tables = n_tables 78 | self.auxiliary_id = auxiliary_id 79 | self.capabilities = capabilities 80 | self.reserved = reserved 81 | 82 | 83 | class FeaturesReply(SwitchFeatures): 84 | """'OFPT_FEATURES_REPLY' message.""" 85 | -------------------------------------------------------------------------------- /pyof/v0x04/controller2switch/features_request.py: -------------------------------------------------------------------------------- 1 | """Defines Features Request classes and related items.""" 2 | 3 | from pyof.foundation.base import GenericMessage 4 | from pyof.v0x04.common.header import Header, Type 5 | 6 | __all__ = ('FeaturesRequest',) 7 | 8 | # Classes 9 | 10 | 11 | class FeaturesRequest(GenericMessage): 12 | """Features request message. 13 | 14 | This message does not contain a body in addition to the OpenFlow Header. 15 | """ 16 | 17 | header = Header( 18 | message_type=Type.OFPT_FEATURES_REQUEST) 19 | -------------------------------------------------------------------------------- /pyof/v0x04/controller2switch/get_async_reply.py: -------------------------------------------------------------------------------- 1 | """Define GetAsyncReply message. 2 | 3 | Response to the GetAsyncRequest message. 4 | """ 5 | 6 | # System imports 7 | 8 | # Third-party imports 9 | 10 | # Local imports 11 | from pyof.v0x04.common.header import Type 12 | from pyof.v0x04.controller2switch.common import AsyncConfig 13 | 14 | __all__ = ('GetAsyncReply',) 15 | 16 | 17 | class GetAsyncReply(AsyncConfig): 18 | """GetAsyncReply message. 19 | 20 | Response to GetAsyncRequest message. 21 | """ 22 | 23 | def __init__(self, xid=None, packet_in_mask1=None, packet_in_mask2=None, 24 | port_status_mask1=None, port_status_mask2=None, 25 | flow_removed_mask1=None, flow_removed_mask2=None): 26 | """Set attributes for GetAsyncReply message. 27 | 28 | Args: 29 | xid (int): xid to be used on the message header. 30 | packet_in_mask1 (|PacketInReason_v0x04|): 31 | A instance of PacketInReason 32 | packet_in_mask2 (|PacketInReason_v0x04|): 33 | A instance of PacketInReason 34 | port_status_mask1 (|PortReason_v0x04|): 35 | A instance of PortReason 36 | port_status_mask2 (|PortReason_v0x04|): 37 | A instance of PortReason 38 | flow_removed_mask1 (|FlowRemoved_v0x04|): 39 | A instance of FlowRemoved. 40 | flow_removed_mask2 (|FlowRemoved_v0x04|): 41 | A instance of FlowRemoved. 42 | """ 43 | super().__init__(xid, packet_in_mask1, packet_in_mask2, 44 | port_status_mask1, port_status_mask2, 45 | flow_removed_mask1, flow_removed_mask2) 46 | self.header.message_type = Type.OFPT_GET_ASYNC_REPLY 47 | -------------------------------------------------------------------------------- /pyof/v0x04/controller2switch/get_async_request.py: -------------------------------------------------------------------------------- 1 | """Get Async Request Message.""" 2 | # System imports 3 | 4 | # Third-party imports 5 | 6 | # Local imports 7 | from pyof.foundation.base import GenericMessage 8 | from pyof.v0x04.common.header import Header, Type 9 | 10 | __all__ = ('GetAsyncRequest',) 11 | 12 | 13 | class GetAsyncRequest(GenericMessage): 14 | """Request Asynchronous messages. 15 | 16 | Query the asynchronous messages that it wants to receive (other than error 17 | messages) on a given OpenFlow channel. 18 | """ 19 | 20 | #: OpenFlow :class:`~pyof.v0x04.common.header.Header` 21 | header = Header(message_type=Type.OFPT_GET_ASYNC_REQUEST) 22 | -------------------------------------------------------------------------------- /pyof/v0x04/controller2switch/get_config_reply.py: -------------------------------------------------------------------------------- 1 | """Defines Get Config Reply message.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | from pyof.v0x04.common.header import Type 8 | from pyof.v0x04.controller2switch.common import SwitchConfig 9 | 10 | __all__ = ('GetConfigReply',) 11 | 12 | 13 | class GetConfigReply(SwitchConfig): 14 | """Get Config Reply message.""" 15 | 16 | def __init__(self, xid=None, flags=None, miss_send_len=None): 17 | """Create a GetConfigReply with the optional parameters below. 18 | 19 | Args: 20 | xid (int): xid to be used on the message header. 21 | flags (~pyof.v0x04.controller2switch.common.ConfigFlag): 22 | OFPC_* flags. 23 | miss_send_len (int): UBInt16 max bytes of new flow that the 24 | datapath should send to the controller. 25 | """ 26 | super().__init__(xid, flags, miss_send_len) 27 | self.header.message_type = Type.OFPT_GET_CONFIG_REPLY 28 | -------------------------------------------------------------------------------- /pyof/v0x04/controller2switch/get_config_request.py: -------------------------------------------------------------------------------- 1 | """Defines Get Config Request classes and related items.""" 2 | 3 | from pyof.foundation.base import GenericMessage 4 | from pyof.v0x04.common.header import Header, Type 5 | 6 | __all__ = ('GetConfigRequest',) 7 | 8 | # Classe 9 | 10 | 11 | class GetConfigRequest(GenericMessage): 12 | """Get Config Request message.""" 13 | 14 | header = Header(message_type=Type.OFPT_GET_CONFIG_REQUEST) 15 | -------------------------------------------------------------------------------- /pyof/v0x04/controller2switch/group_mod.py: -------------------------------------------------------------------------------- 1 | """Modify Group Entry Message.""" 2 | from enum import IntEnum 3 | 4 | from pyof.foundation.base import GenericMessage 5 | from pyof.foundation.basic_types import ( 6 | FixedTypeList, Pad, UBInt8, UBInt16, UBInt32) 7 | from pyof.v0x04.common.header import Header, Type 8 | from pyof.v0x04.controller2switch.common import Bucket 9 | 10 | __all__ = ('GroupMod', 'GroupModCommand', 'GroupType', 'Group', 11 | 'ListOfBuckets') 12 | 13 | 14 | class Group(IntEnum): 15 | """Group numbering. Groups can use any number up to attr:`OFPG_MAX`.""" 16 | 17 | #: Last usable group number. 18 | OFPG_MAX = 0xffffff00 19 | #: Fake groups. 20 | #: Represents all groups for group delete commands. 21 | OFPG_ALL = 0xfffffffc 22 | #: Wildcard group used only for flow stats requests. 23 | # Select all flows regardless of group (including flows with no group). 24 | OFPG_ANY = 0xffffffff 25 | 26 | 27 | class GroupModCommand(IntEnum): 28 | """Group commands.""" 29 | 30 | #: New group. 31 | OFPGC_ADD = 0 32 | #: Modify all matching groups. 33 | OFPGC_MODIFY = 1 34 | #: Delete all matching groups. 35 | OFPGC_DELETE = 2 36 | 37 | 38 | class GroupType(IntEnum): 39 | """Group types. Range [128, 255] is reserved for experimental use.""" 40 | 41 | #: All (multicast/broadcast) group. 42 | OFPGT_ALL = 0 43 | #: Select group. 44 | OFPGT_SELECT = 1 45 | #: Indirect group. 46 | OFPGT_INDIRECT = 2 47 | #: Fast failover group. 48 | OFPGT_FF = 3 49 | 50 | 51 | class ListOfBuckets(FixedTypeList): 52 | """List of buckets. 53 | 54 | Represented by instances of Bucket. 55 | """ 56 | 57 | def __init__(self, items=None): 58 | """Create a ListOfBuckets with the optional parameters below. 59 | 60 | Args: 61 | items (Bucket): Instance or a list of instances. 62 | """ 63 | super().__init__(pyof_class=Bucket, items=items) 64 | 65 | 66 | class GroupMod(GenericMessage): 67 | """Group setup and teardown (controller -> datapath).""" 68 | 69 | header = Header(message_type=Type.OFPT_GROUP_MOD) 70 | command = UBInt16(enum_ref=GroupModCommand) 71 | group_type = UBInt8() 72 | #: Pad to 64 bits. 73 | pad = Pad(1) 74 | group_id = UBInt32() 75 | buckets = ListOfBuckets() 76 | 77 | def __init__(self, xid=None, command=None, group_type=None, group_id=None, 78 | buckets=None): 79 | """Create a GroupMod with the optional parameters below. 80 | 81 | Args: 82 | xid (int): Header's transaction id. Defaults to random. 83 | command (GroupModCommand): One of OFPGC_*. 84 | group_type (GroupType): One of OFPGT_*. 85 | group_id (int): Group identifier. 86 | buckets (:class:`ListOfBuckets`): The length of the bucket 87 | array is inferred from the length field in the header. 88 | """ 89 | super().__init__(xid) 90 | self.command = command 91 | self.group_type = group_type 92 | self.group_id = group_id 93 | self.buckets = buckets 94 | 95 | def __repr__(self): 96 | return (f"{type(self).__name__}(xid={self.header.xid}, {self.command}," 97 | f" group_type={self.group_type}, group_id={self.group_id}," 98 | f" buckets={self.buckets!r})") 99 | -------------------------------------------------------------------------------- /pyof/v0x04/controller2switch/port_mod.py: -------------------------------------------------------------------------------- 1 | """Modifications to the port from the controller.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | # Local source tree imports 8 | from pyof.foundation.base import GenericMessage 9 | from pyof.foundation.basic_types import HWAddress, Pad, UBInt32 10 | from pyof.v0x04.common.header import Header, Type 11 | from pyof.v0x04.common.port import PortConfig, PortFeatures 12 | 13 | __all__ = ('PortMod',) 14 | 15 | # Classes 16 | 17 | 18 | class PortMod(GenericMessage): 19 | """Implement messages to modify the physical port behavior.""" 20 | 21 | header = Header(message_type=Type.OFPT_PORT_MOD) 22 | port_no = UBInt32() 23 | pad = Pad(4) 24 | hw_addr = HWAddress() 25 | pad2 = Pad(2) 26 | config = UBInt32(enum_ref=PortConfig) 27 | mask = UBInt32(enum_ref=PortConfig) 28 | advertise = UBInt32(enum_ref=PortFeatures) 29 | #: Pad to 64-bits. 30 | pad3 = Pad(4) 31 | 32 | def __init__(self, xid=None, port_no=None, hw_addr=None, config=None, 33 | mask=None, advertise=None): 34 | """Create a PortMod with the optional parameters below. 35 | 36 | Args: 37 | xid (int): OpenFlow xid to the header. 38 | port_no (int): Physical port number. 39 | hw_addr (HWAddress): The hardware address is not configurable. 40 | This is used to sanity-check the request, 41 | so it must be the same as returned in an ofp_phy_port struct. 42 | config (~pyof.v0x04.common.port.PortConfig): 43 | Bitmap of OFPPC_* flags 44 | mask (~pyof.v0x04.common.port.PortConfig): 45 | Bitmap of OFPPC_* flags to be changed 46 | advertise (~pyof.v0x04.common.port.PortFeatures): 47 | Bitmap of OFPPF_*. Zero all bits to prevent any action taking 48 | place. 49 | """ 50 | super().__init__(xid) 51 | self.port_no = port_no 52 | self.hw_addr = hw_addr 53 | self.config = config 54 | self.mask = mask 55 | self.advertise = advertise 56 | -------------------------------------------------------------------------------- /pyof/v0x04/controller2switch/queue_get_config_reply.py: -------------------------------------------------------------------------------- 1 | """Switch replies to controller.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | # Local source tree imports 8 | from pyof.foundation.base import GenericMessage 9 | from pyof.foundation.basic_types import Pad, UBInt32 10 | from pyof.v0x04.common.header import Header, Type 11 | from pyof.v0x04.common.port import PortNo 12 | from pyof.v0x04.common.queue import ListOfQueues 13 | 14 | __all__ = ('QueueGetConfigReply',) 15 | 16 | 17 | class QueueGetConfigReply(GenericMessage): 18 | """Class implements the response to the config request.""" 19 | 20 | #: Openflow :class:`~pyof.v0x04.common.header.Header`. 21 | header = Header(message_type=Type.OFPT_GET_CONFIG_REPLY) 22 | #: Port to be queried. Should refer to a valid physical port 23 | #: (i.e. < OFPP_MAX), or OFPP_ANY to request all configured queues. 24 | port = UBInt32(enum_ref=PortNo) 25 | #: Pad to 64-bits. 26 | pad = Pad(4) 27 | #: List of configured queues. 28 | queues = ListOfQueues() 29 | 30 | def __init__(self, xid=None, port=None, queues=None): 31 | """Create a QueueGetConfigReply with the optional parameters below. 32 | 33 | Args: 34 | xid (int): xid of OpenFlow header. 35 | port (:class:`~pyof.v0x04.common.port.PortNo`): 36 | Target port for the query. 37 | queue (:class:`~pyof.v0x04.common.queue.ListOfQueues`): 38 | List of configured queues. 39 | """ 40 | super().__init__(xid) 41 | self.port = port 42 | self.queues = [] if queues is None else queues 43 | -------------------------------------------------------------------------------- /pyof/v0x04/controller2switch/queue_get_config_request.py: -------------------------------------------------------------------------------- 1 | """Query the switch for configured queues on a port.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | # Local source tree imports 8 | from pyof.foundation.base import GenericMessage 9 | from pyof.foundation.basic_types import Pad, UBInt32 10 | from pyof.v0x04.common.header import Header, Type 11 | from pyof.v0x04.common.port import PortNo 12 | 13 | __all__ = ('QueueGetConfigRequest',) 14 | 15 | 16 | class QueueGetConfigRequest(GenericMessage): 17 | """Query structure for configured queues on a port.""" 18 | 19 | #: Openflow :class:`~pyof.v0x04.common.header.Header`. 20 | header = Header(message_type=Type.OFPT_GET_CONFIG_REQUEST) 21 | #: Port to be queried. Should refer to a valid physical port 22 | #: (i.e. < OFPP_MAX), or OFPP_ANY to request all configured queues. 23 | port = UBInt32(enum_ref=PortNo) 24 | pad = Pad(4) 25 | 26 | def __init__(self, xid=None, port=None): 27 | """Create a QueueGetConfigRequest with the optional parameters below. 28 | 29 | Args: 30 | xid (int): xid of OpenFlow header 31 | port (:class:`~.common.port.PortNo`): Target port for the query. 32 | """ 33 | super().__init__(xid) 34 | self.port = port 35 | -------------------------------------------------------------------------------- /pyof/v0x04/controller2switch/role_reply.py: -------------------------------------------------------------------------------- 1 | """Request a change of the role of the controller.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | # Local source tree imports 8 | from pyof.foundation.constants import UBINT64_MAX_VALUE 9 | from pyof.v0x04.common.header import Type 10 | from pyof.v0x04.controller2switch.common import ControllerRole, RoleBaseMessage 11 | 12 | __all__ = ('RoleReply',) 13 | 14 | # Classes 15 | 16 | 17 | class RoleReply(RoleBaseMessage): 18 | """RoleReply Message.""" 19 | 20 | def __init__(self, xid=None, role=ControllerRole.OFPCR_ROLE_NOCHANGE, 21 | generation_id=UBINT64_MAX_VALUE): 22 | """Create a RoleReply with the optional parameters below. 23 | 24 | Args: 25 | xid (int): OpenFlow xid to the header. 26 | role (:class:`~.controller2switch.common.ControllerRole`): 27 | Is the new role that the controller wants to assume. 28 | generation_id (int): Master Election Generation Id. The default 29 | value is -1. For this field, it's UBINT64_MAX_VALUE. 30 | """ 31 | super().__init__(xid, role, generation_id) 32 | self.header.message_type = Type.OFPT_ROLE_REPLY 33 | -------------------------------------------------------------------------------- /pyof/v0x04/controller2switch/role_request.py: -------------------------------------------------------------------------------- 1 | """Request a change of the role of the controller.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | # Local source tree imports 8 | from pyof.v0x04.common.header import Type 9 | from pyof.v0x04.controller2switch.common import RoleBaseMessage 10 | 11 | __all__ = ('RoleRequest',) 12 | 13 | # Classes 14 | 15 | 16 | class RoleRequest(RoleBaseMessage): 17 | """RoleRequest Message. 18 | 19 | When the controller wants to change its role, it uses the OFPT_ROLE_REQUEST 20 | message. 21 | """ 22 | 23 | def __init__(self, xid=None, role=None, generation_id=None): 24 | """Create a RoleRequest with the optional parameters below. 25 | 26 | Args: 27 | xid (int): OpenFlow xid to the header. 28 | role (:class:`~.controller2switch.common.ControllerRole`): 29 | Is the new role that the controller wants to assume. 30 | generation_id (int): Master Election Generation Id. 31 | """ 32 | super().__init__(xid, role, generation_id) 33 | self.header.message_type = Type.OFPT_ROLE_REQUEST 34 | -------------------------------------------------------------------------------- /pyof/v0x04/controller2switch/set_async.py: -------------------------------------------------------------------------------- 1 | """Define SetAsync message. 2 | 3 | Sets whether a controller should receive a given asynchronous message that is 4 | generated by the switch. 5 | """ 6 | 7 | # System imports 8 | 9 | # Third-party imports 10 | 11 | # Local imports 12 | from pyof.v0x04.common.header import Type 13 | from pyof.v0x04.controller2switch.common import AsyncConfig 14 | 15 | __all__ = ('SetAsync',) 16 | 17 | 18 | class SetAsync(AsyncConfig): 19 | """SetAsync message. 20 | 21 | Sets whether a controller should receive a given asynchronous message that 22 | is generated by the switch. 23 | """ 24 | 25 | def __init__(self, xid=None, packet_in_mask1=None, packet_in_mask2=None, 26 | port_status_mask1=None, port_status_mask2=None, 27 | flow_removed_mask1=None, flow_removed_mask2=None): 28 | """Set attributes for SetAsync message. 29 | 30 | Args: 31 | xid (int): xid to be used on the message header. 32 | packet_in_mask1 (|PacketInReason_v0x04|): 33 | A instance of PacketInReason 34 | packet_in_mask2 (|PacketInReason_v0x04|): 35 | A instance of PacketInReason 36 | port_status_mask1 (|PortReason_v0x04|): 37 | A instance of PortReason 38 | port_status_mask2 (|PortReason_v0x04|): 39 | A instance of PortReason 40 | flow_removed_mask1 (|FlowRemoved_v0x04|): 41 | A instance of FlowRemoved. 42 | flow_removed_mask2 (|FlowRemoved_v0x04|): 43 | A instance of FlowRemoved. 44 | """ 45 | super().__init__(xid, packet_in_mask1, packet_in_mask2, 46 | port_status_mask1, port_status_mask2, 47 | flow_removed_mask1, flow_removed_mask2) 48 | self.header.message_type = Type.OFPT_SET_ASYNC 49 | -------------------------------------------------------------------------------- /pyof/v0x04/controller2switch/set_config.py: -------------------------------------------------------------------------------- 1 | """Define SetConfig message.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | # Local imports 8 | from pyof.v0x04.common.action import ControllerMaxLen 9 | from pyof.v0x04.common.header import Type 10 | from pyof.v0x04.controller2switch.common import ConfigFlag, SwitchConfig 11 | 12 | __all__ = ('SetConfig',) 13 | 14 | 15 | class SetConfig(SwitchConfig): 16 | """Set config message.""" 17 | 18 | def __init__(self, xid=None, flags=ConfigFlag.OFPC_FRAG_NORMAL, 19 | miss_send_len=ControllerMaxLen.OFPCML_NO_BUFFER): 20 | """Create a SetConfig with the optional parameters below. 21 | 22 | Args: 23 | xid (int): xid to be used on the message header. 24 | flags (:class:`~pyof.v0x01.controller2switch.common.ConfigFlag`): 25 | OFPC_* flags. 26 | miss_send_len (int): UBInt16 max bytes of new flow that the 27 | datapath should send to the controller. 28 | """ 29 | super().__init__(xid, flags, miss_send_len) 30 | self.header.message_type = Type.OFPT_SET_CONFIG 31 | -------------------------------------------------------------------------------- /pyof/v0x04/controller2switch/table_mod.py: -------------------------------------------------------------------------------- 1 | """Flow Table Modification message.""" 2 | from enum import IntEnum 3 | 4 | from pyof.foundation.base import GenericMessage 5 | from pyof.foundation.basic_types import Pad, UBInt8, UBInt32 6 | from pyof.v0x04.common.header import Header, Type 7 | 8 | __all__ = ('Table', 'TableMod') 9 | 10 | 11 | class Table(IntEnum): 12 | """Table numbering. Tables can use any number up to OFPT_MAX.""" 13 | 14 | #: Last usable table number. 15 | OFPTT_MAX = 0xfe 16 | # Fake tables. 17 | #: Wildcard table used for table config, flow stats and flow deletes. 18 | OFPTT_ALL = 0xff 19 | 20 | 21 | class TableMod(GenericMessage): 22 | """Configure/Modify behavior of a flow table.""" 23 | 24 | header = Header(message_type=Type.OFPT_TABLE_MOD) 25 | table_id = UBInt8() 26 | #: Pad to 32 bits 27 | pad = Pad(3) 28 | config = UBInt32() 29 | 30 | def __init__(self, xid=None, table_id=Table.OFPTT_ALL, config=3): 31 | """Assing parameters to object attributes. 32 | 33 | Args: 34 | xid (int): :class:`~pyof.v0x04.common.header.Header`'s xid. 35 | Defaults to random. 36 | table_id (int): ID of the table, OFPTT_ALL indicates all tables. 37 | config (int): Bitmap of OFPTC_* flags 38 | """ 39 | super().__init__(xid) 40 | self.table_id = table_id 41 | # This is reserved for future used. The default value is the only valid 42 | # one from the Enum. 43 | self.config = config 44 | -------------------------------------------------------------------------------- /pyof/v0x04/symmetric/__init__.py: -------------------------------------------------------------------------------- 1 | """Symmetric Messages.""" 2 | -------------------------------------------------------------------------------- /pyof/v0x04/symmetric/echo_reply.py: -------------------------------------------------------------------------------- 1 | """Defines Echo Reply message during the handshake.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | from pyof.foundation.base import GenericMessage 8 | from pyof.foundation.basic_types import BinaryData 9 | from pyof.v0x04.common.header import Header, Type 10 | 11 | __all__ = ('EchoReply',) 12 | 13 | # Classes 14 | 15 | 16 | class EchoReply(GenericMessage): 17 | """OpenFlow Reply message. 18 | 19 | This message does not contain a body beyond the OpenFlow Header. 20 | """ 21 | 22 | header = Header(message_type=Type.OFPT_ECHO_REPLY, length=8) 23 | data = BinaryData() 24 | 25 | def __init__(self, xid=None, data=b''): 26 | """Create a EchoReply with the optional parameters below. 27 | 28 | Args: 29 | xid (int): xid to be used on the message header. 30 | data (bytes): arbitrary-length data field. 31 | """ 32 | super().__init__(xid) 33 | self.data = data 34 | -------------------------------------------------------------------------------- /pyof/v0x04/symmetric/echo_request.py: -------------------------------------------------------------------------------- 1 | """Defines Echo Request message during the handshake.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | from pyof.foundation.base import GenericMessage 8 | from pyof.foundation.basic_types import BinaryData 9 | from pyof.v0x04.common.header import Header, Type 10 | 11 | __all__ = ('EchoRequest',) 12 | 13 | # Classes 14 | 15 | 16 | class EchoRequest(GenericMessage): 17 | """OpenFlow Reply message. 18 | 19 | This message does not contain a body after the OpenFlow Header. 20 | """ 21 | 22 | header = Header(message_type=Type.OFPT_ECHO_REQUEST, length=8) 23 | data = BinaryData() 24 | 25 | def __init__(self, xid=None, data=b''): 26 | """Create a EchoRequest with the optional parameters below. 27 | 28 | Args: 29 | xid (int): xid to be used on the message header. 30 | data (bytes): arbitrary-length data field. 31 | """ 32 | super().__init__(xid) 33 | self.data = data 34 | -------------------------------------------------------------------------------- /pyof/v0x04/symmetric/experimenter.py: -------------------------------------------------------------------------------- 1 | """Defines Experimenter message.""" 2 | 3 | # System imports 4 | 5 | # Third-party imports 6 | 7 | from pyof.foundation.base import GenericMessage 8 | from pyof.foundation.basic_types import BinaryData, UBInt32 9 | from pyof.v0x04.common.header import Header, Type 10 | 11 | __all__ = ('ExperimenterHeader',) 12 | 13 | # Classes 14 | 15 | 16 | class ExperimenterHeader(GenericMessage): 17 | """OpenFlow Experimenter message. 18 | 19 | The experimenter field is a 32-bit value that uniquely identifies the 20 | experimenter. If the most significant byte is zero, the next three bytes 21 | are the experimenter’s IEEE OUI. If the most significant byte is not zero, 22 | it is a value allocated by the Open Networking Foundation. If experimenter 23 | does not have (or wish to use) their OUI, they should contact the Open 24 | Networking Foundation to obtain an unique experimenter ID. 25 | 26 | The rest of the body is uninterpreted by standard OpenFlow processing and 27 | is arbitrarily defined by the corresponding experimenter. 28 | 29 | If a switch does not understand an experimenter extension, it must send an 30 | OFPT_ERROR message with a OFPBRC_BAD_EXPERIMENTER error code and 31 | OFPET_BAD_REQUEST error type. 32 | """ 33 | 34 | header = Header(message_type=Type.OFPT_EXPERIMENTER) 35 | experimenter = UBInt32() 36 | exp_type = UBInt32() 37 | data = BinaryData() 38 | 39 | def __init__(self, xid=None, experimenter=None, exp_type=None, data=b''): 40 | """Create a ExperimenterHeader with the optional parameters below. 41 | 42 | Args: 43 | xid (int): xid to be used on the message header. 44 | experimenter (int): Vendor ID: 45 | MSB 0: low-order bytes are IEEE OUI. 46 | MSB != 0: defined by ONF. 47 | exp_type (int): Experimenter defined. 48 | """ 49 | super().__init__(xid) 50 | self.experimenter = experimenter 51 | self.exp_type = exp_type 52 | self.data = data 53 | -------------------------------------------------------------------------------- /raw/README.rst: -------------------------------------------------------------------------------- 1 | Generating raw dump files 2 | ========================= 3 | 4 | OpenFlow 1.0 5 | ------------ 6 | 7 | 1. POX - version 0.2.0 (carp), cloned from `GitHub 8 | `_: 9 | 10 | - ``./pox.py openflow.of_01 --address=127.0.0.1 --port=6633 py`` 11 | 12 | 2. Wireshark - version 2.0.4 with `openflow plugin 13 | `_: 14 | 15 | - Choose *loopback* interface; 16 | - Filter by *of*; 17 | - Start capturing; 18 | - After running Mininet as described below, in order to save the OpenFlow content in a dump file, 19 | right click in the *OpenFlow* tree and then *Export Selected Bytes...*. All done! 20 | 21 | 3. Mininet - version 2.2.1 22 | 23 | - ``sudo mn --controller=remote,ip='127.0.0.1',listenport=6633 --switch ovs,protocols=OpenFlow10`` 24 | 25 | 26 | OpenFlow 1.3 27 | ------------ 28 | 29 | 1. POX forked version, cloned from `GitHub 30 | `_: 31 | 32 | - ``./pox.py openflow.of_04 --address=127.0.0.1 --port=6653 py`` 33 | 34 | 2. Wireshark - version 2.0.4 with `openflow plugin 35 | `_: 36 | 37 | - Choose *loopback* interface; 38 | - Filter by *of*; 39 | - Start capturing; 40 | - After running Mininet as described below, in order to save the OpenFlow content in a dump file, 41 | right click in the *OpenFlow* tree and then *Export Selected Bytes...*. All done! 42 | 43 | 3. Mininet - version 2.2.1 44 | 45 | - ``sudo mn --controller=remote,ip='127.0.0.1',listenport=6653 --switch ovs,protocols=OpenFlow13`` 46 | -------------------------------------------------------------------------------- /raw/v0x01/ofpt_aggregate_stats_reply.dat: -------------------------------------------------------------------------------- 1 | $ -------------------------------------------------------------------------------- /raw/v0x01/ofpt_aggregate_stats_request.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x01/ofpt_aggregate_stats_request.dat -------------------------------------------------------------------------------- /raw/v0x01/ofpt_barrier_reply.dat: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /raw/v0x01/ofpt_barrier_request.dat: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /raw/v0x01/ofpt_desc_stats_reply.dat: -------------------------------------------------------------------------------- 1 | ,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -------------------------------------------------------------------------------- /raw/v0x01/ofpt_echo_reply.dat: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /raw/v0x01/ofpt_echo_request.dat: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /raw/v0x01/ofpt_error_msg.dat: -------------------------------------------------------------------------------- 1 |   -------------------------------------------------------------------------------- /raw/v0x01/ofpt_features_reply.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x01/ofpt_features_reply.dat -------------------------------------------------------------------------------- /raw/v0x01/ofpt_features_request.dat: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /raw/v0x01/ofpt_flow_add.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x01/ofpt_flow_add.dat -------------------------------------------------------------------------------- /raw/v0x01/ofpt_flow_delete.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x01/ofpt_flow_delete.dat -------------------------------------------------------------------------------- /raw/v0x01/ofpt_flow_removed.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x01/ofpt_flow_removed.dat -------------------------------------------------------------------------------- /raw/v0x01/ofpt_flow_stats_reply.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x01/ofpt_flow_stats_reply.dat -------------------------------------------------------------------------------- /raw/v0x01/ofpt_flow_stats_request.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x01/ofpt_flow_stats_request.dat -------------------------------------------------------------------------------- /raw/v0x01/ofpt_get_config_reply.dat: -------------------------------------------------------------------------------- 1 |   -------------------------------------------------------------------------------- /raw/v0x01/ofpt_get_config_request.dat: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /raw/v0x01/ofpt_hello.dat: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /raw/v0x01/ofpt_packet_in.dat: -------------------------------------------------------------------------------- 1 |  2 |  -------------------------------------------------------------------------------- /raw/v0x01/ofpt_packet_out.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x01/ofpt_packet_out.dat -------------------------------------------------------------------------------- /raw/v0x01/ofpt_port_mod.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x01/ofpt_port_mod.dat -------------------------------------------------------------------------------- /raw/v0x01/ofpt_port_stats.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x01/ofpt_port_stats.dat -------------------------------------------------------------------------------- /raw/v0x01/ofpt_port_stats_request.dat: -------------------------------------------------------------------------------- 1 | P -------------------------------------------------------------------------------- /raw/v0x01/ofpt_port_status.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x01/ofpt_port_status.dat -------------------------------------------------------------------------------- /raw/v0x01/ofpt_queue_get_config_reply.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x01/ofpt_queue_get_config_reply.dat -------------------------------------------------------------------------------- /raw/v0x01/ofpt_queue_get_config_request.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x01/ofpt_queue_get_config_request.dat -------------------------------------------------------------------------------- /raw/v0x01/ofpt_queue_stats.dat: -------------------------------------------------------------------------------- 1 | ,P -------------------------------------------------------------------------------- /raw/v0x01/ofpt_queue_stats_request.dat: -------------------------------------------------------------------------------- 1 | P -------------------------------------------------------------------------------- /raw/v0x01/ofpt_set_config.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x01/ofpt_set_config.dat -------------------------------------------------------------------------------- /raw/v0x01/ofpt_stats_reply.dat: -------------------------------------------------------------------------------- 1 |   -------------------------------------------------------------------------------- /raw/v0x01/ofpt_stats_request.dat: -------------------------------------------------------------------------------- 1 |   -------------------------------------------------------------------------------- /raw/v0x01/ofpt_table_stats.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x01/ofpt_table_stats.dat -------------------------------------------------------------------------------- /raw/v0x01/ofpt_vendor_header.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x01/ofpt_vendor_header.dat -------------------------------------------------------------------------------- /raw/v0x01/ofpt_vendor_stats_reply.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x01/ofpt_vendor_stats_reply.dat -------------------------------------------------------------------------------- /raw/v0x04/ofpt_aggregate_stats.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x04/ofpt_aggregate_stats.dat -------------------------------------------------------------------------------- /raw/v0x04/ofpt_aggregate_stats_request.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x04/ofpt_aggregate_stats_request.dat -------------------------------------------------------------------------------- /raw/v0x04/ofpt_barrier_reply.dat: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /raw/v0x04/ofpt_barrier_request.dat: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /raw/v0x04/ofpt_echo_reply.dat: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /raw/v0x04/ofpt_echo_request.dat: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /raw/v0x04/ofpt_error.dat: -------------------------------------------------------------------------------- 1 | $  2 |  -------------------------------------------------------------------------------- /raw/v0x04/ofpt_features_reply.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x04/ofpt_features_reply.dat -------------------------------------------------------------------------------- /raw/v0x04/ofpt_features_request.dat: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /raw/v0x04/ofpt_flow_mod.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x04/ofpt_flow_mod.dat -------------------------------------------------------------------------------- /raw/v0x04/ofpt_flow_removed.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x04/ofpt_flow_removed.dat -------------------------------------------------------------------------------- /raw/v0x04/ofpt_flow_stats.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x04/ofpt_flow_stats.dat -------------------------------------------------------------------------------- /raw/v0x04/ofpt_flow_stats_request.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x04/ofpt_flow_stats_request.dat -------------------------------------------------------------------------------- /raw/v0x04/ofpt_group_stats.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x04/ofpt_group_stats.dat -------------------------------------------------------------------------------- /raw/v0x04/ofpt_group_stats_request.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x04/ofpt_group_stats_request.dat -------------------------------------------------------------------------------- /raw/v0x04/ofpt_hello.dat: -------------------------------------------------------------------------------- 1 | > -------------------------------------------------------------------------------- /raw/v0x04/ofpt_meter_multipart_request.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x04/ofpt_meter_multipart_request.dat -------------------------------------------------------------------------------- /raw/v0x04/ofpt_packet_in.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x04/ofpt_packet_in.dat -------------------------------------------------------------------------------- /raw/v0x04/ofpt_packet_out.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x04/ofpt_packet_out.dat -------------------------------------------------------------------------------- /raw/v0x04/ofpt_port_desc.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x04/ofpt_port_desc.dat -------------------------------------------------------------------------------- /raw/v0x04/ofpt_port_status.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x04/ofpt_port_status.dat -------------------------------------------------------------------------------- /raw/v0x04/ofpt_set_config.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kytos/python-openflow/89940bed83f8e792f5ed5c9f12346016cd380d6f/raw/v0x04/ofpt_set_config.dat -------------------------------------------------------------------------------- /requirements/dev.in: -------------------------------------------------------------------------------- 1 | -e .[dev] 2 | -e git://github.com/kytos/sphinx-theme.git#egg=kytos-sphinx-theme 3 | -------------------------------------------------------------------------------- /requirements/dev.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile 3 | # To update, run: 4 | # 5 | # pip-compile --output-file=requirements/dev.txt requirements/dev.in 6 | # 7 | -e git+git://github.com/kytos/sphinx-theme.git#egg=kytos-sphinx-theme 8 | -e . 9 | alabaster==0.7.12 # via sphinx 10 | appdirs==1.4.3 # via virtualenv 11 | argh==0.26.2 # via sphinx-autobuild 12 | astroid==2.3.3 # via pylint 13 | attrs==19.3.0 # via pytest 14 | babel==2.8.0 # via sphinx 15 | certifi==2019.11.28 # via requests 16 | chardet==3.0.4 # via requests 17 | click==7.1.1 # via pip-tools 18 | coverage==5.0.4 # via python-openflow 19 | distlib==0.3.0 # via virtualenv 20 | docopt==0.6.2 # via yala 21 | docutils==0.16 # via sphinx 22 | filelock==3.0.12 # via tox, virtualenv 23 | idna==2.9 # via requests 24 | imagesize==1.2.0 # via sphinx 25 | importlib-metadata==1.6.0 # via importlib-resources, pluggy, pytest, tox, virtualenv 26 | importlib-resources==1.4.0 # via virtualenv 27 | isort==4.3.21 # via pylint, yala 28 | jinja2==2.11.1 # via sphinx 29 | lazy-object-proxy==1.4.3 # via astroid 30 | livereload==2.6.1 # via sphinx-autobuild 31 | markupsafe==1.1.1 # via jinja2 32 | mccabe==0.6.1 # via pylint 33 | more-itertools==8.2.0 # via pytest 34 | packaging==20.3 # via pytest, sphinx, tox 35 | pathtools==0.1.2 # via sphinx-autobuild, watchdog 36 | pip-tools==4.5.1 # via python-openflow 37 | pluggy==0.13.1 # via pytest, tox 38 | port_for==0.3.1 # via sphinx-autobuild 39 | py==1.8.1 # via pytest, tox 40 | pycodestyle==2.5.0 # via yala 41 | pygments==2.6.1 # via sphinx 42 | pylint==2.4.4 # via yala 43 | pyparsing==2.4.6 # via packaging 44 | pytest==5.4.1 # via python-openflow 45 | pytz==2019.3 # via babel 46 | pyyaml==5.3.1 # via sphinx-autobuild 47 | requests==2.23.0 # via sphinx 48 | six==1.14.0 # via astroid, livereload, packaging, pip-tools, tox, virtualenv 49 | snowballstemmer==2.0.0 # via sphinx 50 | sphinx-autobuild==0.7.1 # via kytos-sphinx-theme 51 | sphinx==3.1.2 # via kytos-sphinx-theme 52 | sphinxcontrib-applehelp==1.0.2 # via sphinx 53 | sphinxcontrib-devhelp==1.0.2 # via sphinx 54 | sphinxcontrib-htmlhelp==1.0.3 # via sphinx 55 | sphinxcontrib-jsmath==1.0.1 # via sphinx 56 | sphinxcontrib-qthelp==1.0.3 # via sphinx 57 | sphinxcontrib-serializinghtml==1.1.4 # via sphinx 58 | toml==0.10.0 # via tox 59 | tornado==6.0.4 # via livereload, sphinx-autobuild 60 | tox==3.14.6 # via python-openflow 61 | typed-ast==1.4.1 # via astroid 62 | urllib3==1.25.8 # via requests 63 | virtualenv==20.0.15 # via tox 64 | watchdog==0.10.2 # via sphinx-autobuild 65 | wcwidth==0.1.9 # via pytest 66 | wrapt==1.11.2 # via astroid 67 | yala==2.2.0 # via python-openflow 68 | zipp==3.1.0 # via importlib-metadata, importlib-resources 69 | 70 | # The following packages are considered to be unsafe in a requirements file: 71 | # setuptools 72 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [aliases] 2 | test=pytest 3 | 4 | [pycodestyle] 5 | exclude = .eggs,ENV,build,docs/conf.py,venv 6 | 7 | [yala] 8 | radon mi args = --min C 9 | pylint args = --disable=too-many-arguments,too-many-locals,too-many-instance-attributes,too-few-public-methods 10 | 11 | [pydocstyle] 12 | add-ignore = D105 13 | # D105: Missing docstring in magic method 14 | 15 | [isort] 16 | known_first_party = pyof,tests 17 | multi_line_output = 4 18 | 19 | [tool:pytest] 20 | markers = 21 | small: marks tests as small 22 | medium: marks tests as medium 23 | large: marks tests as large 24 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | """Test Suit for Python-Openflow.""" 2 | -------------------------------------------------------------------------------- /tests/unit/__init__.py: -------------------------------------------------------------------------------- 1 | """python-openflow unit tests.""" 2 | -------------------------------------------------------------------------------- /tests/unit/raw_dump.py: -------------------------------------------------------------------------------- 1 | """Help reading raw dump files.""" 2 | from pyof.v0x01.common.header import Header 3 | from pyof.v0x01.common.utils import new_message_from_header 4 | 5 | 6 | class RawDump: 7 | """A helper to deal with paths and reading raw files. 8 | 9 | Attributes: 10 | content (bytes): Raw file's content. 11 | """ 12 | 13 | _HEADER_BYTES = 8 # According to OF Protocol specification 14 | 15 | def __init__(self, version, basename): 16 | """Information to locate the dump file. 17 | 18 | Args: 19 | version (str): OpenFlow protocol version, e.g. ``v0x01``. 20 | basename (str): Only the filename without extension. 21 | E.g. ``ofpt_echo_reply``. 22 | """ 23 | self._path = 'raw/{}/{}.dat'.format(version, basename) 24 | 25 | def __repr__(self): 26 | return repr(self.unpack()) 27 | 28 | def __bytes__(self): 29 | return self.read() 30 | 31 | def read(self): 32 | """Read the raw file. 33 | 34 | Returns: 35 | bytes: Raw file's content. 36 | """ 37 | with open(self._path, 'rb') as file: 38 | return file.read() 39 | 40 | def unpack(self): 41 | """Unpack header and message from a byte sequence. 42 | 43 | Returns: 44 | The object type specified in the header with the corresponding 45 | header. 46 | """ 47 | content = self.read() 48 | raw_header = content[:self._HEADER_BYTES] 49 | header = _unpack_header(raw_header) 50 | raw_msg = content[self._HEADER_BYTES:header.length.value] 51 | return _unpack_message(header, raw_msg) 52 | 53 | 54 | def _unpack_header(raw_header): 55 | header = Header() 56 | header.unpack(raw_header) 57 | return header 58 | 59 | 60 | def _unpack_message(header, raw_msg): 61 | msg = new_message_from_header(header) 62 | msg.unpack(raw_msg) 63 | return msg 64 | -------------------------------------------------------------------------------- /tests/unit/test_class_inheritance/__init__.py: -------------------------------------------------------------------------------- 1 | """Tests related to classes inheritance.""" 2 | -------------------------------------------------------------------------------- /tests/unit/test_class_inheritance/test_inheritance.py: -------------------------------------------------------------------------------- 1 | """Testing class inheritance attributes changes.""" 2 | import unittest 3 | 4 | from pyof.foundation.base import GenericStruct 5 | from pyof.foundation.basic_types import UBInt8, UBInt16, UBInt32, UBInt64 6 | 7 | 8 | class TestInheritance(unittest.TestCase): 9 | """Testing GenericStruct class inheritance.""" 10 | 11 | def setUp(self): 12 | """Basic Test Setup.""" 13 | class MyClassA(GenericStruct): 14 | """Example class.""" 15 | 16 | a1 = UBInt8(1) 17 | a2 = UBInt16(2) 18 | a3 = UBInt8(3) 19 | a4 = UBInt16(4) 20 | a5 = UBInt32(5) 21 | 22 | class MyClassB(MyClassA): 23 | """Example class.""" 24 | 25 | a0 = UBInt32(0) 26 | a2 = UBInt64(2) 27 | b6 = UBInt8(6) 28 | 29 | _removed_attributes = ['a3'] 30 | # _rename_attributes = [('a4', 'b4')] 31 | # _insert_attributes_before = {'a0': 'a1'} 32 | 33 | self.MyClassA = MyClassA 34 | self.MyClassB = MyClassB 35 | self.a_expected_names = ['a1', 'a2', 'a3', 'a4', 'a5'] 36 | self.b_expected_names = ['a1', 'a2', 'a4', 'a5', 'a0', 'b6'] 37 | # self.b_expected_names = ['a0', 'a1', 'a2', 'b4', 'a5', 'b6'] 38 | 39 | def test_modifications(self): 40 | """[Foundation/Base/GenericStruct] - Attributes Modifications.""" 41 | m1 = self.MyClassA() 42 | m2 = self.MyClassB() 43 | # Checking keys (attributes names) and its ordering 44 | self.assertEqual([attr[0] for attr in m1.get_class_attributes()], 45 | self.a_expected_names) 46 | self.assertEqual([attr[0] for attr in m2.get_class_attributes()], 47 | self.b_expected_names) 48 | 49 | # Check if there is no shared attribute between instances 50 | self.assertIsNot(m1, m2) 51 | self.assertIsNot(m1.a1, m2.a1) 52 | self.assertIsNot(m1.a2, m2.a2) 53 | self.assertIsNot(m1.a3, m2.a4) 54 | self.assertIsNot(m1.a4, m2.a4) 55 | self.assertIsNot(m1.a5, m2.a5) 56 | 57 | # Check attributes types on MyClassA 58 | self.assertIsInstance(self.MyClassA.a1, UBInt8) 59 | self.assertIsInstance(self.MyClassA.a2, UBInt16) 60 | self.assertIsInstance(self.MyClassA.a3, UBInt8) 61 | self.assertIsInstance(self.MyClassA.a4, UBInt16) 62 | self.assertIsInstance(self.MyClassA.a5, UBInt32) 63 | 64 | # Check attributes types on MyClassA 65 | self.assertIsInstance(self.MyClassB.a1, UBInt8) 66 | self.assertIsInstance(self.MyClassB.a2, UBInt64) 67 | self.assertIsInstance(self.MyClassB.a4, UBInt16) 68 | self.assertIsInstance(self.MyClassB.a5, UBInt32) 69 | self.assertIsInstance(self.MyClassB.a0, UBInt32) 70 | self.assertIsInstance(self.MyClassB.b6, UBInt8) 71 | -------------------------------------------------------------------------------- /tests/unit/test_foundation/__init__.py: -------------------------------------------------------------------------------- 1 | """Test foundation module of Python-openflow.""" 2 | -------------------------------------------------------------------------------- /tests/unit/test_foundation/test_base.py: -------------------------------------------------------------------------------- 1 | """Test Base module of python-openflow.""" 2 | import unittest 3 | 4 | from pyof.foundation import base, basic_types 5 | 6 | 7 | class TestGenericStruct(unittest.TestCase): 8 | """Testing GenericStruct class.""" 9 | 10 | def setUp(self): 11 | """Basic Test Setup.""" 12 | class AttributeA(base.GenericStruct): 13 | """Example class.""" 14 | 15 | a1 = basic_types.UBInt8(1) 16 | a2 = basic_types.UBInt16(2) 17 | 18 | class AttributeC(base.GenericStruct): 19 | """Example class.""" 20 | 21 | c1 = basic_types.UBInt32(3) 22 | c2 = basic_types.UBInt64(4) 23 | 24 | class AttributeB(base.GenericStruct): 25 | """Example class.""" 26 | 27 | c = AttributeC() 28 | 29 | class Header(base.GenericStruct): 30 | """Mock Header class.""" 31 | 32 | version = basic_types.UBInt8(1) 33 | message_type = basic_types.UBInt8(2) 34 | length = basic_types.UBInt8(8) 35 | xid = basic_types.UBInt8(4) 36 | 37 | class MyMessage(base.GenericMessage): 38 | """Example class.""" 39 | 40 | header = Header() 41 | a = AttributeA() 42 | b = AttributeB() 43 | i = basic_types.UBInt32(5) 44 | 45 | def __init__(self): 46 | """Init method of example class.""" 47 | super().__init__(None) 48 | 49 | self.MyMessage = MyMessage 50 | 51 | def test_basic_attributes(self): 52 | """[Foundation/Base/GenericStruct] - Attributes Creation.""" 53 | message1 = self.MyMessage() 54 | message2 = self.MyMessage() 55 | self.assertIsNot(message1, message2) 56 | self.assertIsNot(message1.i, message2.i) 57 | self.assertIsNot(message1.a, message2.a) 58 | self.assertIsNot(message1.b, message2.b) 59 | self.assertIsNot(message1.a.a1, message2.a.a1) 60 | self.assertIsNot(message1.a.a2, message2.a.a2) 61 | self.assertIsNot(message1.b.c, message2.b.c) 62 | self.assertIsNot(message1.b.c.c1, message2.b.c.c1) 63 | self.assertIsNot(message1.b.c.c2, message2.b.c.c2) 64 | 65 | 66 | class TestGenericType(unittest.TestCase): 67 | """Testing GenericType class.""" 68 | 69 | def test_basic_operator(self): 70 | """[Foundation/Base/GenericType] - Basic Operators.""" 71 | a = basic_types.UBInt32(1) 72 | b = basic_types.UBInt32(2) 73 | 74 | self.assertEqual(a + 1, 2) 75 | self.assertEqual(1 + a, 2) 76 | self.assertEqual(b + 1, 3) 77 | self.assertEqual(1 + b, 3) 78 | 79 | self.assertEqual(a - 1, 0) 80 | self.assertEqual(1 - a, 0) 81 | self.assertEqual(b - 1, 1) 82 | self.assertEqual(1 - b, 1) 83 | 84 | self.assertEqual(a & 1, 1) 85 | self.assertEqual(1 & a, 1) 86 | self.assertEqual(b & 1, 0) 87 | self.assertEqual(1 & b, 0) 88 | 89 | self.assertEqual(a | 1, 1) 90 | self.assertEqual(1 | a, 1) 91 | self.assertEqual(b | 1, 3) 92 | self.assertEqual(1 | b, 3) 93 | 94 | self.assertEqual(a ^ 1, 0) 95 | self.assertEqual(1 ^ a, 0) 96 | self.assertEqual(b ^ 1, 3) 97 | self.assertEqual(1 ^ b, 3) 98 | -------------------------------------------------------------------------------- /tests/unit/test_utils.py: -------------------------------------------------------------------------------- 1 | """Automate utils tests.""" 2 | import unittest 3 | 4 | from pyof.utils import UnpackException, unpack, validate_packet 5 | from pyof.v0x01.symmetric.hello import Hello as Hello_v0x01 6 | from pyof.v0x04.symmetric.hello import Hello as Hello_v0x04 7 | 8 | 9 | class TestUtils(unittest.TestCase): 10 | """Run tests to verify unpack independent of version.""" 11 | 12 | def test_unpack_v0x01_packet(self): 13 | """Test if the package in version v0x01 is properly unpacked.""" 14 | data = Hello_v0x01().pack() 15 | expected = unpack(data) 16 | self.assertEqual(expected.pack(), data) 17 | 18 | def test_unpack_v0x04_packet(self): 19 | """Test if the package in version v0x04 is properly unpacked.""" 20 | data = Hello_v0x04().pack() 21 | expected = unpack(data) 22 | self.assertEqual(expected.pack(), data) 23 | 24 | def test_invalid_packet_with_version_more_then_128(self): 25 | """Test validate a invalid packet with version more than 128.""" 26 | hello = Hello_v0x04() 27 | hello.header.version = 129 28 | 29 | self.assertRaises(UnpackException, validate_packet, hello.pack()) 30 | 31 | def test_validate_packet_with_invalid_packets(self): 32 | """Test validate a invalid packet with invalid packets.""" 33 | hello = Hello_v0x04() 34 | 35 | hello.header.version = 128 36 | self.assertRaises(UnpackException, validate_packet, hello.pack()) 37 | 38 | hello.header.version = 0 39 | self.assertRaises(UnpackException, validate_packet, hello.pack()) 40 | -------------------------------------------------------------------------------- /tests/unit/v0x01/__init__.py: -------------------------------------------------------------------------------- 1 | """Module with tests for v0x01 (v1.0.0).""" 2 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_asynchronous/__init__.py: -------------------------------------------------------------------------------- 1 | """Testing asynchronous messages from v0x01.""" 2 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_asynchronous/test_error_msg.py: -------------------------------------------------------------------------------- 1 | """Testing Error Message.""" 2 | from pyof.v0x01.asynchronous.error_msg import ( 3 | BadRequestCode, ErrorMsg, ErrorType, FlowModFailedCode) 4 | from tests.unit.test_struct import TestStruct 5 | 6 | 7 | class TestErrorMessage(TestStruct): 8 | """Test the Error Message.""" 9 | 10 | @classmethod 11 | def setUpClass(cls): 12 | """Setup TestStruct.""" 13 | super().setUpClass() 14 | super().set_raw_dump_file('v0x01', 'ofpt_error_msg') 15 | super().set_raw_dump_object(ErrorMsg, xid=12, 16 | error_type=ErrorType.OFPET_BAD_REQUEST, 17 | code=BadRequestCode.OFPBRC_BAD_STAT, 18 | data=b'') 19 | super().set_minimum_size(12) 20 | 21 | def test_unpack_error_msg(self): 22 | """Test Unpack a sample ErrorMsg.""" 23 | expected = b'\x01\x01\x00\x1b\x00\x00\x00\x18\x00\x03\x00\x02FLOW' 24 | 25 | error_msg = ErrorMsg(xid=24, 26 | error_type=ErrorType.OFPET_FLOW_MOD_FAILED, 27 | code=FlowModFailedCode.OFPFMFC_EPERM, 28 | data=b'FLOW') 29 | 30 | actual = ErrorMsg(xid=24) 31 | actual.unpack(expected[8:]) 32 | 33 | self.assertEqual(actual, error_msg) 34 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_asynchronous/test_flow_removed.py: -------------------------------------------------------------------------------- 1 | """Testing FlowRemoved message.""" 2 | from pyof.foundation.basic_types import HWAddress, IPAddress 3 | from pyof.v0x01.asynchronous.flow_removed import FlowRemoved, FlowRemovedReason 4 | from pyof.v0x01.common.flow_match import Match 5 | from tests.unit.test_struct import TestStruct 6 | 7 | 8 | class TestFlowRemoved(TestStruct): 9 | """Test the FlowRemoved message.""" 10 | 11 | @classmethod 12 | def setUpClass(cls): 13 | """Setup TestStruct.""" 14 | reason = FlowRemovedReason.OFPRR_IDLE_TIMEOUT 15 | match = Match(in_port=80, dl_vlan=1, dl_vlan_pcp=1, dl_type=1, 16 | nw_tos=1, nw_proto=1, tp_src=80, tp_dst=80, 17 | dl_src=HWAddress('00:00:00:00:00:00'), 18 | dl_dst=HWAddress('00:00:00:00:00:00'), 19 | nw_src=IPAddress('192.168.0.1'), 20 | nw_dst=IPAddress('192.168.0.2')) 21 | 22 | super().setUpClass() 23 | super().set_raw_dump_file('v0x01', 'ofpt_flow_removed') 24 | super().set_raw_dump_object(FlowRemoved, xid=12, 25 | match=match, cookie=0, priority=1, 26 | reason=reason, duration_sec=4, 27 | duration_nsec=23, idle_timeout=9, 28 | packet_count=10, byte_count=4) 29 | super().set_minimum_size(88) 30 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_asynchronous/test_packet_in.py: -------------------------------------------------------------------------------- 1 | """Packet in message tests.""" 2 | from pyof.v0x01.asynchronous.packet_in import PacketIn, PacketInReason 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestPacketIn(TestStruct): 7 | """Packet in message tests (also those in :class:`.TestDump`).""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x01', 'ofpt_packet_in') 14 | super().set_raw_dump_object(PacketIn, xid=15, buffer_id=1, total_len=1, 15 | in_port=1, 16 | reason=PacketInReason.OFPR_ACTION) 17 | # Different from the specification, the minimum size of this class is 18 | # 18, not 20. 19 | super().set_minimum_size(18) 20 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_asynchronous/test_port_status.py: -------------------------------------------------------------------------------- 1 | """Port Status message tests.""" 2 | from pyof.foundation.basic_types import HWAddress 3 | from pyof.v0x01.asynchronous.port_status import PortReason, PortStatus 4 | from pyof.v0x01.common.phy_port import PhyPort, PortFeatures, PortState 5 | from tests.unit.test_struct import TestStruct 6 | 7 | 8 | class TestPortStatus(TestStruct): 9 | """Test the Port Status message.""" 10 | 11 | @classmethod 12 | def setUpClass(cls): 13 | """Configure raw file and its object in parent class (TestDump).""" 14 | super().setUpClass() 15 | super().set_raw_dump_file('v0x01', 'ofpt_port_status') 16 | super().set_raw_dump_object(_new_portstatus) 17 | super().set_minimum_size(64) 18 | 19 | 20 | def _new_portstatus(): 21 | """Crate new PortStatus and PhyPort instances.""" 22 | desc_name = 's1-eth1' 23 | desc = PhyPort(port_no=1, 24 | hw_addr=HWAddress('9a:da:11:8a:f4:0c'), 25 | name=desc_name, 26 | state=PortState.OFPPS_STP_LISTEN, 27 | curr=PortFeatures.OFPPF_10GB_FD | PortFeatures.OFPPF_COPPER) 28 | return PortStatus(xid=0, 29 | reason=PortReason.OFPPR_MODIFY, 30 | desc=desc) 31 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_common/__init__.py: -------------------------------------------------------------------------------- 1 | """Test common modules.""" 2 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_common/test_flow_match.py: -------------------------------------------------------------------------------- 1 | """Testing FlowMatch structure.""" 2 | import unittest 3 | 4 | from pyof.v0x01.common import flow_match 5 | 6 | 7 | class TestMatch(unittest.TestCase): 8 | """Test Match structure.""" 9 | 10 | def setUp(self): 11 | """Basic setup for test.""" 12 | self.message = flow_match.Match() 13 | self.message.in_port = 22 14 | self.message.dl_src = [1, 2, 3, 4, 5, 6] 15 | self.message.dl_dst = [1, 2, 3, 4, 5, 6] 16 | self.message.dl_vlan = 1 17 | self.message.dl_vlan_pcp = 1 18 | self.message.dl_type = 1 19 | self.message.nw_tos = 1 20 | self.message.nw_proto = 1 21 | self.message.nw_src = [192, 168, 0, 1] 22 | self.message.nw_dst = [192, 168, 0, 2] 23 | self.message.tp_src = 22 24 | self.message.tp_dst = 22 25 | 26 | def test_get_size(self): 27 | """[Common/FlowMatch] - size 40.""" 28 | self.assertEqual(self.message.get_size(), 40) 29 | 30 | def test_pack_unpack(self): 31 | """[Common/FlowMatch] - packing and unpacking.""" 32 | pack = self.message.pack() 33 | unpacked = flow_match.Match() 34 | unpacked.unpack(pack) 35 | self.assertEqual(self.message.pack(), unpacked.pack()) 36 | 37 | @unittest.skip('Not yet implemented') 38 | def test_pack(self): 39 | """[Common/FlowMatch] - packing.""" 40 | pass 41 | 42 | @unittest.skip('Not yet implemented') 43 | def test_unpack(self): 44 | """[Common/FlowMatch] - unpacking.""" 45 | pass 46 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_common/test_header.py: -------------------------------------------------------------------------------- 1 | """Testing Header structure.""" 2 | import os 3 | import unittest 4 | from unittest.mock import patch 5 | 6 | from pyof.v0x01.common.header import Header, Type 7 | 8 | 9 | class TestHeader(unittest.TestCase): 10 | """Test the message Header.""" 11 | 12 | def setUp(self): 13 | """Setup the TestHeader Class instantiating a HELLO header.""" 14 | self.message = Header() 15 | self.message.message_type = Type.OFPT_HELLO 16 | self.message.xid = 1 17 | self.message.length = 0 18 | 19 | def test_size(self): 20 | """[Common/Header] - size 8.""" 21 | self.assertEqual(self.message.get_size(), 8) 22 | 23 | @unittest.expectedFailure 24 | def test_pack_empty(self): 25 | """[Common/Header] - packing empty header.""" 26 | self.assertRaises(TypeError, 27 | Header().pack()) 28 | 29 | def test_pack(self): 30 | """[Common/Header] - packing Hello.""" 31 | packed_header = b'\x01\x00\x00\x00\x00\x00\x00\x01' 32 | self.assertEqual(self.message.pack(), packed_header) 33 | 34 | def test_unpack(self): 35 | """[Common/Header] - unpacking Hello.""" 36 | filename = os.path.join(os.path.dirname(os.path.realpath('__file__')), 37 | 'raw/v0x01/ofpt_hello.dat') 38 | f = open(filename, 'rb') 39 | self.message.unpack(f.read(8)) 40 | 41 | self.assertEqual(self.message.length, 8) 42 | self.assertEqual(self.message.xid, 1) 43 | self.assertEqual(self.message.message_type, Type.OFPT_HELLO) 44 | self.assertEqual(self.message.version, 1) 45 | 46 | f.close() 47 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_common/test_phy_port.py: -------------------------------------------------------------------------------- 1 | """Testing PhyPort structure.""" 2 | import os 3 | from unittest import TestCase 4 | 5 | from pyof.foundation.basic_types import HWAddress 6 | from pyof.foundation.constants import OFP_MAX_PORT_NAME_LEN 7 | from pyof.v0x01.common.phy_port import ( 8 | PhyPort, PortConfig, PortFeatures, PortState) 9 | 10 | 11 | class TestPhyPort(TestCase): 12 | """Test PhyPort.""" 13 | 14 | def setUp(self): 15 | """Basic setup for test.""" 16 | self.message = PhyPort() 17 | self.message.port_no = 1 18 | self.message.hw_addr = HWAddress('9a:da:11:8a:f4:0c') 19 | self.message.name = 's1-eth1' 20 | self.message.state = PortState.OFPPS_STP_LISTEN 21 | self.message.curr = (PortFeatures.OFPPF_10GB_FD | 22 | PortFeatures.OFPPF_COPPER) 23 | 24 | def test_get_size(self): 25 | """[Common/PhyPort] - size 48.""" 26 | self.assertEqual(self.message.get_size(), 48) 27 | 28 | def test_pack(self): 29 | """[Common/PhyPort] - packing.""" 30 | data = b'\x00\x01\x9a\xda\x11\x8a\xf4\x0cs1-eth1\x00\x00\x00\x00\x00' 31 | data += 15 * b'\x00' 32 | data += b'\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 33 | self.assertEqual(self.message.pack(), data) 34 | 35 | def test_unpack(self): 36 | """[Common/PhyPort] - unpacking.""" 37 | filename = os.path.join(os.path.dirname(os.path.realpath('__file__')), 38 | 'raw/v0x01/ofpt_port_status.dat') 39 | f = open(filename, 'rb') 40 | f.seek(16, 1) 41 | self.message.unpack(f.read(48)) 42 | 43 | self.assertEqual(self.message.port_no, 1) 44 | self.assertEqual(self.message.hw_addr, '9a:da:11:8a:f4:0c') 45 | self.assertEqual(self.message.name, 's1-eth1') 46 | self.assertEqual(self.message.state, PortState.OFPPS_STP_LISTEN) 47 | self.assertEqual(self.message.curr, (PortFeatures.OFPPF_10GB_FD | 48 | PortFeatures.OFPPF_COPPER)) 49 | 50 | f.close() 51 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_common/test_queue.py: -------------------------------------------------------------------------------- 1 | """Testing Queue structure.""" 2 | import unittest 3 | 4 | from pyof.v0x01.common import queue 5 | 6 | 7 | class TestQueuePropHeader(unittest.TestCase): 8 | """Test QueuePropHeader.""" 9 | 10 | def setUp(self): 11 | """Basic setup for test.""" 12 | self.message = queue.QueuePropHeader() 13 | self.message.queue_property = queue.QueueProperties.OFPQT_MIN_RATE 14 | self.message.length = 12 15 | 16 | def test_get_size(self): 17 | """[Common/QueuePropHeader] - size 8.""" 18 | self.assertEqual(self.message.get_size(), 8) 19 | 20 | @unittest.skip('Not yet implemented') 21 | def test_pack(self): 22 | """[Common/QueuePropHeader] - packing.""" 23 | pass 24 | 25 | @unittest.skip('Not yet implemented') 26 | def test_unpack(self): 27 | """[Common/QueuePropHeader] - unpacking.""" 28 | pass 29 | 30 | 31 | class TestPacketQueue(unittest.TestCase): 32 | """TestPacketQueue.""" 33 | 34 | def setUp(self): 35 | """Basic setup for test.""" 36 | self.message = queue.PacketQueue() 37 | self.message.queue_id = 1 38 | self.message.length = 8 39 | 40 | def test_get_size(self): 41 | """[Common/PacketQueue] - size 8.""" 42 | self.assertEqual(self.message.get_size(), 8) 43 | 44 | @unittest.skip('Not yet implemented') 45 | def test_pack(self): 46 | """[Common/PacketQueue] - packing.""" 47 | pass 48 | 49 | @unittest.skip('Not yet implemented') 50 | def test_unpack(self): 51 | """[Common/PacketQueue] - unpacking.""" 52 | pass 53 | 54 | 55 | class TestQueuePropMinRate(unittest.TestCase): 56 | """Test QueuePropMinRate.""" 57 | 58 | def setUp(self): 59 | """Basic setup for test.""" 60 | self.message = queue.QueuePropMinRate() 61 | self.message.rate = 1000 62 | 63 | def test_get_size(self): 64 | """[Common/PropMinRate] - size 16.""" 65 | self.assertEqual(self.message.get_size(), 16) 66 | 67 | @unittest.skip('Not yet implemented') 68 | def test_pack(self): 69 | """[Common/PropMinRate] - packing.""" 70 | pass 71 | 72 | @unittest.skip('Not yet implemented') 73 | def test_unpack(self): 74 | """[Common/PropMinRate] - unpacking.""" 75 | pass 76 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/__init__.py: -------------------------------------------------------------------------------- 1 | """Testing Controller2Switch messages.""" 2 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_aggregate_stats_reply.py: -------------------------------------------------------------------------------- 1 | """Test for AggregateStatsReply message.""" 2 | from pyof.v0x01.controller2switch.common import AggregateStatsReply, StatsType 3 | from pyof.v0x01.controller2switch.stats_reply import StatsReply 4 | from tests.unit.test_struct import TestStruct 5 | 6 | 7 | class TestAggregateStatsReply(TestStruct): 8 | """Test for AggregateStatsReply message.""" 9 | 10 | @classmethod 11 | def setUpClass(cls): 12 | """[Controller2Switch/AggregateStatsReply] - size 24.""" 13 | aggregate_stats_reply = AggregateStatsReply(packet_count=5, 14 | byte_count=1, flow_count=8) 15 | super().setUpClass() 16 | super().set_raw_dump_file('v0x01', 'ofpt_aggregate_stats_reply') 17 | super().set_raw_dump_object(StatsReply, xid=17, 18 | body_type=StatsType.OFPST_AGGREGATE, 19 | flags=0, body=aggregate_stats_reply) 20 | super().set_minimum_size(12) 21 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_aggregate_stats_request.py: -------------------------------------------------------------------------------- 1 | """Test AggregateStatsRequest message.""" 2 | from pyof.v0x01.common.flow_match import Match 3 | from pyof.v0x01.common.phy_port import Port 4 | from pyof.v0x01.controller2switch.common import ( 5 | AggregateStatsRequest, StatsType) 6 | from pyof.v0x01.controller2switch.stats_request import StatsRequest 7 | from tests.unit.test_struct import TestStruct 8 | 9 | 10 | class TestAggregateStatsRequest(TestStruct): 11 | """Test class for TestAggregateStatsRequest.""" 12 | 13 | @classmethod 14 | def setUpClass(cls): 15 | """[Controller2Switch/AggregateStatsRequest] - size 44.""" 16 | request = AggregateStatsRequest(table_id=1, out_port=Port.OFPP_NONE, 17 | match=_get_match()) 18 | 19 | super().setUpClass() 20 | super().set_raw_dump_file('v0x01', 'ofpt_aggregate_request') 21 | super().set_raw_dump_object(StatsRequest, xid=17, 22 | body_type=StatsType.OFPST_AGGREGATE, 23 | flags=0, body=request) 24 | super().set_minimum_size(12) 25 | 26 | 27 | def _get_match(): 28 | """Function used to built Match instance used by AggregateStatsRequest.""" 29 | return Match(in_port=80, dl_src="01:02:03:04:05:06", 30 | dl_dst="01:02:03:04:05:06", dl_vlan=1, 31 | dl_vlan_pcp=1, dl_type=1, 32 | nw_tos=1, nw_proto=1, 33 | nw_src='192.168.0.1', nw_dst='192.168.0.1', 34 | tp_src=80, tp_dst=80) 35 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_barrier_reply.py: -------------------------------------------------------------------------------- 1 | """Barrier reply message tests.""" 2 | from pyof.v0x01.controller2switch.barrier_reply import BarrierReply 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestBarrierReply(TestStruct): 7 | """Barrier reply message tests (also those in :class:`.TestDump`).""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x01', 'ofpt_barrier_reply') 14 | super().set_raw_dump_object(BarrierReply, xid=5) 15 | super().set_minimum_size(8) 16 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_barrier_request.py: -------------------------------------------------------------------------------- 1 | """Barrier request message tests.""" 2 | from pyof.v0x01.controller2switch.barrier_request import BarrierRequest 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestBarrierRequest(TestStruct): 7 | """Barrier reply message tests (also those in :class:`.TestDump`).""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x01', 'ofpt_barrier_request') 14 | super().set_raw_dump_object(BarrierRequest, xid=5) 15 | super().set_minimum_size(8) 16 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_desc_stats.py: -------------------------------------------------------------------------------- 1 | """Test DescStats message.""" 2 | from pyof.foundation.constants import DESC_STR_LEN 3 | from pyof.v0x01.controller2switch.common import DescStats, StatsType 4 | from pyof.v0x01.controller2switch.stats_reply import StatsReply 5 | from tests.unit.test_struct import TestStruct 6 | 7 | 8 | class TestDescStats(TestStruct): 9 | """Test class for TestDescStats.""" 10 | 11 | @classmethod 12 | def setUpClass(cls): 13 | """[Controller2Switch/DescStats] - size 1056.""" 14 | super().setUpClass() 15 | super().set_raw_dump_file('v0x01', 'ofpt_desc_stats_reply') 16 | super().set_raw_dump_object(StatsReply, xid=14, 17 | body_type=StatsType.OFPST_DESC, 18 | flags=0, body=_get_desc_stats()) 19 | super().set_minimum_size(12) 20 | 21 | 22 | def _get_desc_stats(): 23 | """Function used to return desc_stat used by StatsReply instance.""" 24 | content = 'A' * DESC_STR_LEN 25 | return DescStats(mfr_desc=content, hw_desc=content, 26 | sw_desc=content, serial_num=content, 27 | dp_desc=content) 28 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_features_reply.py: -------------------------------------------------------------------------------- 1 | """Echo request message tests.""" 2 | from pyof.foundation.basic_types import DPID, HWAddress 3 | from pyof.v0x01.common.phy_port import PhyPort, PortConfig, PortState 4 | from pyof.v0x01.controller2switch.features_reply import FeaturesReply 5 | from tests.unit.test_struct import TestStruct 6 | 7 | 8 | class TestFeaturesReply(TestStruct): 9 | """Feature reply message tests (also those in :class:`.TestDump`).""" 10 | 11 | @classmethod 12 | def setUpClass(cls): 13 | """Configure raw file and its object in parent class (TestDump).""" 14 | super().setUpClass() 15 | super().set_raw_dump_file('v0x01', 'ofpt_features_reply') 16 | kwargs = _get_kwargs() 17 | super().set_raw_dump_object(FeaturesReply, **kwargs) 18 | super().set_minimum_size(32) 19 | 20 | 21 | def _get_kwargs(): 22 | return {'xid': 2, 'datapath_id': DPID('00:00:00:00:00:00:00:01'), 23 | 'n_buffers': 256, 'n_tables': 254, 'capabilities': 0x000000c7, 24 | 'actions': 4095, 'ports': _get_ports()} 25 | 26 | 27 | def _get_ports(): 28 | return [ 29 | PhyPort(port_no=65534, 30 | hw_addr=HWAddress('0e:d3:98:a5:30:47'), 31 | name='s1', 32 | config=PortConfig.OFPPC_PORT_DOWN, 33 | state=PortState.OFPPS_LINK_DOWN, 34 | curr=0, 35 | advertised=0, 36 | supported=0, 37 | peer=0), 38 | PhyPort(port_no=1, 39 | hw_addr=HWAddress('0a:54:cf:fc:4e:6d'), 40 | name='s1-eth1', 41 | config=0, 42 | state=PortState.OFPPS_STP_LISTEN, 43 | curr=0x000000c0, 44 | advertised=0, 45 | supported=0, 46 | peer=0), 47 | PhyPort(port_no=2, 48 | hw_addr=HWAddress('f6:b6:ab:cc:f8:4f'), 49 | name='s1-eth2', 50 | config=0, 51 | state=PortState.OFPPS_STP_LISTEN, 52 | curr=0x000000c0, 53 | advertised=0, 54 | supported=0, 55 | peer=0) 56 | ] 57 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_features_request.py: -------------------------------------------------------------------------------- 1 | """Feature request message tests.""" 2 | from pyof.v0x01.controller2switch.features_request import FeaturesRequest 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestFeaturesRequest(TestStruct): 7 | """Feature request message tests (also those in :class:`.TestDump`).""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x01', 'ofpt_features_request') 14 | super().set_raw_dump_object(FeaturesRequest, xid=3) 15 | super().set_minimum_size(8) 16 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_flow_mod.py: -------------------------------------------------------------------------------- 1 | """Flow modification (add/delete) message tests.""" 2 | from pyof.v0x01.common.action import ActionOutput 3 | from pyof.v0x01.common.flow_match import Match 4 | from pyof.v0x01.common.phy_port import Port 5 | from pyof.v0x01.controller2switch.flow_mod import FlowMod, FlowModCommand 6 | from tests.unit.test_struct import TestStruct 7 | 8 | 9 | class TestFlowAdd(TestStruct): 10 | """Flow addition message tests (also those in :class:`.TestDump`).""" 11 | 12 | @classmethod 13 | def setUpClass(cls): 14 | """Configure raw file and its object in parent class (TestDump).""" 15 | super().setUpClass() 16 | super().set_raw_dump_file('v0x01', 'ofpt_flow_add') 17 | kwargs = _get_flowmod_kwargs(FlowModCommand.OFPFC_ADD) 18 | super().set_raw_dump_object(FlowMod, **kwargs) 19 | super().set_minimum_size(72) 20 | 21 | 22 | class TestFlowDelete(TestStruct): 23 | """Flow deletion message tests (also those in :class:`.TestDump`).""" 24 | 25 | @classmethod 26 | def setUpClass(cls): 27 | """Configure raw file and its object in parent class (TestDump).""" 28 | super().setUpClass() 29 | super().set_raw_dump_file('v0x01', 'ofpt_flow_delete') 30 | kwargs = _get_flowmod_kwargs(FlowModCommand.OFPFC_DELETE) 31 | super().set_raw_dump_object(FlowMod, **kwargs) 32 | super().set_minimum_size(72) 33 | 34 | 35 | def _get_flowmod_kwargs(command): 36 | """Return parameters for FlowMod object.""" 37 | return {'xid': 4, 38 | 'command': command, 39 | 'match': _get_match(), 40 | 'cookie': 0, 41 | 'idle_timeout': 0, 42 | 'hard_timeout': 0, 43 | 'priority': 32768, 44 | 'buffer_id': 4294967295, 45 | 'out_port': Port.OFPP_NONE, 46 | 'flags': 0, 47 | 'actions': _get_actions()} 48 | 49 | 50 | def _get_match(): 51 | """Return a Match object.""" 52 | return Match() 53 | 54 | 55 | def _get_actions(): 56 | """Return a List of actions registered by flow object.""" 57 | action = ActionOutput(port=65533, max_length=65535) 58 | return [action] 59 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_flow_stats.py: -------------------------------------------------------------------------------- 1 | """Test FlowStats message.""" 2 | from pyof.v0x01.common.flow_match import Match 3 | from pyof.v0x01.controller2switch.common import FlowStats, StatsType 4 | from pyof.v0x01.controller2switch.stats_reply import StatsReply 5 | from tests.unit.test_struct import TestStruct 6 | 7 | 8 | class TestFlowStats(TestStruct): 9 | """Test class for TestFlowStats.""" 10 | 11 | @classmethod 12 | def setUpClass(cls): 13 | """[Controller2Switch/FlowStats] - size 88.""" 14 | super().setUpClass() 15 | super().set_raw_dump_file('v0x01', 'ofpt_flow_stats_reply') 16 | super().set_raw_dump_object(StatsReply, xid=12, 17 | body_type=StatsType.OFPST_FLOW, 18 | flags=0, body=_get_flow_stats()) 19 | super().set_minimum_size(12) 20 | 21 | 22 | def _get_flow_stats(): 23 | """Function used to return a FlowStats instance.""" 24 | return FlowStats(length=160, table_id=1, 25 | match=_get_match(), duration_sec=60, 26 | duration_nsec=10000, priority=1, 27 | idle_timeout=300, hard_timeout=6000, 28 | cookie=1, packet_count=1, byte_count=1) 29 | 30 | 31 | def _get_match(): 32 | """Function used to return a Match instance.""" 33 | return Match(in_port=80, dl_src='01:02:03:04:05:06', 34 | dl_dst='01:02:03:04:05:06', dl_vlan=1, 35 | dl_vlan_pcp=1, dl_type=1, 36 | nw_tos=1, nw_proto=1, 37 | nw_src='192.168.0.1', nw_dst='192.168.0.1', 38 | tp_src=80, tp_dst=80) 39 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_flow_stats_request.py: -------------------------------------------------------------------------------- 1 | """Test FlowStatsRequest message.""" 2 | from pyof.v0x01.common.flow_match import Match 3 | from pyof.v0x01.controller2switch.common import FlowStatsRequest, StatsType 4 | from pyof.v0x01.controller2switch.stats_request import StatsRequest 5 | from tests.unit.test_struct import TestStruct 6 | 7 | 8 | class TestFlowStatsRequest(TestStruct): 9 | """Test class for TestFlowStatsRequest.""" 10 | 11 | @classmethod 12 | def setUpClass(cls): 13 | """[Controller2Switch/FlowStatsRequest] - size 44.""" 14 | super().setUpClass() 15 | super().set_raw_dump_file('v0x01', 'ofpt_flow_stats_request') 16 | super().set_raw_dump_object(StatsRequest, xid=12, 17 | body_type=StatsType.OFPST_FLOW, 18 | flags=0, body=_get_flow_stats_request()) 19 | super().set_minimum_size(12) 20 | 21 | 22 | def _get_flow_stats_request(): 23 | return FlowStatsRequest(match=_get_match(), table_id=1, out_port=80) 24 | 25 | 26 | def _get_match(): 27 | """Function used to return a Match instance.""" 28 | return Match(in_port=80, dl_src='01:02:03:04:05:06', 29 | dl_dst='01:02:03:04:05:06', dl_vlan=1, 30 | dl_vlan_pcp=1, dl_type=1, 31 | nw_tos=1, nw_proto=1, 32 | nw_src='192.168.0.1', nw_dst='192.168.0.1', 33 | tp_src=80, tp_dst=80) 34 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_get_config_reply.py: -------------------------------------------------------------------------------- 1 | """Test GetConfigReply message.""" 2 | from pyof.v0x01.controller2switch.common import ConfigFlag 3 | from pyof.v0x01.controller2switch.get_config_reply import GetConfigReply 4 | from tests.unit.test_struct import TestStruct 5 | 6 | 7 | class TestGetConfigReply(TestStruct): 8 | """Test class for TestGetConfigReply.""" 9 | 10 | @classmethod 11 | def setUpClass(cls): 12 | """[Controller2Switch/GetConfigReply] - size 12.""" 13 | super().setUpClass() 14 | super().set_raw_dump_file('v0x01', 'ofpt_get_config_reply') 15 | super().set_raw_dump_object(GetConfigReply, xid=13, 16 | flags=ConfigFlag.OFPC_FRAG_REASM, 17 | miss_send_len=1024) 18 | super().set_minimum_size(12) 19 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_get_config_request.py: -------------------------------------------------------------------------------- 1 | """Test GetConfigRequest message.""" 2 | from pyof.v0x01.controller2switch.get_config_request import GetConfigRequest 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestGetConfigRequest(TestStruct): 7 | """Test class for TestGetConfigRequest.""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """[Controller2Switch/GetConfigRequest] - size 8.""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x01', 'ofpt_get_config_request') 14 | super().set_raw_dump_object(GetConfigRequest, xid=1) 15 | super().set_minimum_size(8) 16 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_packet_out.py: -------------------------------------------------------------------------------- 1 | """Packet out message tests.""" 2 | from pyof.foundation.exceptions import ValidationError 3 | from pyof.v0x01.common.action import ActionOutput 4 | from pyof.v0x01.common.phy_port import Port 5 | from pyof.v0x01.controller2switch.packet_out import PacketOut 6 | from tests.unit.test_struct import TestStruct 7 | 8 | 9 | class TestPacketOut(TestStruct): 10 | """Packet out message tests (also those in :class:`.TestDump`). 11 | 12 | Attributes: 13 | message (PacketOut): The message configured in :meth:`setUpClass`. 14 | """ 15 | 16 | @classmethod 17 | def setUpClass(cls): 18 | """Configure raw file and its object in parent class (TestDump).""" 19 | super().setUpClass() 20 | super().set_raw_dump_file('v0x01', 'ofpt_packet_out') 21 | super().set_raw_dump_object(PacketOut, xid=8, buffer_id=4294967295, 22 | in_port=Port.OFPP_NONE, data=_get_data(), 23 | actions=_get_actions()) 24 | super().set_minimum_size(16) 25 | 26 | def setUp(self): 27 | """Run before every test.""" 28 | self.message = self.get_raw_object() 29 | 30 | def test_valid_virtual_in_ports(self): 31 | """Valid virtual ports as defined in 1.0.1 spec.""" 32 | valid = (Port.OFPP_LOCAL, Port.OFPP_CONTROLLER, Port.OFPP_NONE) 33 | for in_port in valid: 34 | self.message.in_port = in_port 35 | self.assertTrue(self.message.is_valid()) 36 | 37 | def test_invalid_virtual_in_ports(self): 38 | """Invalid virtual ports as defined in 1.0.1 spec.""" 39 | invalid = (Port.OFPP_IN_PORT, Port.OFPP_TABLE, Port.OFPP_NORMAL, 40 | Port.OFPP_FLOOD, Port.OFPP_ALL) 41 | for in_port in invalid: 42 | self.message.in_port = in_port 43 | self.assertFalse(self.message.is_valid()) 44 | self.assertRaises(ValidationError, self.message.validate) 45 | 46 | def test_valid_physical_in_ports(self): 47 | """Physical port limits from 1.0.0 spec.""" 48 | max_valid = int(Port.OFPP_MAX.value) 49 | for in_port in (1, max_valid): 50 | self.message.in_port = in_port 51 | self.assertTrue(self.message.is_valid()) 52 | 53 | def test_invalid_physical_in_port(self): 54 | """Physical port limits from 1.0.0 spec.""" 55 | max_valid = int(Port.OFPP_MAX.value) 56 | for in_port in (-1, 0, max_valid + 1, max_valid + 2): 57 | self.message.in_port = in_port 58 | self.assertFalse(self.message.is_valid()) 59 | self.assertRaises(ValidationError, self.message.validate) 60 | 61 | 62 | def _get_actions(): 63 | """Function used to return a list of actions used by packetout instance.""" 64 | action = ActionOutput(port=1, max_length=0) 65 | return [action] 66 | 67 | 68 | def _get_data(): 69 | """Function used to return a BinaryData used by packetout instance.""" 70 | data = b'\x01# \x00\x00\x01\xd2A\xc6.*@\x88\xcc\x02\x07\x07dpi' 71 | data += b'd:1\x04\x02\x021\x06\x02\x00x\x0c\x06dpid:1\x00\x00' 72 | return data 73 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_port_mod.py: -------------------------------------------------------------------------------- 1 | """Test PortMod message.""" 2 | from pyof.v0x01.common.phy_port import PortConfig, PortFeatures 3 | from pyof.v0x01.controller2switch.port_mod import PortMod 4 | from tests.unit.test_struct import TestStruct 5 | 6 | 7 | class TestPortMod(TestStruct): 8 | """Test class for PortMod.""" 9 | 10 | @classmethod 11 | def setUpClass(cls): 12 | """[Controller2Switch/PortMod] - size 32.""" 13 | super().setUpClass() 14 | super().set_raw_dump_file('v0x01', 'ofpt_port_mod') 15 | super().set_raw_dump_object(PortMod, xid=3, port_no=80, 16 | hw_addr='aa:bb:cc:00:33:9f', 17 | config=PortConfig.OFPPC_PORT_DOWN, 18 | mask=PortConfig.OFPPC_NO_FWD, 19 | advertise=PortFeatures.OFPPF_FIBER) 20 | super().set_minimum_size(32) 21 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_port_stats.py: -------------------------------------------------------------------------------- 1 | """Test for PortStats structure.""" 2 | from pyof.v0x01.controller2switch.common import PortStats, StatsType 3 | from pyof.v0x01.controller2switch.stats_reply import StatsReply 4 | from tests.unit.test_struct import TestStruct 5 | 6 | 7 | class TestPortStats(TestStruct): 8 | """Test for PortStats structure.""" 9 | 10 | @classmethod 11 | def setUpClass(cls): 12 | """[Controller2Switch/PortStats] - size 104.""" 13 | super().setUpClass() 14 | super().set_raw_dump_file('v0x01', 'ofpt_port_stats') 15 | super().set_raw_dump_object(StatsReply, xid=13, 16 | body_type=StatsType.OFPST_PORT, 17 | flags=0, body=_get_port_stats()) 18 | super().set_minimum_size(12) 19 | 20 | 21 | def _get_port_stats(): 22 | """Function used to return a PortStats instance.""" 23 | return PortStats(port_no=80, rx_packets=5, tx_packets=10, 24 | rx_bytes=200, tx_bytes=400, rx_dropped=0, 25 | tx_dropped=0, rx_errors=0, tx_errors=0, 26 | rx_frame_err=0, rx_over_err=0, 27 | rx_crc_err=0, collisions=0) 28 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_port_stats_request.py: -------------------------------------------------------------------------------- 1 | """Test for PortStatsRequest.""" 2 | from pyof.v0x01.controller2switch.common import PortStatsRequest, StatsType 3 | from pyof.v0x01.controller2switch.stats_request import StatsRequest 4 | from tests.unit.test_struct import TestStruct 5 | 6 | 7 | class TestPortStatsRequest(TestStruct): 8 | """Test for PortStatsRequest.""" 9 | 10 | @classmethod 11 | def setUpClass(cls): 12 | """[Controller2Switch/PortStatsRequest] - size 8.""" 13 | super().setUpClass() 14 | super().set_raw_dump_file('v0x01', 'ofpt_port_stats_request') 15 | super().set_raw_dump_object(StatsRequest, xid=17, 16 | body_type=StatsType.OFPST_PORT, 17 | flags=0, body=PortStatsRequest(port_no=80)) 18 | super().set_minimum_size(12) 19 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_queue_get_config_reply.py: -------------------------------------------------------------------------------- 1 | """Test for QueueGetConfigReply message.""" 2 | from pyof.v0x01.common.phy_port import Port 3 | from pyof.v0x01.common.queue import ( 4 | PacketQueue, QueueProperties, QueuePropHeader) 5 | from pyof.v0x01.controller2switch import queue_get_config_reply 6 | from tests.unit.test_struct import TestStruct 7 | 8 | 9 | class TestQueueGetConfigReply(TestStruct): 10 | """Test for QueueGetConfigReply message.""" 11 | 12 | @classmethod 13 | def setUpClass(cls): 14 | """[Controller2Switch/QueueGetConfigReply] - size 16.""" 15 | super().setUpClass() 16 | super().set_raw_dump_file('v0x01', 'ofpt_queue_get_config_reply') 17 | super().set_raw_dump_object(queue_get_config_reply.QueueGetConfigReply, 18 | xid=1, port=Port.OFPP_ALL, 19 | queues=_get_packet_queue()) 20 | super().set_minimum_size(16) 21 | 22 | 23 | def _get_packet_queue(): 24 | """Function used to return a PacketQueue instance.""" 25 | packets = [] 26 | packets.append(PacketQueue(queue_id=1, length=8, 27 | properties=_get_queue_properties())) 28 | return packets 29 | 30 | 31 | def _get_queue_properties(): 32 | """Function used to return a list of queue properties.""" 33 | properties = [] 34 | properties.append(QueuePropHeader( 35 | queue_property=QueueProperties.OFPQT_MIN_RATE, length=12)) 36 | return properties 37 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_queue_get_config_request.py: -------------------------------------------------------------------------------- 1 | """Test for QueueGetConfigRequest message.""" 2 | from pyof.v0x01.common.phy_port import Port 3 | from pyof.v0x01.controller2switch import queue_get_config_request as request 4 | from tests.unit.test_struct import TestStruct 5 | 6 | 7 | class TestQueueGetConfigRequest(TestStruct): 8 | """Test for QueueGetConfigRequest message.""" 9 | 10 | @classmethod 11 | def setUpClass(cls): 12 | """[Controller2Switch/QueueGetConfigRequest] - size 12.""" 13 | super().setUpClass() 14 | super().set_raw_dump_file('v0x01', 'ofpt_queue_get_config_request') 15 | super().set_raw_dump_object(request.QueueGetConfigRequest, 16 | xid=1, port=Port.OFPP_MAX) 17 | super().set_minimum_size(12) 18 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_queue_stats.py: -------------------------------------------------------------------------------- 1 | """Test for QueueStats.""" 2 | from pyof.v0x01.controller2switch.common import QueueStats, StatsType 3 | from pyof.v0x01.controller2switch.stats_reply import StatsReply 4 | from tests.unit.test_struct import TestStruct 5 | 6 | 7 | class TestQueueStats(TestStruct): 8 | """Test for QueueStats.""" 9 | 10 | @classmethod 11 | def setUpClass(cls): 12 | """[Controller2Switch/QueueStats] - size 32.""" 13 | super().setUpClass() 14 | super().set_raw_dump_file('v0x01', 'ofpt_queue_stats') 15 | super().set_raw_dump_object(StatsReply, xid=7, 16 | body_type=StatsType.OFPST_QUEUE, 17 | flags=0, body=_get_queue_stats()) 18 | super().set_minimum_size(12) 19 | 20 | 21 | def _get_queue_stats(): 22 | """Function used to return a QueueStats instance.""" 23 | return QueueStats(port_no=80, queue_id=5, tx_bytes=1, 24 | tx_packets=3, tx_errors=2) 25 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_queue_stats_request.py: -------------------------------------------------------------------------------- 1 | """Test for QueueStatsRequest message.""" 2 | from pyof.v0x01.controller2switch.common import QueueStatsRequest, StatsType 3 | from pyof.v0x01.controller2switch.stats_request import StatsRequest 4 | from tests.unit.test_struct import TestStruct 5 | 6 | 7 | class TestQueueStatsRequest(TestStruct): 8 | """Test for QueueStatsRequest message.""" 9 | 10 | @classmethod 11 | def setUpClass(cls): 12 | """[Controller2Switch/QueueStatsRequest] - size 8.""" 13 | super().setUpClass() 14 | super().set_raw_dump_file('v0x01', 'ofpt_queue_stats_request') 15 | super().set_raw_dump_object(StatsRequest, xid=14, 16 | body_type=StatsType.OFPST_QUEUE, 17 | flags=0, 18 | body=QueueStatsRequest(port_no=80, 19 | queue_id=5)) 20 | super().set_minimum_size(12) 21 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_set_config.py: -------------------------------------------------------------------------------- 1 | """Set Config message tests.""" 2 | from pyof.v0x01.controller2switch.common import ConfigFlag 3 | from pyof.v0x01.controller2switch.set_config import SetConfig 4 | from tests.unit.test_struct import TestStruct 5 | 6 | 7 | class TestSetConfig(TestStruct): 8 | """Test the Set Config message.""" 9 | 10 | @classmethod 11 | def setUpClass(cls): 12 | """Configure raw file and its object in parent class (TestDump).""" 13 | super().setUpClass() 14 | super().set_raw_dump_file('v0x01', 'ofpt_set_config') 15 | super().set_raw_dump_object(SetConfig, xid=3, 16 | flags=ConfigFlag.OFPC_FRAG_NORMAL, 17 | miss_send_len=128) 18 | super().set_minimum_size(12) 19 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_stats_reply.py: -------------------------------------------------------------------------------- 1 | """Test for StatsReply message.""" 2 | from pyof.v0x01.controller2switch.common import StatsType 3 | from pyof.v0x01.controller2switch.stats_reply import StatsReply 4 | from tests.unit.test_struct import TestStruct 5 | 6 | 7 | class TestStatsReply(TestStruct): 8 | """Test for StatsReply message.""" 9 | 10 | @classmethod 11 | def setUpClass(cls): 12 | """[Controller2Switch/StatsReply] - size 12.""" 13 | super().setUpClass() 14 | super().set_raw_dump_file('v0x01', 'ofpt_stats_reply') 15 | super().set_raw_dump_object(StatsReply, xid=1, 16 | body_type=StatsType.OFPST_FLOW, 17 | flags=0x0001, body=b'') 18 | super().set_minimum_size(12) 19 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_stats_request.py: -------------------------------------------------------------------------------- 1 | """Test for StatsRequest message.""" 2 | from pyof.v0x01.controller2switch.common import StatsType 3 | from pyof.v0x01.controller2switch.stats_request import StatsRequest 4 | from tests.unit.test_struct import TestStruct 5 | 6 | 7 | class TestStatsRequest(TestStruct): 8 | """Test for StatsRequest message.""" 9 | 10 | @classmethod 11 | def setUpClass(cls): 12 | """[Controller2Switch/StatsRequest] - size 12.""" 13 | super().setUpClass() 14 | super().set_raw_dump_file('v0x01', 'ofpt_stats_request') 15 | super().set_raw_dump_object(StatsRequest, xid=1, 16 | body_type=StatsType.OFPST_FLOW, 17 | flags=1, body=b'') 18 | super().set_minimum_size(12) 19 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_table_stats.py: -------------------------------------------------------------------------------- 1 | """Test TableStats message.""" 2 | from pyof.foundation.constants import OFP_MAX_TABLE_NAME_LEN 3 | from pyof.v0x01.common.flow_match import FlowWildCards 4 | from pyof.v0x01.controller2switch.common import StatsType, TableStats 5 | from pyof.v0x01.controller2switch.stats_reply import StatsReply 6 | from tests.unit.test_struct import TestStruct 7 | 8 | 9 | class TestTableStats(TestStruct): 10 | """Test class for TableStats.""" 11 | 12 | @classmethod 13 | def setUpClass(cls): 14 | """[Controller2Switch/TableStats] - size 64.""" 15 | super().setUpClass() 16 | super().set_raw_dump_file('v0x01', 'ofpt_table_stats') 17 | super().set_raw_dump_object(StatsReply, xid=14, 18 | body_type=StatsType.OFPST_TABLE, 19 | flags=0, body=_get_table_stats()) 20 | super().set_minimum_size(12) 21 | 22 | 23 | def _get_table_stats(): 24 | return TableStats(table_id=1, 25 | name='X' * OFP_MAX_TABLE_NAME_LEN, 26 | wildcards=FlowWildCards.OFPFW_TP_DST, max_entries=1, 27 | active_count=10, count_lookup=10, count_matched=0) 28 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_controller2switch/test_vendor_stats.py: -------------------------------------------------------------------------------- 1 | """Test VendorStats message.""" 2 | from pyof.v0x01.controller2switch.common import StatsType, VendorStats 3 | from pyof.v0x01.controller2switch.stats_request import StatsRequest 4 | from tests.unit.test_struct import TestStruct 5 | 6 | 7 | class TestVendorStats(TestStruct): 8 | """Test class for TestVendorStats. 9 | 10 | The dump and unpacked data were provided by user bug report. 11 | """ 12 | 13 | @classmethod 14 | def setUpClass(cls): 15 | """[Controller2Switch/VendorStats] - size 1056.""" 16 | super().setUpClass() 17 | super().set_raw_dump_file('v0x01', 'ofpt_vendor_stats_reply') 18 | super().set_raw_dump_object(StatsRequest, xid=4, 19 | body_type=StatsType.OFPST_VENDOR, 20 | flags=0, body=_get_vendor_stats()) 21 | super().set_minimum_size(12) 22 | 23 | 24 | def _get_vendor_stats(): 25 | """Return vendor stats found in StatsReply.body.""" 26 | body = b'\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\xff\x00\x00\x00' 27 | return VendorStats(vendor=0x2320, body=body) 28 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_symmetric/__init__.py: -------------------------------------------------------------------------------- 1 | """Testing symmetric messages from v0x01.""" 2 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_symmetric/test_echo_reply.py: -------------------------------------------------------------------------------- 1 | """Echo reply message tests.""" 2 | from pyof.v0x01.symmetric.echo_reply import EchoReply 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestEchoReply(TestStruct): 7 | """Echo reply message tests (also those in :class:`.TestDump`).""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x01', 'ofpt_echo_reply') 14 | super().set_raw_dump_object(EchoReply, xid=0) 15 | super().set_minimum_size(8) 16 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_symmetric/test_echo_request.py: -------------------------------------------------------------------------------- 1 | """Echo request message tests.""" 2 | from pyof.v0x01.symmetric.echo_request import EchoRequest 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestEchoRequest(TestStruct): 7 | """Echo request message tests (also those in :class:`.TestDump`).""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x01', 'ofpt_echo_request') 14 | super().set_raw_dump_object(EchoRequest, xid=0) 15 | super().set_minimum_size(8) 16 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_symmetric/test_hello.py: -------------------------------------------------------------------------------- 1 | """Hello message tests.""" 2 | from pyof.v0x01.symmetric.hello import Hello 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestHello(TestStruct): 7 | """Hello message tests (also those in :class:`.TestDump`).""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x01', 'ofpt_hello') 14 | super().set_raw_dump_object(Hello, xid=1) 15 | super().set_minimum_size(8) 16 | -------------------------------------------------------------------------------- /tests/unit/v0x01/test_symmetric/test_vendor_header.py: -------------------------------------------------------------------------------- 1 | """Testing VendorHeader message.""" 2 | from pyof.v0x01.symmetric.vendor_header import VendorHeader 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestVendorHeader(TestStruct): 7 | """Vendor message tests (also those in :class:`.TestDump`).""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | super().set_minimum_size(8) 12 | 13 | def test_unpack(self): 14 | """Test unpack VendorHeader message.""" 15 | message = b'My custom vendor extra data.' 16 | vendor_header = VendorHeader(xid=4, vendor=128, 17 | data=message) 18 | data = b'\x01\x04\x00(\x00\x00\x00\x04\x00\x00\x00\x80' + message 19 | self._test_unpack(vendor_header, bytes2unpack=data) 20 | -------------------------------------------------------------------------------- /tests/unit/v0x04/__init__.py: -------------------------------------------------------------------------------- 1 | """v0x04 tests.""" 2 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_asynchronous/__init__.py: -------------------------------------------------------------------------------- 1 | """Tests for asynchronous package from v0x04 - OF 1.3.0.""" 2 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_asynchronous/test_error_msg.py: -------------------------------------------------------------------------------- 1 | """Testing v0x04 error message class.""" 2 | from pyof.foundation.exceptions import MethodNotImplemented 3 | from pyof.v0x04.asynchronous.error_msg import ( 4 | ErrorExperimenterMsg, ErrorMsg, ErrorType, MeterModFailedCode) 5 | from tests.unit.test_struct import TestStruct 6 | 7 | 8 | class TestErrorMsg(TestStruct): 9 | """ErroMsg message tests (also those in :class:`.TestDump`).""" 10 | 11 | @classmethod 12 | def setUpClass(cls): 13 | """Configure raw file and its object in parent class (TestDump).""" 14 | error_type = ErrorType.OFPET_METER_MOD_FAILED 15 | code = MeterModFailedCode.OFPMMFC_UNKNOWN_METER 16 | super().setUpClass() 17 | super().set_raw_dump_file('v0x04', 'ofpt_error') 18 | super().set_raw_dump_object(ErrorMsg, xid=1, error_type=error_type, 19 | code=code, data=_get_data()) 20 | super().set_minimum_size(12) 21 | 22 | @staticmethod 23 | def test_unpack(): 24 | """[Asynchronous/error_msg] - unpack ErrorExperimenterMsg.""" 25 | unpacked = ErrorExperimenterMsg() 26 | try: 27 | unpacked.unpack("pack") 28 | except MethodNotImplemented: 29 | pass 30 | 31 | 32 | def _get_data(): 33 | """Return data for ErrorMsg object.""" 34 | data = b'\x04\x12\x00\x18\x00\x00\x00\x01\x00\x0a\x00\x00\x00\x00\x00' 35 | data += b'\x00\x00\x00\x00\x01\x00\x00\x00\x00' 36 | return data 37 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_asynchronous/test_flow_removed.py: -------------------------------------------------------------------------------- 1 | """Testing v0x04 FlowRemoved message.""" 2 | from pyof.v0x04.asynchronous.flow_removed import FlowRemoved, FlowRemovedReason 3 | from pyof.v0x04.common.flow_match import ( 4 | Match, MatchType, OxmClass, OxmOfbMatchField, OxmTLV) 5 | from tests.unit.test_struct import TestStruct 6 | 7 | 8 | class TestFlowRemovedMsg(TestStruct): 9 | """FlowRemoved message tests (also those in :class:`.TestDump`).""" 10 | 11 | @classmethod 12 | def setUpClass(cls): 13 | """Configure raw file and its object in parent class (TestDump).""" 14 | super().setUpClass() 15 | super().set_raw_dump_file('v0x04', 'ofpt_flow_removed') 16 | super().set_raw_dump_object(FlowRemoved, xid=0, 17 | cookie=0x0000000000000000, priority=1000, 18 | reason=FlowRemovedReason.OFPRR_DELETE, 19 | table_id=0, duration_sec=77, 20 | duration_nsec=559000000, idle_timeout=0, 21 | hard_timeout=0, packet_count=0, 22 | byte_count=0, match=_new_match()) 23 | super().set_minimum_size(56) 24 | 25 | 26 | def _new_match(): 27 | """Crate new Match instance.""" 28 | tlv1 = OxmTLV(oxm_class=OxmClass.OFPXMC_OPENFLOW_BASIC, 29 | oxm_field=OxmOfbMatchField.OFPXMT_OFB_ETH_TYPE, 30 | oxm_hasmask=False, oxm_value=b'\x88\xcc') 31 | tlv2 = OxmTLV(oxm_class=OxmClass.OFPXMC_OPENFLOW_BASIC, 32 | oxm_field=OxmOfbMatchField.OFPXMT_OFB_VLAN_VID, 33 | oxm_hasmask=False, oxm_value=b'\x1e\xd7') 34 | return Match(match_type=MatchType.OFPMT_OXM, 35 | oxm_match_fields=[tlv1, tlv2]) 36 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_asynchronous/test_packet_in.py: -------------------------------------------------------------------------------- 1 | """Packet in message tests.""" 2 | from pyof.v0x04.asynchronous.packet_in import PacketIn, PacketInReason 3 | from pyof.v0x04.common.constants import OFP_NO_BUFFER 4 | from pyof.v0x04.common.flow_match import ( 5 | Match, MatchType, OxmClass, OxmOfbMatchField, OxmTLV) 6 | from pyof.v0x04.common.port import PortNo 7 | from tests.unit.test_struct import TestStruct 8 | 9 | 10 | class TestPacketInRaw(TestStruct): 11 | """Test PacketIn using a dump file.""" 12 | 13 | @classmethod 14 | def setUpClass(cls): 15 | """Configure raw file and its object in parent class (TestDump).""" 16 | super().setUpClass() 17 | super().set_raw_dump_file('v0x04', 'ofpt_packet_in') 18 | super().set_raw_dump_object(PacketIn, xid=0, buffer_id=OFP_NO_BUFFER, 19 | total_len=90, 20 | reason=PacketInReason.OFPR_ACTION, 21 | table_id=0, cookie=0x0000000000000000, 22 | match=_new_match(), data=_get_data()) 23 | super().set_minimum_size(34) 24 | 25 | def test_valid_physical_in_port(self): 26 | """Physical port limits from 1.3.0 spec.""" 27 | try: 28 | msg = self.get_raw_dump().read() 29 | except FileNotFoundError: 30 | raise self.skipTest('No raw dump file found.') 31 | else: 32 | max_valid = int(PortNo.OFPP_MAX.value) - 1 33 | msg = self.get_raw_object() 34 | if msg.in_port in (1, max_valid): 35 | self.assertTrue(msg.is_valid()) 36 | 37 | 38 | def _new_match(): 39 | """Crate new Match instance.""" 40 | oxmtlv = OxmTLV(oxm_class=OxmClass.OFPXMC_OPENFLOW_BASIC, 41 | oxm_field=OxmOfbMatchField.OFPXMT_OFB_IN_PORT, 42 | oxm_hasmask=False, oxm_value=b'\x00\x00\x00\x02') 43 | return Match(match_type=MatchType.OFPMT_OXM, 44 | oxm_match_fields=[oxmtlv]) 45 | 46 | 47 | def _get_data(): 48 | """Return data for PacketIn object.""" 49 | data = b'\x33\x33\x00\x00\x00\x16\x92\xfd\x3d\x2a\x06\x0c\x86\xdd\x60\x00' 50 | data += b'\x00\x00\x00\x24\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 51 | data += b'\x00\x00\x00\x00\x00\x00\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00' 52 | data += b'\x00\x00\x00\x00\x00\x16\x3a\x00\x05\x02\x00\x00\x01\x00\x8f\x00' 53 | data += b'\x69\x54\x00\x00\x00\x01\x04\x00\x00\x00\xff\x02\x00\x00\x00\x00' 54 | data += b'\x00\x00\x00\x00\x00\x01\xff\x2a\x06\x0c' 55 | return data 56 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_asynchronous/test_port_status.py: -------------------------------------------------------------------------------- 1 | """Port Status message tests.""" 2 | from pyof.foundation.basic_types import HWAddress 3 | from pyof.v0x04.asynchronous.port_status import PortReason, PortStatus 4 | from pyof.v0x04.common.port import Port, PortFeatures, PortState 5 | from tests.unit.test_struct import TestStruct 6 | 7 | 8 | class TestPortStatus(TestStruct): 9 | """Test the Port Status message.""" 10 | 11 | @classmethod 12 | def setUpClass(cls): 13 | """Configure raw file and its object in parent class (TestDump).""" 14 | super().setUpClass() 15 | super().set_raw_dump_file('v0x04', 'ofpt_port_status') 16 | super().set_raw_dump_object(_new_portstatus) 17 | super().set_minimum_size(80) 18 | 19 | 20 | def _new_portstatus(): 21 | """Crate new PortStatus and Port instances.""" 22 | desc_name = 's1-eth1' 23 | desc = Port(port_no=1, 24 | hw_addr=HWAddress('62:43:e5:db:35:0a'), 25 | name=desc_name, 26 | config=0, 27 | state=PortState.OFPPS_LIVE, 28 | curr=PortFeatures.OFPPF_10GB_FD | PortFeatures.OFPPF_COPPER, 29 | advertised=0, 30 | supported=0, 31 | peer=0, 32 | curr_speed=10000000, 33 | max_speed=0) 34 | return PortStatus(xid=0, 35 | reason=PortReason.OFPPR_MODIFY, 36 | desc=desc) 37 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_common/__init__.py: -------------------------------------------------------------------------------- 1 | """Tests common strucutres of v0x04.""" 2 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_common/test_port.py: -------------------------------------------------------------------------------- 1 | """Test of Port class from common module.""" 2 | from pyof.v0x04.common.port import Port 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestPort(TestStruct): 7 | """Port structure tests (also those in :class:`.TestDump`).""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x04', 'port') 14 | super().set_raw_dump_object(Port) 15 | super().set_minimum_size(64) 16 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_common/test_queue.py: -------------------------------------------------------------------------------- 1 | """Test of v0x04 queue module.""" 2 | from pyof.v0x04.common.queue import ( 3 | PacketQueue, QueuePropExperimenter, QueuePropHeader, QueuePropMaxRate, 4 | QueuePropMinRate) 5 | from tests.unit.test_struct import TestStruct 6 | 7 | 8 | class TestPacketQueue(TestStruct): 9 | """Packet Queue structure tests (also those in :class:`.TestDump`).""" 10 | 11 | @classmethod 12 | def setUpClass(cls): 13 | """Configure raw file and its object in parent class (TestDump).""" 14 | super().setUpClass() 15 | super().set_raw_dump_file('v0x04', 'packet_queue') 16 | super().set_raw_dump_object(PacketQueue) 17 | super().set_minimum_size(16) 18 | 19 | 20 | class TestQueuePropExperimenter(TestStruct): 21 | """QueuePropExperimenter tests (also those in :class:`.TestDump`).""" 22 | 23 | @classmethod 24 | def setUpClass(cls): 25 | """Configure raw file and its object in parent class (TestDump).""" 26 | super().setUpClass() 27 | super().set_raw_dump_file('v0x04', 'queue_prop_experimenter') 28 | super().set_raw_dump_object(QueuePropExperimenter) 29 | super().set_minimum_size(16) 30 | 31 | 32 | class TestQueuePropHeader(TestStruct): 33 | """QueuePropHeader structure tests (also those in :class:`.TestDump`).""" 34 | 35 | @classmethod 36 | def setUpClass(cls): 37 | """Configure raw file and its object in parent class (TestDump).""" 38 | super().setUpClass() 39 | super().set_raw_dump_file('v0x04', 'queue_prop_header') 40 | super().set_raw_dump_object(QueuePropHeader) 41 | super().set_minimum_size(8) 42 | 43 | 44 | class TestQueuePropMaxRate(TestStruct): 45 | """QueuePropMaxRate structure tests (also those in :class:`.TestDump`).""" 46 | 47 | @classmethod 48 | def setUpClass(cls): 49 | """Configure raw file and its object in parent class (TestDump).""" 50 | super().setUpClass() 51 | super().set_raw_dump_file('v0x04', 'queue_prop_max_rate') 52 | super().set_raw_dump_object(QueuePropMaxRate) 53 | super().set_minimum_size(16) 54 | 55 | 56 | class TestQueuePropMinRate(TestStruct): 57 | """QueuePropMinRate structure tests (also those in :class:`.TestDump`).""" 58 | 59 | @classmethod 60 | def setUpClass(cls): 61 | """Configure raw file and its object in parent class (TestDump).""" 62 | super().setUpClass() 63 | super().set_raw_dump_file('v0x04', 'queue_prop_min_rate') 64 | super().set_raw_dump_object(QueuePropMinRate) 65 | super().set_minimum_size(16) 66 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/__init__.py: -------------------------------------------------------------------------------- 1 | """Controller-to-switch tests.""" 2 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_aggregate_stats.py: -------------------------------------------------------------------------------- 1 | """Aggregate stats request message.""" 2 | from pyof.v0x04.controller2switch.common import MultipartType 3 | from pyof.v0x04.controller2switch.multipart_reply import ( 4 | AggregateStatsReply, MultipartReply) 5 | from tests.unit.test_struct import TestStruct 6 | 7 | 8 | class TestAggregateStats(TestStruct): 9 | """Aggregate stats message.""" 10 | 11 | @classmethod 12 | def setUpClass(cls): 13 | """Configure raw file and its object in parent class (TestDump).""" 14 | mp_type = MultipartType.OFPMP_AGGREGATE 15 | super().setUpClass() 16 | super().set_raw_dump_file('v0x04', 'ofpt_aggregate_stats') 17 | super().set_raw_dump_object(MultipartReply, xid=1, 18 | multipart_type=mp_type, 19 | flags=0, 20 | body=_get_body()) 21 | super().set_minimum_size(16) 22 | 23 | 24 | def _get_body(): 25 | """Return the body used by MultipartReply message.""" 26 | return AggregateStatsReply(packet_count=2, byte_count=220, flow_count=2) 27 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_aggregate_stats_request.py: -------------------------------------------------------------------------------- 1 | """Aggregate stats request message.""" 2 | from pyof.v0x04.common.flow_match import Match 3 | from pyof.v0x04.controller2switch.common import MultipartType 4 | from pyof.v0x04.controller2switch.multipart_request import ( 5 | AggregateStatsRequest, MultipartRequest) 6 | from tests.unit.test_struct import TestStruct 7 | 8 | 9 | class TestAggregateStatsRequest(TestStruct): 10 | """Aggregate stats request message.""" 11 | 12 | @classmethod 13 | def setUpClass(cls): 14 | """Configure raw file and its object in parent class (TestDump).""" 15 | mp_type = MultipartType.OFPMP_AGGREGATE 16 | super().setUpClass() 17 | super().set_raw_dump_file('v0x04', 'ofpt_aggregate_stats_request') 18 | super().set_raw_dump_object(MultipartRequest, xid=1, 19 | multipart_type=mp_type, 20 | flags=0, body=_get_body()) 21 | super().set_minimum_size(16) 22 | 23 | 24 | def _get_body(): 25 | """Return the body used by MultipartRequest message.""" 26 | return AggregateStatsRequest(match=Match()) 27 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_barrier_reply.py: -------------------------------------------------------------------------------- 1 | """Barrier reply message tests.""" 2 | from pyof.v0x04.controller2switch.barrier_reply import BarrierReply 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestBarrierReply(TestStruct): 7 | """Barrier reply message tests (also those in :class:`.TestDump`).""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x04', 'ofpt_barrier_reply') 14 | super().set_raw_dump_object(BarrierReply, xid=5) 15 | super().set_minimum_size(8) 16 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_barrier_request.py: -------------------------------------------------------------------------------- 1 | """Barrier request message tests.""" 2 | from pyof.v0x04.controller2switch.barrier_request import BarrierRequest 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestBarrierRequest(TestStruct): 7 | """Barrier reply message tests (also those in :class:`.TestDump`).""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x04', 'ofpt_barrier_request') 14 | super().set_raw_dump_object(BarrierRequest, xid=5) 15 | super().set_minimum_size(8) 16 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_features_reply.py: -------------------------------------------------------------------------------- 1 | """Echo request message tests.""" 2 | from pyof.foundation.basic_types import DPID 3 | from pyof.v0x04.controller2switch.features_reply import FeaturesReply 4 | from tests.unit.test_struct import TestStruct 5 | 6 | 7 | class TestFeaturesReply(TestStruct): 8 | """Feature reply message tests (also those in :class:`.TestDump`).""" 9 | 10 | @classmethod 11 | def setUpClass(cls): 12 | """Configure raw file and its object in parent class (TestDump).""" 13 | super().setUpClass() 14 | super().set_raw_dump_file('v0x04', 'ofpt_features_reply') 15 | kwargs = _get_kwargs() 16 | super().set_raw_dump_object(FeaturesReply, **kwargs) 17 | super().set_minimum_size(32) 18 | 19 | 20 | def _get_kwargs(): 21 | return {'xid': 3, 'datapath_id': DPID('00:00:00:00:00:00:00:01'), 22 | 'n_buffers': 0, 'n_tables': 254, 'auxiliary_id': 0, 23 | 'capabilities': 0x0000004f, 'reserved': 0x00000000} 24 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_features_request.py: -------------------------------------------------------------------------------- 1 | """Feature request message tests.""" 2 | from pyof.v0x04.controller2switch.features_request import FeaturesRequest 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestFeaturesRequest(TestStruct): 7 | """Feature request message tests (also those in :class:`.TestDump`).""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x04', 'ofpt_features_request') 14 | super().set_raw_dump_object(FeaturesRequest, xid=3) 15 | super().set_minimum_size(8) 16 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_flow_mod.py: -------------------------------------------------------------------------------- 1 | """FlowMod test.""" 2 | from pyof.v0x04.common.action import ActionOutput, ListOfActions 3 | from pyof.v0x04.common.flow_instructions import ( 4 | InstructionApplyAction, ListOfInstruction) 5 | from pyof.v0x04.common.flow_match import ( 6 | Match, MatchType, OxmClass, OxmOfbMatchField, OxmTLV) 7 | from pyof.v0x04.common.port import PortNo 8 | from pyof.v0x04.controller2switch.flow_mod import FlowMod, FlowModCommand 9 | from tests.unit.test_struct import TestStruct 10 | 11 | 12 | class TestFlowMod(TestStruct): 13 | """FlowMod test.""" 14 | 15 | def test_min_size(self): 16 | """Test struct minimum size.""" 17 | super().set_raw_dump_file('v0x04', 'ofpt_flow_mod') 18 | super().set_raw_dump_object(FlowMod, xid=2219910763, 19 | command=FlowModCommand.OFPFC_ADD, 20 | priority=1000, 21 | match=_new_match(), 22 | instructions=_new_list_of_instructions()) 23 | super().set_minimum_size(56) 24 | 25 | 26 | def _new_match(): 27 | """Crate new Match instance.""" 28 | oxm_tlv1 = OxmTLV(oxm_class=OxmClass.OFPXMC_OPENFLOW_BASIC, 29 | oxm_field=OxmOfbMatchField.OFPXMT_OFB_ETH_TYPE, 30 | oxm_hasmask=False, oxm_value=b'\x88\xcc') 31 | oxmt_lv2 = OxmTLV(oxm_class=OxmClass.OFPXMC_OPENFLOW_BASIC, 32 | oxm_field=OxmOfbMatchField.OFPXMT_OFB_VLAN_VID, 33 | oxm_hasmask=False, oxm_value=b'\x1e\xd7') 34 | return Match(match_type=MatchType.OFPMT_OXM, 35 | oxm_match_fields=[oxm_tlv1, oxmt_lv2]) 36 | 37 | def _new_list_of_instructions(): 38 | """Crate new ListOfInstruction.""" 39 | output = ActionOutput(port=PortNo.OFPP_CONTROLLER) 40 | loa = ListOfActions([output]) 41 | instruction = InstructionApplyAction(loa) 42 | return ListOfInstruction([instruction]) 43 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_flow_stats.py: -------------------------------------------------------------------------------- 1 | """Flow stats message.""" 2 | from pyof.v0x04.common.action import ActionOutput, ListOfActions 3 | from pyof.v0x04.common.flow_instructions import ( 4 | InstructionApplyAction, ListOfInstruction) 5 | from pyof.v0x04.common.flow_match import ( 6 | Match, MatchType, OxmClass, OxmOfbMatchField, OxmTLV) 7 | from pyof.v0x04.common.port import PortNo 8 | from pyof.v0x04.controller2switch.common import MultipartType 9 | from pyof.v0x04.controller2switch.multipart_reply import ( 10 | FlowStats, MultipartReply) 11 | from tests.unit.test_struct import TestStruct 12 | 13 | 14 | class TestFlowStats(TestStruct): 15 | """Flow stats message.""" 16 | 17 | @classmethod 18 | def setUpClass(cls): 19 | """Configure raw file and its object in parent class (TestDump).""" 20 | super().setUpClass() 21 | super().set_raw_dump_file('v0x04', 'ofpt_flow_stats') 22 | super().set_raw_dump_object(MultipartReply, xid=2898845528, 23 | multipart_type=MultipartType.OFPMP_FLOW, 24 | flags=0, 25 | body=_get_body()) 26 | super().set_minimum_size(16) 27 | 28 | 29 | def _get_body(): 30 | """Return the body used by MultipartReply message.""" 31 | return FlowStats(length=88, table_id=0, duration_sec=56, 32 | duration_nsec=635000000, priority=1000, idle_timeout=0, 33 | hard_timeout=0, flags=0x00000001, 34 | cookie=0x0000000000000000, packet_count=18, 35 | byte_count=756, match=_new_match(), 36 | instructions=_new_list_of_instructions()) 37 | 38 | 39 | def _new_match(): 40 | """Crate new Match instance.""" 41 | oxmtlv1 = OxmTLV(oxm_class=OxmClass.OFPXMC_OPENFLOW_BASIC, 42 | oxm_field=OxmOfbMatchField.OFPXMT_OFB_ETH_TYPE, 43 | oxm_hasmask=False, oxm_value=b'\x88\xcc') 44 | oxmtlv2 = OxmTLV(oxm_class=OxmClass.OFPXMC_OPENFLOW_BASIC, 45 | oxm_field=OxmOfbMatchField.OFPXMT_OFB_VLAN_VID, 46 | oxm_hasmask=False, oxm_value=b'\x1e\xd7') 47 | return Match(match_type=MatchType.OFPMT_OXM, 48 | oxm_match_fields=[oxmtlv1, oxmtlv2]) 49 | 50 | 51 | def _new_list_of_instructions(): 52 | """Crate new ListOfInstruction.""" 53 | action_output = ActionOutput(port=PortNo.OFPP_CONTROLLER) 54 | loa = ListOfActions([action_output]) 55 | instruction = InstructionApplyAction(loa) 56 | return ListOfInstruction([instruction]) 57 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_flow_stats_request.py: -------------------------------------------------------------------------------- 1 | """Flow stats request message.""" 2 | from pyof.v0x04.common.flow_match import Match 3 | from pyof.v0x04.controller2switch.common import MultipartType 4 | from pyof.v0x04.controller2switch.multipart_request import ( 5 | FlowStatsRequest, MultipartRequest) 6 | from tests.unit.test_struct import TestStruct 7 | 8 | 9 | class TestFlowStatsRequest(TestStruct): 10 | """Flow stats request message.""" 11 | 12 | @classmethod 13 | def setUpClass(cls): 14 | """Configure raw file and its object in parent class (TestDump).""" 15 | super().setUpClass() 16 | super().set_raw_dump_file('v0x04', 'ofpt_flow_stats_request') 17 | super().set_raw_dump_object(MultipartRequest, xid=590715727, 18 | multipart_type=MultipartType.OFPMP_FLOW, 19 | flags=0, body=_get_body()) 20 | super().set_minimum_size(16) 21 | 22 | 23 | def _get_body(): 24 | """Return the body used by MultipartRequest message.""" 25 | return FlowStatsRequest(match=Match()) 26 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_get_async_reply.py: -------------------------------------------------------------------------------- 1 | """GetAsyncReply message tests.""" 2 | from pyof.v0x04.controller2switch.get_async_reply import GetAsyncReply 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestGetAsyncReply(TestStruct): 7 | """Test the GetAsyncReply message.""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x04', 'ofpt_get_async_reply') 14 | super().set_raw_dump_object(GetAsyncReply, xid=3, packet_in_mask1=0, 15 | packet_in_mask2=0, port_status_mask1=0, 16 | port_status_mask2=0, flow_removed_mask1=0, 17 | flow_removed_mask2=0) 18 | super().set_minimum_size(32) 19 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_get_async_request.py: -------------------------------------------------------------------------------- 1 | """GetAsyncRequest message tests.""" 2 | from pyof.v0x04.controller2switch.get_async_request import GetAsyncRequest 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestGetAsyncRequest(TestStruct): 7 | """Test the GetAsyncRequest message.""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x04', 'ofpt_get_async_request') 14 | super().set_raw_dump_object(GetAsyncRequest, xid=3) 15 | super().set_minimum_size(8) 16 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_get_config_reply.py: -------------------------------------------------------------------------------- 1 | """Config Reply message tests.""" 2 | from pyof.v0x04.controller2switch.get_config_reply import GetConfigReply 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestGetConfigReply(TestStruct): 7 | """Config Reply message tests.""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x04', 'ofpt_get_config_reply') 14 | super().set_raw_dump_object(GetConfigReply, xid=1) 15 | super().set_minimum_size(12) 16 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_get_config_request.py: -------------------------------------------------------------------------------- 1 | """Config Request message tests.""" 2 | from pyof.v0x04.controller2switch.get_config_request import GetConfigRequest 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestGetConfigRequest(TestStruct): 7 | """Config Request message tests.""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x04', 'ofpt_get_config_request') 14 | super().set_raw_dump_object(GetConfigRequest) 15 | super().set_minimum_size(8) 16 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_group_stats.py: -------------------------------------------------------------------------------- 1 | """Group stats message.""" 2 | from pyof.v0x04.common.action import ActionOutput, ListOfActions 3 | from pyof.v0x04.common.flow_instructions import ( 4 | InstructionApplyAction, ListOfInstruction) 5 | from pyof.v0x04.common.flow_match import ( 6 | Match, MatchType, OxmClass, OxmOfbMatchField, OxmTLV) 7 | from pyof.v0x04.common.port import PortNo 8 | from pyof.v0x04.controller2switch.common import ( 9 | BucketCounter, ListOfBucketCounter, MultipartType) 10 | from pyof.v0x04.controller2switch.multipart_reply import ( 11 | GroupStats, MultipartReply) 12 | from tests.unit.test_struct import TestStruct 13 | 14 | 15 | class TestGroupStats(TestStruct): 16 | """Group stats message.""" 17 | 18 | @classmethod 19 | def setUpClass(cls): 20 | """Configure raw file and its object in parent class (TestDump).""" 21 | super().setUpClass() 22 | super().set_raw_dump_file('v0x04', 'ofpt_group_stats') 23 | super().set_raw_dump_object(MultipartReply, xid=1, 24 | multipart_type=MultipartType.OFPMP_GROUP, 25 | flags=0, 26 | body=_get_body()) 27 | super().set_minimum_size(16) 28 | 29 | 30 | def _get_body(): 31 | """Return the body used by MultipartReply message.""" 32 | bs = ListOfBucketCounter([BucketCounter(packet_count=0, byte_count=0), 33 | BucketCounter(packet_count=0, byte_count=0)]) 34 | return GroupStats(length=72, group_id=1, ref_count=0, 35 | packet_count=0, byte_count=0, duration_sec=14, 36 | duration_nsec=837000000, bucket_stats=bs) 37 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_group_stats_request.py: -------------------------------------------------------------------------------- 1 | """Group stats request message.""" 2 | from pyof.v0x04.controller2switch.common import MultipartType 3 | from pyof.v0x04.controller2switch.multipart_request import ( 4 | GroupStatsRequest, MultipartRequest) 5 | from tests.unit.test_struct import TestStruct 6 | 7 | 8 | class TestGroupStatsRequest(TestStruct): 9 | """Group stats request message.""" 10 | 11 | @classmethod 12 | def setUpClass(cls): 13 | """Configure raw file and its object in parent class (TestDump).""" 14 | super().setUpClass() 15 | super().set_raw_dump_file('v0x04', 'ofpt_group_stats_request') 16 | super().set_raw_dump_object(MultipartRequest, xid=1, 17 | multipart_type=MultipartType.OFPMP_GROUP, 18 | flags=0, body=_get_body()) 19 | super().set_minimum_size(16) 20 | 21 | 22 | def _get_body(): 23 | """Return the body used by MultipartRequest message.""" 24 | return GroupStatsRequest() 25 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_meter_mod.py: -------------------------------------------------------------------------------- 1 | """MeterMod tests.""" 2 | from unittest import TestCase 3 | 4 | from pyof.v0x04.controller2switch.meter_mod import ( 5 | MeterBandDrop, MeterBandDscpRemark, MeterBandExperimenter, MeterBandHeader, 6 | MeterMod) 7 | 8 | 9 | class TestMeterMod(TestCase): 10 | """MeterMod test.""" 11 | 12 | def test_min_size(self): 13 | """Test minimum message size.""" 14 | self.assertEqual(16, MeterMod().get_size()) 15 | 16 | 17 | class TestMeterBandHeader(TestCase): 18 | """MeterBandHeader test.""" 19 | 20 | def test_min_size(self): 21 | """Test minimum message size.""" 22 | self.assertEqual(12, MeterBandHeader().get_size()) 23 | 24 | 25 | class TestMeterBandDrop(TestCase): 26 | """MeterBandDrop test.""" 27 | 28 | def test_min_size(self): 29 | """Test minimum message size.""" 30 | self.assertEqual(16, MeterBandDrop().get_size()) 31 | 32 | 33 | class TestMeterBandDscpRemark(TestCase): 34 | """MeterBandDscpRemark test.""" 35 | 36 | def test_min_size(self): 37 | """Test minimum message size.""" 38 | self.assertEqual(16, MeterBandDscpRemark().get_size()) 39 | 40 | 41 | class TestMeterBandExperimenter(TestCase): 42 | """MeterBandExperimenter test.""" 43 | 44 | def test_min_size(self): 45 | """Test minimum message size.""" 46 | self.assertEqual(16, MeterBandExperimenter().get_size()) 47 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_meter_multipart_request.py: -------------------------------------------------------------------------------- 1 | """MultipartRequest message test.""" 2 | from pyof.v0x04.controller2switch.multipart_request import ( 3 | MeterMultipartRequest, MultipartRequest, MultipartType) 4 | from tests.unit.test_struct import TestStruct 5 | 6 | 7 | class TestMeterMultipartRequest(TestStruct): 8 | """Test the MultipartRequest message.""" 9 | 10 | @classmethod 11 | def setUpClass(cls): 12 | """Configure raw file and its object in parent class (TestDump).""" 13 | mp_type = MultipartType.OFPMP_METER 14 | super().setUpClass() 15 | super().set_raw_dump_file('v0x04', 'ofpt_meter_multipart_request') 16 | super().set_raw_dump_object(MultipartRequest, xid=1, 17 | multipart_type=mp_type, 18 | flags=0, body=_get_body()) 19 | super().set_minimum_size(16) 20 | 21 | 22 | def _get_body(): 23 | """Return the body used by MultipartRequest message.""" 24 | return MeterMultipartRequest() 25 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_multipart_reply.py: -------------------------------------------------------------------------------- 1 | """MultipartReply message test.""" 2 | 3 | from pyof.v0x04.controller2switch.common import MultipartType 4 | from pyof.v0x04.controller2switch.multipart_reply import ( 5 | Desc, MultipartReply, MultipartReplyFlags) 6 | from tests.unit.v0x04.test_struct import TestStruct 7 | 8 | 9 | class TestMultipartReply(TestStruct): 10 | """Test MultipartReply.""" 11 | 12 | @classmethod 13 | def setUpClass(cls): 14 | """Configure raw file and its object in parent class (TestDump).""" 15 | super().setUpClass() 16 | super().set_message(MultipartReply, xid=16, 17 | multipart_type=MultipartType.OFPMP_METER_CONFIG, 18 | flags=MultipartReplyFlags.OFPMPF_REPLY_MORE, 19 | body=b'') 20 | super().set_minimum_size(16) 21 | 22 | @staticmethod 23 | def get_attributes(multipart_type=MultipartType.OFPMP_DESC, 24 | flags=MultipartReplyFlags.OFPMPF_REPLY_MORE, 25 | body=b''): 26 | """Method used to return a dict with instance paramenters.""" 27 | return {'xid': 32, 'multipart_type': multipart_type, 'flags': flags, 28 | 'body': body} 29 | 30 | def test_pack_unpack_desc(self): 31 | """Testing multipart_type with OFPMP_DESC.""" 32 | instances = Desc(mfr_desc="MANUFACTURER DESCRIPTION", 33 | hw_desc="HARDWARE DESCRIPTION", 34 | sw_desc="SOFTWARE DESCRIPTION", 35 | serial_num="SERIAL NUMBER", 36 | dp_desc="DATAPATH DESCRIPTION") 37 | options = TestMultipartReply.get_attributes( 38 | multipart_type=MultipartType.OFPMP_DESC, body=instances) 39 | self._test_pack_unpack(**options) 40 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_multipart_request.py: -------------------------------------------------------------------------------- 1 | """MultipartRequest message test.""" 2 | from pyof.v0x04.controller2switch.multipart_request import ( 3 | MultipartRequest, MultipartRequestFlags, MultipartType, PortStatsRequest, 4 | TableFeatures) 5 | from tests.unit.v0x04.test_struct import TestStruct 6 | 7 | 8 | class TestMultipartRequest(TestStruct): 9 | """Test the MultipartRequest message.""" 10 | 11 | @classmethod 12 | def setUpClass(cls): 13 | """Configure raw file and its object in parent class (TestDump).""" 14 | super().setUpClass() 15 | 16 | super().set_message(MultipartRequest, xid=16, 17 | multipart_type=MultipartType.OFPMP_TABLE_FEATURES, 18 | flags=MultipartRequestFlags.OFPMPF_REQ_MORE, 19 | body=b'') 20 | super().set_minimum_size(16) 21 | 22 | @staticmethod 23 | def get_attributes(multipart_type=MultipartType.OFPMP_DESC, 24 | flags=MultipartRequestFlags.OFPMPF_REQ_MORE, 25 | body=b''): 26 | """Method used to return a dict with instance paramenters.""" 27 | return {'xid': 32, 'multipart_type': multipart_type, 'flags': flags, 28 | 'body': body} 29 | 30 | def test_pack_unpack_desc(self): 31 | """Testing multipart_type with OFPMP_DESC.""" 32 | options = TestMultipartRequest.get_attributes( 33 | multipart_type=MultipartType.OFPMP_DESC) 34 | self._test_pack_unpack(**options) 35 | 36 | def test_pack_unpack_table(self): 37 | """Testing multipart_type with OFPMP_TABLE.""" 38 | options = TestMultipartRequest.get_attributes( 39 | multipart_type=MultipartType.OFPMP_TABLE) 40 | self._test_pack_unpack(**options) 41 | 42 | def test_pack_unpack__port_stats_request(self): 43 | """Testing multipart_type with OFPMP_PORT_STATS.""" 44 | options = TestMultipartRequest.get_attributes( 45 | multipart_type=MultipartType.OFPMP_PORT_STATS, 46 | body=PortStatsRequest(port_no=33)) 47 | self._test_pack_unpack(**options) 48 | 49 | def test_pack_unpack_port_desc(self): 50 | """Testing multipart_type with OFPMP_PORT_DESC.""" 51 | options = TestMultipartRequest.get_attributes( 52 | multipart_type=MultipartType.OFPMP_PORT_DESC) 53 | self._test_pack_unpack(**options) 54 | 55 | def test_pack_unpack_table_features(self): 56 | """Testing multipart_type with OFPMP_TABLE_FEATURES.""" 57 | instance = [TableFeatures(table_id=2), 58 | TableFeatures(table_id=6), 59 | TableFeatures(table_id=4)] 60 | options = TestMultipartRequest.get_attributes( 61 | multipart_type=MultipartType.OFPMP_TABLE_FEATURES, 62 | body=instance) 63 | self._test_pack_unpack(**options) 64 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_packet_out.py: -------------------------------------------------------------------------------- 1 | """Packet out message tests.""" 2 | from pyof.foundation.exceptions import ValidationError 3 | from pyof.v0x04.common.action import ActionOutput 4 | from pyof.v0x04.common.constants import OFP_NO_BUFFER 5 | from pyof.v0x04.common.port import PortNo 6 | from pyof.v0x04.controller2switch.packet_out import PacketOut 7 | from tests.unit.test_struct import TestStruct 8 | 9 | NO_RAW = 'No raw dump file found.' 10 | 11 | 12 | class TestPacketOut(TestStruct): 13 | """Packet out message tests (also those in :class:`.TestDump`). 14 | 15 | Attributes: 16 | message (PacketOut): The message configured in :meth:`setUpClass`. 17 | 18 | """ 19 | 20 | @classmethod 21 | def setUpClass(cls): 22 | """Configure raw file and its object in parent class (TestDump).""" 23 | super().setUpClass() 24 | super().set_raw_dump_file('v0x04', 'ofpt_packet_out') 25 | super().set_raw_dump_object(PacketOut, xid=2544740805, 26 | buffer_id=OFP_NO_BUFFER, 27 | in_port=PortNo.OFPP_CONTROLLER, 28 | actions=_get_actions(), data=_get_data()) 29 | super().set_minimum_size(24) 30 | 31 | def test_valid_virtual_in_ports(self): 32 | """Valid virtual ports as defined in 1.3.0 spec.""" 33 | virtual_ports = (PortNo.OFPP_LOCAL, PortNo.OFPP_CONTROLLER, 34 | PortNo.OFPP_ANY) 35 | for port in virtual_ports: 36 | with self.subTest(port=port): 37 | msg = PacketOut(in_port=port) 38 | self.assertTrue(msg.is_valid(), 39 | f'{port.name} should be a valid in_port') 40 | 41 | def test_invalid_virtual_in_ports(self): 42 | """Invalid virtual ports as defined in 1.3.0 spec.""" 43 | try: 44 | msg = self.get_raw_dump().read() 45 | except FileNotFoundError: 46 | raise self.skipTest(NO_RAW) 47 | else: 48 | invalid = (PortNo.OFPP_IN_PORT, PortNo.OFPP_TABLE, 49 | PortNo.OFPP_NORMAL, PortNo.OFPP_FLOOD, PortNo.OFPP_ALL) 50 | msg = self.get_raw_object() 51 | for in_port in invalid: 52 | msg.in_port = in_port 53 | self.assertFalse(msg.is_valid()) 54 | self.assertRaises(ValidationError, msg.validate) 55 | 56 | def test_valid_physical_in_ports(self): 57 | """Physical port limits from 1.3.0 spec.""" 58 | try: 59 | msg = self.get_raw_dump().read() 60 | except FileNotFoundError: 61 | raise self.skipTest(NO_RAW) 62 | else: 63 | max_valid = int(PortNo.OFPP_MAX.value) - 1 64 | msg = self.get_raw_object() 65 | for in_port in (1, max_valid): 66 | msg.in_port = in_port 67 | self.assertTrue(msg.is_valid()) 68 | 69 | 70 | def _get_actions(): 71 | """Return a list of actions used by packetout instance.""" 72 | action = ActionOutput(port=1) 73 | return [action] 74 | 75 | 76 | def _get_data(): 77 | """Return a BinaryData used by packetout instance.""" 78 | data = b'\x01\x80\xc2\x00\x00\x0e\x4e\xbf\xca\x27\x8e\xca\x81\x00\x0e' 79 | data += b'\xd7\x88\xcc\x02\x09\x07\x00\x00\x00\x00\x00\x00\x00\x01\x04' 80 | data += b'\x05\x07\x00\x00\x00\x01\x06\x02\x00\x78\x00\x00' 81 | return data 82 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_port_desc.py: -------------------------------------------------------------------------------- 1 | """MultipartReply message test.""" 2 | 3 | from pyof.foundation.basic_types import HWAddress 4 | from pyof.v0x04.common.port import ( 5 | ListOfPorts, Port, PortConfig, PortFeatures, PortNo, PortState) 6 | from pyof.v0x04.controller2switch.common import MultipartType 7 | from pyof.v0x04.controller2switch.multipart_reply import MultipartReply 8 | from tests.unit.test_struct import TestStruct 9 | 10 | 11 | class TestPortDesc(TestStruct): 12 | """Test PortDesc.""" 13 | 14 | @classmethod 15 | def setUpClass(cls): 16 | """Configure raw file and its object in parent class (TestDump).""" 17 | mp_type = MultipartType.OFPMP_PORT_DESC 18 | super().setUpClass() 19 | super().set_raw_dump_file('v0x04', 'ofpt_port_desc') 20 | super().set_raw_dump_object(MultipartReply, xid=1917225664, 21 | multipart_type=mp_type, 22 | flags=0, 23 | body=_get_body()) 24 | super().set_minimum_size(16) 25 | 26 | 27 | def _get_body(): 28 | """Return the body used by MultipartReply message.""" 29 | port1 = Port(port_no=PortNo.OFPP_LOCAL, 30 | hw_addr=HWAddress('5a:ee:a5:a0:62:4f'), 31 | name='s1', 32 | config=PortConfig.OFPPC_PORT_DOWN, 33 | state=PortState.OFPPS_LINK_DOWN, 34 | curr=0, 35 | advertised=0, 36 | supported=0, 37 | peer=0, 38 | curr_speed=0, 39 | max_speed=0) 40 | port2 = Port(port_no=1, 41 | hw_addr=HWAddress('4e:bf:ca:27:8e:ca'), 42 | name='s1-eth1', 43 | config=0, 44 | state=PortState.OFPPS_LIVE, 45 | curr=PortFeatures.OFPPF_10GB_FD | PortFeatures.OFPPF_COPPER, 46 | advertised=0, 47 | supported=0, 48 | peer=0, 49 | curr_speed=10000000, 50 | max_speed=0) 51 | port3 = Port(port_no=2, 52 | hw_addr=HWAddress('26:1f:b9:5e:3c:c7'), 53 | name='s1-eth2', 54 | config=0, 55 | state=PortState.OFPPS_LIVE, 56 | curr=PortFeatures.OFPPF_10GB_FD | PortFeatures.OFPPF_COPPER, 57 | advertised=0, 58 | supported=0, 59 | peer=0, 60 | curr_speed=10000000, 61 | max_speed=0) 62 | lop = ListOfPorts([port1, port2, port3]) 63 | return lop 64 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_port_mod.py: -------------------------------------------------------------------------------- 1 | """PortMod tests.""" 2 | import unittest 3 | 4 | from pyof.v0x04.controller2switch.port_mod import PortMod 5 | 6 | 7 | class TestPortMod(unittest.TestCase): 8 | """PortMod test.""" 9 | 10 | def test_min_size(self): 11 | """Test minimum message size.""" 12 | self.assertEqual(40, PortMod().get_size()) 13 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_port_stats.py: -------------------------------------------------------------------------------- 1 | """Config Port Stats message tests.""" 2 | from pyof.v0x04.controller2switch.multipart_reply import PortStats 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestPortStats(TestStruct): 7 | """Config Port Stats message tests.""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x04', 'ofpt_port_stats') 14 | super().set_raw_dump_object(PortStats) 15 | super().set_minimum_size(112) 16 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_port_stats_request.py: -------------------------------------------------------------------------------- 1 | """Config Port Stats Request message tests.""" 2 | from pyof.v0x04.controller2switch.multipart_request import PortStatsRequest 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestPortStatsRequest(TestStruct): 7 | """Config Port Stats Request message tests.""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x04', 'ofpt_port_stats_request') 14 | super().set_raw_dump_object(PortStatsRequest) 15 | super().set_minimum_size(8) 16 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_queue_get_config_reply.py: -------------------------------------------------------------------------------- 1 | """Testing QueueGetConfigReply message.""" 2 | from pyof.v0x04.common.queue import ListOfQueues, PacketQueue 3 | from pyof.v0x04.controller2switch.queue_get_config_reply import ( 4 | QueueGetConfigReply) 5 | from tests.unit.test_struct import TestStruct 6 | 7 | 8 | class TestQueueGetConfigReply(TestStruct): 9 | """Test the QueueGetConfigReply message.""" 10 | 11 | @classmethod 12 | def setUpClass(cls): 13 | """Configure raw file and its object in parent class (TestDump).""" 14 | super().setUpClass() 15 | super().set_raw_dump_file('v0x04', 'ofpt_queue_get_config_reply') 16 | super().set_raw_dump_object(QueueGetConfigReply, xid=1, port=1, 17 | queues=_new_list_of_queues()) 18 | super().set_minimum_size(16) 19 | 20 | 21 | def _new_list_of_queues(): 22 | """Crate new ListOfQueues.""" 23 | queue = PacketQueue(1, 2, 3) 24 | loq = ListOfQueues([queue, queue]) 25 | return loq 26 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_queue_get_config_request.py: -------------------------------------------------------------------------------- 1 | """Testing QueueGetConfigRequest message.""" 2 | from pyof.v0x04.controller2switch.queue_get_config_request import ( 3 | QueueGetConfigRequest) 4 | from tests.unit.test_struct import TestStruct 5 | 6 | 7 | class TestQueueGetConfigRequest(TestStruct): 8 | """Test the QueueGetConfigRequest message.""" 9 | 10 | @classmethod 11 | def setUpClass(cls): 12 | """Configure raw file and its object in parent class (TestDump).""" 13 | super().setUpClass() 14 | super().set_raw_dump_file('v0x04', 'ofpt_queue_get_config_request') 15 | super().set_raw_dump_object(QueueGetConfigRequest, xid=1, port=1) 16 | super().set_minimum_size(16) 17 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_queue_stats.py: -------------------------------------------------------------------------------- 1 | """Stats queue message.""" 2 | from pyof.v0x04.controller2switch.multipart_reply import QueueStats 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestQueueStats(TestStruct): 7 | """Stats queue message.""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x04', 'ofpt_queue_stats') 14 | super().set_raw_dump_object(QueueStats) 15 | super().set_minimum_size(40) 16 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_queue_stats_request.py: -------------------------------------------------------------------------------- 1 | """Queue Stat Request message.""" 2 | from pyof.v0x04.controller2switch.multipart_request import QueueStatsRequest 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestQueueStatsRequest(TestStruct): 7 | """Queue Stat Request message.""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x04', 'ofpt_queue_stats_request') 14 | super().set_raw_dump_object(QueueStatsRequest) 15 | super().set_minimum_size(8) 16 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_role_reply.py: -------------------------------------------------------------------------------- 1 | """RoleReply message tests.""" 2 | from pyof.v0x04.controller2switch.role_reply import RoleReply 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestRoleReply(TestStruct): 7 | """Test the RoleReply message.""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x04', 'ofpt_role_reply') 14 | super().set_raw_dump_object(RoleReply, xid=3, role=0, 15 | generation_id=0) 16 | super().set_minimum_size(24) 17 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_role_request.py: -------------------------------------------------------------------------------- 1 | """RoleRequest message tests.""" 2 | from pyof.v0x04.controller2switch.role_request import RoleRequest 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestRoleRequest(TestStruct): 7 | """Test the RoleRequest message.""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x04', 'ofpt_role_request') 14 | super().set_raw_dump_object(RoleRequest, xid=3, role=0, 15 | generation_id=0) 16 | super().set_minimum_size(24) 17 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_set_async.py: -------------------------------------------------------------------------------- 1 | """SetAsync message tests.""" 2 | from pyof.v0x04.controller2switch.set_async import SetAsync 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestSetAsync(TestStruct): 7 | """Test the SetAsync message.""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x04', 'ofpt_set_async') 14 | super().set_raw_dump_object(SetAsync, xid=3, packet_in_mask1=0, 15 | packet_in_mask2=0, port_status_mask1=0, 16 | port_status_mask2=0, flow_removed_mask1=0, 17 | flow_removed_mask2=0) 18 | super().set_minimum_size(32) 19 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_set_config.py: -------------------------------------------------------------------------------- 1 | """Set Config message tests.""" 2 | from pyof.v0x04.common.action import ControllerMaxLen 3 | from pyof.v0x04.controller2switch.common import ConfigFlag 4 | from pyof.v0x04.controller2switch.set_config import SetConfig 5 | from tests.unit.test_struct import TestStruct 6 | 7 | 8 | class TestSetConfig(TestStruct): 9 | """Test the Set Config message.""" 10 | 11 | @classmethod 12 | def setUpClass(cls): 13 | """Configure raw file and its object in parent class (TestDump).""" 14 | buffer = ControllerMaxLen.OFPCML_NO_BUFFER 15 | super().setUpClass() 16 | super().set_raw_dump_file('v0x04', 'ofpt_set_config') 17 | super().set_raw_dump_object(SetConfig, xid=1201346349, 18 | flags=ConfigFlag.OFPC_FRAG_NORMAL, 19 | miss_send_len=buffer) 20 | super().set_minimum_size(12) 21 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_table_mod.py: -------------------------------------------------------------------------------- 1 | """Table flow modification tests.""" 2 | import unittest 3 | 4 | from pyof.v0x04.common.header import Type 5 | from pyof.v0x04.controller2switch.table_mod import TableMod 6 | 7 | 8 | class TestTableMod(unittest.TestCase): 9 | """TableMod test.""" 10 | 11 | def test_min_size(self): 12 | """Test minimum message size.""" 13 | self.assertEqual(16, TableMod().get_size()) 14 | 15 | def test_header_type(self): 16 | """Test header type.""" 17 | self.assertEqual(Type.OFPT_TABLE_MOD, TableMod().header.message_type) 18 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_controller2switch/test_table_stats.py: -------------------------------------------------------------------------------- 1 | """Table stats message.""" 2 | from pyof.v0x04.controller2switch.multipart_reply import TableStats 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestTableStats(TestStruct): 7 | """Table stats message.""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x04', 'ofpt_table_stats') 14 | super().set_raw_dump_object(TableStats) 15 | super().set_minimum_size(24) 16 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_symmetric/__init__.py: -------------------------------------------------------------------------------- 1 | """Testing symmetric messages.""" 2 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_symmetric/test_echo_reply.py: -------------------------------------------------------------------------------- 1 | """Echo reply message tests.""" 2 | from pyof.v0x04.symmetric.echo_reply import EchoReply 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestEchoReply(TestStruct): 7 | """Echo reply message tests (also those in :class:`.TestDump`).""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x04', 'ofpt_echo_reply') 14 | super().set_raw_dump_object(EchoReply, xid=0) 15 | super().set_minimum_size(8) 16 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_symmetric/test_echo_request.py: -------------------------------------------------------------------------------- 1 | """Echo request message tests.""" 2 | from pyof.v0x04.symmetric.echo_request import EchoRequest 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestEchoRequest(TestStruct): 7 | """Echo request message tests (also those in :class:`.TestDump`).""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x04', 'ofpt_echo_request') 14 | super().set_raw_dump_object(EchoRequest, xid=0) 15 | super().set_minimum_size(8) 16 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_symmetric/test_hello.py: -------------------------------------------------------------------------------- 1 | """Hello message tests.""" 2 | from pyof.v0x04.symmetric.hello import ( 3 | Hello, HelloElemHeader, HelloElemType, ListOfHelloElements) 4 | from tests.unit.test_struct import TestStruct 5 | 6 | 7 | class TestHello(TestStruct): 8 | """Hello message tests (also those in :class:`.TestDump`).""" 9 | 10 | @classmethod 11 | def setUpClass(cls): 12 | """Configure raw file and its object in parent class (TestDump).""" 13 | super().setUpClass() 14 | super().set_raw_dump_file('v0x04', 'ofpt_hello') 15 | super().set_raw_dump_object(Hello, xid=62, 16 | elements=_new_list_of_elements()) 17 | super().set_minimum_size(8) 18 | 19 | 20 | def _new_list_of_elements(): 21 | """Crate new ListOfHelloElements.""" 22 | hello_elem = HelloElemHeader(HelloElemType.OFPHET_VERSIONBITMAP, 23 | length=8, content=b'\x00\x00\x00\x10') 24 | elements = ListOfHelloElements() 25 | elements.append(hello_elem) 26 | return elements 27 | -------------------------------------------------------------------------------- /tests/unit/v0x04/test_symmetric/test_vendor_header.py: -------------------------------------------------------------------------------- 1 | """Experimenter message tests.""" 2 | from pyof.v0x04.symmetric.experimenter import ExperimenterHeader 3 | from tests.unit.test_struct import TestStruct 4 | 5 | 6 | class TestExperimenter(TestStruct): 7 | """Experimenter message tests (also those in :class:`.TestDump`).""" 8 | 9 | @classmethod 10 | def setUpClass(cls): 11 | """Configure raw file and its object in parent class (TestDump).""" 12 | super().setUpClass() 13 | super().set_raw_dump_file('v0x04', 'ofpt_experimenter') 14 | super().set_raw_dump_object(ExperimenterHeader, xid=1, experimenter=1, 15 | exp_type=0) 16 | super().set_minimum_size(16) 17 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py36 3 | 4 | [testenv] 5 | whitelist_externals= 6 | rm 7 | make 8 | 9 | commands= 10 | ; Force packaging even if setup.{py,cfg} haven't changed 11 | rm -rf ./python_openflow.egg-info/ 12 | python setup.py ci 13 | 14 | deps= 15 | -rrequirements/dev.txt 16 | --------------------------------------------------------------------------------