mirror of
https://github.com/faucetsdn/ryu.git
synced 2026-05-09 14:26:10 +02:00
Compare commits
No commits in common. "master" and "v4.0" have entirely different histories.
25
.github/workflows/tests-unit.yml
vendored
25
.github/workflows/tests-unit.yml
vendored
@ -1,25 +0,0 @@
|
|||||||
name: Unit tests
|
|
||||||
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
unit-tests:
|
|
||||||
name: Unit tests
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
python-version: [3.5, 3.6, 3.7, 3.8, 3.9]
|
|
||||||
steps:
|
|
||||||
- name: Checkout repo
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
|
||||||
uses: actions/setup-python@v2
|
|
||||||
with:
|
|
||||||
python-version: ${{ matrix.python-version }}
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade -r pip-requirements.txt
|
|
||||||
pip install tox tox-gh-actions coveralls
|
|
||||||
bash ryu/tests/integrated/common/install_docker_test_pkg_for_github_actions.sh
|
|
||||||
- name: Test with tox
|
|
||||||
run: NOSE_VERBOSE=0 tox
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
version: 2
|
|
||||||
build:
|
|
||||||
image: latest
|
|
||||||
python:
|
|
||||||
version: 3.6
|
|
||||||
install:
|
|
||||||
- method: pip
|
|
||||||
path: .
|
|
||||||
sphinx:
|
|
||||||
configuration: doc/source/conf.py
|
|
||||||
formats: all
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"separateMajorMinor": false,
|
|
||||||
"schedule": [
|
|
||||||
"after 10pm every weekday",
|
|
||||||
"before 5am every weekday",
|
|
||||||
"every weekend"
|
|
||||||
],
|
|
||||||
"timezone": "Pacific/Auckland",
|
|
||||||
"extends": [
|
|
||||||
"config:base",
|
|
||||||
":prHourlyLimit1",
|
|
||||||
":preserveSemverRanges",
|
|
||||||
"docker:enableMajor"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
---
|
|
||||||
linters:
|
|
||||||
flake8:
|
|
||||||
python: 3
|
|
||||||
max-line-length: 120
|
|
||||||
pep8:
|
|
||||||
python: 3
|
|
||||||
max-line-length: 120
|
|
||||||
py3k:
|
|
||||||
17
.travis.yml
Normal file
17
.travis.yml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
language: python
|
||||||
|
python:
|
||||||
|
- "2.7"
|
||||||
|
|
||||||
|
env:
|
||||||
|
- TOX_ENV=py27
|
||||||
|
- TOX_ENV=py34
|
||||||
|
- TOX_ENV=pypy
|
||||||
|
- TOX_ENV=pep8
|
||||||
|
|
||||||
|
install:
|
||||||
|
- "pip install tox"
|
||||||
|
|
||||||
|
script:
|
||||||
|
- NOSE_VERBOSE=0 tox -e $TOX_ENV
|
||||||
|
|
||||||
|
sudo: false
|
||||||
@ -5,59 +5,46 @@ How to Get Your Change Into Ryu
|
|||||||
Submitting a change
|
Submitting a change
|
||||||
===================
|
===================
|
||||||
|
|
||||||
To send patches to ryu, please make a
|
Send patches to ryu-devel@lists.sourceforge.net. Please don't use 'pull
|
||||||
`pull request <https://github.com/faucetsdn/ryu>`_ on GitHub.
|
request' on github. We expect you to send a patch in Linux kernel
|
||||||
|
development style. If you are not familiar with it, please read the
|
||||||
|
following document:
|
||||||
|
|
||||||
Please check your changes with autopep8, pycodestyle(pep8) and running
|
https://www.kernel.org/doc/Documentation/SubmittingPatches
|
||||||
unit tests to make sure that they don't break the existing features.
|
|
||||||
The following command does all for you.
|
|
||||||
|
|
||||||
.. code-block:: bash
|
Please check your changes with pep8 and run unittests to make sure
|
||||||
|
that they don't break the existing features. The following command
|
||||||
|
does both for you:
|
||||||
|
|
||||||
# Install dependencies of tests
|
fujita@rose:~/git/ryu$ ./run_tests.sh
|
||||||
$ pip install -r tools/test-requires
|
|
||||||
|
|
||||||
# Execute autopep8
|
Of course, you are encouraged to add unittests when you add new
|
||||||
# Also, it is convenient to add settings of your editor or IDE for
|
|
||||||
# applying autopep8 automatically.
|
|
||||||
$ autopep8 --recursive --in-place ryu/
|
|
||||||
|
|
||||||
# Execute unit tests and pycodestyle(pep8)
|
|
||||||
$ ./run_tests.sh
|
|
||||||
|
|
||||||
Of course, you are encouraged to add unit tests when you add new
|
|
||||||
features (it's not a must though).
|
features (it's not a must though).
|
||||||
|
|
||||||
Python version and libraries
|
Python version and libraries
|
||||||
============================
|
============================
|
||||||
* Python 3.5, 3.6, 3.7, 3.8, 3.9:
|
* Python 2.6+
|
||||||
|
As RHEL 6 adopted python 2.6, features only for 2.7+ should be avoided.
|
||||||
|
|
||||||
Ryu supports multiple Python versions. CI tests on GitHub Actions is running
|
* standard library + widely used library
|
||||||
on these versions.
|
Basically widely used == OpenStack adopted
|
||||||
|
As usual there are exceptions. gevents. Or python binding library for other
|
||||||
* standard library + widely used library:
|
|
||||||
|
|
||||||
Basically widely used == OpenStack adopted.
|
|
||||||
As usual there are exceptions. Or python binding library for other
|
|
||||||
component.
|
component.
|
||||||
|
|
||||||
Coding style guide
|
Coding style guide
|
||||||
==================
|
==================
|
||||||
* pep8:
|
* pep8
|
||||||
|
|
||||||
As python is used, PEP8 is would be hopefully mandatory for
|
As python is used, PEP8 is would be hopefully mandatory for
|
||||||
https://www.python.org/dev/peps/pep-0008/
|
http://www.python.org/dev/peps/pep-0008/
|
||||||
|
|
||||||
* pylint:
|
|
||||||
|
|
||||||
|
* pylint
|
||||||
Although pylint is useful for finding bugs, but pylint score not very
|
Although pylint is useful for finding bugs, but pylint score not very
|
||||||
important for now because we're still at early development stage.
|
important for now because we're still at early development stage.
|
||||||
https://www.pylint.org/
|
|
||||||
|
|
||||||
* Google python style guide is very helpful:
|
* Google python style guide is very helpful
|
||||||
http://google.github.io/styleguide/pyguide.html
|
http://google-styleguide.googlecode.com/svn/trunk/pyguide.html
|
||||||
|
|
||||||
* Guidelines derived from Guido's Recommendations:
|
Guidelines derived from Guido's Recommendations
|
||||||
|
|
||||||
============================= ================= ========
|
============================= ================= ========
|
||||||
Type Public Internal
|
Type Public Internal
|
||||||
@ -75,11 +62,10 @@ Coding style guide
|
|||||||
Local Variables lower_with_under
|
Local Variables lower_with_under
|
||||||
============================= ================= ========
|
============================= ================= ========
|
||||||
|
|
||||||
* OpenStack Nova style guide:
|
* OpenStack Nova style guide
|
||||||
https://github.com/openstack/nova/blob/master/HACKING.rst
|
https://github.com/openstack/nova/blob/master/HACKING.rst
|
||||||
|
|
||||||
* JSON files:
|
* JSON files
|
||||||
|
|
||||||
Ryu source tree has JSON files under ryu/tests/unit/ofproto/json.
|
Ryu source tree has JSON files under ryu/tests/unit/ofproto/json.
|
||||||
They are used by unit tests. To make patches easier to read,
|
They are used by unit tests. To make patches easier to read,
|
||||||
they are normalized using tools/normalize_json.py. Please re-run
|
they are normalized using tools/normalize_json.py. Please re-run
|
||||||
|
|||||||
39
README.rst
39
README.rst
@ -1,13 +1,8 @@
|
|||||||
**PLEASE READ: RYU NOT CURRENTLY MAINTAINED**
|
|
||||||
|
|
||||||
* The Ryu project needs new maintainers - please file an issue if you are able to assist.
|
|
||||||
* see OpenStack's os-ken (`<https://github.com/openstack/os-ken>`_) for a maintained Ryu alternative.
|
|
||||||
|
|
||||||
What's Ryu
|
What's Ryu
|
||||||
==========
|
==========
|
||||||
Ryu is a component-based software defined networking framework.
|
Ryu is a component-based software defined networking framework.
|
||||||
|
|
||||||
Ryu provides software components with well defined API's that make it
|
Ryu provides software components with well defined API that make it
|
||||||
easy for developers to create new network management and control
|
easy for developers to create new network management and control
|
||||||
applications. Ryu supports various protocols for managing network
|
applications. Ryu supports various protocols for managing network
|
||||||
devices, such as OpenFlow, Netconf, OF-config, etc. About OpenFlow,
|
devices, such as OpenFlow, Netconf, OF-config, etc. About OpenFlow,
|
||||||
@ -25,11 +20,11 @@ Installing Ryu is quite easy::
|
|||||||
|
|
||||||
If you prefer to install Ryu from the source code::
|
If you prefer to install Ryu from the source code::
|
||||||
|
|
||||||
% git clone https://github.com/faucetsdn/ryu.git
|
% git clone git://github.com/osrg/ryu.git
|
||||||
% cd ryu; pip install .
|
% cd ryu; python ./setup.py install
|
||||||
|
|
||||||
If you want to write your Ryu application, have a look at
|
If you want to write your Ryu application, have a look at
|
||||||
`Writing ryu application <http://ryu.readthedocs.io/en/latest/writing_ryu_app.html>`_ document.
|
`Writing ryu application <http://ryu.readthedocs.org/en/latest/writing_ryu_app.html>`_ document.
|
||||||
After writing your application, just type::
|
After writing your application, just type::
|
||||||
|
|
||||||
% ryu-manager yourapp.py
|
% ryu-manager yourapp.py
|
||||||
@ -38,33 +33,21 @@ After writing your application, just type::
|
|||||||
Optional Requirements
|
Optional Requirements
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
Some functions of ryu require extra packages:
|
Some functionalities of ryu requires extra packages:
|
||||||
|
|
||||||
- OF-Config requires lxml and ncclient
|
- OF-Config requires lxml
|
||||||
- NETCONF requires paramiko
|
- NETCONF requires paramiko
|
||||||
- BGP speaker (SSH console) requires paramiko
|
- BGP speaker (ssh console) requires paramiko
|
||||||
- Zebra protocol service (database) requires SQLAlchemy
|
|
||||||
|
|
||||||
If you want to use these functions, please install the requirements::
|
If you want to use the functionalities, please install requirements::
|
||||||
|
|
||||||
% pip install -r tools/optional-requires
|
% pip install lxml
|
||||||
|
% pip install paramiko
|
||||||
Please refer to tools/optional-requires for details.
|
|
||||||
|
|
||||||
|
|
||||||
Prerequisites
|
|
||||||
=============
|
|
||||||
If you got some error messages at the installation stage, please confirm
|
|
||||||
dependencies for building the required Python packages.
|
|
||||||
|
|
||||||
On Ubuntu(16.04 LTS or later)::
|
|
||||||
|
|
||||||
% apt install gcc python-dev libffi-dev libssl-dev libxml2-dev libxslt1-dev zlib1g-dev
|
|
||||||
|
|
||||||
|
|
||||||
Support
|
Support
|
||||||
=======
|
=======
|
||||||
Ryu Official site is `<https://ryu-sdn.org/>`_.
|
Ryu Official site is `<http://osrg.github.io/ryu/>`_.
|
||||||
|
|
||||||
If you have any
|
If you have any
|
||||||
questions, suggestions, and patches, the mailing list is available at
|
questions, suggestions, and patches, the mailing list is available at
|
||||||
|
|||||||
10
debian/control
vendored
10
debian/control
vendored
@ -6,7 +6,7 @@ Build-Depends: debhelper (>= 9.0.0), python-all (>= 2.6), python-sphinx
|
|||||||
Build-Depends-Indep:
|
Build-Depends-Indep:
|
||||||
python-eventlet,
|
python-eventlet,
|
||||||
python-lxml,
|
python-lxml,
|
||||||
python-msgpack (>= 0.4.0),
|
python-msgpack (>= 0.3.0),
|
||||||
python-netaddr,
|
python-netaddr,
|
||||||
python-oslo.config (>= 1:1.2.0),
|
python-oslo.config (>= 1:1.2.0),
|
||||||
python-paramiko,
|
python-paramiko,
|
||||||
@ -17,9 +17,9 @@ Build-Depends-Indep:
|
|||||||
python-pip,
|
python-pip,
|
||||||
python-pbr
|
python-pbr
|
||||||
Standards-Version: 3.9.5
|
Standards-Version: 3.9.5
|
||||||
Homepage: https://ryu-sdn.org
|
Homepage: http://osrg.github.io/ryu/
|
||||||
Vcs-Git: git://github.com/faucetsdn/ryu.git
|
Vcs-Git: git://github.com/osrg/ryu.git
|
||||||
Vcs-Browser: https://github.com/faucetsdn/ryu
|
Vcs-Browser: http://github.com/osrg/ryu
|
||||||
XS-Python-Version: >= 2.6
|
XS-Python-Version: >= 2.6
|
||||||
|
|
||||||
Package: python-ryu
|
Package: python-ryu
|
||||||
@ -28,7 +28,7 @@ Section: python
|
|||||||
Depends:
|
Depends:
|
||||||
python-eventlet,
|
python-eventlet,
|
||||||
python-lxml,
|
python-lxml,
|
||||||
python-msgpack (>= 0.4.0),
|
python-msgpack (>= 0.3.0),
|
||||||
python-netaddr,
|
python-netaddr,
|
||||||
python-oslo.config (>= 1:1.2.0),
|
python-oslo.config (>= 1:1.2.0),
|
||||||
python-paramiko,
|
python-paramiko,
|
||||||
|
|||||||
2
debian/copyright
vendored
2
debian/copyright
vendored
@ -1,6 +1,6 @@
|
|||||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
Upstream-Name: ryu
|
Upstream-Name: ryu
|
||||||
Source: http://github.com/faucetsdn/ryu
|
Source: http://github.com/osrg/ryu
|
||||||
|
|
||||||
Files: *
|
Files: *
|
||||||
Copyright: 2014 Ryu Project Team <ryu-devel@lists.sourceforge.net>
|
Copyright: 2014 Ryu Project Team <ryu-devel@lists.sourceforge.net>
|
||||||
|
|||||||
@ -11,5 +11,3 @@ Others provide some functionalities to other Ryu applications.
|
|||||||
|
|
||||||
app/ofctl.rst
|
app/ofctl.rst
|
||||||
app/ofctl_rest.rst
|
app/ofctl_rest.rst
|
||||||
app/rest_vtep.rst
|
|
||||||
app/bgp_application.rst
|
|
||||||
|
|||||||
@ -1,6 +0,0 @@
|
|||||||
**************************************
|
|
||||||
ryu.services.protocols.bgp.application
|
|
||||||
**************************************
|
|
||||||
|
|
||||||
.. automodule:: ryu.services.protocols.bgp.application
|
|
||||||
:members:
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,12 +0,0 @@
|
|||||||
*****************
|
|
||||||
ryu.app.rest_vtep
|
|
||||||
*****************
|
|
||||||
|
|
||||||
.. automodule:: ryu.app.rest_vtep
|
|
||||||
|
|
||||||
REST API
|
|
||||||
========
|
|
||||||
|
|
||||||
.. autoclass:: ryu.app.rest_vtep.RestVtepController
|
|
||||||
:members:
|
|
||||||
:member-order: bysource
|
|
||||||
@ -132,7 +132,6 @@ html_static_path = ['_static']
|
|||||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||||
# typographically correct entities.
|
# typographically correct entities.
|
||||||
#html_use_smartypants = True
|
#html_use_smartypants = True
|
||||||
# (Deprecated since version 1.6)
|
|
||||||
|
|
||||||
# Custom sidebar templates, maps document names to template names.
|
# Custom sidebar templates, maps document names to template names.
|
||||||
#html_sidebars = {}
|
#html_sidebars = {}
|
||||||
|
|||||||
@ -1,2 +0,0 @@
|
|||||||
[parsers]
|
|
||||||
smart_quotes: false
|
|
||||||
@ -16,6 +16,7 @@ Contents:
|
|||||||
developing.rst
|
developing.rst
|
||||||
configuration.rst
|
configuration.rst
|
||||||
tests.rst
|
tests.rst
|
||||||
|
using_with_openstack.rst
|
||||||
snort_integrate.rst
|
snort_integrate.rst
|
||||||
app.rst
|
app.rst
|
||||||
|
|
||||||
|
|||||||
@ -9,10 +9,7 @@ Ryu provides some useful library for your network applications.
|
|||||||
|
|
||||||
library_packet.rst
|
library_packet.rst
|
||||||
library_packet_ref.rst
|
library_packet_ref.rst
|
||||||
library_pcap.rst
|
|
||||||
library_of_config.rst
|
library_of_config.rst
|
||||||
library_bgp_speaker.rst
|
library_bgp_speaker.rst
|
||||||
library_bgp_speaker_ref.rst
|
library_bgp_speaker_ref.rst
|
||||||
library_mrt.rst
|
|
||||||
library_ovsdb_manager.rst
|
library_ovsdb_manager.rst
|
||||||
library_ovsdb.rst
|
|
||||||
|
|||||||
@ -6,8 +6,8 @@ Introduction
|
|||||||
============
|
============
|
||||||
|
|
||||||
Ryu BGP speaker library helps you to enable your code to speak BGP
|
Ryu BGP speaker library helps you to enable your code to speak BGP
|
||||||
protocol. The library supports IPv4, IPv4 MPLS-labeled VPN, IPv6
|
protocol. The library supports ipv4, ipv4 vpn, and ipv6 vpn address
|
||||||
MPLS-labeled VPN and L2VPN EVPN address families.
|
families.
|
||||||
|
|
||||||
Example
|
Example
|
||||||
=======
|
=======
|
||||||
|
|||||||
@ -1,28 +0,0 @@
|
|||||||
****************
|
|
||||||
MRT file library
|
|
||||||
****************
|
|
||||||
|
|
||||||
Introduction
|
|
||||||
============
|
|
||||||
|
|
||||||
Ryu MRT file library helps you to read/write MRT
|
|
||||||
(Multi-Threaded Routing Toolkit) Routing Information Export Format
|
|
||||||
[`RFC6396`_].
|
|
||||||
|
|
||||||
.. _RFC6396: https://tools.ietf.org/html/rfc6396
|
|
||||||
|
|
||||||
Reading MRT file
|
|
||||||
================
|
|
||||||
|
|
||||||
For loading the routing information contained in MRT files, you can use
|
|
||||||
mrtlib.Reader.
|
|
||||||
|
|
||||||
.. autoclass:: ryu.lib.mrtlib.Reader
|
|
||||||
|
|
||||||
Writing MRT file
|
|
||||||
================
|
|
||||||
|
|
||||||
For dumping the routing information which your RyuApp generated, you can use
|
|
||||||
mrtlib.Writer.
|
|
||||||
|
|
||||||
.. autoclass:: ryu.lib.mrtlib.Writer
|
|
||||||
@ -1,76 +0,0 @@
|
|||||||
*************
|
|
||||||
OVSDB library
|
|
||||||
*************
|
|
||||||
|
|
||||||
Path: ``ryu.lib.ovs``
|
|
||||||
|
|
||||||
Similar to the :doc:`library_ovsdb_manager`, this library enables your
|
|
||||||
application to speak the OVSDB protocol (RFC7047_), but differ from the
|
|
||||||
:doc:`library_ovsdb_manager`, this library will initiate connections from
|
|
||||||
controller side as ovs-vsctl_ command does.
|
|
||||||
Please make sure that your devices are listening on either the Unix domain
|
|
||||||
socket or TCP/SSL port before calling the APIs of this library.
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
# Show current configuration
|
|
||||||
$ ovs-vsctl get-manager
|
|
||||||
|
|
||||||
# Set TCP listen address
|
|
||||||
$ ovs-vsctl set-manager "ptcp:6640"
|
|
||||||
|
|
||||||
See manpage of ovs-vsctl_ command for more details.
|
|
||||||
|
|
||||||
.. _RFC7047: https://tools.ietf.org/html/rfc7047
|
|
||||||
.. _ovs-vsctl: http://openvswitch.org/support/dist-docs/ovs-vsctl.8.txt
|
|
||||||
|
|
||||||
Basic Usage
|
|
||||||
===========
|
|
||||||
|
|
||||||
1. Instantiate :py:mod:`ryu.lib.ovs.vsctl.VSCtl`.
|
|
||||||
|
|
||||||
2. Construct commands with :py:mod:`ryu.lib.ovs.vsctl.VSCtlCommand`.
|
|
||||||
The syntax is almost the same as ovs-vsctl_ command.
|
|
||||||
|
|
||||||
3. Execute commands via :py:mod:`ryu.lib.ovs.vsctl.VSCtl.run_command`.
|
|
||||||
|
|
||||||
Example
|
|
||||||
-------
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from ryu.lib.ovs import vsctl
|
|
||||||
|
|
||||||
OVSDB_ADDR = 'tcp:127.0.0.1:6640'
|
|
||||||
ovs_vsctl = vsctl.VSCtl(OVSDB_ADDR)
|
|
||||||
|
|
||||||
# Equivalent to
|
|
||||||
# $ ovs-vsctl show
|
|
||||||
command = vsctl.VSCtlCommand('show')
|
|
||||||
ovs_vsctl.run_command([command])
|
|
||||||
print(command)
|
|
||||||
# >>> VSCtlCommand(args=[],command='show',options=[],result='830d781f-c3c8-4b4f-837e-106e1b33d058\n ovs_version: "2.8.90"\n')
|
|
||||||
|
|
||||||
# Equivalent to
|
|
||||||
# $ ovs-vsctl list Port s1-eth1
|
|
||||||
command = vsctl.VSCtlCommand('list', ('Port', 's1-eth1'))
|
|
||||||
ovs_vsctl.run_command([command])
|
|
||||||
print(command)
|
|
||||||
# >>> VSCtlCommand(args=('Port', 's1-eth1'),command='list',options=[],result=[<ovs.db.idl.Row object at 0x7f525fb682e8>])
|
|
||||||
print(command.result[0].name)
|
|
||||||
# >>> s1-eth1
|
|
||||||
|
|
||||||
API Reference
|
|
||||||
=============
|
|
||||||
|
|
||||||
ryu.lib.ovs.vsctl
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.ovs.vsctl
|
|
||||||
:members:
|
|
||||||
|
|
||||||
ryu.lib.ovs.bridge
|
|
||||||
------------------
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.ovs.bridge
|
|
||||||
:members:
|
|
||||||
@ -2,8 +2,6 @@
|
|||||||
OVSDB Manager library
|
OVSDB Manager library
|
||||||
*********************
|
*********************
|
||||||
|
|
||||||
Path: ``ryu.services.protocols.ovsdb``
|
|
||||||
|
|
||||||
Introduction
|
Introduction
|
||||||
============
|
============
|
||||||
|
|
||||||
@ -11,47 +9,17 @@ Ryu OVSDB Manager library allows your code to interact with devices
|
|||||||
speaking the OVSDB protocol. This enables your code to perform remote
|
speaking the OVSDB protocol. This enables your code to perform remote
|
||||||
management of the devices and react to topology changes on them.
|
management of the devices and react to topology changes on them.
|
||||||
|
|
||||||
Please note this library will spawn a server listening on the port 6640 (the
|
|
||||||
IANA registered for OVSDB protocol), but does not initiate connections from
|
|
||||||
controller side.
|
|
||||||
Then, to make your devices connect to Ryu, you need to tell the controller IP
|
|
||||||
address and port to your devices.
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
# Show current configuration
|
|
||||||
$ ovs-vsctl get-manager
|
|
||||||
|
|
||||||
# Set manager (controller) address
|
|
||||||
$ ovs-vsctl set-manager "tcp:127.0.0.1:6640"
|
|
||||||
|
|
||||||
# If you want to specify IPv6 address, wrap ip with brackets
|
|
||||||
$ ovs-vsctl set-manager "tcp:[::1]:6640"
|
|
||||||
|
|
||||||
Also this library identifies the devices by "system-id" which should be unique,
|
|
||||||
persistent identifier among all devices connecting to a single controller.
|
|
||||||
Please make sure "system-id" is configured before connecting.
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
# Show current configuration
|
|
||||||
$ ovs-vsctl get Open_vSwitch . external_ids:system-id
|
|
||||||
|
|
||||||
# Set system-id manually
|
|
||||||
$ ovs-vsctl set Open_vSwitch . external_ids:system-id=<SYSTEM-ID>
|
|
||||||
|
|
||||||
Example
|
Example
|
||||||
=======
|
=======
|
||||||
|
|
||||||
The following logs all new OVSDB connections in "handle_new_ovsdb_connection"
|
The following logs all new OVSDB connections and allows creating a port
|
||||||
and also provides the API "create_port" for creating a port on a bridge.
|
on a bridge.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from ryu.base import app_manager
|
from ryu.base import app_manager
|
||||||
from ryu.controller.handler import set_ev_cls
|
|
||||||
from ryu.services.protocols.ovsdb import api as ovsdb
|
from ryu.services.protocols.ovsdb import api as ovsdb
|
||||||
from ryu.services.protocols.ovsdb import event as ovsdb_event
|
from ryu.services.protocols.ovsdb import event as ovsdb_event
|
||||||
|
|
||||||
@ -60,22 +28,16 @@ and also provides the API "create_port" for creating a port on a bridge.
|
|||||||
@set_ev_cls(ovsdb_event.EventNewOVSDBConnection)
|
@set_ev_cls(ovsdb_event.EventNewOVSDBConnection)
|
||||||
def handle_new_ovsdb_connection(self, ev):
|
def handle_new_ovsdb_connection(self, ev):
|
||||||
system_id = ev.system_id
|
system_id = ev.system_id
|
||||||
address = ev.client.address
|
self.logger.info('New OVSDB connection from system id %s',
|
||||||
self.logger.info(
|
systemd_id)
|
||||||
'New OVSDB connection from system-id=%s, address=%s',
|
|
||||||
system_id, address)
|
|
||||||
|
|
||||||
# Example: If device has bridge "s1", add port "s1-eth99"
|
def create_port(self, systemd_id, bridge_name, name):
|
||||||
if ovsdb.bridge_exists(self, system_id, "s1"):
|
|
||||||
self.create_port(system_id, "s1", "s1-eth99")
|
|
||||||
|
|
||||||
def create_port(self, system_id, bridge_name, name):
|
|
||||||
new_iface_uuid = uuid.uuid4()
|
new_iface_uuid = uuid.uuid4()
|
||||||
new_port_uuid = uuid.uuid4()
|
new_port_uuid = uuid.uuid4()
|
||||||
|
|
||||||
bridge = ovsdb.row_by_name(self, system_id, bridge_name)
|
|
||||||
|
|
||||||
def _create_port(tables, insert):
|
def _create_port(tables, insert):
|
||||||
|
bridge = ovsdb.row_by_name(self, system_id, bridge_name)
|
||||||
|
|
||||||
iface = insert(tables['Interface'], new_iface_uuid)
|
iface = insert(tables['Interface'], new_iface_uuid)
|
||||||
iface.name = name
|
iface.name = name
|
||||||
iface.type = 'internal'
|
iface.type = 'internal'
|
||||||
@ -84,9 +46,9 @@ and also provides the API "create_port" for creating a port on a bridge.
|
|||||||
port.name = name
|
port.name = name
|
||||||
port.interfaces = [iface]
|
port.interfaces = [iface]
|
||||||
|
|
||||||
bridge.ports = bridge.ports + [port]
|
brdige.ports = bridfe.ports + [port]
|
||||||
|
|
||||||
return new_port_uuid, new_iface_uuid
|
return (new_port_uuid, new_iface_uuid)
|
||||||
|
|
||||||
req = ovsdb_event.EventModifyRequest(system_id, _create_port)
|
req = ovsdb_event.EventModifyRequest(system_id, _create_port)
|
||||||
rep = self.send_request(req)
|
rep = self.send_request(req)
|
||||||
@ -96,4 +58,4 @@ and also provides the API "create_port" for creating a port on a bridge.
|
|||||||
name, bridge, rep.status)
|
name, bridge, rep.status)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return rep.insert_uuids[new_port_uuid]
|
return reply.insert_uuid[new_port_uuid]
|
||||||
|
|||||||
@ -14,15 +14,119 @@ Stream Parser class
|
|||||||
.. automodule:: ryu.lib.packet.stream_parser
|
.. automodule:: ryu.lib.packet.stream_parser
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
List of the sub-classes:
|
.. autoclass:: ryu.lib.packet.bgp.StreamParser
|
||||||
|
:members:
|
||||||
- :py:mod:`ryu.lib.packet.bgp.StreamParser`
|
|
||||||
|
|
||||||
Protocol Header classes
|
Protocol Header classes
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
.. toctree::
|
.. automodule:: ryu.lib.packet.packet_base
|
||||||
:glob:
|
:members:
|
||||||
|
|
||||||
library_packet_ref/packet_base
|
.. automodule:: ryu.lib.packet.ethernet
|
||||||
library_packet_ref/*
|
:members:
|
||||||
|
|
||||||
|
.. automodule:: ryu.lib.packet.vlan
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. automodule:: ryu.lib.packet.pbb
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. automodule:: ryu.lib.packet.mpls
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. automodule:: ryu.lib.packet.arp
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. automodule:: ryu.lib.packet.ipv4
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. automodule:: ryu.lib.packet.icmp
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. automodule:: ryu.lib.packet.ipv6
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. automodule:: ryu.lib.packet.icmpv6
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. automodule:: ryu.lib.packet.cfm
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. automodule:: ryu.lib.packet.tcp
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. automodule:: ryu.lib.packet.udp
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: ryu.lib.packet.dhcp.dhcp
|
||||||
|
:members:
|
||||||
|
.. autoclass:: ryu.lib.packet.dhcp.options
|
||||||
|
:members:
|
||||||
|
.. autoclass:: ryu.lib.packet.dhcp.option
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: ryu.lib.packet.vrrp.vrrp
|
||||||
|
:members:
|
||||||
|
.. autoclass:: ryu.lib.packet.vrrp.vrrpv2
|
||||||
|
:members:
|
||||||
|
.. autoclass:: ryu.lib.packet.vrrp.vrrpv3
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: ryu.lib.packet.slow.slow
|
||||||
|
:members:
|
||||||
|
.. autoclass:: ryu.lib.packet.slow.lacp
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: ryu.lib.packet.llc.llc
|
||||||
|
:members:
|
||||||
|
.. autoclass:: ryu.lib.packet.llc.ControlFormatI
|
||||||
|
:members:
|
||||||
|
.. autoclass:: ryu.lib.packet.llc.ControlFormatS
|
||||||
|
:members:
|
||||||
|
.. autoclass:: ryu.lib.packet.llc.ControlFormatU
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: ryu.lib.packet.bpdu.bpdu
|
||||||
|
:members:
|
||||||
|
.. autoclass:: ryu.lib.packet.bpdu.ConfigurationBPDUs
|
||||||
|
:members:
|
||||||
|
.. autoclass:: ryu.lib.packet.bpdu.TopologyChangeNotificationBPDUs
|
||||||
|
:members:
|
||||||
|
.. autoclass:: ryu.lib.packet.bpdu.RstBPDUs
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: ryu.lib.packet.igmp.igmp
|
||||||
|
:members:
|
||||||
|
.. autoclass:: ryu.lib.packet.igmp.igmpv3_query
|
||||||
|
:members:
|
||||||
|
.. autoclass:: ryu.lib.packet.igmp.igmpv3_report
|
||||||
|
:members:
|
||||||
|
.. autoclass:: ryu.lib.packet.igmp.igmpv3_report_group
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: ryu.lib.packet.bgp.BGPMessage
|
||||||
|
:members:
|
||||||
|
.. autoclass:: ryu.lib.packet.bgp.BGPOpen
|
||||||
|
:members:
|
||||||
|
.. autoclass:: ryu.lib.packet.bgp.BGPUpdate
|
||||||
|
:members:
|
||||||
|
.. autoclass:: ryu.lib.packet.bgp.BGPKeepAlive
|
||||||
|
:members:
|
||||||
|
.. autoclass:: ryu.lib.packet.bgp.BGPNotification
|
||||||
|
:members:
|
||||||
|
.. automodule:: ryu.lib.packet.sctp
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: ryu.lib.packet.bfd.bfd
|
||||||
|
:members:
|
||||||
|
.. autoclass:: ryu.lib.packet.bfd.SimplePassword
|
||||||
|
:members:
|
||||||
|
.. autoclass:: ryu.lib.packet.bfd.KeyedMD5
|
||||||
|
:members:
|
||||||
|
.. autoclass:: ryu.lib.packet.bfd.MeticulousKeyedMD5
|
||||||
|
:members:
|
||||||
|
.. autoclass:: ryu.lib.packet.bfd.KeyedSHA1
|
||||||
|
:members:
|
||||||
|
.. autoclass:: ryu.lib.packet.bfd.MeticulousKeyedSHA1
|
||||||
|
:members:
|
||||||
|
|||||||
@ -1,6 +0,0 @@
|
|||||||
***
|
|
||||||
ARP
|
|
||||||
***
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.arp
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
*****************
|
|
||||||
Packet Base Class
|
|
||||||
*****************
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.packet_base
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
***
|
|
||||||
BFD
|
|
||||||
***
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.bfd
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
***
|
|
||||||
BGP
|
|
||||||
***
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.bgp
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
***
|
|
||||||
BMP
|
|
||||||
***
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.bmp
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
BPDU
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.bpdu
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
***
|
|
||||||
CFM
|
|
||||||
***
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.cfm
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
DHCP
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.dhcp
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
*****
|
|
||||||
DHCP6
|
|
||||||
*****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.dhcp6
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
********
|
|
||||||
Ethernet
|
|
||||||
********
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.ethernet
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
******
|
|
||||||
Geneve
|
|
||||||
******
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.geneve
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
***
|
|
||||||
GRE
|
|
||||||
***
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.gre
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
ICMP
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.icmp
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
******
|
|
||||||
ICMPv6
|
|
||||||
******
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.icmpv6
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
IGMP
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.igmp
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
IPv4
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.ipv4
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
IPv6
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.ipv6
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
***
|
|
||||||
LLC
|
|
||||||
***
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.llc
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
LLDP
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.lldp
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
MPLS
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.mpls
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
********
|
|
||||||
OpenFlow
|
|
||||||
********
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.openflow
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
OSPF
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.ospf
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
***
|
|
||||||
PBB
|
|
||||||
***
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.pbb
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
SCTP
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.sctp
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
Slow
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.slow
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
***
|
|
||||||
TCP
|
|
||||||
***
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.tcp
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
***
|
|
||||||
UDP
|
|
||||||
***
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.udp
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
VLAN
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.vlan
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
****
|
|
||||||
VRRP
|
|
||||||
****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.vrrp
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
*****
|
|
||||||
VXLAN
|
|
||||||
*****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.vxlan
|
|
||||||
:members:
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
*****
|
|
||||||
Zebra
|
|
||||||
*****
|
|
||||||
|
|
||||||
.. automodule:: ryu.lib.packet.zebra
|
|
||||||
:members:
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
*****************
|
|
||||||
PCAP file library
|
|
||||||
*****************
|
|
||||||
|
|
||||||
Introduction
|
|
||||||
============
|
|
||||||
|
|
||||||
Ryu PCAP file library helps you to read/write PCAP file which file
|
|
||||||
format are described in `The Wireshark Wiki`_.
|
|
||||||
|
|
||||||
.. _The Wireshark Wiki: https://wiki.wireshark.org/Development/LibpcapFileFormat
|
|
||||||
|
|
||||||
Reading PCAP file
|
|
||||||
=================
|
|
||||||
|
|
||||||
For loading the packet data containing in PCAP files, you can use
|
|
||||||
pcaplib.Reader.
|
|
||||||
|
|
||||||
.. autoclass:: ryu.lib.pcaplib.Reader
|
|
||||||
|
|
||||||
Writing PCAP file
|
|
||||||
=================
|
|
||||||
|
|
||||||
For dumping the packet data which your RyuApp received, you can use
|
|
||||||
pcaplib.Writer.
|
|
||||||
|
|
||||||
.. autoclass:: ryu.lib.pcaplib.Writer
|
|
||||||
@ -7,63 +7,25 @@ Nicira Extension Structures
|
|||||||
Nicira Extension Actions Structures
|
Nicira Extension Actions Structures
|
||||||
===================================
|
===================================
|
||||||
|
|
||||||
The followings shows the supported NXAction classes only in OpenFlow1.0
|
The followings shows the supported NXAction classes in OF1.3,
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
but also available in OF1.2+.
|
||||||
|
|
||||||
.. py:currentmodule:: ryu.ofproto.ofproto_v1_0_parser
|
|
||||||
|
|
||||||
.. autoclass:: NXActionSetQueue
|
|
||||||
.. autoclass:: NXActionDecTtl
|
|
||||||
.. autoclass:: NXActionPushMpls
|
|
||||||
.. autoclass:: NXActionPopMpls
|
|
||||||
.. autoclass:: NXActionSetMplsTtl
|
|
||||||
.. autoclass:: NXActionDecMplsTtl
|
|
||||||
.. autoclass:: NXActionSetMplsLabel
|
|
||||||
.. autoclass:: NXActionSetMplsTc
|
|
||||||
|
|
||||||
The followings shows the supported NXAction classes in OpenFlow1.0 or later
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
.. py:currentmodule:: ryu.ofproto.ofproto_v1_3_parser
|
.. py:currentmodule:: ryu.ofproto.ofproto_v1_3_parser
|
||||||
|
|
||||||
.. autoclass:: NXActionPopQueue
|
|
||||||
.. autoclass:: NXActionRegLoad
|
|
||||||
.. autoclass:: NXActionRegLoad2
|
|
||||||
.. autoclass:: NXActionNote
|
|
||||||
.. autoclass:: NXActionSetTunnel
|
|
||||||
.. autoclass:: NXActionSetTunnel64
|
|
||||||
.. autoclass:: NXActionRegMove
|
.. autoclass:: NXActionRegMove
|
||||||
.. autoclass:: NXActionResubmit
|
|
||||||
.. autoclass:: NXActionResubmitTable
|
|
||||||
.. autoclass:: NXActionOutputReg
|
|
||||||
.. autoclass:: NXActionOutputReg2
|
|
||||||
.. autoclass:: NXActionLearn
|
.. autoclass:: NXActionLearn
|
||||||
.. autoclass:: NXActionExit
|
|
||||||
.. autoclass:: NXActionController
|
|
||||||
.. autoclass:: NXActionController2
|
|
||||||
.. autoclass:: NXActionDecTtlCntIds
|
|
||||||
.. autoclass:: NXActionStackPush
|
|
||||||
.. autoclass:: NXActionStackPop
|
|
||||||
.. autoclass:: NXActionSample
|
|
||||||
.. autoclass:: NXActionSample2
|
|
||||||
.. autoclass:: NXActionFinTimeout
|
|
||||||
.. autoclass:: NXActionConjunction
|
.. autoclass:: NXActionConjunction
|
||||||
.. autoclass:: NXActionMultipath
|
.. autoclass:: NXActionResubmitTable
|
||||||
.. autoclass:: NXActionBundle
|
|
||||||
.. autoclass:: NXActionBundleLoad
|
|
||||||
.. autoclass:: NXActionCT
|
.. autoclass:: NXActionCT
|
||||||
.. autoclass:: NXActionNAT
|
|
||||||
.. autoclass:: NXActionOutputTrunc
|
|
||||||
.. autoclass:: NXActionDecNshTtl
|
|
||||||
.. autoclass:: NXFlowSpecMatch
|
.. autoclass:: NXFlowSpecMatch
|
||||||
.. autoclass:: NXFlowSpecLoad
|
.. autoclass:: NXFlowSpecLoad
|
||||||
.. autoclass:: NXFlowSpecOutput
|
.. autoclass:: NXFlowSpecOutput
|
||||||
.. autofunction:: ryu.ofproto.nicira_ext.ofs_nbits
|
|
||||||
|
|
||||||
.. _nx_match_structures:
|
.. _nx_match_structures:
|
||||||
|
|
||||||
Nicira Extended Match Structures
|
Nicira Extended Match Structures
|
||||||
================================
|
================================
|
||||||
|
|
||||||
.. automodule:: ryu.ofproto.nicira_ext
|
.. automodule:: ryu.ofproto.nx_match
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@ Threads, events, and event queues
|
|||||||
Ryu applications are single-threaded entities which implement
|
Ryu applications are single-threaded entities which implement
|
||||||
various functionalities in Ryu. Events are messages between them.
|
various functionalities in Ryu. Events are messages between them.
|
||||||
|
|
||||||
Ryu applications send asynchronous events to each other.
|
Ryu applications send asynchronous events each other.
|
||||||
Besides that, there are some Ryu-internal event sources which
|
Besides that, there are some Ryu-internal event sources which
|
||||||
are not Ryu applications. One of examples of such event sources
|
are not Ryu applications. One of examples of such event sources
|
||||||
is OpenFlow controller.
|
is OpenFlow controller.
|
||||||
@ -22,11 +22,11 @@ between Ryu applications.
|
|||||||
Each Ryu application has a receive queue for events.
|
Each Ryu application has a receive queue for events.
|
||||||
The queue is FIFO and preserves the order of events.
|
The queue is FIFO and preserves the order of events.
|
||||||
Each Ryu application has a thread for event processing.
|
Each Ryu application has a thread for event processing.
|
||||||
The thread keeps draining the receive queue by dequeueing an event
|
The thread keep draining the receive queue by dequeueing an event
|
||||||
and calling the appropritate event handler for the event type.
|
and calling the appropritate event handler for the event type.
|
||||||
Because the event handler is called in the context of
|
Because the event handler is called in the context of
|
||||||
the event processing thread, it should be careful when blocking.
|
the event processing thread, it should be careful for blocking.
|
||||||
While an event handler is blocked, no further events for
|
I.e. while an event handler is blocked, no further events for
|
||||||
the Ryu application will be processed.
|
the Ryu application will be processed.
|
||||||
|
|
||||||
There are kinds of events which are used to implement synchronous
|
There are kinds of events which are used to implement synchronous
|
||||||
@ -82,10 +82,20 @@ For example, EventOFPPacketIn for packet-in message.
|
|||||||
The OpenFlow controller part of Ryu automatically decodes OpenFlow messages
|
The OpenFlow controller part of Ryu automatically decodes OpenFlow messages
|
||||||
received from switches and send these events to Ryu applications which
|
received from switches and send these events to Ryu applications which
|
||||||
expressed an interest using ryu.controller.handler.set_ev_cls.
|
expressed an interest using ryu.controller.handler.set_ev_cls.
|
||||||
OpenFlow event classes are subclasses of the following class.
|
OpenFlow event classes have at least the following attributes.
|
||||||
|
|
||||||
.. autoclass:: ryu.controller.ofp_event.EventOFPMsgBase
|
.. tabularcolumns:: |l|L|
|
||||||
|
|
||||||
|
============ =============================================================
|
||||||
|
Attribute Description
|
||||||
|
============ =============================================================
|
||||||
|
msg An object which describes the corresponding OpenFlow message.
|
||||||
|
msg.datapath A ryu.controller.controller.Datapath instance which describes
|
||||||
|
an OpenFlow switch from which we received this OpenFlow message.
|
||||||
|
============ =============================================================
|
||||||
|
|
||||||
|
The msg object has some more additional members whose values are extracted
|
||||||
|
from the original OpenFlow message.
|
||||||
See :ref:`ofproto_ref` for more info about OpenFlow messages.
|
See :ref:`ofproto_ref` for more info about OpenFlow messages.
|
||||||
|
|
||||||
ryu.base.app_manager.RyuApp
|
ryu.base.app_manager.RyuApp
|
||||||
@ -93,87 +103,267 @@ ryu.base.app_manager.RyuApp
|
|||||||
|
|
||||||
See :ref:`api_ref`.
|
See :ref:`api_ref`.
|
||||||
|
|
||||||
ryu.controller.handler.set_ev_cls
|
ryu.controller.handler.set_ev_cls(ev_cls, dispatchers=None)
|
||||||
=================================
|
===========================================================
|
||||||
|
|
||||||
.. autofunction:: ryu.controller.handler.set_ev_cls
|
A decorator for Ryu application to declare an event handler.
|
||||||
|
Decorated method will become an event handler.
|
||||||
|
ev_cls is an event class whose instances this RyuApp wants to receive.
|
||||||
|
dispatchers argument specifies one of the following negotiation phases
|
||||||
|
(or a list of them) for which events should be generated for this handler.
|
||||||
|
Note that, in case an event changes the phase, the phase before the change
|
||||||
|
is used to check the interest.
|
||||||
|
|
||||||
|
.. tabularcolumns:: |l|L|
|
||||||
|
|
||||||
|
=========================================== ==================================
|
||||||
|
Negotiation phase Description
|
||||||
|
=========================================== ==================================
|
||||||
|
ryu.controller.handler.HANDSHAKE_DISPATCHER Sending and waiting for hello
|
||||||
|
message
|
||||||
|
ryu.controller.handler.CONFIG_DISPATCHER Version negotiated and sent
|
||||||
|
features-request message
|
||||||
|
ryu.controller.handler.MAIN_DISPATCHER Switch-features message received
|
||||||
|
and sent set-config message
|
||||||
|
ryu.controller.handler.DEAD_DISPATCHER Disconnect from the peer. Or
|
||||||
|
disconnecting due to some
|
||||||
|
unrecoverable errors.
|
||||||
|
=========================================== ==================================
|
||||||
|
|
||||||
ryu.controller.controller.Datapath
|
ryu.controller.controller.Datapath
|
||||||
==================================
|
==================================
|
||||||
|
|
||||||
.. autoclass:: ryu.controller.controller.Datapath
|
A class to describe an OpenFlow switch connected to this controller.
|
||||||
|
An instance has the following attributes.
|
||||||
|
|
||||||
|
.. tabularcolumns:: |l|L|
|
||||||
|
|
||||||
|
====================================== =======================================
|
||||||
|
Attribute Description
|
||||||
|
====================================== =======================================
|
||||||
|
id 64-bit OpenFlow Datapath ID.
|
||||||
|
Only available for
|
||||||
|
ryu.controller.handler.MAIN_DISPATCHER
|
||||||
|
phase.
|
||||||
|
ofproto A module which exports OpenFlow
|
||||||
|
definitions, mainly constants appeared
|
||||||
|
in the specification, for the
|
||||||
|
negotiated OpenFlow version. For
|
||||||
|
example, ryu.ofproto.ofproto_v1_0 for
|
||||||
|
OpenFlow 1.0.
|
||||||
|
ofproto_parser A module which exports OpenFlow wire
|
||||||
|
message encoder and decoder for the
|
||||||
|
negotiated OpenFlow version. For
|
||||||
|
example, ryu.ofproto.ofproto_v1_0_parser
|
||||||
|
for OpenFlow 1.0.
|
||||||
|
ofproto_parser.OFPxxxx(datapath, ....) A callable to prepare an OpenFlow
|
||||||
|
message for the given switch. It can
|
||||||
|
be sent with Datapath.send_msg later.
|
||||||
|
xxxx is a name of the message. For
|
||||||
|
example OFPFlowMod for flow-mod
|
||||||
|
message. Arguemnts depend on the
|
||||||
|
message.
|
||||||
|
set_xid(self, msg) Generate an OpenFlow XID and put it
|
||||||
|
in msg.xid.
|
||||||
|
send_msg(self, msg) Queue an OpenFlow message to send to
|
||||||
|
the corresponding switch. If msg.xid
|
||||||
|
is None, set_xid is automatically
|
||||||
|
called on the message before queueing.
|
||||||
|
send_packet_out deprecated
|
||||||
|
send_flow_mod deprecated
|
||||||
|
send_flow_del deprecated
|
||||||
|
send_delete_all_flows deprecated
|
||||||
|
send_barrier Queue an OpenFlow barrier message to
|
||||||
|
send to the switch.
|
||||||
|
send_nxt_set_flow_format deprecated
|
||||||
|
is_reserved_port deprecated
|
||||||
|
====================================== =======================================
|
||||||
|
|
||||||
ryu.controller.event.EventBase
|
ryu.controller.event.EventBase
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
.. autoclass:: ryu.controller.event.EventBase
|
The base of all event classes.
|
||||||
|
A Ryu application can define its own event type by creating a subclass.
|
||||||
|
|
||||||
ryu.controller.event.EventRequestBase
|
ryu.controller.event.EventRequestBase
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
.. autoclass:: ryu.controller.event.EventRequestBase
|
The base class for synchronous request for RyuApp.send_request.
|
||||||
|
|
||||||
ryu.controller.event.EventReplyBase
|
ryu.controller.event.EventReplyBase
|
||||||
===================================
|
===================================
|
||||||
|
|
||||||
.. autoclass:: ryu.controller.event.EventReplyBase
|
The base class for synchronous request reply for RyuApp.send_reply.
|
||||||
|
|
||||||
ryu.controller.ofp_event.EventOFPStateChange
|
ryu.controller.ofp_event.EventOFPStateChange
|
||||||
============================================
|
============================================
|
||||||
|
|
||||||
.. autoclass:: ryu.controller.ofp_event.EventOFPStateChange
|
An event class for negotiation phase change notification.
|
||||||
|
An instance of this class is sent to observer after changing
|
||||||
|
the negotiation phase.
|
||||||
|
An instance has at least the following attributes.
|
||||||
|
|
||||||
ryu.controller.ofp_event.EventOFPPortStateChange
|
========= ====================================================================
|
||||||
================================================
|
Attribute Description
|
||||||
|
========= ====================================================================
|
||||||
.. autoclass:: ryu.controller.ofp_event.EventOFPPortStateChange
|
datapath ryu.controller.controller.Datapath instance of the switch
|
||||||
|
========= ====================================================================
|
||||||
|
|
||||||
ryu.controller.dpset.EventDP
|
ryu.controller.dpset.EventDP
|
||||||
============================
|
============================
|
||||||
|
|
||||||
.. autoclass:: ryu.controller.dpset.EventDP
|
An event class to notify connect/disconnect of a switch.
|
||||||
|
For OpenFlow switches, one can get the same notification by observing
|
||||||
|
ryu.controller.ofp_event.EventOFPStateChange.
|
||||||
|
An instance has at least the following attributes.
|
||||||
|
|
||||||
|
========= ====================================================================
|
||||||
|
Attribute Description
|
||||||
|
========= ====================================================================
|
||||||
|
dp A ryu.controller.controller.Datapath instance of the switch
|
||||||
|
enter True when the switch connected to our controller. False for
|
||||||
|
disconnect.
|
||||||
|
========= ====================================================================
|
||||||
|
|
||||||
ryu.controller.dpset.EventPortAdd
|
ryu.controller.dpset.EventPortAdd
|
||||||
=================================
|
=================================
|
||||||
|
|
||||||
.. autoclass:: ryu.controller.dpset.EventPortAdd
|
An event class for switch port status notification.
|
||||||
|
This event is generated when a new port is added to a switch.
|
||||||
|
For OpenFlow switches, one can get the same notification by observing
|
||||||
|
ryu.controller.ofp_event.EventOFPPortStatus.
|
||||||
|
An instance has at least the following attributes.
|
||||||
|
|
||||||
|
========= ====================================================================
|
||||||
|
Attribute Description
|
||||||
|
========= ====================================================================
|
||||||
|
dp A ryu.controller.controller.Datapath instance of the switch
|
||||||
|
port port number
|
||||||
|
========= ====================================================================
|
||||||
|
|
||||||
ryu.controller.dpset.EventPortDelete
|
ryu.controller.dpset.EventPortDelete
|
||||||
====================================
|
====================================
|
||||||
|
|
||||||
.. autoclass:: ryu.controller.dpset.EventPortDelete
|
An event class for switch port status notification.
|
||||||
|
This event is generated when a port is removed from a switch.
|
||||||
|
For OpenFlow switches, one can get the same notification by observing
|
||||||
|
ryu.controller.ofp_event.EventOFPPortStatus.
|
||||||
|
An instance has at least the following attributes.
|
||||||
|
|
||||||
|
========= ====================================================================
|
||||||
|
Attribute Description
|
||||||
|
========= ====================================================================
|
||||||
|
dp A ryu.controller.controller.Datapath instance of the switch
|
||||||
|
port port number
|
||||||
|
========= ====================================================================
|
||||||
|
|
||||||
ryu.controller.dpset.EventPortModify
|
ryu.controller.dpset.EventPortModify
|
||||||
====================================
|
====================================
|
||||||
|
|
||||||
.. autoclass:: ryu.controller.dpset.EventPortModify
|
An event class for switch port status notification.
|
||||||
|
This event is generated when some attribute of a port is changed.
|
||||||
|
For OpenFlow switches, one can get the same notification by observing
|
||||||
|
ryu.controller.ofp_event.EventOFPPortStatus.
|
||||||
|
An instance has at least the following attributes.
|
||||||
|
|
||||||
|
========= ====================================================================
|
||||||
|
Attribute Description
|
||||||
|
========= ====================================================================
|
||||||
|
dp A ryu.controller.controller.Datapath instance of the switch
|
||||||
|
port port number
|
||||||
|
========= ====================================================================
|
||||||
|
|
||||||
ryu.controller.network.EventNetworkPort
|
ryu.controller.network.EventNetworkPort
|
||||||
=======================================
|
=======================================
|
||||||
|
|
||||||
.. autoclass:: ryu.controller.network.EventNetworkPort
|
An event class for notification of port arrival and deperture.
|
||||||
|
This event is generated when a port is introduced to or removed from a network
|
||||||
|
by the REST API.
|
||||||
|
An instance has at least the following attributes.
|
||||||
|
|
||||||
|
========== ===================================================================
|
||||||
|
Attribute Description
|
||||||
|
========== ===================================================================
|
||||||
|
network_id Network ID
|
||||||
|
dpid OpenFlow Datapath ID of the switch to which the port belongs.
|
||||||
|
port_no OpenFlow port number of the port
|
||||||
|
add_del True for adding a port. False for removing a port.
|
||||||
|
========== ===================================================================
|
||||||
|
|
||||||
ryu.controller.network.EventNetworkDel
|
ryu.controller.network.EventNetworkDel
|
||||||
======================================
|
======================================
|
||||||
|
|
||||||
.. autoclass:: ryu.controller.network.EventNetworkDel
|
An event class for network deletion.
|
||||||
|
This event is generated when a network is deleted by the REST API.
|
||||||
|
An instance has at least the following attributes.
|
||||||
|
|
||||||
|
========== ===================================================================
|
||||||
|
Attribute Description
|
||||||
|
========== ===================================================================
|
||||||
|
network_id Network ID
|
||||||
|
========== ===================================================================
|
||||||
|
|
||||||
ryu.controller.network.EventMacAddress
|
ryu.controller.network.EventMacAddress
|
||||||
======================================
|
======================================
|
||||||
|
|
||||||
.. autoclass:: ryu.controller.network.EventMacAddress
|
An event class for end-point MAC address registration.
|
||||||
|
This event is generated when a end-point MAC address is updated
|
||||||
|
by the REST API.
|
||||||
|
An instance has at least the following attributes.
|
||||||
|
|
||||||
|
=========== ==================================================================
|
||||||
|
Attribute Description
|
||||||
|
=========== ==================================================================
|
||||||
|
network_id Network ID
|
||||||
|
dpid OpenFlow Datapath ID of the switch to which the port belongs.
|
||||||
|
port_no OpenFlow port number of the port
|
||||||
|
mac_address The old MAC address of the port if add_del is False. Otherwise
|
||||||
|
the new MAC address.
|
||||||
|
add_del False if this event is a result of a port removal. Otherwise
|
||||||
|
True.
|
||||||
|
=========== ==================================================================
|
||||||
|
|
||||||
ryu.controller.tunnels.EventTunnelKeyAdd
|
ryu.controller.tunnels.EventTunnelKeyAdd
|
||||||
========================================
|
========================================
|
||||||
|
|
||||||
.. autoclass:: ryu.controller.tunnels.EventTunnelKeyAdd
|
An event class for tunnel key registration.
|
||||||
|
This event is generated when a tunnel key is registered or updated
|
||||||
|
by the REST API.
|
||||||
|
An instance has at least the following attributes.
|
||||||
|
|
||||||
|
=========== ==================================================================
|
||||||
|
Attribute Description
|
||||||
|
=========== ==================================================================
|
||||||
|
network_id Network ID
|
||||||
|
tunnel_key Tunnel Key
|
||||||
|
=========== ==================================================================
|
||||||
|
|
||||||
ryu.controller.tunnels.EventTunnelKeyDel
|
ryu.controller.tunnels.EventTunnelKeyDel
|
||||||
========================================
|
========================================
|
||||||
|
|
||||||
.. autoclass:: ryu.controller.tunnels.EventTunnelKeyDel
|
An event class for tunnel key registration.
|
||||||
|
This event is generated when a tunnel key is removed by the REST API.
|
||||||
|
An instance has at least the following attributes.
|
||||||
|
|
||||||
|
=========== ==================================================================
|
||||||
|
Attribute Description
|
||||||
|
=========== ==================================================================
|
||||||
|
network_id Network ID
|
||||||
|
tunnel_key Tunnel Key
|
||||||
|
=========== ==================================================================
|
||||||
|
|
||||||
ryu.controller.tunnels.EventTunnelPort
|
ryu.controller.tunnels.EventTunnelPort
|
||||||
======================================
|
======================================
|
||||||
|
|
||||||
.. autoclass:: ryu.controller.tunnels.EventTunnelPort
|
An event class for tunnel port registration.
|
||||||
|
This event is generated when a tunnel port is added or removed by the REST API.
|
||||||
|
An instance has at least the following attributes.
|
||||||
|
|
||||||
|
=========== ==================================================================
|
||||||
|
Attribute Description
|
||||||
|
=========== ==================================================================
|
||||||
|
dpid OpenFlow Datapath ID
|
||||||
|
port_no OpenFlow port number
|
||||||
|
remote_dpid OpenFlow port number of the tunnel peer
|
||||||
|
add_del True for adding a tunnel. False for removal.
|
||||||
|
=========== ==================================================================
|
||||||
|
|||||||
@ -19,7 +19,7 @@ The test procedure
|
|||||||
* run LINC switch
|
* run LINC switch
|
||||||
* run Ryu test_of_config app
|
* run Ryu test_of_config app
|
||||||
|
|
||||||
For getting/installing Ryu itself, please refer to https://ryu-sdn.org/
|
For getting/installing Ryu itself, please refer to http://osrg.github.io/ryu/
|
||||||
|
|
||||||
|
|
||||||
Install Erlang environment
|
Install Erlang environment
|
||||||
|
|||||||
@ -4,17 +4,10 @@
|
|||||||
Using Ryu Network Operating System with OpenStack as OpenFlow controller
|
Using Ryu Network Operating System with OpenStack as OpenFlow controller
|
||||||
************************************************************************
|
************************************************************************
|
||||||
|
|
||||||
.. CAUTION::
|
|
||||||
|
|
||||||
The Ryu plugin and OFAgent described in the following is deprecated,
|
|
||||||
because Ryu is officially integrated into Open vSwitch agent with
|
|
||||||
"of_interface = native" mode.
|
|
||||||
|
|
||||||
|
|
||||||
Ryu cooperates with OpenStack using Quantum Ryu plugin. The plugin is
|
Ryu cooperates with OpenStack using Quantum Ryu plugin. The plugin is
|
||||||
available in the official Quantum releases.
|
available in the official Quantum releases.
|
||||||
|
|
||||||
For more information, please visit https://github.com/faucetsdn/ryu/wiki/OpenStack .
|
For more information, please visit http://github.com/osrg/ryu/wiki/OpenStack .
|
||||||
We described instructions of the installation / configuration of OpenStack
|
We described instructions of the installation / configuration of OpenStack
|
||||||
with Ryu, and we provide pre-configured VM image to be able to easily try
|
with Ryu, and we provide pre-configured VM image to be able to easily try
|
||||||
OpenStack with Ryu.
|
OpenStack with Ryu.
|
||||||
|
|||||||
@ -5,21 +5,21 @@ The First Application
|
|||||||
Whetting Your Appetite
|
Whetting Your Appetite
|
||||||
======================
|
======================
|
||||||
|
|
||||||
If you want to manage network gear (switches, routers, etc) your
|
If you want to manage the network gears (switches, routers, etc) at
|
||||||
own way, you just need to write your own Ryu application. Your application
|
your way, you need to write your Ryu application. Your application
|
||||||
tells Ryu how you want to manage the gear. Then Ryu configures the
|
tells Ryu how you want to manage the gears. Then Ryu configures the
|
||||||
gear by using OpenFlow protocol, etc.
|
gears by using OpenFlow protocol, etc.
|
||||||
|
|
||||||
Writing Ryu applications is easy. They're just Python scripts.
|
Writing Ryu application is easy. It's just Python scripts.
|
||||||
|
|
||||||
|
|
||||||
Start Writing
|
Start Writing
|
||||||
=============
|
=============
|
||||||
|
|
||||||
Here we show a Ryu application that makes an OpenFlow switch work as a dumb
|
We show a Ryu application that make OpenFlow switches work as a dumb
|
||||||
layer 2 switch.
|
layer 2 switch.
|
||||||
|
|
||||||
Open a text editor and create a new file with the following content:
|
Open a text editor creating a new file with the following content:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@ -29,9 +29,9 @@ Open a text editor and create a new file with the following content:
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(L2Switch, self).__init__(*args, **kwargs)
|
super(L2Switch, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
Ryu applications are just Python scripts so you can save the file with
|
Ryu application is just a Python script so you can save the file with
|
||||||
any name, any extension, and any place you want. Let's name the file
|
any name, extensions, and any place you want. Let's name the file
|
||||||
'l2.py' in your home directory.
|
'l2.py' at your home directory.
|
||||||
|
|
||||||
This application does nothing useful yet, however it's a complete Ryu
|
This application does nothing useful yet, however it's a complete Ryu
|
||||||
application. In fact, you can run this Ryu application::
|
application. In fact, you can run this Ryu application::
|
||||||
@ -41,10 +41,10 @@ application. In fact, you can run this Ryu application::
|
|||||||
instantiating app /Users/fujita/l2.py
|
instantiating app /Users/fujita/l2.py
|
||||||
|
|
||||||
|
|
||||||
All you have to do is define a new subclass of RyuApp to run
|
All you have to do is defining needs a new subclass of RyuApp to run
|
||||||
your Python script as a Ryu application.
|
your Python script as a Ryu application.
|
||||||
|
|
||||||
Next let's add some functionality that sends a received packet to all
|
Next let's add the functionality of sending a received packet to all
|
||||||
the ports.
|
the ports.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
@ -53,11 +53,8 @@ the ports.
|
|||||||
from ryu.controller import ofp_event
|
from ryu.controller import ofp_event
|
||||||
from ryu.controller.handler import MAIN_DISPATCHER
|
from ryu.controller.handler import MAIN_DISPATCHER
|
||||||
from ryu.controller.handler import set_ev_cls
|
from ryu.controller.handler import set_ev_cls
|
||||||
from ryu.ofproto import ofproto_v1_0
|
|
||||||
|
|
||||||
class L2Switch(app_manager.RyuApp):
|
class L2Switch(app_manager.RyuApp):
|
||||||
OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION]
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(L2Switch, self).__init__(*args, **kwargs)
|
super(L2Switch, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
@ -67,31 +64,26 @@ the ports.
|
|||||||
dp = msg.datapath
|
dp = msg.datapath
|
||||||
ofp = dp.ofproto
|
ofp = dp.ofproto
|
||||||
ofp_parser = dp.ofproto_parser
|
ofp_parser = dp.ofproto_parser
|
||||||
|
|
||||||
actions = [ofp_parser.OFPActionOutput(ofp.OFPP_FLOOD)]
|
actions = [ofp_parser.OFPActionOutput(ofp.OFPP_FLOOD)]
|
||||||
|
|
||||||
data = None
|
|
||||||
if msg.buffer_id == ofp.OFP_NO_BUFFER:
|
|
||||||
data = msg.data
|
|
||||||
|
|
||||||
out = ofp_parser.OFPPacketOut(
|
out = ofp_parser.OFPPacketOut(
|
||||||
datapath=dp, buffer_id=msg.buffer_id, in_port=msg.in_port,
|
datapath=dp, buffer_id=msg.buffer_id, in_port=msg.in_port,
|
||||||
actions=actions, data = data)
|
actions=actions)
|
||||||
dp.send_msg(out)
|
dp.send_msg(out)
|
||||||
|
|
||||||
|
|
||||||
A new method 'packet_in_handler' is added to the L2Switch class. This is
|
A new method 'packet_in_handler' is added to L2Switch class. This is
|
||||||
called when Ryu receives an OpenFlow packet_in message. The trick is the
|
called when Ryu receives an OpenFlow packet_in message. The trick is
|
||||||
'set_ev_cls' decorator. This decorator tells Ryu when the decorated
|
'set_ev_cls' decorator. This decorator tells Ryu when the decorated
|
||||||
function should be called.
|
function should be called.
|
||||||
|
|
||||||
The first argument of the decorator indicates which type of event this
|
The first argument of the decorator indicates an event that makes
|
||||||
function should be called for. As you might expect, every time Ryu gets a
|
function called. As you expect easily, every time Ryu gets a
|
||||||
packet_in message, this function is called.
|
packet_in message, this function is called.
|
||||||
|
|
||||||
The second argument indicates the state of the switch. You probably
|
The second argument indicates the state of the switch. Probably, you
|
||||||
want to ignore packet_in messages before the negotiation between Ryu
|
want to ignore packet_in messages before the negotiation between Ryu
|
||||||
and the switch is finished. Using 'MAIN_DISPATCHER' as the second
|
and the switch finishes. Using 'MAIN_DISPATCHER' as the second
|
||||||
argument means this function is called only after the negotiation
|
argument means this function is called only after the negotiation
|
||||||
completes.
|
completes.
|
||||||
|
|
||||||
@ -108,24 +100,24 @@ Ready for the second half.
|
|||||||
|
|
||||||
* OFPActionOutput class is used with a packet_out message to specify a
|
* OFPActionOutput class is used with a packet_out message to specify a
|
||||||
switch port that you want to send the packet out of. This
|
switch port that you want to send the packet out of. This
|
||||||
application uses the OFPP_FLOOD flag to indicate that the packet should
|
application need a switch to send out of all the ports so OFPP_FLOOD
|
||||||
be sent out on all ports.
|
constant is used.
|
||||||
|
|
||||||
* OFPPacketOut class is used to build a packet_out message.
|
* OFPPacketOut class is used to build a packet_out message.
|
||||||
|
|
||||||
* If you call Datapath class's send_msg method with a OpenFlow message
|
* If you call Datapath class's send_msg method with a OpenFlow message
|
||||||
class object, Ryu builds and sends the on-wire data format to the switch.
|
class object, Ryu builds and send the on-wire data format to the switch.
|
||||||
|
|
||||||
|
|
||||||
There, you finished implementing your first Ryu application. You are ready to
|
Here, you finished implementing your first Ryu application. You are ready to
|
||||||
run a Ryu application that does something useful.
|
run this Ryu application that does something useful.
|
||||||
|
|
||||||
|
|
||||||
Is a dumb L2 switch is too dumb? You want to implement a learning L2
|
A dumb l2 switch is too dumb? You want to implement a learning l2
|
||||||
switch? Move to `the next step
|
switch? Move to `the next step
|
||||||
<https://github.com/faucetsdn/ryu/blob/master/ryu/app/simple_switch.py>`_. You
|
<https://github.com/osrg/ryu/blob/master/ryu/app/simple_switch.py>`_. You
|
||||||
can learn from the existing Ryu applications at `ryu/app
|
can learn from the existing Ryu applications at `ryu/app
|
||||||
<https://github.com/faucetsdn/ryu/blob/master/ryu/app/>`_ directory and
|
<https://github.com/osrg/ryu/blob/master/ryu/app/>`_ directory and
|
||||||
`integrated tests
|
`integrated tests
|
||||||
<https://github.com/faucetsdn/ryu/blob/master/ryu/tests/integrated/>`_
|
<https://github.com/osrg/ryu/blob/master/ryu/tests/integrated/>`_
|
||||||
directory.
|
directory.
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
pip==20.3.4
|
|
||||||
56
run_tests.sh
56
run_tests.sh
@ -8,16 +8,16 @@ usage() {
|
|||||||
echo "Usage: $0 [OPTION]..."
|
echo "Usage: $0 [OPTION]..."
|
||||||
echo "Run Ryu's test suite(s)"
|
echo "Run Ryu's test suite(s)"
|
||||||
echo ""
|
echo ""
|
||||||
echo " -V, --virtual-env Always use virtualenv. Install automatically if not present"
|
echo " -V, --virtual-env Always use virtualenv. Install automatically if not present"
|
||||||
echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment"
|
echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment"
|
||||||
echo " -c, --coverage Generate coverage report"
|
echo " -c, --coverage Generate coverage report"
|
||||||
echo " -f, --force Force a clean re-build of the virtual environment. Useful when dependencies have been added."
|
echo " -f, --force Force a clean re-build of the virtual environment. Useful when dependencies have been added."
|
||||||
echo " -p, --pycodestyle, --pep8 Just run pycodestyle(pep8)"
|
echo " -p, --pep8 Just run pep8"
|
||||||
echo " -P, --no-pycodestyle, --no-pep8 Don't run pycodestyle(pep8)"
|
echo " -P, --no-pep8 Don't run pep8"
|
||||||
echo " -l, --pylint Just run pylint"
|
echo " -l, --pylint Just run pylint"
|
||||||
echo " -i, --integrated Run integrated test"
|
echo " -i, --integrated Run integrated test"
|
||||||
echo " -v, --verbose Run verbose pylint analysis"
|
echo " -v, --verbose Run verbose pylint analysis"
|
||||||
echo " -h, --help Print this usage message"
|
echo " -h, --help Print this usage message"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Note: with no options specified, the script will try to run the tests in a virtual environment,"
|
echo "Note: with no options specified, the script will try to run the tests in a virtual environment,"
|
||||||
echo " If no virtualenv is found, the script will ask if you would like to create one. If you "
|
echo " If no virtualenv is found, the script will ask if you would like to create one. If you "
|
||||||
@ -31,8 +31,8 @@ process_option() {
|
|||||||
-V|--virtual-env) always_venv=1; never_venv=0;;
|
-V|--virtual-env) always_venv=1; never_venv=0;;
|
||||||
-N|--no-virtual-env) always_venv=0; never_venv=1;;
|
-N|--no-virtual-env) always_venv=0; never_venv=1;;
|
||||||
-f|--force) force=1;;
|
-f|--force) force=1;;
|
||||||
-p|--pycodestyle|--pep8) just_pycodestyle=1; never_venv=1; always_venv=0;;
|
-p|--pep8) just_pep8=1; never_venv=1; always_venv=0;;
|
||||||
-P|--no-pycodestyle|--no-pep8) no_pycodestyle=1;;
|
-P|--no-pep8) no_pep8=1;;
|
||||||
-l|--pylint) just_pylint=1;;
|
-l|--pylint) just_pylint=1;;
|
||||||
-i|--integrated) integrated=1;;
|
-i|--integrated) integrated=1;;
|
||||||
-c|--coverage) coverage=1;;
|
-c|--coverage) coverage=1;;
|
||||||
@ -46,8 +46,8 @@ venv=.venv
|
|||||||
with_venv=tools/with_venv.sh
|
with_venv=tools/with_venv.sh
|
||||||
always_venv=0
|
always_venv=0
|
||||||
never_venv=0
|
never_venv=0
|
||||||
just_pycodestyle=0
|
just_pep8=0
|
||||||
no_pycodestyle=0
|
no_pep8=0
|
||||||
just_pylint=0
|
just_pylint=0
|
||||||
integrated=0
|
integrated=0
|
||||||
force=0
|
force=0
|
||||||
@ -103,26 +103,20 @@ run_pylint() {
|
|||||||
export PYTHONPATH=$OLD_PYTHONPATH
|
export PYTHONPATH=$OLD_PYTHONPATH
|
||||||
}
|
}
|
||||||
|
|
||||||
run_pycodestyle() {
|
run_pep8() {
|
||||||
PYCODESTYLE=$(which pycodestyle || which pep8)
|
echo "Running pep8 ..."
|
||||||
if [ -z "${PYCODESTYLE}" ]
|
|
||||||
then
|
|
||||||
echo "Please install pycodestyle or pep8"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
echo "Running $(basename ${PYCODESTYLE}) ..."
|
|
||||||
|
|
||||||
PYCODESTYLE_OPTIONS="--repeat --show-source"
|
PEP8_OPTIONS="--repeat --show-source"
|
||||||
PYCODESTYLE_INCLUDE="ryu setup*.py"
|
PEP8_INCLUDE="ryu setup*.py"
|
||||||
PYCODESTYLE_LOG=pycodestyle.log
|
PEP8_LOG=pep8.log
|
||||||
${wrapper} ${PYCODESTYLE} $PYCODESTYLE_OPTIONS $PYCODESTYLE_INCLUDE | tee $PYCODESTYLE_LOG
|
${wrapper} pep8 $PEP8_OPTIONS $PEP8_INCLUDE | tee $PEP8_LOG
|
||||||
}
|
}
|
||||||
|
|
||||||
run_integrated() {
|
run_integrated() {
|
||||||
echo "Running integrated test ..."
|
echo "Running integrated test ..."
|
||||||
|
|
||||||
INTEGRATED_TEST_RUNNER="./ryu/tests/integrated/run_tests_with_ovs12.py"
|
INTEGRATED_TEST_RUNNER="./ryu/tests/integrated/run_tests_with_ovs12.py"
|
||||||
sudo PYTHONPATH=. nosetests -s $INTEGRATED_TEST_RUNNER
|
sudo PYTHONPATH=. nosetests -s $INTEGRATED_TEST_RUNNER
|
||||||
}
|
}
|
||||||
#NOSETESTS="nosetests $noseopts $noseargs"
|
#NOSETESTS="nosetests $noseopts $noseargs"
|
||||||
NOSETESTS="${PYTHON} ./ryu/tests/run_tests.py $noseopts $noseargs"
|
NOSETESTS="${PYTHON} ./ryu/tests/run_tests.py $noseopts $noseargs"
|
||||||
@ -167,8 +161,8 @@ if [ $coverage -eq 1 ]; then
|
|||||||
${wrapper} coverage erase
|
${wrapper} coverage erase
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $just_pycodestyle -eq 1 ]; then
|
if [ $just_pep8 -eq 1 ]; then
|
||||||
run_pycodestyle
|
run_pep8
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
if [ $just_pylint -eq 1 ]; then
|
if [ $just_pylint -eq 1 ]; then
|
||||||
@ -183,8 +177,8 @@ fi
|
|||||||
|
|
||||||
run_tests
|
run_tests
|
||||||
RV=$?
|
RV=$?
|
||||||
if [ $no_pycodestyle -eq 0 ]; then
|
if [ $no_pep8 -eq 0 ]; then
|
||||||
run_pycodestyle
|
run_pep8
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $coverage -eq 1 ]; then
|
if [ $coverage -eq 1 ]; then
|
||||||
|
|||||||
@ -14,5 +14,5 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
version_info = (4, 34)
|
version_info = (4, 0)
|
||||||
version = '.'.join(map(str, version_info))
|
version = '.'.join(map(str, version_info))
|
||||||
|
|||||||
@ -56,7 +56,7 @@ class GUIServerController(ControllerBase):
|
|||||||
path = "%s/html/" % PATH
|
path = "%s/html/" % PATH
|
||||||
self.static_app = DirectoryApp(path)
|
self.static_app = DirectoryApp(path)
|
||||||
|
|
||||||
@route('topology', '/{filename:[^/]*}')
|
@route('topology', '/{filename:.*}')
|
||||||
def static_handler(self, req, **kwargs):
|
def static_handler(self, req, **kwargs):
|
||||||
if kwargs['filename']:
|
if kwargs['filename']:
|
||||||
req.path_info = kwargs['filename']
|
req.path_info = kwargs['filename']
|
||||||
|
|||||||
@ -16,37 +16,22 @@
|
|||||||
|
|
||||||
# client for ryu.app.ofctl.service
|
# client for ryu.app.ofctl.service
|
||||||
|
|
||||||
|
import numbers
|
||||||
|
|
||||||
from ryu.base import app_manager
|
from ryu.base import app_manager
|
||||||
from . import event
|
from . import event
|
||||||
|
|
||||||
|
|
||||||
def get_datapath(app, dpid=None):
|
def get_datapath(app, dpid):
|
||||||
"""
|
"""
|
||||||
Get datapath object by dpid.
|
Get datapath object by dpid.
|
||||||
|
|
||||||
:param app: Client RyuApp instance
|
:param app: Client RyuApp instance
|
||||||
:param dpid: Datapath ID (int type) or None to get all datapath objects
|
:param dpid: Datapath-id (in integer)
|
||||||
|
|
||||||
Returns a object of datapath, a list of datapath objects when no dpid
|
Returns None on error.
|
||||||
given or None when error.
|
|
||||||
|
|
||||||
Raises an exception if any of the given values is invalid.
|
|
||||||
|
|
||||||
Example::
|
|
||||||
|
|
||||||
# ...(snip)...
|
|
||||||
import ryu.app.ofctl.api as ofctl_api
|
|
||||||
|
|
||||||
|
|
||||||
class MyApp(app_manager.RyuApp):
|
|
||||||
|
|
||||||
def _my_handler(self, ev):
|
|
||||||
# Get all datapath objects
|
|
||||||
result = ofctl_api.get_datapath(self)
|
|
||||||
|
|
||||||
# Get the datapath object which has the given dpid
|
|
||||||
result = ofctl_api.get_datapath(self, dpid=1)
|
|
||||||
"""
|
"""
|
||||||
|
assert isinstance(dpid, numbers.Integral)
|
||||||
return app.send_request(event.GetDatapathRequest(dpid=dpid))()
|
return app.send_request(event.GetDatapathRequest(dpid=dpid))()
|
||||||
|
|
||||||
|
|
||||||
@ -70,19 +55,12 @@ def send_msg(app, msg, reply_cls=None, reply_multi=False):
|
|||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
# ...(snip)...
|
import ryu.app.ofctl.api as api
|
||||||
import ryu.app.ofctl.api as ofctl_api
|
|
||||||
|
|
||||||
|
msg = parser.OFPPortDescStatsRequest(datapath=datapath)
|
||||||
class MyApp(app_manager.RyuApp):
|
result = api.send_msg(self, msg,
|
||||||
|
reply_cls=parser.OFPPortDescStatsReply,
|
||||||
def _my_handler(self, ev):
|
reply_multi=True)
|
||||||
# ...(snip)...
|
|
||||||
msg = parser.OFPPortDescStatsRequest(datapath=datapath)
|
|
||||||
result = ofctl_api.send_msg(
|
|
||||||
self, msg,
|
|
||||||
reply_cls=parser.OFPPortDescStatsReply,
|
|
||||||
reply_multi=True)
|
|
||||||
"""
|
"""
|
||||||
return app.send_request(event.SendMsgRequest(msg=msg,
|
return app.send_request(event.SendMsgRequest(msg=msg,
|
||||||
reply_cls=reply_cls,
|
reply_cls=reply_cls,
|
||||||
|
|||||||
@ -33,8 +33,8 @@ class _ReplyBase(event.EventReplyBase):
|
|||||||
# get datapath
|
# get datapath
|
||||||
|
|
||||||
class GetDatapathRequest(_RequestBase):
|
class GetDatapathRequest(_RequestBase):
|
||||||
def __init__(self, dpid=None):
|
def __init__(self, dpid):
|
||||||
assert dpid is None or isinstance(dpid, numbers.Integral)
|
assert isinstance(dpid, numbers.Integral)
|
||||||
super(GetDatapathRequest, self).__init__()
|
super(GetDatapathRequest, self).__init__()
|
||||||
self.dpid = dpid
|
self.dpid = dpid
|
||||||
|
|
||||||
|
|||||||
@ -64,22 +64,6 @@ class OfctlService(app_manager.RyuApp):
|
|||||||
self.unobserve_event(ev_cls)
|
self.unobserve_event(ev_cls)
|
||||||
self.logger.debug('ofctl: stop observing %s', ev_cls)
|
self.logger.debug('ofctl: stop observing %s', ev_cls)
|
||||||
|
|
||||||
def _cancel(self, info, barrier_xid, exception):
|
|
||||||
xid = info.barriers.pop(barrier_xid)
|
|
||||||
req = info.xids.pop(xid)
|
|
||||||
msg = req.msg
|
|
||||||
datapath = msg.datapath
|
|
||||||
parser = datapath.ofproto_parser
|
|
||||||
is_barrier = isinstance(msg, parser.OFPBarrierRequest)
|
|
||||||
|
|
||||||
info.results.pop(xid)
|
|
||||||
|
|
||||||
if not is_barrier and req.reply_cls is not None:
|
|
||||||
self._unobserve_msg(req.reply_cls)
|
|
||||||
|
|
||||||
self.logger.error('failed to send message <%s>', req.msg)
|
|
||||||
self.reply_to_request(req, event.Reply(exception=exception))
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _is_error(msg):
|
def _is_error(msg):
|
||||||
return (ofp_event.ofp_msg_to_ev_cls(type(msg)) ==
|
return (ofp_event.ofp_msg_to_ev_cls(type(msg)) ==
|
||||||
@ -95,11 +79,6 @@ class OfctlService(app_manager.RyuApp):
|
|||||||
self.logger.debug('add dpid %s datapath %s new_info %s old_info %s',
|
self.logger.debug('add dpid %s datapath %s new_info %s old_info %s',
|
||||||
id, datapath, new_info, old_info)
|
id, datapath, new_info, old_info)
|
||||||
self._switches[id] = new_info
|
self._switches[id] = new_info
|
||||||
if old_info:
|
|
||||||
old_info.datapath.close()
|
|
||||||
for xid in list(old_info.barriers):
|
|
||||||
self._cancel(
|
|
||||||
old_info, xid, exception.InvalidDatapath(result=id))
|
|
||||||
|
|
||||||
@set_ev_cls(ofp_event.EventOFPStateChange, DEAD_DISPATCHER)
|
@set_ev_cls(ofp_event.EventOFPStateChange, DEAD_DISPATCHER)
|
||||||
def _handle_dead(self, ev):
|
def _handle_dead(self, ev):
|
||||||
@ -115,25 +94,23 @@ class OfctlService(app_manager.RyuApp):
|
|||||||
if info.datapath is datapath:
|
if info.datapath is datapath:
|
||||||
self.logger.debug('forget info %s', info)
|
self.logger.debug('forget info %s', info)
|
||||||
self._switches.pop(id)
|
self._switches.pop(id)
|
||||||
for xid in list(info.barriers):
|
|
||||||
self._cancel(info, xid, exception.InvalidDatapath(result=id))
|
|
||||||
|
|
||||||
@set_ev_cls(event.GetDatapathRequest, MAIN_DISPATCHER)
|
@set_ev_cls(event.GetDatapathRequest, MAIN_DISPATCHER)
|
||||||
def _handle_get_datapath(self, req):
|
def _handle_get_datapath(self, req):
|
||||||
result = None
|
id = req.dpid
|
||||||
if req.dpid is None:
|
assert isinstance(id, numbers.Integral)
|
||||||
result = [v.datapath for v in self._switches.values()]
|
try:
|
||||||
else:
|
datapath = self._switches[id].datapath
|
||||||
if req.dpid in self._switches:
|
except KeyError:
|
||||||
result = self._switches[req.dpid].datapath
|
datapath = None
|
||||||
self.reply_to_request(req, event.Reply(result=result))
|
self.logger.debug('dpid %s -> datapath %s', id, datapath)
|
||||||
|
rep = event.Reply(result=datapath)
|
||||||
|
self.reply_to_request(req, rep)
|
||||||
|
|
||||||
@set_ev_cls(event.SendMsgRequest, MAIN_DISPATCHER)
|
@set_ev_cls(event.SendMsgRequest, MAIN_DISPATCHER)
|
||||||
def _handle_send_msg(self, req):
|
def _handle_send_msg(self, req):
|
||||||
msg = req.msg
|
msg = req.msg
|
||||||
datapath = msg.datapath
|
datapath = msg.datapath
|
||||||
parser = datapath.ofproto_parser
|
|
||||||
is_barrier = isinstance(msg, parser.OFPBarrierRequest)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
si = self._switches[datapath.id]
|
si = self._switches[datapath.id]
|
||||||
@ -144,40 +121,28 @@ class OfctlService(app_manager.RyuApp):
|
|||||||
self.reply_to_request(req, rep)
|
self.reply_to_request(req, rep)
|
||||||
return
|
return
|
||||||
|
|
||||||
def _store_xid(xid, barrier_xid):
|
if req.reply_cls is not None:
|
||||||
assert xid not in si.results
|
self._observe_msg(req.reply_cls)
|
||||||
assert xid not in si.xids
|
|
||||||
assert barrier_xid not in si.barriers
|
|
||||||
si.results[xid] = []
|
|
||||||
si.xids[xid] = req
|
|
||||||
si.barriers[barrier_xid] = xid
|
|
||||||
|
|
||||||
if is_barrier:
|
datapath.set_xid(msg)
|
||||||
barrier = msg
|
xid = msg.xid
|
||||||
datapath.set_xid(barrier)
|
barrier = datapath.ofproto_parser.OFPBarrierRequest(datapath)
|
||||||
_store_xid(barrier.xid, barrier.xid)
|
datapath.set_xid(barrier)
|
||||||
else:
|
barrier_xid = barrier.xid
|
||||||
if req.reply_cls is not None:
|
assert xid not in si.results
|
||||||
self._observe_msg(req.reply_cls)
|
assert xid not in si.xids
|
||||||
datapath.set_xid(msg)
|
assert barrier_xid not in si.barriers
|
||||||
barrier = datapath.ofproto_parser.OFPBarrierRequest(datapath)
|
si.results[xid] = []
|
||||||
datapath.set_xid(barrier)
|
si.xids[xid] = req
|
||||||
_store_xid(msg.xid, barrier.xid)
|
si.barriers[barrier_xid] = xid
|
||||||
if not datapath.send_msg(msg):
|
|
||||||
return self._cancel(
|
|
||||||
si, barrier.xid,
|
|
||||||
exception.InvalidDatapath(result=datapath.id))
|
|
||||||
|
|
||||||
if not datapath.send_msg(barrier):
|
datapath.send_msg(msg)
|
||||||
return self._cancel(
|
datapath.send_msg(barrier)
|
||||||
si, barrier.xid,
|
|
||||||
exception.InvalidDatapath(result=datapath.id))
|
|
||||||
|
|
||||||
@set_ev_cls(ofp_event.EventOFPBarrierReply, MAIN_DISPATCHER)
|
@set_ev_cls(ofp_event.EventOFPBarrierReply, MAIN_DISPATCHER)
|
||||||
def _handle_barrier(self, ev):
|
def _handle_barrier(self, ev):
|
||||||
msg = ev.msg
|
msg = ev.msg
|
||||||
datapath = msg.datapath
|
datapath = msg.datapath
|
||||||
parser = datapath.ofproto_parser
|
|
||||||
try:
|
try:
|
||||||
si = self._switches[datapath.id]
|
si = self._switches[datapath.id]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@ -190,12 +155,9 @@ class OfctlService(app_manager.RyuApp):
|
|||||||
return
|
return
|
||||||
result = si.results.pop(xid)
|
result = si.results.pop(xid)
|
||||||
req = si.xids.pop(xid)
|
req = si.xids.pop(xid)
|
||||||
is_barrier = isinstance(req.msg, parser.OFPBarrierRequest)
|
if req.reply_cls is not None:
|
||||||
if req.reply_cls is not None and not is_barrier:
|
|
||||||
self._unobserve_msg(req.reply_cls)
|
self._unobserve_msg(req.reply_cls)
|
||||||
if is_barrier and req.reply_cls == parser.OFPBarrierReply:
|
if any(self._is_error(r) for r in result):
|
||||||
rep = event.Reply(result=ev.msg)
|
|
||||||
elif any(self._is_error(r) for r in result):
|
|
||||||
rep = event.Reply(exception=exception.OFError(result=result))
|
rep = event.Reply(exception=exception.OFError(result=result))
|
||||||
elif req.reply_multi:
|
elif req.reply_multi:
|
||||||
rep = event.Reply(result=result)
|
rep = event.Reply(result=result)
|
||||||
@ -223,7 +185,7 @@ class OfctlService(app_manager.RyuApp):
|
|||||||
self.logger.error('unknown error xid %s', msg.xid)
|
self.logger.error('unknown error xid %s', msg.xid)
|
||||||
return
|
return
|
||||||
if ((not isinstance(ev, ofp_event.EventOFPErrorMsg)) and
|
if ((not isinstance(ev, ofp_event.EventOFPErrorMsg)) and
|
||||||
(req.reply_cls is None or not isinstance(ev.msg, req.reply_cls))):
|
(req.reply_cls is None or not isinstance(ev.msg, req.reply_cls))):
|
||||||
self.logger.error('unexpected reply %s for xid %s', ev, msg.xid)
|
self.logger.error('unexpected reply %s for xid %s', ev, msg.xid)
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -21,12 +21,12 @@ This module provides a set of REST API for switch configuration.
|
|||||||
Used by OpenStack Ryu agent.
|
Used by OpenStack Ryu agent.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
|
||||||
|
|
||||||
from six.moves import http_client
|
from six.moves import http_client
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
from webob import Response
|
||||||
|
|
||||||
from ryu.app.wsgi import ControllerBase
|
from ryu.app.wsgi import ControllerBase
|
||||||
from ryu.app.wsgi import Response
|
|
||||||
from ryu.base import app_manager
|
from ryu.base import app_manager
|
||||||
from ryu.controller import conf_switch
|
from ryu.controller import conf_switch
|
||||||
from ryu.lib import dpid as dpid_lib
|
from ryu.lib import dpid as dpid_lib
|
||||||
@ -111,11 +111,7 @@ class ConfSwitchController(ControllerBase):
|
|||||||
|
|
||||||
def set_key(self, req, dpid, key, **_kwargs):
|
def set_key(self, req, dpid, key, **_kwargs):
|
||||||
def _set_val(dpid, key):
|
def _set_val(dpid, key):
|
||||||
try:
|
val = json.loads(req.body)
|
||||||
val = req.json if req.body else {}
|
|
||||||
except ValueError:
|
|
||||||
return Response(status=http_client.BAD_REQUEST,
|
|
||||||
body='invalid syntax %s' % req.body)
|
|
||||||
self.conf_switch.set_key(dpid, key, val)
|
self.conf_switch.set_key(dpid, key, val)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|||||||
@ -17,8 +17,9 @@
|
|||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from webob import Response
|
||||||
|
|
||||||
from ryu.app.wsgi import ControllerBase
|
from ryu.app.wsgi import ControllerBase
|
||||||
from ryu.app.wsgi import Response
|
|
||||||
from ryu.app.wsgi import WSGIApplication
|
from ryu.app.wsgi import WSGIApplication
|
||||||
from ryu.base import app_manager
|
from ryu.base import app_manager
|
||||||
from ryu.controller import ofp_event
|
from ryu.controller import ofp_event
|
||||||
@ -491,8 +492,8 @@ class FirewallController(ControllerBase):
|
|||||||
|
|
||||||
def _set_rule(self, req, switchid, vlan_id=VLANID_NONE):
|
def _set_rule(self, req, switchid, vlan_id=VLANID_NONE):
|
||||||
try:
|
try:
|
||||||
rule = req.json if req.body else {}
|
rule = json.loads(req.body)
|
||||||
except ValueError:
|
except SyntaxError:
|
||||||
FirewallController._LOGGER.debug('invalid syntax %s', req.body)
|
FirewallController._LOGGER.debug('invalid syntax %s', req.body)
|
||||||
return Response(status=400)
|
return Response(status=400)
|
||||||
|
|
||||||
@ -515,8 +516,8 @@ class FirewallController(ControllerBase):
|
|||||||
|
|
||||||
def _delete_rule(self, req, switchid, vlan_id=VLANID_NONE):
|
def _delete_rule(self, req, switchid, vlan_id=VLANID_NONE):
|
||||||
try:
|
try:
|
||||||
ruleid = req.json if req.body else {}
|
ruleid = json.loads(req.body)
|
||||||
except ValueError:
|
except SyntaxError:
|
||||||
FirewallController._LOGGER.debug('invalid syntax %s', req.body)
|
FirewallController._LOGGER.debug('invalid syntax %s', req.body)
|
||||||
return Response(status=400)
|
return Response(status=400)
|
||||||
|
|
||||||
@ -678,7 +679,8 @@ class Firewall(object):
|
|||||||
|
|
||||||
def _set_log_status(self, is_enable, waiters):
|
def _set_log_status(self, is_enable, waiters):
|
||||||
if is_enable:
|
if is_enable:
|
||||||
actions = Action.to_openflow({REST_ACTION: REST_ACTION_PACKETIN})
|
actions = Action.to_openflow(self.dp,
|
||||||
|
{REST_ACTION: REST_ACTION_PACKETIN})
|
||||||
details = 'Log collection started.'
|
details = 'Log collection started.'
|
||||||
else:
|
else:
|
||||||
actions = []
|
actions = []
|
||||||
@ -720,7 +722,7 @@ class Firewall(object):
|
|||||||
priority = ARP_FLOW_PRIORITY
|
priority = ARP_FLOW_PRIORITY
|
||||||
match = {REST_DL_TYPE: ether.ETH_TYPE_ARP}
|
match = {REST_DL_TYPE: ether.ETH_TYPE_ARP}
|
||||||
action = {REST_ACTION: REST_ACTION_ALLOW}
|
action = {REST_ACTION: REST_ACTION_ALLOW}
|
||||||
actions = Action.to_openflow(action)
|
actions = Action.to_openflow(self.dp, action)
|
||||||
flow = self._to_of_flow(cookie=cookie, priority=priority,
|
flow = self._to_of_flow(cookie=cookie, priority=priority,
|
||||||
match=match, actions=actions)
|
match=match, actions=actions)
|
||||||
|
|
||||||
@ -752,7 +754,7 @@ class Firewall(object):
|
|||||||
result = self.get_log_status(waiters)
|
result = self.get_log_status(waiters)
|
||||||
if result[REST_LOG_STATUS] == REST_STATUS_ENABLE:
|
if result[REST_LOG_STATUS] == REST_STATUS_ENABLE:
|
||||||
rest[REST_ACTION] = REST_ACTION_PACKETIN
|
rest[REST_ACTION] = REST_ACTION_PACKETIN
|
||||||
actions = Action.to_openflow(rest)
|
actions = Action.to_openflow(self.dp, rest)
|
||||||
flow = self._to_of_flow(cookie=cookie, priority=priority,
|
flow = self._to_of_flow(cookie=cookie, priority=priority,
|
||||||
match=match, actions=actions)
|
match=match, actions=actions)
|
||||||
|
|
||||||
@ -879,7 +881,7 @@ class Firewall(object):
|
|||||||
rule = {REST_RULE_ID: ruleid}
|
rule = {REST_RULE_ID: ruleid}
|
||||||
rule.update({REST_PRIORITY: flow[REST_PRIORITY]})
|
rule.update({REST_PRIORITY: flow[REST_PRIORITY]})
|
||||||
rule.update(Match.to_rest(flow))
|
rule.update(Match.to_rest(flow))
|
||||||
rule.update(Action.to_rest(flow))
|
rule.update(Action.to_rest(self.dp, flow))
|
||||||
return rule
|
return rule
|
||||||
|
|
||||||
|
|
||||||
@ -1077,17 +1079,19 @@ class Match(object):
|
|||||||
class Action(object):
|
class Action(object):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def to_openflow(rest):
|
def to_openflow(dp, rest):
|
||||||
value = rest.get(REST_ACTION, REST_ACTION_ALLOW)
|
value = rest.get(REST_ACTION, REST_ACTION_ALLOW)
|
||||||
|
|
||||||
if value == REST_ACTION_ALLOW:
|
if value == REST_ACTION_ALLOW:
|
||||||
|
out_port = dp.ofproto.OFPP_NORMAL
|
||||||
action = [{'type': 'OUTPUT',
|
action = [{'type': 'OUTPUT',
|
||||||
'port': 'NORMAL'}]
|
'port': out_port}]
|
||||||
elif value == REST_ACTION_DENY:
|
elif value == REST_ACTION_DENY:
|
||||||
action = []
|
action = []
|
||||||
elif value == REST_ACTION_PACKETIN:
|
elif value == REST_ACTION_PACKETIN:
|
||||||
|
out_port = dp.ofproto.OFPP_CONTROLLER
|
||||||
action = [{'type': 'OUTPUT',
|
action = [{'type': 'OUTPUT',
|
||||||
'port': 'CONTROLLER',
|
'port': out_port,
|
||||||
'max_len': 128}]
|
'max_len': 128}]
|
||||||
else:
|
else:
|
||||||
raise ValueError('Invalid action type.')
|
raise ValueError('Invalid action type.')
|
||||||
@ -1095,9 +1099,9 @@ class Action(object):
|
|||||||
return action
|
return action
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def to_rest(openflow):
|
def to_rest(dp, openflow):
|
||||||
if REST_ACTION in openflow:
|
if REST_ACTION in openflow:
|
||||||
action_allow = 'OUTPUT:NORMAL'
|
action_allow = 'OUTPUT:%d' % dp.ofproto.OFPP_NORMAL
|
||||||
if openflow[REST_ACTION] == [action_allow]:
|
if openflow[REST_ACTION] == [action_allow]:
|
||||||
action = {REST_ACTION: REST_ACTION_ALLOW}
|
action = {REST_ACTION: REST_ACTION_ALLOW}
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -18,11 +18,10 @@ import logging
|
|||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from webob import Response
|
||||||
|
|
||||||
from ryu.app import conf_switch_key as cs_key
|
from ryu.app import conf_switch_key as cs_key
|
||||||
from ryu.app.wsgi import ControllerBase
|
from ryu.app.wsgi import ControllerBase, WSGIApplication, route
|
||||||
from ryu.app.wsgi import Response
|
|
||||||
from ryu.app.wsgi import route
|
|
||||||
from ryu.app.wsgi import WSGIApplication
|
|
||||||
from ryu.base import app_manager
|
from ryu.base import app_manager
|
||||||
from ryu.controller import conf_switch
|
from ryu.controller import conf_switch
|
||||||
from ryu.controller import ofp_event
|
from ryu.controller import ofp_event
|
||||||
@ -425,8 +424,7 @@ class QoSController(ControllerBase):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def delete_ovsdb_addr(dpid):
|
def delete_ovsdb_addr(dpid):
|
||||||
ofs = QoSController._OFS_LIST.get(dpid, None)
|
ofs = QoSController._OFS_LIST.get(dpid, None)
|
||||||
if ofs is not None:
|
ofs.set_ovsdb_addr(dpid, None)
|
||||||
ofs.set_ovsdb_addr(dpid, None)
|
|
||||||
|
|
||||||
@route('qos_switch', BASE_URL + '/queue/{switchid}',
|
@route('qos_switch', BASE_URL + '/queue/{switchid}',
|
||||||
methods=['GET'], requirements=REQUIREMENTS)
|
methods=['GET'], requirements=REQUIREMENTS)
|
||||||
@ -508,8 +506,8 @@ class QoSController(ControllerBase):
|
|||||||
|
|
||||||
def _access_switch(self, req, switchid, vlan_id, func, waiters):
|
def _access_switch(self, req, switchid, vlan_id, func, waiters):
|
||||||
try:
|
try:
|
||||||
rest = req.json if req.body else {}
|
rest = json.loads(req.body) if req.body else {}
|
||||||
except ValueError:
|
except SyntaxError:
|
||||||
QoSController._LOGGER.debug('invalid syntax %s', req.body)
|
QoSController._LOGGER.debug('invalid syntax %s', req.body)
|
||||||
return Response(status=400)
|
return Response(status=400)
|
||||||
|
|
||||||
@ -558,22 +556,6 @@ class QoS(object):
|
|||||||
self.vlan_list[VLANID_NONE] = 0 # for VLAN=None
|
self.vlan_list[VLANID_NONE] = 0 # for VLAN=None
|
||||||
self.dp = dp
|
self.dp = dp
|
||||||
self.version = dp.ofproto.OFP_VERSION
|
self.version = dp.ofproto.OFP_VERSION
|
||||||
# Dictionary of port name to Queue config.
|
|
||||||
# e.g.)
|
|
||||||
# self.queue_list = {
|
|
||||||
# "s1-eth1": {
|
|
||||||
# "0": {
|
|
||||||
# "config": {
|
|
||||||
# "max-rate": "600000"
|
|
||||||
# }
|
|
||||||
# },
|
|
||||||
# "1": {
|
|
||||||
# "config": {
|
|
||||||
# "min-rate": "900000"
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
self.queue_list = {}
|
self.queue_list = {}
|
||||||
self.CONF = CONF
|
self.CONF = CONF
|
||||||
self.ovsdb_addr = None
|
self.ovsdb_addr = None
|
||||||
@ -601,22 +583,25 @@ class QoS(object):
|
|||||||
self.ofctl.mod_flow_entry(self.dp, flow, cmd)
|
self.ofctl.mod_flow_entry(self.dp, flow, cmd)
|
||||||
|
|
||||||
def set_ovsdb_addr(self, dpid, ovsdb_addr):
|
def set_ovsdb_addr(self, dpid, ovsdb_addr):
|
||||||
|
# easy check if the address format valid
|
||||||
|
_proto, _host, _port = ovsdb_addr.split(':')
|
||||||
|
|
||||||
old_address = self.ovsdb_addr
|
old_address = self.ovsdb_addr
|
||||||
if old_address == ovsdb_addr:
|
if old_address == ovsdb_addr:
|
||||||
return
|
return
|
||||||
elif ovsdb_addr is None:
|
if ovsdb_addr is None:
|
||||||
# Determine deleting OVSDB address was requested.
|
|
||||||
if self.ovs_bridge:
|
if self.ovs_bridge:
|
||||||
|
self.ovs_bridge.del_controller()
|
||||||
self.ovs_bridge = None
|
self.ovs_bridge = None
|
||||||
return
|
return
|
||||||
|
|
||||||
ovs_bridge = bridge.OVSBridge(self.CONF, dpid, ovsdb_addr)
|
|
||||||
try:
|
|
||||||
ovs_bridge.init()
|
|
||||||
except:
|
|
||||||
raise ValueError('ovsdb addr is not available.')
|
|
||||||
self.ovsdb_addr = ovsdb_addr
|
self.ovsdb_addr = ovsdb_addr
|
||||||
self.ovs_bridge = ovs_bridge
|
if self.ovs_bridge is None:
|
||||||
|
ovs_bridge = bridge.OVSBridge(self.CONF, dpid, ovsdb_addr)
|
||||||
|
self.ovs_bridge = ovs_bridge
|
||||||
|
try:
|
||||||
|
ovs_bridge.init()
|
||||||
|
except:
|
||||||
|
raise ValueError('ovsdb addr is not available.')
|
||||||
|
|
||||||
def _update_vlan_list(self, vlan_list):
|
def _update_vlan_list(self, vlan_list):
|
||||||
for vlan_id in self.vlan_list.keys():
|
for vlan_id in self.vlan_list.keys():
|
||||||
@ -679,15 +664,7 @@ class QoS(object):
|
|||||||
'details': 'ovs_bridge is not exists'}
|
'details': 'ovs_bridge is not exists'}
|
||||||
return REST_COMMAND_RESULT, msg
|
return REST_COMMAND_RESULT, msg
|
||||||
|
|
||||||
port_name = rest.get(REST_PORT_NAME, None)
|
self.queue_list.clear()
|
||||||
vif_ports = self.ovs_bridge.get_port_name_list()
|
|
||||||
|
|
||||||
if port_name is not None:
|
|
||||||
if port_name not in vif_ports:
|
|
||||||
raise ValueError('%s port is not exists' % port_name)
|
|
||||||
vif_ports = [port_name]
|
|
||||||
|
|
||||||
queue_list = {}
|
|
||||||
queue_type = rest.get(REST_QUEUE_TYPE, 'linux-htb')
|
queue_type = rest.get(REST_QUEUE_TYPE, 'linux-htb')
|
||||||
parent_max_rate = rest.get(REST_QUEUE_MAX_RATE, None)
|
parent_max_rate = rest.get(REST_QUEUE_MAX_RATE, None)
|
||||||
queues = rest.get(REST_QUEUES, [])
|
queues = rest.get(REST_QUEUES, [])
|
||||||
@ -705,9 +682,17 @@ class QoS(object):
|
|||||||
config['min-rate'] = min_rate
|
config['min-rate'] = min_rate
|
||||||
if len(config):
|
if len(config):
|
||||||
queue_config.append(config)
|
queue_config.append(config)
|
||||||
queue_list[queue_id] = {'config': config}
|
self.queue_list[queue_id] = {'config': config}
|
||||||
queue_id += 1
|
queue_id += 1
|
||||||
|
|
||||||
|
port_name = rest.get(REST_PORT_NAME, None)
|
||||||
|
vif_ports = self.ovs_bridge.get_port_name_list()
|
||||||
|
|
||||||
|
if port_name is not None:
|
||||||
|
if port_name not in vif_ports:
|
||||||
|
raise ValueError('%s port is not exists' % port_name)
|
||||||
|
vif_ports = [port_name]
|
||||||
|
|
||||||
for port_name in vif_ports:
|
for port_name in vif_ports:
|
||||||
try:
|
try:
|
||||||
self.ovs_bridge.set_qos(port_name, type=queue_type,
|
self.ovs_bridge.set_qos(port_name, type=queue_type,
|
||||||
@ -715,10 +700,9 @@ class QoS(object):
|
|||||||
queues=queue_config)
|
queues=queue_config)
|
||||||
except Exception as msg:
|
except Exception as msg:
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
self.queue_list[port_name] = queue_list
|
|
||||||
|
|
||||||
msg = {'result': 'success',
|
msg = {'result': 'success',
|
||||||
'details': queue_list}
|
'details': self.queue_list}
|
||||||
|
|
||||||
return REST_COMMAND_RESULT, msg
|
return REST_COMMAND_RESULT, msg
|
||||||
|
|
||||||
@ -733,9 +717,9 @@ class QoS(object):
|
|||||||
|
|
||||||
@rest_command
|
@rest_command
|
||||||
def delete_queue(self, rest, vlan_id):
|
def delete_queue(self, rest, vlan_id):
|
||||||
|
self.queue_list.clear()
|
||||||
if self._delete_queue():
|
if self._delete_queue():
|
||||||
msg = 'success'
|
msg = 'success'
|
||||||
self.queue_list.clear()
|
|
||||||
else:
|
else:
|
||||||
msg = 'failure'
|
msg = 'failure'
|
||||||
|
|
||||||
@ -1143,20 +1127,20 @@ class Match(object):
|
|||||||
class Action(object):
|
class Action(object):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def to_rest(flow):
|
def to_rest(openflow):
|
||||||
if REST_ACTION in flow:
|
if REST_ACTION in openflow:
|
||||||
actions = []
|
actions = []
|
||||||
for act in flow[REST_ACTION]:
|
for action in openflow[REST_ACTION]:
|
||||||
field_value = re.search(r'SET_FIELD: \{ip_dscp:(\d+)', act)
|
field_value = re.search('SET_FIELD: {ip_dscp:(\d+)', action)
|
||||||
if field_value:
|
if field_value:
|
||||||
actions.append({REST_ACTION_MARK: field_value.group(1)})
|
actions.append({REST_ACTION_MARK: field_value.group(1)})
|
||||||
meter_value = re.search(r'METER:(\d+)', act)
|
meter_value = re.search('METER:(\d+)', action)
|
||||||
if meter_value:
|
if meter_value:
|
||||||
actions.append({REST_ACTION_METER: meter_value.group(1)})
|
actions.append({REST_ACTION_METER: meter_value.group(1)})
|
||||||
queue_value = re.search(r'SET_QUEUE:(\d+)', act)
|
queue_value = re.search('SET_QUEUE:(\d+)', action)
|
||||||
if queue_value:
|
if queue_value:
|
||||||
actions.append({REST_ACTION_QUEUE: queue_value.group(1)})
|
actions.append({REST_ACTION_QUEUE: queue_value.group(1)})
|
||||||
action = {REST_ACTION: actions}
|
action = {REST_ACTION: actions}
|
||||||
else:
|
else:
|
||||||
action = {REST_ACTION: 'Unknown action type.'}
|
action = {REST_ACTION: 'Unknown action type.'}
|
||||||
|
|
||||||
|
|||||||
@ -20,9 +20,9 @@ import socket
|
|||||||
import struct
|
import struct
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
from webob import Response
|
||||||
|
|
||||||
from ryu.app.wsgi import ControllerBase
|
from ryu.app.wsgi import ControllerBase
|
||||||
from ryu.app.wsgi import Response
|
|
||||||
from ryu.app.wsgi import WSGIApplication
|
from ryu.app.wsgi import WSGIApplication
|
||||||
from ryu.base import app_manager
|
from ryu.base import app_manager
|
||||||
from ryu.controller import dpset
|
from ryu.controller import dpset
|
||||||
@ -40,7 +40,6 @@ from ryu.lib.packet import ethernet
|
|||||||
from ryu.lib.packet import icmp
|
from ryu.lib.packet import icmp
|
||||||
from ryu.lib.packet import ipv4
|
from ryu.lib.packet import ipv4
|
||||||
from ryu.lib.packet import packet
|
from ryu.lib.packet import packet
|
||||||
from ryu.lib.packet import packet_base
|
|
||||||
from ryu.lib.packet import tcp
|
from ryu.lib.packet import tcp
|
||||||
from ryu.lib.packet import udp
|
from ryu.lib.packet import udp
|
||||||
from ryu.lib.packet import vlan
|
from ryu.lib.packet import vlan
|
||||||
@ -377,45 +376,42 @@ class RouterController(ControllerBase):
|
|||||||
@rest_command
|
@rest_command
|
||||||
def get_data(self, req, switch_id, **_kwargs):
|
def get_data(self, req, switch_id, **_kwargs):
|
||||||
return self._access_router(switch_id, VLANID_NONE,
|
return self._access_router(switch_id, VLANID_NONE,
|
||||||
'get_data', req)
|
'get_data', req.body)
|
||||||
|
|
||||||
# GET /router/{switch_id}/{vlan_id}
|
# GET /router/{switch_id}/{vlan_id}
|
||||||
@rest_command
|
@rest_command
|
||||||
def get_vlan_data(self, req, switch_id, vlan_id, **_kwargs):
|
def get_vlan_data(self, req, switch_id, vlan_id, **_kwargs):
|
||||||
return self._access_router(switch_id, vlan_id,
|
return self._access_router(switch_id, vlan_id,
|
||||||
'get_data', req)
|
'get_data', req.body)
|
||||||
|
|
||||||
# POST /router/{switch_id}
|
# POST /router/{switch_id}
|
||||||
@rest_command
|
@rest_command
|
||||||
def set_data(self, req, switch_id, **_kwargs):
|
def set_data(self, req, switch_id, **_kwargs):
|
||||||
return self._access_router(switch_id, VLANID_NONE,
|
return self._access_router(switch_id, VLANID_NONE,
|
||||||
'set_data', req)
|
'set_data', req.body)
|
||||||
|
|
||||||
# POST /router/{switch_id}/{vlan_id}
|
# POST /router/{switch_id}/{vlan_id}
|
||||||
@rest_command
|
@rest_command
|
||||||
def set_vlan_data(self, req, switch_id, vlan_id, **_kwargs):
|
def set_vlan_data(self, req, switch_id, vlan_id, **_kwargs):
|
||||||
return self._access_router(switch_id, vlan_id,
|
return self._access_router(switch_id, vlan_id,
|
||||||
'set_data', req)
|
'set_data', req.body)
|
||||||
|
|
||||||
# DELETE /router/{switch_id}
|
# DELETE /router/{switch_id}
|
||||||
@rest_command
|
@rest_command
|
||||||
def delete_data(self, req, switch_id, **_kwargs):
|
def delete_data(self, req, switch_id, **_kwargs):
|
||||||
return self._access_router(switch_id, VLANID_NONE,
|
return self._access_router(switch_id, VLANID_NONE,
|
||||||
'delete_data', req)
|
'delete_data', req.body)
|
||||||
|
|
||||||
# DELETE /router/{switch_id}/{vlan_id}
|
# DELETE /router/{switch_id}/{vlan_id}
|
||||||
@rest_command
|
@rest_command
|
||||||
def delete_vlan_data(self, req, switch_id, vlan_id, **_kwargs):
|
def delete_vlan_data(self, req, switch_id, vlan_id, **_kwargs):
|
||||||
return self._access_router(switch_id, vlan_id,
|
return self._access_router(switch_id, vlan_id,
|
||||||
'delete_data', req)
|
'delete_data', req.body)
|
||||||
|
|
||||||
def _access_router(self, switch_id, vlan_id, func, req):
|
def _access_router(self, switch_id, vlan_id, func, rest_param):
|
||||||
rest_message = []
|
rest_message = []
|
||||||
routers = self._get_router(switch_id)
|
routers = self._get_router(switch_id)
|
||||||
try:
|
param = json.loads(rest_param) if rest_param else {}
|
||||||
param = req.json if req.body else {}
|
|
||||||
except ValueError:
|
|
||||||
raise SyntaxError('invalid syntax %s', req.body)
|
|
||||||
for router in routers.values():
|
for router in routers.values():
|
||||||
function = getattr(router, func)
|
function = getattr(router, func)
|
||||||
data = function(vlan_id, param, self.waiters)
|
data = function(vlan_id, param, self.waiters)
|
||||||
@ -570,8 +566,7 @@ class Router(dict):
|
|||||||
# TODO: Packet library convert to string
|
# TODO: Packet library convert to string
|
||||||
# self.logger.debug('Packet in = %s', str(pkt), self.sw_id)
|
# self.logger.debug('Packet in = %s', str(pkt), self.sw_id)
|
||||||
header_list = dict((p.protocol_name, p)
|
header_list = dict((p.protocol_name, p)
|
||||||
for p in pkt.protocols
|
for p in pkt.protocols if type(p) != str)
|
||||||
if isinstance(p, packet_base.PacketBase))
|
|
||||||
if header_list:
|
if header_list:
|
||||||
# Check vlan-tag
|
# Check vlan-tag
|
||||||
vlan_id = VLANID_NONE
|
vlan_id = VLANID_NONE
|
||||||
@ -1010,14 +1005,14 @@ class VlanRouter(object):
|
|||||||
else:
|
else:
|
||||||
if header_list[ARP].opcode == arp.ARP_REQUEST:
|
if header_list[ARP].opcode == arp.ARP_REQUEST:
|
||||||
# ARP request to router port -> send ARP reply
|
# ARP request to router port -> send ARP reply
|
||||||
src_mac = self.port_data[in_port].mac
|
src_mac = header_list[ARP].src_mac
|
||||||
dst_mac = header_list[ARP].src_mac
|
dst_mac = self.port_data[in_port].mac
|
||||||
arp_target_mac = dst_mac
|
arp_target_mac = dst_mac
|
||||||
output = in_port
|
output = in_port
|
||||||
in_port = self.ofctl.dp.ofproto.OFPP_CONTROLLER
|
in_port = self.ofctl.dp.ofproto.OFPP_CONTROLLER
|
||||||
|
|
||||||
self.ofctl.send_arp(arp.ARP_REPLY, self.vlan_id,
|
self.ofctl.send_arp(arp.ARP_REPLY, self.vlan_id,
|
||||||
src_mac, dst_mac, dst_ip, src_ip,
|
dst_mac, src_mac, dst_ip, src_ip,
|
||||||
arp_target_mac, in_port, output)
|
arp_target_mac, in_port, output)
|
||||||
|
|
||||||
log_msg = 'Receive ARP request from [%s] to router port [%s].'
|
log_msg = 'Receive ARP request from [%s] to router port [%s].'
|
||||||
|
|||||||
@ -14,11 +14,9 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
from webob import Response
|
||||||
|
|
||||||
from ryu.app.wsgi import ControllerBase
|
from ryu.app.wsgi import ControllerBase, WSGIApplication, route
|
||||||
from ryu.app.wsgi import Response
|
|
||||||
from ryu.app.wsgi import route
|
|
||||||
from ryu.app.wsgi import WSGIApplication
|
|
||||||
from ryu.base import app_manager
|
from ryu.base import app_manager
|
||||||
from ryu.lib import dpid as dpid_lib
|
from ryu.lib import dpid as dpid_lib
|
||||||
from ryu.topology.api import get_switch, get_link, get_host
|
from ryu.topology.api import get_switch, get_link, get_host
|
||||||
|
|||||||
1842
ryu/app/rest_vtep.py
1842
ryu/app/rest_vtep.py
File diff suppressed because it is too large
Load Diff
@ -1,95 +0,0 @@
|
|||||||
# Copyright (C) 2016 Nippon Telegraph and Telephone Corporation.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
# implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
from operator import attrgetter
|
|
||||||
|
|
||||||
from ryu.app import simple_switch_13
|
|
||||||
from ryu.controller import ofp_event
|
|
||||||
from ryu.controller.handler import MAIN_DISPATCHER, DEAD_DISPATCHER
|
|
||||||
from ryu.controller.handler import set_ev_cls
|
|
||||||
from ryu.lib import hub
|
|
||||||
|
|
||||||
|
|
||||||
class SimpleMonitor13(simple_switch_13.SimpleSwitch13):
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(SimpleMonitor13, self).__init__(*args, **kwargs)
|
|
||||||
self.datapaths = {}
|
|
||||||
self.monitor_thread = hub.spawn(self._monitor)
|
|
||||||
|
|
||||||
@set_ev_cls(ofp_event.EventOFPStateChange,
|
|
||||||
[MAIN_DISPATCHER, DEAD_DISPATCHER])
|
|
||||||
def _state_change_handler(self, ev):
|
|
||||||
datapath = ev.datapath
|
|
||||||
if ev.state == MAIN_DISPATCHER:
|
|
||||||
if datapath.id not in self.datapaths:
|
|
||||||
self.logger.debug('register datapath: %016x', datapath.id)
|
|
||||||
self.datapaths[datapath.id] = datapath
|
|
||||||
elif ev.state == DEAD_DISPATCHER:
|
|
||||||
if datapath.id in self.datapaths:
|
|
||||||
self.logger.debug('unregister datapath: %016x', datapath.id)
|
|
||||||
del self.datapaths[datapath.id]
|
|
||||||
|
|
||||||
def _monitor(self):
|
|
||||||
while True:
|
|
||||||
for dp in self.datapaths.values():
|
|
||||||
self._request_stats(dp)
|
|
||||||
hub.sleep(10)
|
|
||||||
|
|
||||||
def _request_stats(self, datapath):
|
|
||||||
self.logger.debug('send stats request: %016x', datapath.id)
|
|
||||||
ofproto = datapath.ofproto
|
|
||||||
parser = datapath.ofproto_parser
|
|
||||||
|
|
||||||
req = parser.OFPFlowStatsRequest(datapath)
|
|
||||||
datapath.send_msg(req)
|
|
||||||
|
|
||||||
req = parser.OFPPortStatsRequest(datapath, 0, ofproto.OFPP_ANY)
|
|
||||||
datapath.send_msg(req)
|
|
||||||
|
|
||||||
@set_ev_cls(ofp_event.EventOFPFlowStatsReply, MAIN_DISPATCHER)
|
|
||||||
def _flow_stats_reply_handler(self, ev):
|
|
||||||
body = ev.msg.body
|
|
||||||
|
|
||||||
self.logger.info('datapath '
|
|
||||||
'in-port eth-dst '
|
|
||||||
'out-port packets bytes')
|
|
||||||
self.logger.info('---------------- '
|
|
||||||
'-------- ----------------- '
|
|
||||||
'-------- -------- --------')
|
|
||||||
for stat in sorted([flow for flow in body if flow.priority == 1],
|
|
||||||
key=lambda flow: (flow.match['in_port'],
|
|
||||||
flow.match['eth_dst'])):
|
|
||||||
self.logger.info('%016x %8x %17s %8x %8d %8d',
|
|
||||||
ev.msg.datapath.id,
|
|
||||||
stat.match['in_port'], stat.match['eth_dst'],
|
|
||||||
stat.instructions[0].actions[0].port,
|
|
||||||
stat.packet_count, stat.byte_count)
|
|
||||||
|
|
||||||
@set_ev_cls(ofp_event.EventOFPPortStatsReply, MAIN_DISPATCHER)
|
|
||||||
def _port_stats_reply_handler(self, ev):
|
|
||||||
body = ev.msg.body
|
|
||||||
|
|
||||||
self.logger.info('datapath port '
|
|
||||||
'rx-pkts rx-bytes rx-error '
|
|
||||||
'tx-pkts tx-bytes tx-error')
|
|
||||||
self.logger.info('---------------- -------- '
|
|
||||||
'-------- -------- -------- '
|
|
||||||
'-------- -------- --------')
|
|
||||||
for stat in sorted(body, key=attrgetter('port_no')):
|
|
||||||
self.logger.info('%016x %8x %8d %8d %8d %8d %8d %8d',
|
|
||||||
ev.msg.datapath.id, stat.port_no,
|
|
||||||
stat.rx_packets, stat.rx_bytes, stat.rx_errors,
|
|
||||||
stat.tx_packets, stat.tx_bytes, stat.tx_errors)
|
|
||||||
@ -36,12 +36,11 @@ class SimpleSwitch(app_manager.RyuApp):
|
|||||||
super(SimpleSwitch, self).__init__(*args, **kwargs)
|
super(SimpleSwitch, self).__init__(*args, **kwargs)
|
||||||
self.mac_to_port = {}
|
self.mac_to_port = {}
|
||||||
|
|
||||||
def add_flow(self, datapath, in_port, dst, src, actions):
|
def add_flow(self, datapath, in_port, dst, actions):
|
||||||
ofproto = datapath.ofproto
|
ofproto = datapath.ofproto
|
||||||
|
|
||||||
match = datapath.ofproto_parser.OFPMatch(
|
match = datapath.ofproto_parser.OFPMatch(
|
||||||
in_port=in_port,
|
in_port=in_port, dl_dst=haddr_to_bin(dst))
|
||||||
dl_dst=haddr_to_bin(dst), dl_src=haddr_to_bin(src))
|
|
||||||
|
|
||||||
mod = datapath.ofproto_parser.OFPFlowMod(
|
mod = datapath.ofproto_parser.OFPFlowMod(
|
||||||
datapath=datapath, match=match, cookie=0,
|
datapath=datapath, match=match, cookie=0,
|
||||||
@ -82,7 +81,7 @@ class SimpleSwitch(app_manager.RyuApp):
|
|||||||
|
|
||||||
# install a flow to avoid packet_in next time
|
# install a flow to avoid packet_in next time
|
||||||
if out_port != ofproto.OFPP_FLOOD:
|
if out_port != ofproto.OFPP_FLOOD:
|
||||||
self.add_flow(datapath, msg.in_port, dst, src, actions)
|
self.add_flow(datapath, msg.in_port, dst, actions)
|
||||||
|
|
||||||
data = None
|
data = None
|
||||||
if msg.buffer_id == ofproto.OFP_NO_BUFFER:
|
if msg.buffer_id == ofproto.OFP_NO_BUFFER:
|
||||||
|
|||||||
@ -30,12 +30,11 @@ class SimpleSwitch12(app_manager.RyuApp):
|
|||||||
super(SimpleSwitch12, self).__init__(*args, **kwargs)
|
super(SimpleSwitch12, self).__init__(*args, **kwargs)
|
||||||
self.mac_to_port = {}
|
self.mac_to_port = {}
|
||||||
|
|
||||||
def add_flow(self, datapath, port, dst, src, actions):
|
def add_flow(self, datapath, port, dst, actions):
|
||||||
ofproto = datapath.ofproto
|
ofproto = datapath.ofproto
|
||||||
|
|
||||||
match = datapath.ofproto_parser.OFPMatch(in_port=port,
|
match = datapath.ofproto_parser.OFPMatch(in_port=port,
|
||||||
eth_dst=dst,
|
eth_dst=dst)
|
||||||
eth_src=src)
|
|
||||||
inst = [datapath.ofproto_parser.OFPInstructionActions(
|
inst = [datapath.ofproto_parser.OFPInstructionActions(
|
||||||
ofproto.OFPIT_APPLY_ACTIONS, actions)]
|
ofproto.OFPIT_APPLY_ACTIONS, actions)]
|
||||||
|
|
||||||
@ -81,7 +80,7 @@ class SimpleSwitch12(app_manager.RyuApp):
|
|||||||
|
|
||||||
# install a flow to avoid packet_in next time
|
# install a flow to avoid packet_in next time
|
||||||
if out_port != ofproto.OFPP_FLOOD:
|
if out_port != ofproto.OFPP_FLOOD:
|
||||||
self.add_flow(datapath, in_port, dst, src, actions)
|
self.add_flow(datapath, in_port, dst, actions)
|
||||||
|
|
||||||
data = None
|
data = None
|
||||||
if msg.buffer_id == ofproto.OFP_NO_BUFFER:
|
if msg.buffer_id == ofproto.OFP_NO_BUFFER:
|
||||||
|
|||||||
@ -85,7 +85,7 @@ class SimpleSwitch13(app_manager.RyuApp):
|
|||||||
dst = eth.dst
|
dst = eth.dst
|
||||||
src = eth.src
|
src = eth.src
|
||||||
|
|
||||||
dpid = format(datapath.id, "d").zfill(16)
|
dpid = datapath.id
|
||||||
self.mac_to_port.setdefault(dpid, {})
|
self.mac_to_port.setdefault(dpid, {})
|
||||||
|
|
||||||
self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)
|
self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)
|
||||||
@ -102,7 +102,7 @@ class SimpleSwitch13(app_manager.RyuApp):
|
|||||||
|
|
||||||
# install a flow to avoid packet_in next time
|
# install a flow to avoid packet_in next time
|
||||||
if out_port != ofproto.OFPP_FLOOD:
|
if out_port != ofproto.OFPP_FLOOD:
|
||||||
match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src)
|
match = parser.OFPMatch(in_port=in_port, eth_dst=dst)
|
||||||
# verify if we have a valid buffer_id, if yes avoid to send both
|
# verify if we have a valid buffer_id, if yes avoid to send both
|
||||||
# flow_mod & packet_out
|
# flow_mod & packet_out
|
||||||
if msg.buffer_id != ofproto.OFP_NO_BUFFER:
|
if msg.buffer_id != ofproto.OFP_NO_BUFFER:
|
||||||
|
|||||||
@ -93,7 +93,7 @@ class SimpleSwitch14(app_manager.RyuApp):
|
|||||||
|
|
||||||
# install a flow to avoid packet_in next time
|
# install a flow to avoid packet_in next time
|
||||||
if out_port != ofproto.OFPP_FLOOD:
|
if out_port != ofproto.OFPP_FLOOD:
|
||||||
match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src)
|
match = parser.OFPMatch(in_port=in_port, eth_dst=dst)
|
||||||
self.add_flow(datapath, 1, match, actions)
|
self.add_flow(datapath, 1, match, actions)
|
||||||
|
|
||||||
data = None
|
data = None
|
||||||
|
|||||||
@ -1,107 +0,0 @@
|
|||||||
# Copyright (C) 2011 Nippon Telegraph and Telephone Corporation.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
# implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
from ryu.base import app_manager
|
|
||||||
from ryu.controller import ofp_event
|
|
||||||
from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER
|
|
||||||
from ryu.controller.handler import set_ev_cls
|
|
||||||
from ryu.ofproto import ofproto_v1_5
|
|
||||||
from ryu.lib.packet import packet
|
|
||||||
from ryu.lib.packet import ethernet
|
|
||||||
from ryu.lib.packet import ether_types
|
|
||||||
|
|
||||||
|
|
||||||
class SimpleSwitch15(app_manager.RyuApp):
|
|
||||||
OFP_VERSIONS = [ofproto_v1_5.OFP_VERSION]
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(SimpleSwitch15, self).__init__(*args, **kwargs)
|
|
||||||
self.mac_to_port = {}
|
|
||||||
|
|
||||||
@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
|
|
||||||
def switch_features_handler(self, ev):
|
|
||||||
datapath = ev.msg.datapath
|
|
||||||
ofproto = datapath.ofproto
|
|
||||||
parser = datapath.ofproto_parser
|
|
||||||
|
|
||||||
# install table-miss flow entry
|
|
||||||
#
|
|
||||||
# We specify NO BUFFER to max_len of the output action due to
|
|
||||||
# OVS bug. At this moment, if we specify a lesser number, e.g.,
|
|
||||||
# 128, OVS will send Packet-In with invalid buffer_id and
|
|
||||||
# truncated packet data. In that case, we cannot output packets
|
|
||||||
# correctly. The bug has been fixed in OVS v2.1.0.
|
|
||||||
match = parser.OFPMatch()
|
|
||||||
actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
|
|
||||||
ofproto.OFPCML_NO_BUFFER)]
|
|
||||||
self.add_flow(datapath, 0, match, actions)
|
|
||||||
|
|
||||||
def add_flow(self, datapath, priority, match, actions):
|
|
||||||
ofproto = datapath.ofproto
|
|
||||||
parser = datapath.ofproto_parser
|
|
||||||
|
|
||||||
inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
|
|
||||||
actions)]
|
|
||||||
|
|
||||||
mod = parser.OFPFlowMod(datapath=datapath, priority=priority,
|
|
||||||
match=match, instructions=inst)
|
|
||||||
datapath.send_msg(mod)
|
|
||||||
|
|
||||||
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
|
|
||||||
def _packet_in_handler(self, ev):
|
|
||||||
msg = ev.msg
|
|
||||||
datapath = msg.datapath
|
|
||||||
ofproto = datapath.ofproto
|
|
||||||
parser = datapath.ofproto_parser
|
|
||||||
in_port = msg.match['in_port']
|
|
||||||
|
|
||||||
pkt = packet.Packet(msg.data)
|
|
||||||
eth = pkt.get_protocols(ethernet.ethernet)[0]
|
|
||||||
|
|
||||||
if eth.ethertype == ether_types.ETH_TYPE_LLDP:
|
|
||||||
# ignore lldp packet
|
|
||||||
return
|
|
||||||
dst = eth.dst
|
|
||||||
src = eth.src
|
|
||||||
|
|
||||||
dpid = datapath.id
|
|
||||||
self.mac_to_port.setdefault(dpid, {})
|
|
||||||
|
|
||||||
self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)
|
|
||||||
|
|
||||||
# learn a mac address to avoid FLOOD next time.
|
|
||||||
self.mac_to_port[dpid][src] = in_port
|
|
||||||
|
|
||||||
if dst in self.mac_to_port[dpid]:
|
|
||||||
out_port = self.mac_to_port[dpid][dst]
|
|
||||||
else:
|
|
||||||
out_port = ofproto.OFPP_FLOOD
|
|
||||||
|
|
||||||
actions = [parser.OFPActionOutput(out_port)]
|
|
||||||
|
|
||||||
# install a flow to avoid packet_in next time
|
|
||||||
if out_port != ofproto.OFPP_FLOOD:
|
|
||||||
match = parser.OFPMatch(in_port=in_port, eth_dst=dst)
|
|
||||||
self.add_flow(datapath, 1, match, actions)
|
|
||||||
|
|
||||||
data = None
|
|
||||||
if msg.buffer_id == ofproto.OFP_NO_BUFFER:
|
|
||||||
data = msg.data
|
|
||||||
|
|
||||||
match = parser.OFPMatch(in_port=in_port)
|
|
||||||
|
|
||||||
out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
|
|
||||||
match=match, actions=actions, data=data)
|
|
||||||
datapath.send_msg(out)
|
|
||||||
@ -1,92 +0,0 @@
|
|||||||
# Copyright (C) 2016 Nippon Telegraph and Telephone Corporation.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
# implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
from ryu.base import app_manager
|
|
||||||
from ryu.controller import ofp_event
|
|
||||||
from ryu.controller.handler import CONFIG_DISPATCHER
|
|
||||||
from ryu.controller.handler import MAIN_DISPATCHER
|
|
||||||
from ryu.controller.handler import set_ev_cls
|
|
||||||
from ryu.ofproto import ofproto_v1_3
|
|
||||||
from ryu.lib import igmplib
|
|
||||||
from ryu.lib.dpid import str_to_dpid
|
|
||||||
from ryu.lib.packet import packet
|
|
||||||
from ryu.lib.packet import ethernet
|
|
||||||
from ryu.app import simple_switch_13
|
|
||||||
|
|
||||||
|
|
||||||
class SimpleSwitchIgmp13(simple_switch_13.SimpleSwitch13):
|
|
||||||
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
|
|
||||||
_CONTEXTS = {'igmplib': igmplib.IgmpLib}
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(SimpleSwitchIgmp13, self).__init__(*args, **kwargs)
|
|
||||||
self.mac_to_port = {}
|
|
||||||
self._snoop = kwargs['igmplib']
|
|
||||||
self._snoop.set_querier_mode(
|
|
||||||
dpid=str_to_dpid('0000000000000001'), server_port=2)
|
|
||||||
|
|
||||||
@set_ev_cls(igmplib.EventPacketIn, MAIN_DISPATCHER)
|
|
||||||
def _packet_in_handler(self, ev):
|
|
||||||
msg = ev.msg
|
|
||||||
datapath = msg.datapath
|
|
||||||
ofproto = datapath.ofproto
|
|
||||||
parser = datapath.ofproto_parser
|
|
||||||
in_port = msg.match['in_port']
|
|
||||||
|
|
||||||
pkt = packet.Packet(msg.data)
|
|
||||||
eth = pkt.get_protocols(ethernet.ethernet)[0]
|
|
||||||
|
|
||||||
dst = eth.dst
|
|
||||||
src = eth.src
|
|
||||||
|
|
||||||
dpid = datapath.id
|
|
||||||
self.mac_to_port.setdefault(dpid, {})
|
|
||||||
|
|
||||||
self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)
|
|
||||||
|
|
||||||
# learn a mac address to avoid FLOOD next time.
|
|
||||||
self.mac_to_port[dpid][src] = in_port
|
|
||||||
|
|
||||||
if dst in self.mac_to_port[dpid]:
|
|
||||||
out_port = self.mac_to_port[dpid][dst]
|
|
||||||
else:
|
|
||||||
out_port = ofproto.OFPP_FLOOD
|
|
||||||
|
|
||||||
actions = [parser.OFPActionOutput(out_port)]
|
|
||||||
|
|
||||||
# install a flow to avoid packet_in next time
|
|
||||||
if out_port != ofproto.OFPP_FLOOD:
|
|
||||||
match = parser.OFPMatch(in_port=in_port, eth_dst=dst)
|
|
||||||
self.add_flow(datapath, 1, match, actions)
|
|
||||||
|
|
||||||
data = None
|
|
||||||
if msg.buffer_id == ofproto.OFP_NO_BUFFER:
|
|
||||||
data = msg.data
|
|
||||||
|
|
||||||
out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
|
|
||||||
in_port=in_port, actions=actions, data=data)
|
|
||||||
datapath.send_msg(out)
|
|
||||||
|
|
||||||
@set_ev_cls(igmplib.EventMulticastGroupStateChanged,
|
|
||||||
MAIN_DISPATCHER)
|
|
||||||
def _status_changed(self, ev):
|
|
||||||
msg = {
|
|
||||||
igmplib.MG_GROUP_ADDED: 'Multicast Group Added',
|
|
||||||
igmplib.MG_MEMBER_CHANGED: 'Multicast Group Member Changed',
|
|
||||||
igmplib.MG_GROUP_REMOVED: 'Multicast Group Removed',
|
|
||||||
}
|
|
||||||
self.logger.info("%s: [%s] querier:[%s] hosts:%s",
|
|
||||||
msg.get(ev.reason), ev.address, ev.src,
|
|
||||||
ev.dsts)
|
|
||||||
@ -1,106 +0,0 @@
|
|||||||
# Copyright (C) 2016 Nippon Telegraph and Telephone Corporation.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
# implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
from ryu.base import app_manager
|
|
||||||
from ryu.controller import ofp_event
|
|
||||||
from ryu.controller.handler import CONFIG_DISPATCHER
|
|
||||||
from ryu.controller.handler import MAIN_DISPATCHER
|
|
||||||
from ryu.controller.handler import set_ev_cls
|
|
||||||
from ryu.ofproto import ofproto_v1_3
|
|
||||||
from ryu.lib import lacplib
|
|
||||||
from ryu.lib.dpid import str_to_dpid
|
|
||||||
from ryu.lib.packet import packet
|
|
||||||
from ryu.lib.packet import ethernet
|
|
||||||
from ryu.app import simple_switch_13
|
|
||||||
|
|
||||||
|
|
||||||
class SimpleSwitchLacp13(simple_switch_13.SimpleSwitch13):
|
|
||||||
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
|
|
||||||
_CONTEXTS = {'lacplib': lacplib.LacpLib}
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(SimpleSwitchLacp13, self).__init__(*args, **kwargs)
|
|
||||||
self.mac_to_port = {}
|
|
||||||
self._lacp = kwargs['lacplib']
|
|
||||||
self._lacp.add(
|
|
||||||
dpid=str_to_dpid('0000000000000001'), ports=[1, 2])
|
|
||||||
|
|
||||||
def del_flow(self, datapath, match):
|
|
||||||
ofproto = datapath.ofproto
|
|
||||||
parser = datapath.ofproto_parser
|
|
||||||
|
|
||||||
mod = parser.OFPFlowMod(datapath=datapath,
|
|
||||||
command=ofproto.OFPFC_DELETE,
|
|
||||||
out_port=ofproto.OFPP_ANY,
|
|
||||||
out_group=ofproto.OFPG_ANY,
|
|
||||||
match=match)
|
|
||||||
datapath.send_msg(mod)
|
|
||||||
|
|
||||||
@set_ev_cls(lacplib.EventPacketIn, MAIN_DISPATCHER)
|
|
||||||
def _packet_in_handler(self, ev):
|
|
||||||
msg = ev.msg
|
|
||||||
datapath = msg.datapath
|
|
||||||
ofproto = datapath.ofproto
|
|
||||||
parser = datapath.ofproto_parser
|
|
||||||
in_port = msg.match['in_port']
|
|
||||||
|
|
||||||
pkt = packet.Packet(msg.data)
|
|
||||||
eth = pkt.get_protocols(ethernet.ethernet)[0]
|
|
||||||
|
|
||||||
dst = eth.dst
|
|
||||||
src = eth.src
|
|
||||||
|
|
||||||
dpid = datapath.id
|
|
||||||
self.mac_to_port.setdefault(dpid, {})
|
|
||||||
|
|
||||||
self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)
|
|
||||||
|
|
||||||
# learn a mac address to avoid FLOOD next time.
|
|
||||||
self.mac_to_port[dpid][src] = in_port
|
|
||||||
|
|
||||||
if dst in self.mac_to_port[dpid]:
|
|
||||||
out_port = self.mac_to_port[dpid][dst]
|
|
||||||
else:
|
|
||||||
out_port = ofproto.OFPP_FLOOD
|
|
||||||
|
|
||||||
actions = [parser.OFPActionOutput(out_port)]
|
|
||||||
|
|
||||||
# install a flow to avoid packet_in next time
|
|
||||||
if out_port != ofproto.OFPP_FLOOD:
|
|
||||||
match = parser.OFPMatch(in_port=in_port, eth_dst=dst)
|
|
||||||
self.add_flow(datapath, 1, match, actions)
|
|
||||||
|
|
||||||
data = None
|
|
||||||
if msg.buffer_id == ofproto.OFP_NO_BUFFER:
|
|
||||||
data = msg.data
|
|
||||||
|
|
||||||
out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
|
|
||||||
in_port=in_port, actions=actions, data=data)
|
|
||||||
datapath.send_msg(out)
|
|
||||||
|
|
||||||
@set_ev_cls(lacplib.EventSlaveStateChanged, MAIN_DISPATCHER)
|
|
||||||
def _slave_state_changed_handler(self, ev):
|
|
||||||
datapath = ev.datapath
|
|
||||||
dpid = datapath.id
|
|
||||||
port_no = ev.port
|
|
||||||
enabled = ev.enabled
|
|
||||||
self.logger.info("slave state changed port: %d enabled: %s",
|
|
||||||
port_no, enabled)
|
|
||||||
if dpid in self.mac_to_port:
|
|
||||||
for mac in self.mac_to_port[dpid]:
|
|
||||||
match = datapath.ofproto_parser.OFPMatch(eth_dst=mac)
|
|
||||||
self.del_flow(datapath, match)
|
|
||||||
del self.mac_to_port[dpid]
|
|
||||||
self.mac_to_port.setdefault(dpid, {})
|
|
||||||
@ -1,116 +0,0 @@
|
|||||||
# Copyright (C) 2016 Nippon Telegraph and Telephone Corporation.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
# implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
import json
|
|
||||||
|
|
||||||
from ryu.app import simple_switch_13
|
|
||||||
from ryu.controller import ofp_event
|
|
||||||
from ryu.controller.handler import CONFIG_DISPATCHER
|
|
||||||
from ryu.controller.handler import set_ev_cls
|
|
||||||
from ryu.app.wsgi import ControllerBase
|
|
||||||
from ryu.app.wsgi import Response
|
|
||||||
from ryu.app.wsgi import route
|
|
||||||
from ryu.app.wsgi import WSGIApplication
|
|
||||||
from ryu.lib import dpid as dpid_lib
|
|
||||||
|
|
||||||
simple_switch_instance_name = 'simple_switch_api_app'
|
|
||||||
url = '/simpleswitch/mactable/{dpid}'
|
|
||||||
|
|
||||||
|
|
||||||
class SimpleSwitchRest13(simple_switch_13.SimpleSwitch13):
|
|
||||||
|
|
||||||
_CONTEXTS = {'wsgi': WSGIApplication}
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(SimpleSwitchRest13, self).__init__(*args, **kwargs)
|
|
||||||
self.switches = {}
|
|
||||||
wsgi = kwargs['wsgi']
|
|
||||||
wsgi.register(SimpleSwitchController,
|
|
||||||
{simple_switch_instance_name: self})
|
|
||||||
|
|
||||||
@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
|
|
||||||
def switch_features_handler(self, ev):
|
|
||||||
super(SimpleSwitchRest13, self).switch_features_handler(ev)
|
|
||||||
datapath = ev.msg.datapath
|
|
||||||
self.switches[datapath.id] = datapath
|
|
||||||
self.mac_to_port.setdefault(datapath.id, {})
|
|
||||||
|
|
||||||
def set_mac_to_port(self, dpid, entry):
|
|
||||||
mac_table = self.mac_to_port.setdefault(dpid, {})
|
|
||||||
datapath = self.switches.get(dpid)
|
|
||||||
|
|
||||||
entry_port = entry['port']
|
|
||||||
entry_mac = entry['mac']
|
|
||||||
|
|
||||||
if datapath is not None:
|
|
||||||
parser = datapath.ofproto_parser
|
|
||||||
if entry_port not in mac_table.values():
|
|
||||||
|
|
||||||
for mac, port in mac_table.items():
|
|
||||||
|
|
||||||
# from known device to new device
|
|
||||||
actions = [parser.OFPActionOutput(entry_port)]
|
|
||||||
match = parser.OFPMatch(in_port=port, eth_dst=entry_mac)
|
|
||||||
self.add_flow(datapath, 1, match, actions)
|
|
||||||
|
|
||||||
# from new device to known device
|
|
||||||
actions = [parser.OFPActionOutput(port)]
|
|
||||||
match = parser.OFPMatch(in_port=entry_port, eth_dst=mac)
|
|
||||||
self.add_flow(datapath, 1, match, actions)
|
|
||||||
|
|
||||||
mac_table.update({entry_mac: entry_port})
|
|
||||||
return mac_table
|
|
||||||
|
|
||||||
|
|
||||||
class SimpleSwitchController(ControllerBase):
|
|
||||||
|
|
||||||
def __init__(self, req, link, data, **config):
|
|
||||||
super(SimpleSwitchController, self).__init__(req, link, data, **config)
|
|
||||||
self.simple_switch_app = data[simple_switch_instance_name]
|
|
||||||
|
|
||||||
@route('simpleswitch', url, methods=['GET'],
|
|
||||||
requirements={'dpid': dpid_lib.DPID_PATTERN})
|
|
||||||
def list_mac_table(self, req, **kwargs):
|
|
||||||
|
|
||||||
simple_switch = self.simple_switch_app
|
|
||||||
dpid = kwargs['dpid']
|
|
||||||
|
|
||||||
if dpid not in simple_switch.mac_to_port:
|
|
||||||
return Response(status=404)
|
|
||||||
|
|
||||||
mac_table = simple_switch.mac_to_port.get(dpid, {})
|
|
||||||
body = json.dumps(mac_table)
|
|
||||||
return Response(content_type='application/json', text=body)
|
|
||||||
|
|
||||||
@route('simpleswitch', url, methods=['PUT'],
|
|
||||||
requirements={'dpid': dpid_lib.DPID_PATTERN})
|
|
||||||
def put_mac_table(self, req, **kwargs):
|
|
||||||
|
|
||||||
simple_switch = self.simple_switch_app
|
|
||||||
dpid = kwargs['dpid']
|
|
||||||
try:
|
|
||||||
new_entry = req.json if req.body else {}
|
|
||||||
except ValueError:
|
|
||||||
raise Response(status=400)
|
|
||||||
|
|
||||||
if dpid not in simple_switch.mac_to_port:
|
|
||||||
return Response(status=404)
|
|
||||||
|
|
||||||
try:
|
|
||||||
mac_table = simple_switch.set_mac_to_port(dpid, new_entry)
|
|
||||||
body = json.dumps(mac_table)
|
|
||||||
return Response(content_type='application/json', text=body)
|
|
||||||
except Exception as e:
|
|
||||||
return Response(status=500)
|
|
||||||
@ -1,121 +0,0 @@
|
|||||||
# Copyright (C) 2016 Nippon Telegraph and Telephone Corporation.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
# implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
from ryu.base import app_manager
|
|
||||||
from ryu.controller import ofp_event
|
|
||||||
from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER
|
|
||||||
from ryu.controller.handler import set_ev_cls
|
|
||||||
from ryu.ofproto import ofproto_v1_3
|
|
||||||
from ryu.lib import dpid as dpid_lib
|
|
||||||
from ryu.lib import stplib
|
|
||||||
from ryu.lib.packet import packet
|
|
||||||
from ryu.lib.packet import ethernet
|
|
||||||
from ryu.app import simple_switch_13
|
|
||||||
|
|
||||||
|
|
||||||
class SimpleSwitch13(simple_switch_13.SimpleSwitch13):
|
|
||||||
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
|
|
||||||
_CONTEXTS = {'stplib': stplib.Stp}
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(SimpleSwitch13, self).__init__(*args, **kwargs)
|
|
||||||
self.mac_to_port = {}
|
|
||||||
self.stp = kwargs['stplib']
|
|
||||||
|
|
||||||
# Sample of stplib config.
|
|
||||||
# please refer to stplib.Stp.set_config() for details.
|
|
||||||
config = {dpid_lib.str_to_dpid('0000000000000001'):
|
|
||||||
{'bridge': {'priority': 0x8000}},
|
|
||||||
dpid_lib.str_to_dpid('0000000000000002'):
|
|
||||||
{'bridge': {'priority': 0x9000}},
|
|
||||||
dpid_lib.str_to_dpid('0000000000000003'):
|
|
||||||
{'bridge': {'priority': 0xa000}}}
|
|
||||||
self.stp.set_config(config)
|
|
||||||
|
|
||||||
def delete_flow(self, datapath):
|
|
||||||
ofproto = datapath.ofproto
|
|
||||||
parser = datapath.ofproto_parser
|
|
||||||
|
|
||||||
for dst in self.mac_to_port[datapath.id].keys():
|
|
||||||
match = parser.OFPMatch(eth_dst=dst)
|
|
||||||
mod = parser.OFPFlowMod(
|
|
||||||
datapath, command=ofproto.OFPFC_DELETE,
|
|
||||||
out_port=ofproto.OFPP_ANY, out_group=ofproto.OFPG_ANY,
|
|
||||||
priority=1, match=match)
|
|
||||||
datapath.send_msg(mod)
|
|
||||||
|
|
||||||
@set_ev_cls(stplib.EventPacketIn, MAIN_DISPATCHER)
|
|
||||||
def _packet_in_handler(self, ev):
|
|
||||||
msg = ev.msg
|
|
||||||
datapath = msg.datapath
|
|
||||||
ofproto = datapath.ofproto
|
|
||||||
parser = datapath.ofproto_parser
|
|
||||||
in_port = msg.match['in_port']
|
|
||||||
|
|
||||||
pkt = packet.Packet(msg.data)
|
|
||||||
eth = pkt.get_protocols(ethernet.ethernet)[0]
|
|
||||||
|
|
||||||
dst = eth.dst
|
|
||||||
src = eth.src
|
|
||||||
|
|
||||||
dpid = datapath.id
|
|
||||||
self.mac_to_port.setdefault(dpid, {})
|
|
||||||
|
|
||||||
self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)
|
|
||||||
|
|
||||||
# learn a mac address to avoid FLOOD next time.
|
|
||||||
self.mac_to_port[dpid][src] = in_port
|
|
||||||
|
|
||||||
if dst in self.mac_to_port[dpid]:
|
|
||||||
out_port = self.mac_to_port[dpid][dst]
|
|
||||||
else:
|
|
||||||
out_port = ofproto.OFPP_FLOOD
|
|
||||||
|
|
||||||
actions = [parser.OFPActionOutput(out_port)]
|
|
||||||
|
|
||||||
# install a flow to avoid packet_in next time
|
|
||||||
if out_port != ofproto.OFPP_FLOOD:
|
|
||||||
match = parser.OFPMatch(in_port=in_port, eth_dst=dst)
|
|
||||||
self.add_flow(datapath, 1, match, actions)
|
|
||||||
|
|
||||||
data = None
|
|
||||||
if msg.buffer_id == ofproto.OFP_NO_BUFFER:
|
|
||||||
data = msg.data
|
|
||||||
|
|
||||||
out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
|
|
||||||
in_port=in_port, actions=actions, data=data)
|
|
||||||
datapath.send_msg(out)
|
|
||||||
|
|
||||||
@set_ev_cls(stplib.EventTopologyChange, MAIN_DISPATCHER)
|
|
||||||
def _topology_change_handler(self, ev):
|
|
||||||
dp = ev.dp
|
|
||||||
dpid_str = dpid_lib.dpid_to_str(dp.id)
|
|
||||||
msg = 'Receive topology change event. Flush MAC table.'
|
|
||||||
self.logger.debug("[dpid=%s] %s", dpid_str, msg)
|
|
||||||
|
|
||||||
if dp.id in self.mac_to_port:
|
|
||||||
self.delete_flow(dp)
|
|
||||||
del self.mac_to_port[dp.id]
|
|
||||||
|
|
||||||
@set_ev_cls(stplib.EventPortStateChange, MAIN_DISPATCHER)
|
|
||||||
def _port_state_change_handler(self, ev):
|
|
||||||
dpid_str = dpid_lib.dpid_to_str(ev.dp.id)
|
|
||||||
of_state = {stplib.PORT_STATE_DISABLE: 'DISABLE',
|
|
||||||
stplib.PORT_STATE_BLOCK: 'BLOCK',
|
|
||||||
stplib.PORT_STATE_LISTEN: 'LISTEN',
|
|
||||||
stplib.PORT_STATE_LEARN: 'LEARN',
|
|
||||||
stplib.PORT_STATE_FORWARD: 'FORWARD'}
|
|
||||||
self.logger.debug("[dpid=%s][port=%d] state=%s",
|
|
||||||
dpid_str, ev.port_no, of_state[ev.port_state])
|
|
||||||
@ -42,14 +42,15 @@ Get arp table:
|
|||||||
15:0c:de:49": 2}}}
|
15:0c:de:49": 2}}}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
from webob import Response
|
||||||
from ryu.app import simple_switch_13
|
from ryu.app import simple_switch_13
|
||||||
from ryu.app.wsgi import ControllerBase
|
from ryu.app.wsgi import route, websocket, ControllerBase, WSGIApplication
|
||||||
from ryu.app.wsgi import rpc_public
|
from ryu.app.wsgi import rpc_public, WebSocketRPCServer
|
||||||
from ryu.app.wsgi import websocket
|
|
||||||
from ryu.app.wsgi import WebSocketRPCServer
|
|
||||||
from ryu.app.wsgi import WSGIApplication
|
|
||||||
from ryu.controller import ofp_event
|
from ryu.controller import ofp_event
|
||||||
from ryu.controller.handler import set_ev_cls
|
from ryu.controller.handler import set_ev_cls
|
||||||
|
from ryu.lib import hub
|
||||||
from ryu.lib.packet import packet
|
from ryu.lib.packet import packet
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -35,7 +35,7 @@ $ sudo mn --controller=remote --topo linear,2
|
|||||||
""" # noqa
|
""" # noqa
|
||||||
|
|
||||||
from socket import error as SocketError
|
from socket import error as SocketError
|
||||||
from tinyrpc.exc import InvalidReplyError
|
from ryu.contrib.tinyrpc.exc import InvalidReplyError
|
||||||
|
|
||||||
|
|
||||||
from ryu.app.wsgi import (
|
from ryu.app.wsgi import (
|
||||||
|
|||||||
@ -17,34 +17,27 @@
|
|||||||
import inspect
|
import inspect
|
||||||
from types import MethodType
|
from types import MethodType
|
||||||
|
|
||||||
|
import webob.dec
|
||||||
|
from webob.response import Response
|
||||||
|
from ryu import cfg
|
||||||
|
from ryu.lib import hub
|
||||||
from routes import Mapper
|
from routes import Mapper
|
||||||
from routes.util import URLGenerator
|
from routes.util import URLGenerator
|
||||||
import six
|
|
||||||
|
import ryu.contrib
|
||||||
|
ryu.contrib.update_module_path()
|
||||||
from tinyrpc.server import RPCServer
|
from tinyrpc.server import RPCServer
|
||||||
from tinyrpc.dispatch import RPCDispatcher
|
from tinyrpc.dispatch import RPCDispatcher
|
||||||
from tinyrpc.dispatch import public as rpc_public
|
from tinyrpc.dispatch import public as rpc_public
|
||||||
from tinyrpc.protocols.jsonrpc import JSONRPCProtocol
|
from tinyrpc.protocols.jsonrpc import JSONRPCProtocol
|
||||||
from tinyrpc.transports import ServerTransport, ClientTransport
|
from tinyrpc.transports import ServerTransport, ClientTransport
|
||||||
from tinyrpc.client import RPCClient
|
from tinyrpc.client import RPCClient
|
||||||
import webob.dec
|
ryu.contrib.restore_module_path()
|
||||||
import webob.exc
|
|
||||||
from webob.request import Request as webob_Request
|
|
||||||
from webob.response import Response as webob_Response
|
|
||||||
|
|
||||||
from ryu import cfg
|
|
||||||
from ryu.lib import hub
|
|
||||||
|
|
||||||
DEFAULT_WSGI_HOST = '0.0.0.0'
|
|
||||||
DEFAULT_WSGI_PORT = 8080
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
CONF.register_cli_opts([
|
CONF.register_cli_opts([
|
||||||
cfg.StrOpt(
|
cfg.StrOpt('wsapi-host', default='', help='webapp listen host'),
|
||||||
'wsapi-host', default=DEFAULT_WSGI_HOST,
|
cfg.IntOpt('wsapi-port', default=8080, help='webapp listen port')
|
||||||
help='webapp listen host (default %s)' % DEFAULT_WSGI_HOST),
|
|
||||||
cfg.IntOpt(
|
|
||||||
'wsapi-port', default=DEFAULT_WSGI_PORT,
|
|
||||||
help='webapp listen port (default %s)' % DEFAULT_WSGI_PORT),
|
|
||||||
])
|
])
|
||||||
|
|
||||||
HEX_PATTERN = r'0x[0-9a-z]+'
|
HEX_PATTERN = r'0x[0-9a-z]+'
|
||||||
@ -63,33 +56,6 @@ def route(name, path, methods=None, requirements=None):
|
|||||||
return _route
|
return _route
|
||||||
|
|
||||||
|
|
||||||
class Request(webob_Request):
|
|
||||||
"""
|
|
||||||
Wrapper class for webob.request.Request.
|
|
||||||
|
|
||||||
The behavior of this class is the same as webob.request.Request
|
|
||||||
except for setting "charset" to "UTF-8" automatically.
|
|
||||||
"""
|
|
||||||
DEFAULT_CHARSET = "UTF-8"
|
|
||||||
|
|
||||||
def __init__(self, environ, charset=DEFAULT_CHARSET, *args, **kwargs):
|
|
||||||
super(Request, self).__init__(
|
|
||||||
environ, charset=charset, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class Response(webob_Response):
|
|
||||||
"""
|
|
||||||
Wrapper class for webob.response.Response.
|
|
||||||
|
|
||||||
The behavior of this class is the same as webob.response.Response
|
|
||||||
except for setting "charset" to "UTF-8" automatically.
|
|
||||||
"""
|
|
||||||
DEFAULT_CHARSET = "UTF-8"
|
|
||||||
|
|
||||||
def __init__(self, charset=DEFAULT_CHARSET, *args, **kwargs):
|
|
||||||
super(Response, self).__init__(charset=charset, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class WebSocketRegistrationWrapper(object):
|
class WebSocketRegistrationWrapper(object):
|
||||||
|
|
||||||
def __init__(self, func, controller):
|
def __init__(self, func, controller):
|
||||||
@ -108,15 +74,8 @@ class WebSocketRegistrationWrapper(object):
|
|||||||
|
|
||||||
class _AlreadyHandledResponse(Response):
|
class _AlreadyHandledResponse(Response):
|
||||||
# XXX: Eventlet API should not be used directly.
|
# XXX: Eventlet API should not be used directly.
|
||||||
# https://github.com/benoitc/gunicorn/pull/2581
|
from eventlet.wsgi import ALREADY_HANDLED
|
||||||
from packaging import version
|
_ALREADY_HANDLED = ALREADY_HANDLED
|
||||||
import eventlet
|
|
||||||
if version.parse(eventlet.__version__) >= version.parse("0.30.3"):
|
|
||||||
import eventlet.wsgi
|
|
||||||
_ALREADY_HANDLED = getattr(eventlet.wsgi, "ALREADY_HANDLED", None)
|
|
||||||
else:
|
|
||||||
from eventlet.wsgi import ALREADY_HANDLED
|
|
||||||
_ALREADY_HANDLED = ALREADY_HANDLED
|
|
||||||
|
|
||||||
def __call__(self, environ, start_response):
|
def __call__(self, environ, start_response):
|
||||||
return self._ALREADY_HANDLED
|
return self._ALREADY_HANDLED
|
||||||
@ -124,7 +83,7 @@ class _AlreadyHandledResponse(Response):
|
|||||||
|
|
||||||
def websocket(name, path):
|
def websocket(name, path):
|
||||||
def _websocket(controller_func):
|
def _websocket(controller_func):
|
||||||
def __websocket(self, req, **_):
|
def __websocket(self, req, **kwargs):
|
||||||
wrapper = WebSocketRegistrationWrapper(controller_func, self)
|
wrapper = WebSocketRegistrationWrapper(controller_func, self)
|
||||||
ws_wsgi = hub.WebSocketWSGI(wrapper)
|
ws_wsgi = hub.WebSocketWSGI(wrapper)
|
||||||
ws_wsgi(req.environ, req.start_response)
|
ws_wsgi(req.environ, req.start_response)
|
||||||
@ -149,7 +108,6 @@ class ControllerBase(object):
|
|||||||
def __init__(self, req, link, data, **config):
|
def __init__(self, req, link, data, **config):
|
||||||
self.req = req
|
self.req = req
|
||||||
self.link = link
|
self.link = link
|
||||||
self.data = data
|
|
||||||
self.parent = None
|
self.parent = None
|
||||||
for name, value in config.items():
|
for name, value in config.items():
|
||||||
setattr(self, name, value)
|
setattr(self, name, value)
|
||||||
@ -180,10 +138,10 @@ class WebSocketServerTransport(ServerTransport):
|
|||||||
if message is None:
|
if message is None:
|
||||||
raise WebSocketDisconnectedError()
|
raise WebSocketDisconnectedError()
|
||||||
context = None
|
context = None
|
||||||
return context, message
|
return (context, message)
|
||||||
|
|
||||||
def send_reply(self, context, reply):
|
def send_reply(self, context, reply):
|
||||||
self.ws.send(six.text_type(reply))
|
self.ws.send(unicode(reply))
|
||||||
|
|
||||||
|
|
||||||
class WebSocketRPCServer(RPCServer):
|
class WebSocketRPCServer(RPCServer):
|
||||||
@ -213,7 +171,7 @@ class WebSocketClientTransport(ClientTransport):
|
|||||||
self.queue = queue
|
self.queue = queue
|
||||||
|
|
||||||
def send_message(self, message, expect_reply=True):
|
def send_message(self, message, expect_reply=True):
|
||||||
self.ws.send(six.text_type(message))
|
self.ws.send(unicode(message))
|
||||||
|
|
||||||
if expect_reply:
|
if expect_reply:
|
||||||
return self.queue.get()
|
return self.queue.get()
|
||||||
@ -266,15 +224,23 @@ class WSGIApplication(object):
|
|||||||
self.registory = {}
|
self.registory = {}
|
||||||
self._wsmanager = WebSocketManager()
|
self._wsmanager = WebSocketManager()
|
||||||
super(WSGIApplication, self).__init__()
|
super(WSGIApplication, self).__init__()
|
||||||
|
# XXX: Switch how to call the API of Routes for every version
|
||||||
|
match_argspec = inspect.getargspec(self.mapper.match)
|
||||||
|
if 'environ' in match_argspec.args:
|
||||||
|
# New API
|
||||||
|
self._match = self._match_with_environ
|
||||||
|
else:
|
||||||
|
# Old API
|
||||||
|
self._match = self._match_with_path_info
|
||||||
|
|
||||||
def _match(self, req):
|
def _match_with_environ(self, req):
|
||||||
# Note: Invoke the new API, first. If the arguments unmatched,
|
match = self.mapper.match(environ=req.environ)
|
||||||
# invoke the old API.
|
return match
|
||||||
try:
|
|
||||||
return self.mapper.match(environ=req.environ)
|
def _match_with_path_info(self, req):
|
||||||
except TypeError:
|
self.mapper.environ = req.environ
|
||||||
self.mapper.environ = req.environ
|
match = self.mapper.match(req.path_info)
|
||||||
return self.mapper.match(req.path_info)
|
return match
|
||||||
|
|
||||||
@wsgify_hack
|
@wsgify_hack
|
||||||
def __call__(self, req, start_response):
|
def __call__(self, req, start_response):
|
||||||
|
|||||||
@ -386,7 +386,6 @@ class AppManager(object):
|
|||||||
self.applications = {}
|
self.applications = {}
|
||||||
self.contexts_cls = {}
|
self.contexts_cls = {}
|
||||||
self.contexts = {}
|
self.contexts = {}
|
||||||
self.close_sem = hub.Semaphore()
|
|
||||||
|
|
||||||
def load_app(self, name):
|
def load_app(self, name):
|
||||||
mod = utils.import_module(name)
|
mod = utils.import_module(name)
|
||||||
@ -542,10 +541,7 @@ class AppManager(object):
|
|||||||
self._close(app)
|
self._close(app)
|
||||||
close_dict.clear()
|
close_dict.clear()
|
||||||
|
|
||||||
# This semaphore prevents parallel execution of this function,
|
for app_name in list(self.applications.keys()):
|
||||||
# as run_apps's finally clause starts another close() call.
|
self.uninstantiate(app_name)
|
||||||
with self.close_sem:
|
assert not self.applications
|
||||||
for app_name in list(self.applications.keys()):
|
close_all(self.contexts)
|
||||||
self.uninstantiate(app_name)
|
|
||||||
assert not self.applications
|
|
||||||
close_all(self.contexts)
|
|
||||||
|
|||||||
@ -36,7 +36,6 @@ CONF = oslo_config.cfg.ConfigOpts()
|
|||||||
|
|
||||||
from oslo_config.cfg import ConfigOpts
|
from oslo_config.cfg import ConfigOpts
|
||||||
|
|
||||||
from oslo_config.cfg import Opt
|
|
||||||
from oslo_config.cfg import BoolOpt
|
from oslo_config.cfg import BoolOpt
|
||||||
from oslo_config.cfg import IntOpt
|
from oslo_config.cfg import IntOpt
|
||||||
from oslo_config.cfg import ListOpt
|
from oslo_config.cfg import ListOpt
|
||||||
|
|||||||
@ -16,15 +16,22 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from ryu.lib import hub
|
from ryu.lib import hub
|
||||||
hub.patch(thread=False)
|
hub.patch(thread=False)
|
||||||
|
|
||||||
from ryu import cfg
|
# TODO:
|
||||||
|
# Right now, we have our own patched copy of ovs python bindings
|
||||||
|
# Once our modification is upstreamed and widely deployed,
|
||||||
|
# use it
|
||||||
|
#
|
||||||
|
# NOTE: this modifies sys.path and thus affects the following imports.
|
||||||
|
import ryu.contrib
|
||||||
|
ryu.contrib.update_module_path()
|
||||||
|
|
||||||
|
from ryu import cfg
|
||||||
import logging
|
import logging
|
||||||
|
import sys
|
||||||
|
|
||||||
from ryu import log
|
from ryu import log
|
||||||
log.early_init_log(logging.DEBUG)
|
log.early_init_log(logging.DEBUG)
|
||||||
|
|
||||||
@ -46,28 +53,10 @@ CONF.register_cli_opts([
|
|||||||
cfg.BoolOpt('enable-debugger', default=False,
|
cfg.BoolOpt('enable-debugger', default=False,
|
||||||
help='don\'t overwrite Python standard threading library'
|
help='don\'t overwrite Python standard threading library'
|
||||||
'(use only for debugging)'),
|
'(use only for debugging)'),
|
||||||
cfg.StrOpt('user-flags', default=None,
|
|
||||||
help='Additional flags file for user applications'),
|
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
def _parse_user_flags():
|
|
||||||
"""
|
|
||||||
Parses user-flags file and loads it to register user defined options.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
idx = list(sys.argv).index('--user-flags')
|
|
||||||
user_flags_file = sys.argv[idx + 1]
|
|
||||||
except (ValueError, IndexError):
|
|
||||||
user_flags_file = ''
|
|
||||||
|
|
||||||
if user_flags_file and os.path.isfile(user_flags_file):
|
|
||||||
from ryu.utils import _import_module_file
|
|
||||||
_import_module_file(user_flags_file)
|
|
||||||
|
|
||||||
|
|
||||||
def main(args=None, prog=None):
|
def main(args=None, prog=None):
|
||||||
_parse_user_flags()
|
|
||||||
try:
|
try:
|
||||||
CONF(args=args, prog=prog,
|
CONF(args=args, prog=prog,
|
||||||
project='ryu', version='ryu-manager %s' % version,
|
project='ryu', version='ryu-manager %s' % version,
|
||||||
@ -77,20 +66,21 @@ def main(args=None, prog=None):
|
|||||||
project='ryu', version='ryu-manager %s' % version)
|
project='ryu', version='ryu-manager %s' % version)
|
||||||
|
|
||||||
log.init_log()
|
log.init_log()
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
if CONF.enable_debugger:
|
if CONF.enable_debugger:
|
||||||
|
LOG = logging.getLogger('ryu.cmd.manager')
|
||||||
msg = 'debugging is available (--enable-debugger option is turned on)'
|
msg = 'debugging is available (--enable-debugger option is turned on)'
|
||||||
logger.info(msg)
|
LOG.info(msg)
|
||||||
else:
|
else:
|
||||||
hub.patch(thread=True)
|
hub.patch(thread=True)
|
||||||
|
|
||||||
if CONF.pid_file:
|
if CONF.pid_file:
|
||||||
|
import os
|
||||||
with open(CONF.pid_file, 'w') as pid_file:
|
with open(CONF.pid_file, 'w') as pid_file:
|
||||||
pid_file.write(str(os.getpid()))
|
pid_file.write(str(os.getpid()))
|
||||||
|
|
||||||
app_lists = CONF.app_lists + CONF.app
|
app_lists = CONF.app_lists + CONF.app
|
||||||
# keep old behavior, run ofp if no application is specified.
|
# keep old behaivor, run ofp if no application is specified.
|
||||||
if not app_lists:
|
if not app_lists:
|
||||||
app_lists = ['ryu.controller.ofp_handler']
|
app_lists = ['ryu.controller.ofp_handler']
|
||||||
|
|
||||||
@ -107,9 +97,6 @@ def main(args=None, prog=None):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
hub.joinall(services)
|
hub.joinall(services)
|
||||||
except KeyboardInterrupt:
|
|
||||||
logger.debug("Keyboard Interrupt received. "
|
|
||||||
"Closing RYU application manager...")
|
|
||||||
finally:
|
finally:
|
||||||
app_mgr.close()
|
app_mgr.close()
|
||||||
|
|
||||||
|
|||||||
@ -25,15 +25,18 @@
|
|||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
import cmd
|
import ryu.contrib
|
||||||
import sys
|
ryu.contrib.update_module_path()
|
||||||
|
|
||||||
import lxml.etree as ET
|
|
||||||
from ncclient.operations.rpc import RPCError
|
|
||||||
|
|
||||||
from ryu import cfg
|
from ryu import cfg
|
||||||
|
|
||||||
|
import cmd
|
||||||
|
import sys
|
||||||
|
import lxml.etree as ET
|
||||||
|
|
||||||
from ryu.lib import of_config
|
from ryu.lib import of_config
|
||||||
from ryu.lib.of_config import capable_switch
|
from ryu.lib.of_config import capable_switch
|
||||||
|
from ncclient.operations.rpc import RPCError
|
||||||
import ryu.lib.of_config.classes as ofc
|
import ryu.lib.of_config.classes as ofc
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -31,25 +31,24 @@
|
|||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
import ast
|
import ryu.contrib
|
||||||
|
ryu.contrib.update_module_path()
|
||||||
|
|
||||||
|
from ryu import cfg
|
||||||
|
|
||||||
import cmd
|
import cmd
|
||||||
import signal
|
import signal
|
||||||
import socket
|
import socket
|
||||||
import sys
|
import sys
|
||||||
import termios
|
import termios
|
||||||
|
|
||||||
from ryu import cfg
|
|
||||||
from ryu.lib import rpc
|
from ryu.lib import rpc
|
||||||
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
CONF.register_cli_opts([
|
CONF.register_cli_opts([
|
||||||
cfg.ListOpt('peers', default=[],
|
# eg. rpc-cli --peers=hoge=localhost:9998,fuga=localhost:9999
|
||||||
help='List of peers, separated by commas. '
|
cfg.ListOpt('peers', default=[], help='list of peers')
|
||||||
'(e.g., "hoge=localhost:9998,fuga=localhost:9999")'),
|
|
||||||
cfg.StrOpt('command', short='c', default=None,
|
|
||||||
help='Command to be executed as single command. '
|
|
||||||
'The default is None and opens interactive console.'),
|
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
@ -57,18 +56,16 @@ class Peer(object):
|
|||||||
def __init__(self, name, addr):
|
def __init__(self, name, addr):
|
||||||
self._name = name
|
self._name = name
|
||||||
self._addr = addr
|
self._addr = addr
|
||||||
self.socket = None
|
|
||||||
self.client = None
|
self.client = None
|
||||||
try:
|
try:
|
||||||
self.connect()
|
self.connect()
|
||||||
except ConnectionError as e:
|
except:
|
||||||
print('Exception when connecting to peer "%s": %s' % (name, e))
|
pass
|
||||||
raise e
|
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
self.socket = socket.create_connection(self._addr)
|
self.client = None
|
||||||
self.client = rpc.Client(self.socket,
|
s = socket.create_connection(self._addr)
|
||||||
notification_callback=self.notification)
|
self.client = rpc.Client(s, notification_callback=self.notification)
|
||||||
|
|
||||||
def try_to_connect(self, verbose=False):
|
def try_to_connect(self, verbose=False):
|
||||||
if self.client:
|
if self.client:
|
||||||
@ -107,25 +104,12 @@ class Peer(object):
|
|||||||
print("connected. retrying the request...")
|
print("connected. retrying the request...")
|
||||||
return g()
|
return g()
|
||||||
|
|
||||||
def close(self):
|
|
||||||
self.socket.close()
|
|
||||||
|
|
||||||
|
|
||||||
peers = {}
|
peers = {}
|
||||||
|
|
||||||
|
|
||||||
def add_peer(name, host, port):
|
def add_peer(name, host, port):
|
||||||
try:
|
peers[name] = Peer(name, (host, port))
|
||||||
peer = Peer(name, (host, port))
|
|
||||||
except ConnectionError:
|
|
||||||
return
|
|
||||||
|
|
||||||
peers[name] = peer
|
|
||||||
|
|
||||||
|
|
||||||
def close_peers():
|
|
||||||
for peer in peers.values():
|
|
||||||
peer.socket.close()
|
|
||||||
|
|
||||||
|
|
||||||
class Cmd(cmd.Cmd):
|
class Cmd(cmd.Cmd):
|
||||||
@ -140,9 +124,9 @@ class Cmd(cmd.Cmd):
|
|||||||
try:
|
try:
|
||||||
peer = args[0]
|
peer = args[0]
|
||||||
method = args[1]
|
method = args[1]
|
||||||
params = ast.literal_eval(args[2])
|
params = eval(args[2])
|
||||||
except (IndexError, ValueError) as e:
|
except:
|
||||||
print("argument error: %s" % e)
|
print("argument error")
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
p = peers[peer]
|
p = peers[peer]
|
||||||
@ -190,8 +174,7 @@ class Cmd(cmd.Cmd):
|
|||||||
def complete_notify(self, text, line, begidx, endidx):
|
def complete_notify(self, text, line, begidx, endidx):
|
||||||
return self._complete_peer(text, line, begidx, endidx)
|
return self._complete_peer(text, line, begidx, endidx)
|
||||||
|
|
||||||
def do_EOF(self, _line=None):
|
def do_EOF(self, _line):
|
||||||
close_peers()
|
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
def emptyline(self):
|
def emptyline(self):
|
||||||
@ -222,9 +205,6 @@ class Cmd(cmd.Cmd):
|
|||||||
signal.signal(signal.SIGALRM, self._timeout)
|
signal.signal(signal.SIGALRM, self._timeout)
|
||||||
signal.alarm(1)
|
signal.alarm(1)
|
||||||
|
|
||||||
def postloop(self):
|
|
||||||
close_peers()
|
|
||||||
|
|
||||||
def onecmd(self, string):
|
def onecmd(self, string):
|
||||||
self._in_onecmd = True
|
self._in_onecmd = True
|
||||||
try:
|
try:
|
||||||
@ -253,11 +233,6 @@ def main(args=None, prog=None):
|
|||||||
host, port = addr.rsplit(':', 1)
|
host, port = addr.rsplit(':', 1)
|
||||||
add_peer(name, host, port)
|
add_peer(name, host, port)
|
||||||
|
|
||||||
if CONF.command:
|
|
||||||
command = Cmd()
|
|
||||||
command.onecmd(CONF.command)
|
|
||||||
command.do_EOF()
|
|
||||||
|
|
||||||
Cmd().cmdloop()
|
Cmd().cmdloop()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -14,13 +14,15 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import argparse
|
import ryu.contrib
|
||||||
import os.path
|
ryu.contrib.update_module_path()
|
||||||
import sys
|
|
||||||
|
|
||||||
from ryu import cfg
|
from ryu import cfg
|
||||||
from ryu import utils
|
from ryu import utils
|
||||||
from ryu import version
|
from ryu import version
|
||||||
|
import argparse
|
||||||
|
import os.path
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
subcommands = {
|
subcommands = {
|
||||||
|
|||||||
@ -1,9 +1,7 @@
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
_orig_sys_path = None
|
_orig_sys_path = None
|
||||||
|
|
||||||
|
|
||||||
def update_module_path():
|
def update_module_path():
|
||||||
# Adjust module loading path for third party libraries
|
# Adjust module loading path for third party libraries
|
||||||
import os
|
import os
|
||||||
@ -18,7 +16,6 @@ def update_module_path():
|
|||||||
sys.path.remove(path)
|
sys.path.remove(path)
|
||||||
sys.path.insert(0, path) # prioritize our own copy than system's
|
sys.path.insert(0, path) # prioritize our own copy than system's
|
||||||
|
|
||||||
|
|
||||||
def restore_module_path():
|
def restore_module_path():
|
||||||
global _orig_sys_path
|
global _orig_sys_path
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
# Copyright (C) 2017 Nippon Telegraph and Telephone Corporation.
|
# Copyright 2009 Shikhar Bhushan
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -8,13 +8,15 @@
|
|||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
"""
|
import sys
|
||||||
Client implementation for Zebra protocol service.
|
|
||||||
|
|
||||||
This module provides the client side implementation for Zebra protocol.
|
if sys.version_info < (2, 6):
|
||||||
"""
|
raise RuntimeError('You need Python 2.6+ for this module.')
|
||||||
|
|
||||||
|
class NCClientError(Exception):
|
||||||
|
"Base type for all NCClient errors"
|
||||||
|
pass
|
||||||
68
ryu/contrib/ncclient/capabilities.py
Normal file
68
ryu/contrib/ncclient/capabilities.py
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
# Copyright 2009 Shikhar Bhushan
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
def _abbreviate(uri):
|
||||||
|
if uri.startswith("urn:ietf:params") and ":netconf:" in uri:
|
||||||
|
splitted = uri.split(":")
|
||||||
|
if ":capability:" in uri:
|
||||||
|
if uri.startswith("urn:ietf:params:xml:ns:netconf"):
|
||||||
|
name, version = splitted[7], splitted[8]
|
||||||
|
else:
|
||||||
|
name, version = splitted[5], splitted[6]
|
||||||
|
return [ ":" + name, ":" + name + ":" + version ]
|
||||||
|
elif ":base:" in uri:
|
||||||
|
if uri.startswith("urn:ietf:params:xml:ns:netconf"):
|
||||||
|
return [ ":base", ":base" + ":" + splitted[7] ]
|
||||||
|
else:
|
||||||
|
return [ ":base", ":base" + ":" + splitted[5] ]
|
||||||
|
return []
|
||||||
|
|
||||||
|
def schemes(url_uri):
|
||||||
|
"Given a URI that has a *scheme* query string (i.e. `:url` capability URI), will return a list of supported schemes."
|
||||||
|
return url_uri.partition("?scheme=")[2].split(",")
|
||||||
|
|
||||||
|
class Capabilities:
|
||||||
|
|
||||||
|
"Represents the set of capabilities available to a NETCONF client or server. It is initialized with a list of capability URI's."
|
||||||
|
|
||||||
|
def __init__(self, capabilities):
|
||||||
|
self._dict = {}
|
||||||
|
for uri in capabilities:
|
||||||
|
self._dict[uri] = _abbreviate(uri)
|
||||||
|
|
||||||
|
def __contains__(self, key):
|
||||||
|
if key in self._dict:
|
||||||
|
return True
|
||||||
|
for abbrs in self._dict.values():
|
||||||
|
if key in abbrs:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._dict)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return self._dict.iterkeys()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return repr(self._dict.keys())
|
||||||
|
|
||||||
|
def add(self, uri):
|
||||||
|
"Add a capability."
|
||||||
|
self._dict[uri] = _abbreviate(uri)
|
||||||
|
|
||||||
|
def remove(self, uri):
|
||||||
|
"Remove a capability."
|
||||||
|
if key in self._dict:
|
||||||
|
del self._dict[key]
|
||||||
24
ryu/contrib/ncclient/debug.py
Normal file
24
ryu/contrib/ncclient/debug.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Copyright 2009 Shikhar Bhushan
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from ncclient.transport import SessionListener
|
||||||
|
|
||||||
|
class PrintListener(SessionListener):
|
||||||
|
|
||||||
|
def callback(self, root, raw):
|
||||||
|
print('\n# RECEIVED MESSAGE with root=[tag=%r, attrs=%r] #\n%r\n' %
|
||||||
|
(root[0], root[1], raw))
|
||||||
|
|
||||||
|
def errback(self, err):
|
||||||
|
print('\n# RECEIVED ERROR #\n%r\n' % err)
|
||||||
177
ryu/contrib/ncclient/manager.py
Normal file
177
ryu/contrib/ncclient/manager.py
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
# Copyright 2009 Shikhar Bhushan
|
||||||
|
# Copyright 2011 Leonidas Poulopoulos
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
"""This module is a thin layer of abstraction around the library. It exposes all core functionality."""
|
||||||
|
|
||||||
|
import capabilities
|
||||||
|
import operations
|
||||||
|
import transport
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger('ncclient.manager')
|
||||||
|
|
||||||
|
CAPABILITIES = [
|
||||||
|
"urn:ietf:params:netconf:base:1.0",
|
||||||
|
"urn:ietf:params:netconf:capability:writable-running:1.0",
|
||||||
|
"urn:ietf:params:netconf:capability:candidate:1.0",
|
||||||
|
"urn:ietf:params:netconf:capability:confirmed-commit:1.0",
|
||||||
|
"urn:ietf:params:netconf:capability:rollback-on-error:1.0",
|
||||||
|
"urn:ietf:params:netconf:capability:startup:1.0",
|
||||||
|
"urn:ietf:params:netconf:capability:url:1.0?scheme=http,ftp,file,https,sftp",
|
||||||
|
"urn:ietf:params:netconf:capability:validate:1.0",
|
||||||
|
"urn:ietf:params:netconf:capability:xpath:1.0",
|
||||||
|
"urn:liberouter:params:netconf:capability:power-control:1.0",
|
||||||
|
"urn:ietf:params:netconf:capability:interleave:1.0"
|
||||||
|
]
|
||||||
|
"""A list of URI's representing the client's capabilities. This is used during the initial capability exchange. Modify this if you need to announce some capability not already included."""
|
||||||
|
|
||||||
|
OPERATIONS = {
|
||||||
|
"get": operations.Get,
|
||||||
|
"get_config": operations.GetConfig,
|
||||||
|
"dispatch": operations.Dispatch,
|
||||||
|
"edit_config": operations.EditConfig,
|
||||||
|
"copy_config": operations.CopyConfig,
|
||||||
|
"validate": operations.Validate,
|
||||||
|
"commit": operations.Commit,
|
||||||
|
"discard_changes": operations.DiscardChanges,
|
||||||
|
"delete_config": operations.DeleteConfig,
|
||||||
|
"lock": operations.Lock,
|
||||||
|
"unlock": operations.Unlock,
|
||||||
|
"close_session": operations.CloseSession,
|
||||||
|
"kill_session": operations.KillSession,
|
||||||
|
"poweroff_machine": operations.PoweroffMachine,
|
||||||
|
"reboot_machine": operations.RebootMachine
|
||||||
|
}
|
||||||
|
"""Dictionary of method names and corresponding :class:`~ncclient.operations.RPC` subclasses. It is used to lookup operations, e.g. `get_config` is mapped to :class:`~ncclient.operations.GetConfig`. It is thus possible to add additional operations to the :class:`Manager` API."""
|
||||||
|
|
||||||
|
def connect_ssh(*args, **kwds):
|
||||||
|
"""Initialize a :class:`Manager` over the SSH transport. For documentation of arguments see :meth:`ncclient.transport.SSHSession.connect`.
|
||||||
|
|
||||||
|
The underlying :class:`ncclient.transport.SSHSession` is created with :data:`CAPABILITIES`. It is first instructed to :meth:`~ncclient.transport.SSHSession.load_known_hosts` and then all the provided arguments are passed directly to its implementation of :meth:`~ncclient.transport.SSHSession.connect`.
|
||||||
|
"""
|
||||||
|
session = transport.SSHSession(capabilities.Capabilities(CAPABILITIES))
|
||||||
|
session.load_known_hosts()
|
||||||
|
session.connect(*args, **kwds)
|
||||||
|
return Manager(session)
|
||||||
|
|
||||||
|
connect = connect_ssh
|
||||||
|
"Same as :func:`connect_ssh`, since SSH is the default (and currently, the only) transport."
|
||||||
|
|
||||||
|
class OpExecutor(type):
|
||||||
|
|
||||||
|
def __new__(cls, name, bases, attrs):
|
||||||
|
def make_wrapper(op_cls):
|
||||||
|
def wrapper(self, *args, **kwds):
|
||||||
|
return self.execute(op_cls, *args, **kwds)
|
||||||
|
wrapper.func_doc = op_cls.request.func_doc
|
||||||
|
return wrapper
|
||||||
|
for op_name, op_cls in OPERATIONS.iteritems():
|
||||||
|
attrs[op_name] = make_wrapper(op_cls)
|
||||||
|
return super(OpExecutor, cls).__new__(cls, name, bases, attrs)
|
||||||
|
|
||||||
|
class Manager(object):
|
||||||
|
|
||||||
|
"""For details on the expected behavior of the operations and their parameters refer to :rfc:`4741`.
|
||||||
|
|
||||||
|
Manager instances are also context managers so you can use it like this::
|
||||||
|
|
||||||
|
with manager.connect("host") as m:
|
||||||
|
# do your stuff
|
||||||
|
|
||||||
|
... or like this::
|
||||||
|
|
||||||
|
m = manager.connect("host")
|
||||||
|
try:
|
||||||
|
# do your stuff
|
||||||
|
finally:
|
||||||
|
m.close_session()
|
||||||
|
"""
|
||||||
|
|
||||||
|
__metaclass__ = OpExecutor
|
||||||
|
|
||||||
|
def __init__(self, session, timeout=30):
|
||||||
|
self._session = session
|
||||||
|
self._async_mode = False
|
||||||
|
self._timeout = timeout
|
||||||
|
self._raise_mode = operations.RaiseMode.ALL
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, *args):
|
||||||
|
self.close_session()
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __set_timeout(self, timeout):
|
||||||
|
self._timeout = timeout
|
||||||
|
|
||||||
|
def __set_async_mode(self, mode):
|
||||||
|
self._async_mode = mode
|
||||||
|
|
||||||
|
def __set_raise_mode(self, mode):
|
||||||
|
assert(mode in (operations.RaiseMode.NONE, operations.RaiseMode.ERRORS, operations.RaiseMode.ALL))
|
||||||
|
self._raise_mode = mode
|
||||||
|
|
||||||
|
def execute(self, cls, *args, **kwds):
|
||||||
|
return cls(self._session,
|
||||||
|
async=self._async_mode,
|
||||||
|
timeout=self._timeout,
|
||||||
|
raise_mode=self._raise_mode).request(*args, **kwds)
|
||||||
|
|
||||||
|
def locked(self, target):
|
||||||
|
"""Returns a context manager for a lock on a datastore, where *target* is the name of the configuration datastore to lock, e.g.::
|
||||||
|
|
||||||
|
with m.locked("running"):
|
||||||
|
# do your stuff
|
||||||
|
|
||||||
|
... instead of::
|
||||||
|
|
||||||
|
m.lock("running")
|
||||||
|
try:
|
||||||
|
# do your stuff
|
||||||
|
finally:
|
||||||
|
m.unlock("running")
|
||||||
|
"""
|
||||||
|
return operations.LockContext(self._session, target)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def client_capabilities(self):
|
||||||
|
":class:`~ncclient.capabilities.Capabilities` object representing the client's capabilities."
|
||||||
|
return self._session._client_capabilities
|
||||||
|
|
||||||
|
@property
|
||||||
|
def server_capabilities(self):
|
||||||
|
":class:`~ncclient.capabilities.Capabilities` object representing the server's capabilities."
|
||||||
|
return self._session._server_capabilities
|
||||||
|
|
||||||
|
@property
|
||||||
|
def session_id(self):
|
||||||
|
"`session-id` assigned by the NETCONF server."
|
||||||
|
return self._session.id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def connected(self):
|
||||||
|
"Whether currently connected to the NETCONF server."
|
||||||
|
return self._session.connected
|
||||||
|
|
||||||
|
async_mode = property(fget=lambda self: self._async_mode, fset=__set_async_mode)
|
||||||
|
"Specify whether operations are executed asynchronously (`True`) or synchronously (`False`) (the default)."
|
||||||
|
|
||||||
|
timeout = property(fget=lambda self: self._timeout, fset=__set_timeout)
|
||||||
|
"Specify the timeout for synchronous RPC requests."
|
||||||
|
|
||||||
|
raise_mode = property(fget=lambda self: self._raise_mode, fset=__set_raise_mode)
|
||||||
|
"Specify which errors are raised as :exc:`~ncclient.operations.RPCError` exceptions. Valid values are the constants defined in :class:`~ncclient.operations.RaiseMode`. The default value is :attr:`~ncclient.operations.RaiseMode.ALL`."
|
||||||
51
ryu/contrib/ncclient/operations/__init__.py
Normal file
51
ryu/contrib/ncclient/operations/__init__.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# Copyright 2009 Shikhar Bhushan
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from errors import OperationError, TimeoutExpiredError, MissingCapabilityError
|
||||||
|
from rpc import RPC, RPCReply, RPCError, RaiseMode
|
||||||
|
|
||||||
|
# rfc4741 ops
|
||||||
|
from retrieve import Get, GetConfig, GetReply, Dispatch
|
||||||
|
from edit import EditConfig, CopyConfig, DeleteConfig, Validate, Commit, DiscardChanges
|
||||||
|
from session import CloseSession, KillSession
|
||||||
|
from lock import Lock, Unlock, LockContext
|
||||||
|
# others...
|
||||||
|
from flowmon import PoweroffMachine, RebootMachine
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'RPC',
|
||||||
|
'RPCReply',
|
||||||
|
'RPCError',
|
||||||
|
'RaiseMode',
|
||||||
|
'Get',
|
||||||
|
'GetConfig',
|
||||||
|
'Dispatch',
|
||||||
|
'GetReply',
|
||||||
|
'EditConfig',
|
||||||
|
'CopyConfig',
|
||||||
|
'Validate',
|
||||||
|
'Commit',
|
||||||
|
'DiscardChanges',
|
||||||
|
'DeleteConfig',
|
||||||
|
'Lock',
|
||||||
|
'Unlock',
|
||||||
|
'PoweroffMachine',
|
||||||
|
'RebootMachine',
|
||||||
|
'LockContext',
|
||||||
|
'CloseSession',
|
||||||
|
'KillSession',
|
||||||
|
'OperationError',
|
||||||
|
'TimeoutExpiredError',
|
||||||
|
'MissingCapabilityError'
|
||||||
|
]
|
||||||
143
ryu/contrib/ncclient/operations/edit.py
Normal file
143
ryu/contrib/ncclient/operations/edit.py
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
# Copyright 2009 Shikhar Bhushan
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from ncclient.xml_ import *
|
||||||
|
|
||||||
|
from rpc import RPC
|
||||||
|
|
||||||
|
import util
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger("ncclient.operations.edit")
|
||||||
|
|
||||||
|
"Operations related to changing device configuration"
|
||||||
|
|
||||||
|
class EditConfig(RPC):
|
||||||
|
"`edit-config` RPC"
|
||||||
|
|
||||||
|
def request(self, target, config, default_operation=None, test_option=None, error_option=None):
|
||||||
|
"""Loads all or part of the specified *config* to the *target* configuration datastore.
|
||||||
|
|
||||||
|
*target* is the name of the configuration datastore being edited
|
||||||
|
|
||||||
|
*config* is the configuration, which must be rooted in the `config` element. It can be specified either as a string or an :class:`~xml.etree.ElementTree.Element`.
|
||||||
|
|
||||||
|
*default_operation* if specified must be one of { `"merge"`, `"replace"`, or `"none"` }
|
||||||
|
|
||||||
|
*test_option* if specified must be one of { `"test_then_set"`, `"set"` }
|
||||||
|
|
||||||
|
*error_option* if specified must be one of { `"stop-on-error"`, `"continue-on-error"`, `"rollback-on-error"` }
|
||||||
|
|
||||||
|
The `"rollback-on-error"` *error_option* depends on the `:rollback-on-error` capability.
|
||||||
|
"""
|
||||||
|
node = new_ele("edit-config")
|
||||||
|
node.append(util.datastore_or_url("target", target, self._assert))
|
||||||
|
if error_option is not None:
|
||||||
|
if error_option == "rollback-on-error":
|
||||||
|
self._assert(":rollback-on-error")
|
||||||
|
sub_ele(node, "error-option").text = error_option
|
||||||
|
if test_option is not None:
|
||||||
|
self._assert(':validate')
|
||||||
|
sub_ele(node, "test-option").text = test_option
|
||||||
|
if default_operation is not None:
|
||||||
|
# TODO: check if it is a valid default-operation
|
||||||
|
sub_ele(node, "default-operation").text = default_operation
|
||||||
|
node.append(validated_element(config, ("config", qualify("config"))))
|
||||||
|
return self._request(node)
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteConfig(RPC):
|
||||||
|
"`delete-config` RPC"
|
||||||
|
|
||||||
|
def request(self, target):
|
||||||
|
"""Delete a configuration datastore.
|
||||||
|
|
||||||
|
*target* specifies the name or URL of configuration datastore to delete
|
||||||
|
|
||||||
|
:seealso: :ref:`srctarget_params`"""
|
||||||
|
node = new_ele("delete-config")
|
||||||
|
node.append(util.datastore_or_url("target", target, self._assert))
|
||||||
|
return self._request(node)
|
||||||
|
|
||||||
|
|
||||||
|
class CopyConfig(RPC):
|
||||||
|
"`copy-config` RPC"
|
||||||
|
|
||||||
|
def request(self, source, target):
|
||||||
|
"""Create or replace an entire configuration datastore with the contents of another complete
|
||||||
|
configuration datastore.
|
||||||
|
|
||||||
|
*source* is the name of the configuration datastore to use as the source of the copy operation or `config` element containing the configuration subtree to copy
|
||||||
|
|
||||||
|
*target* is the name of the configuration datastore to use as the destination of the copy operation
|
||||||
|
|
||||||
|
:seealso: :ref:`srctarget_params`"""
|
||||||
|
node = new_ele("copy-config")
|
||||||
|
node.append(util.datastore_or_url("target", target, self._assert))
|
||||||
|
node.append(util.datastore_or_url("source", source, self._assert))
|
||||||
|
return self._request(node)
|
||||||
|
|
||||||
|
|
||||||
|
class Validate(RPC):
|
||||||
|
"`validate` RPC. Depends on the `:validate` capability."
|
||||||
|
|
||||||
|
DEPENDS = [':validate']
|
||||||
|
|
||||||
|
def request(self, source):
|
||||||
|
"""Validate the contents of the specified configuration.
|
||||||
|
|
||||||
|
*source* is the name of the configuration datastore being validated or `config` element containing the configuration subtree to be validated
|
||||||
|
|
||||||
|
:seealso: :ref:`srctarget_params`"""
|
||||||
|
node = new_ele("validate")
|
||||||
|
try:
|
||||||
|
src = validated_element(source, ("config", qualify("config")))
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(e)
|
||||||
|
src = util.datastore_or_url("source", source, self._assert)
|
||||||
|
(node if src.tag == "source" else sub_ele(node, "source")).append(src)
|
||||||
|
return self._request(node)
|
||||||
|
|
||||||
|
|
||||||
|
class Commit(RPC):
|
||||||
|
"`commit` RPC. Depends on the `:candidate` capability, and the `:confirmed-commit`."
|
||||||
|
|
||||||
|
DEPENDS = [':candidate']
|
||||||
|
|
||||||
|
def request(self, confirmed=False, timeout=None):
|
||||||
|
"""Commit the candidate configuration as the device's new current configuration. Depends on the `:candidate` capability.
|
||||||
|
|
||||||
|
A confirmed commit (i.e. if *confirmed* is `True`) is reverted if there is no followup commit within the *timeout* interval. If no timeout is specified the confirm timeout defaults to 600 seconds (10 minutes). A confirming commit may have the *confirmed* parameter but this is not required. Depends on the `:confirmed-commit` capability.
|
||||||
|
|
||||||
|
*confirmed* whether this is a confirmed commit
|
||||||
|
|
||||||
|
*timeout* specifies the confirm timeout in seconds"""
|
||||||
|
node = new_ele("commit")
|
||||||
|
if confirmed:
|
||||||
|
self._assert(":confirmed-commit")
|
||||||
|
sub_ele(node, "confirmed")
|
||||||
|
if timeout is not None:
|
||||||
|
sub_ele(node, "confirm-timeout").text = timeout
|
||||||
|
return self._request(node)
|
||||||
|
|
||||||
|
|
||||||
|
class DiscardChanges(RPC):
|
||||||
|
"`discard-changes` RPC. Depends on the `:candidate` capability."
|
||||||
|
|
||||||
|
DEPENDS = [":candidate"]
|
||||||
|
|
||||||
|
def request(self):
|
||||||
|
"""Revert the candidate configuration to the currently running configuration. Any uncommitted changes are discarded."""
|
||||||
|
return self._request(new_ele("discard-changes"))
|
||||||
@ -1,4 +1,4 @@
|
|||||||
# Copyright (C) 2017 Nippon Telegraph and Telephone Corporation.
|
# Copyright 2009 Shikhar Bhushan
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -8,13 +8,17 @@
|
|||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
"""
|
from ncclient import NCClientError
|
||||||
Server implementation for Zebra protocol service.
|
|
||||||
|
|
||||||
This module provides the server side implementation for Zebra protocol.
|
class OperationError(NCClientError):
|
||||||
"""
|
pass
|
||||||
|
|
||||||
|
class TimeoutExpiredError(NCClientError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class MissingCapabilityError(NCClientError):
|
||||||
|
pass
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user