Compare commits

..

No commits in common. "master" and "v4.0" have entirely different histories.
master ... v4.0

629 changed files with 18435 additions and 46033 deletions

View File

@ -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

View File

@ -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

View File

@ -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"
]
}

View File

@ -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
View 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

View File

@ -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

View File

@ -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
View File

@ -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
View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 = {}

View File

@ -1,2 +0,0 @@
[parsers]
smart_quotes: false

View File

@ -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

View File

@ -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

View File

@ -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
======= =======

View File

@ -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

View File

@ -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:

View File

@ -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]

View File

@ -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:

View File

@ -1,6 +0,0 @@
***
ARP
***
.. automodule:: ryu.lib.packet.arp
:members:

View File

@ -1,6 +0,0 @@
*****************
Packet Base Class
*****************
.. automodule:: ryu.lib.packet.packet_base
:members:

View File

@ -1,6 +0,0 @@
***
BFD
***
.. automodule:: ryu.lib.packet.bfd
:members:

View File

@ -1,6 +0,0 @@
***
BGP
***
.. automodule:: ryu.lib.packet.bgp
:members:

View File

@ -1,6 +0,0 @@
***
BMP
***
.. automodule:: ryu.lib.packet.bmp
:members:

View File

@ -1,6 +0,0 @@
****
BPDU
****
.. automodule:: ryu.lib.packet.bpdu
:members:

View File

@ -1,6 +0,0 @@
***
CFM
***
.. automodule:: ryu.lib.packet.cfm
:members:

View File

@ -1,6 +0,0 @@
****
DHCP
****
.. automodule:: ryu.lib.packet.dhcp
:members:

View File

@ -1,6 +0,0 @@
*****
DHCP6
*****
.. automodule:: ryu.lib.packet.dhcp6
:members:

View File

@ -1,6 +0,0 @@
********
Ethernet
********
.. automodule:: ryu.lib.packet.ethernet
:members:

View File

@ -1,6 +0,0 @@
******
Geneve
******
.. automodule:: ryu.lib.packet.geneve
:members:

View File

@ -1,6 +0,0 @@
***
GRE
***
.. automodule:: ryu.lib.packet.gre
:members:

View File

@ -1,6 +0,0 @@
****
ICMP
****
.. automodule:: ryu.lib.packet.icmp
:members:

View File

@ -1,6 +0,0 @@
******
ICMPv6
******
.. automodule:: ryu.lib.packet.icmpv6
:members:

View File

@ -1,6 +0,0 @@
****
IGMP
****
.. automodule:: ryu.lib.packet.igmp
:members:

View File

@ -1,6 +0,0 @@
****
IPv4
****
.. automodule:: ryu.lib.packet.ipv4
:members:

View File

@ -1,6 +0,0 @@
****
IPv6
****
.. automodule:: ryu.lib.packet.ipv6
:members:

View File

@ -1,6 +0,0 @@
***
LLC
***
.. automodule:: ryu.lib.packet.llc
:members:

View File

@ -1,6 +0,0 @@
****
LLDP
****
.. automodule:: ryu.lib.packet.lldp
:members:

View File

@ -1,6 +0,0 @@
****
MPLS
****
.. automodule:: ryu.lib.packet.mpls
:members:

View File

@ -1,6 +0,0 @@
********
OpenFlow
********
.. automodule:: ryu.lib.packet.openflow
:members:

View File

@ -1,6 +0,0 @@
****
OSPF
****
.. automodule:: ryu.lib.packet.ospf
:members:

View File

@ -1,6 +0,0 @@
***
PBB
***
.. automodule:: ryu.lib.packet.pbb
:members:

View File

@ -1,6 +0,0 @@
****
SCTP
****
.. automodule:: ryu.lib.packet.sctp
:members:

View File

@ -1,6 +0,0 @@
****
Slow
****
.. automodule:: ryu.lib.packet.slow
:members:

View File

@ -1,6 +0,0 @@
***
TCP
***
.. automodule:: ryu.lib.packet.tcp
:members:

View File

@ -1,6 +0,0 @@
***
UDP
***
.. automodule:: ryu.lib.packet.udp
:members:

View File

@ -1,6 +0,0 @@
****
VLAN
****
.. automodule:: ryu.lib.packet.vlan
:members:

View File

@ -1,6 +0,0 @@
****
VRRP
****
.. automodule:: ryu.lib.packet.vrrp
:members:

View File

@ -1,6 +0,0 @@
*****
VXLAN
*****
.. automodule:: ryu.lib.packet.vxlan
:members:

View File

@ -1,6 +0,0 @@
*****
Zebra
*****
.. automodule:: ryu.lib.packet.zebra
:members:

View File

@ -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

View File

@ -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

View File

@ -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.
=========== ==================================================================

View File

@ -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

View File

@ -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.

View File

@ -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)
@ -69,29 +66,24 @@ the ports.
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.

View File

@ -1 +0,0 @@
pip==20.3.4

View File

@ -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,19 +103,13 @@ 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() {
@ -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

View File

@ -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))

View File

@ -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']

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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.'}

View File

@ -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].'

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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)

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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, {})

View File

@ -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)

View File

@ -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])

View File

@ -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

View File

@ -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 (

View File

@ -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):

View File

@ -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)

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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 = {

View File

@ -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

View File

@ -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

View 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]

View 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)

View 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`."

View 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'
]

View 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"))

View File

@ -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