mirror of
https://github.com/faucetsdn/ryu.git
synced 2026-01-21 00:21:24 +01:00
Merge branch 'master' into patch-1
This commit is contained in:
commit
0e29dbe9ef
25
.github/workflows/tests-unit.yml
vendored
Normal file
25
.github/workflows/tests-unit.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
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 pip
|
||||
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
|
||||
11
.readthedocs.yml
Normal file
11
.readthedocs.yml
Normal file
@ -0,0 +1,11 @@
|
||||
version: 2
|
||||
build:
|
||||
image: latest
|
||||
python:
|
||||
version: 3.6
|
||||
install:
|
||||
- method: pip
|
||||
path: .
|
||||
sphinx:
|
||||
configuration: doc/source/conf.py
|
||||
formats: all
|
||||
15
.renovaterc.json
Normal file
15
.renovaterc.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"separateMajorMinor": false,
|
||||
"schedule": [
|
||||
"after 10pm every weekday",
|
||||
"before 5am every weekday",
|
||||
"every weekend"
|
||||
],
|
||||
"timezone": "Pacific/Auckland",
|
||||
"extends": [
|
||||
"config:base",
|
||||
":prHourlyLimit1",
|
||||
":preserveSemverRanges",
|
||||
"docker:enableMajor"
|
||||
]
|
||||
}
|
||||
9
.stickler.yml
Normal file
9
.stickler.yml
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
linters:
|
||||
flake8:
|
||||
python: 3
|
||||
max-line-length: 120
|
||||
pep8:
|
||||
python: 3
|
||||
max-line-length: 120
|
||||
py3k:
|
||||
43
.travis.yml
43
.travis.yml
@ -1,43 +0,0 @@
|
||||
language: python
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- python: 3.6
|
||||
env: TOX_ENV=pycodestyle
|
||||
- python: 3.6
|
||||
env: TOX_ENV=py36
|
||||
- python: 3.6
|
||||
env: TOX_ENV=autopep8
|
||||
|
||||
- python: 2.7
|
||||
env: TOX_ENV=pycodestyle
|
||||
- python: 2.7
|
||||
env: TOX_ENV=py27
|
||||
|
||||
- python: 3.4
|
||||
env: TOX_ENV=py34
|
||||
|
||||
- python: 3.5
|
||||
env: TOX_ENV=py35
|
||||
|
||||
- python: 3.7-dev
|
||||
env: TOX_ENV=py37
|
||||
|
||||
# This is disabled because of trouble running on travis CI.
|
||||
# - python: pypy
|
||||
# env: TOX_ENV=pypy
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
sudo: required # Required to enable Docker service
|
||||
|
||||
install:
|
||||
- pip install tox coveralls
|
||||
- bash ryu/tests/integrated/common/install_docker_test_pkg_for_travis.sh
|
||||
|
||||
script:
|
||||
- NOSE_VERBOSE=0 tox -e $TOX_ENV
|
||||
|
||||
after_success:
|
||||
- coveralls
|
||||
@ -5,20 +5,8 @@ How to Get Your Change Into Ryu
|
||||
Submitting a change
|
||||
===================
|
||||
|
||||
Send patches to ryu-devel@lists.sourceforge.net. Please don't use "Pull
|
||||
Request" on GitHub. We expect you to send patches in "git-format-patch"
|
||||
style.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# "N" means the number of commits to be included
|
||||
$ git format-patch -s HEAD~N
|
||||
|
||||
# To add cover (e.g., [PATCH 0/X]), specify "--cover-letter" option
|
||||
$ git format-patch -s --cover-letter HEAD~N
|
||||
|
||||
# You can send patches by "git send-email" command
|
||||
$ git send-email --to="ryu-devel@lists.sourceforge.net" *.patch
|
||||
To send patches to ryu, please make a
|
||||
`pull request <https://github.com/faucetsdn/ryu>`_ on GitHub.
|
||||
|
||||
Please check your changes with autopep8, pycodestyle(pep8) and running
|
||||
unit tests to make sure that they don't break the existing features.
|
||||
@ -42,9 +30,9 @@ features (it's not a must though).
|
||||
|
||||
Python version and libraries
|
||||
============================
|
||||
* Python 2.7, 3.4, 3.5:
|
||||
* Python 3.5, 3.6, 3.7, 3.8, 3.9:
|
||||
|
||||
Ryu supports multiple Python version. CI tests on Travis-CI is running
|
||||
Ryu supports multiple Python versions. CI tests on GitHub Actions is running
|
||||
on these versions.
|
||||
|
||||
* standard library + widely used library:
|
||||
|
||||
@ -20,7 +20,7 @@ Installing Ryu is quite easy::
|
||||
|
||||
If you prefer to install Ryu from the source code::
|
||||
|
||||
% git clone git://github.com/osrg/ryu.git
|
||||
% git clone https://github.com/faucetsdn/ryu.git
|
||||
% cd ryu; pip install .
|
||||
|
||||
If you want to write your Ryu application, have a look at
|
||||
@ -59,7 +59,7 @@ On Ubuntu(16.04 LTS or later)::
|
||||
|
||||
Support
|
||||
=======
|
||||
Ryu Official site is `<http://osrg.github.io/ryu/>`_.
|
||||
Ryu Official site is `<https://ryu-sdn.org/>`_.
|
||||
|
||||
If you have any
|
||||
questions, suggestions, and patches, the mailing list is available at
|
||||
|
||||
10
debian/control
vendored
10
debian/control
vendored
@ -6,7 +6,7 @@ Build-Depends: debhelper (>= 9.0.0), python-all (>= 2.6), python-sphinx
|
||||
Build-Depends-Indep:
|
||||
python-eventlet,
|
||||
python-lxml,
|
||||
python-msgpack (>= 0.3.0),
|
||||
python-msgpack (>= 0.4.0),
|
||||
python-netaddr,
|
||||
python-oslo.config (>= 1:1.2.0),
|
||||
python-paramiko,
|
||||
@ -17,9 +17,9 @@ Build-Depends-Indep:
|
||||
python-pip,
|
||||
python-pbr
|
||||
Standards-Version: 3.9.5
|
||||
Homepage: http://osrg.github.io/ryu/
|
||||
Vcs-Git: git://github.com/osrg/ryu.git
|
||||
Vcs-Browser: http://github.com/osrg/ryu
|
||||
Homepage: https://ryu-sdn.org
|
||||
Vcs-Git: git://github.com/faucetsdn/ryu.git
|
||||
Vcs-Browser: https://github.com/faucetsdn/ryu
|
||||
XS-Python-Version: >= 2.6
|
||||
|
||||
Package: python-ryu
|
||||
@ -28,7 +28,7 @@ Section: python
|
||||
Depends:
|
||||
python-eventlet,
|
||||
python-lxml,
|
||||
python-msgpack (>= 0.3.0),
|
||||
python-msgpack (>= 0.4.0),
|
||||
python-netaddr,
|
||||
python-oslo.config (>= 1:1.2.0),
|
||||
python-paramiko,
|
||||
|
||||
2
debian/copyright
vendored
2
debian/copyright
vendored
@ -1,6 +1,6 @@
|
||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: ryu
|
||||
Source: http://github.com/osrg/ryu
|
||||
Source: http://github.com/faucetsdn/ryu
|
||||
|
||||
Files: *
|
||||
Copyright: 2014 Ryu Project Team <ryu-devel@lists.sourceforge.net>
|
||||
|
||||
@ -19,7 +19,7 @@ The test procedure
|
||||
* run LINC switch
|
||||
* run Ryu test_of_config app
|
||||
|
||||
For getting/installing Ryu itself, please refer to http://osrg.github.io/ryu/
|
||||
For getting/installing Ryu itself, please refer to https://ryu-sdn.org/
|
||||
|
||||
|
||||
Install Erlang environment
|
||||
|
||||
@ -14,7 +14,7 @@ Using Ryu Network Operating System with OpenStack as OpenFlow controller
|
||||
Ryu cooperates with OpenStack using Quantum Ryu plugin. The plugin is
|
||||
available in the official Quantum releases.
|
||||
|
||||
For more information, please visit http://github.com/osrg/ryu/wiki/OpenStack .
|
||||
For more information, please visit https://github.com/faucetsdn/ryu/wiki/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
|
||||
OpenStack with Ryu.
|
||||
|
||||
@ -118,9 +118,9 @@ run a Ryu application that does something useful.
|
||||
|
||||
Is a dumb L2 switch is too dumb? You want to implement a learning L2
|
||||
switch? Move to `the next step
|
||||
<https://github.com/osrg/ryu/blob/master/ryu/app/simple_switch.py>`_. You
|
||||
<https://github.com/faucetsdn/ryu/blob/master/ryu/app/simple_switch.py>`_. You
|
||||
can learn from the existing Ryu applications at `ryu/app
|
||||
<https://github.com/osrg/ryu/blob/master/ryu/app/>`_ directory and
|
||||
<https://github.com/faucetsdn/ryu/blob/master/ryu/app/>`_ directory and
|
||||
`integrated tests
|
||||
<https://github.com/osrg/ryu/blob/master/ryu/tests/integrated/>`_
|
||||
<https://github.com/faucetsdn/ryu/blob/master/ryu/tests/integrated/>`_
|
||||
directory.
|
||||
|
||||
@ -14,5 +14,5 @@
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
version_info = (4, 31)
|
||||
version_info = (4, 34)
|
||||
version = '.'.join(map(str, version_info))
|
||||
|
||||
@ -85,7 +85,7 @@ class SimpleSwitch13(app_manager.RyuApp):
|
||||
dst = eth.dst
|
||||
src = eth.src
|
||||
|
||||
dpid = datapath.id
|
||||
dpid = format(datapath.id, "d").zfill(16)
|
||||
self.mac_to_port.setdefault(dpid, {})
|
||||
|
||||
self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)
|
||||
|
||||
@ -85,21 +85,21 @@ class SimpleSwitchController(ControllerBase):
|
||||
def list_mac_table(self, req, **kwargs):
|
||||
|
||||
simple_switch = self.simple_switch_app
|
||||
dpid = dpid_lib.str_to_dpid(kwargs['dpid'])
|
||||
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', body=body)
|
||||
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 = dpid_lib.str_to_dpid(kwargs['dpid'])
|
||||
dpid = kwargs['dpid']
|
||||
try:
|
||||
new_entry = req.json if req.body else {}
|
||||
except ValueError:
|
||||
@ -111,6 +111,6 @@ class SimpleSwitchController(ControllerBase):
|
||||
try:
|
||||
mac_table = simple_switch.set_mac_to_port(dpid, new_entry)
|
||||
body = json.dumps(mac_table)
|
||||
return Response(content_type='application/json', body=body)
|
||||
return Response(content_type='application/json', text=body)
|
||||
except Exception as e:
|
||||
return Response(status=500)
|
||||
|
||||
@ -67,6 +67,7 @@ CONF.register_cli_opts([
|
||||
cfg.StrOpt('ctl-privkey', default=None, help='controller private key'),
|
||||
cfg.StrOpt('ctl-cert', default=None, help='controller certificate'),
|
||||
cfg.StrOpt('ca-certs', default=None, help='CA certificates'),
|
||||
cfg.StrOpt('ciphers', default=None, help='list of ciphers to enable'),
|
||||
cfg.ListOpt('ofp-switch-address-list', item_type=str, default=[],
|
||||
help='list of IP address and port pairs (default empty). '
|
||||
'e.g., "127.0.0.1:6653,[::1]:6653"'),
|
||||
@ -182,6 +183,9 @@ class OpenFlowController(object):
|
||||
# Restrict non-safe versions
|
||||
ssl_args['ssl_ctx'].options |= ssl.OP_NO_SSLv3 | ssl.OP_NO_SSLv2
|
||||
|
||||
if CONF.ciphers is not None:
|
||||
ssl_args['ciphers'] = CONF.ciphers
|
||||
|
||||
if CONF.ca_certs is not None:
|
||||
server = StreamServer((CONF.ofp_listen_host,
|
||||
ofp_ssl_listen_port),
|
||||
|
||||
@ -127,21 +127,25 @@ if HUB_TYPE == 'eventlet':
|
||||
self.server = eventlet.listen(listen_info)
|
||||
|
||||
if ssl_args:
|
||||
def wrap_and_handle(sock, addr):
|
||||
ssl_args.setdefault('server_side', True)
|
||||
if 'ssl_ctx' in ssl_args:
|
||||
ctx = ssl_args.pop('ssl_ctx')
|
||||
ctx.load_cert_chain(ssl_args.pop('certfile'),
|
||||
ssl_args.pop('keyfile'))
|
||||
if 'cert_reqs' in ssl_args:
|
||||
ctx.verify_mode = ssl_args.pop('cert_reqs')
|
||||
if 'ca_certs' in ssl_args:
|
||||
ctx.load_verify_locations(ssl_args.pop('ca_certs'))
|
||||
ssl_args.setdefault('server_side', True)
|
||||
if 'ssl_ctx' in ssl_args:
|
||||
ctx = ssl_args.pop('ssl_ctx')
|
||||
ctx.load_cert_chain(ssl_args.pop('certfile'),
|
||||
ssl_args.pop('keyfile'))
|
||||
if 'cert_reqs' in ssl_args:
|
||||
ctx.verify_mode = ssl_args.pop('cert_reqs')
|
||||
if 'ca_certs' in ssl_args:
|
||||
ctx.load_verify_locations(ssl_args.pop('ca_certs'))
|
||||
|
||||
def wrap_and_handle_ctx(sock, addr):
|
||||
handle(ctx.wrap_socket(sock, **ssl_args), addr)
|
||||
else:
|
||||
|
||||
self.handle = wrap_and_handle_ctx
|
||||
else:
|
||||
def wrap_and_handle_ssl(sock, addr):
|
||||
handle(ssl.wrap_socket(sock, **ssl_args), addr)
|
||||
|
||||
self.handle = wrap_and_handle
|
||||
self.handle = wrap_and_handle_ssl
|
||||
else:
|
||||
self.handle = handle
|
||||
|
||||
|
||||
@ -782,7 +782,16 @@ class _LabelledAddrPrefix(_AddrPrefix):
|
||||
# Routes field should be set to 0x800000. (Of course, terminating the
|
||||
# BGP session also withdraws all the previously advertised routes.)
|
||||
#
|
||||
_WITHDRAW_LABEL = 0x800000
|
||||
# RFC8227
|
||||
# 2.4 How to Explicitly Withdraw the Binding of a Label to a Prefix
|
||||
# [RFC3107] also made it possible to withdraw a binding without specifying
|
||||
# the label explicitly, by setting the Compatibility field to 0x800000.
|
||||
# However, some implementations set it to 0x000000. In order to ensure
|
||||
# backwards compatibility, it is RECOMMENDED by this document that the
|
||||
# Compatibility field be set to 0x800000, but it is REQUIRED that it be
|
||||
# ignored upon reception.
|
||||
#
|
||||
_WITHDRAW_LABELS = [0x800000, 0x000000]
|
||||
|
||||
def __init__(self, length, addr, labels=None, **kwargs):
|
||||
labels = labels if labels else []
|
||||
@ -823,7 +832,7 @@ class _LabelledAddrPrefix(_AddrPrefix):
|
||||
labels = addr[0]
|
||||
rest = addr[1:]
|
||||
labels = [x << 4 for x in labels]
|
||||
if labels and labels[-1] != cls._WITHDRAW_LABEL:
|
||||
if labels and labels[-1] not in cls._WITHDRAW_LABELS:
|
||||
labels[-1] |= 1 # bottom of stack
|
||||
bin_labels = list(cls._label_to_bin(l) for l in labels)
|
||||
return bytes(reduce(lambda x, y: x + y, bin_labels,
|
||||
@ -837,7 +846,7 @@ class _LabelledAddrPrefix(_AddrPrefix):
|
||||
while True:
|
||||
(label, bin_) = cls._label_from_bin(bin_)
|
||||
labels.append(label)
|
||||
if label & 1 or label == cls._WITHDRAW_LABEL:
|
||||
if label & 1 or label in cls._WITHDRAW_LABELS:
|
||||
break
|
||||
assert length > struct.calcsize(cls._LABEL_PACK_STR) * len(labels)
|
||||
except struct.error:
|
||||
@ -857,7 +866,7 @@ class _LabelledAddrPrefix(_AddrPrefix):
|
||||
while True:
|
||||
(label, rest) = cls._label_from_bin(rest)
|
||||
labels.append(label >> 4)
|
||||
if label & 1 or label == cls._WITHDRAW_LABEL:
|
||||
if label & 1 or label in cls._WITHDRAW_LABELS:
|
||||
break
|
||||
return (labels,) + cls._prefix_from_bin(rest)
|
||||
|
||||
|
||||
@ -60,6 +60,8 @@ BMP_STAT_TYPE_INV_UPDATE_DUE_TO_ORIGINATOR_ID = 5
|
||||
BMP_STAT_TYPE_INV_UPDATE_DUE_TO_AS_CONFED_LOOP = 6
|
||||
BMP_STAT_TYPE_ADJ_RIB_IN = 7
|
||||
BMP_STAT_TYPE_LOC_RIB = 8
|
||||
BMP_STAT_TYPE_ADJ_RIB_OUT = 14
|
||||
BMP_STAT_TYPE_EXPORT_RIB = 15
|
||||
|
||||
BMP_PEER_DOWN_REASON_UNKNOWN = 0
|
||||
BMP_PEER_DOWN_REASON_LOCAL_BGP_NOTIFICATION = 1
|
||||
@ -157,7 +159,8 @@ class BMPPeerMessage(BMPMessage):
|
||||
type Type field. one of BMP\_MSG\_ constants.
|
||||
peer_type The type of the peer.
|
||||
is_post_policy Indicate the message reflects the post-policy
|
||||
Adj-RIB-In
|
||||
is_adj_rib_out Indicate the message reflects Adj-RIB-Out (defaults
|
||||
to Adj-RIB-In)
|
||||
peer_distinguisher Use for L3VPN router which can have multiple
|
||||
instance.
|
||||
peer_address The remote IP address associated with the TCP
|
||||
@ -179,12 +182,13 @@ class BMPPeerMessage(BMPMessage):
|
||||
|
||||
def __init__(self, peer_type, is_post_policy, peer_distinguisher,
|
||||
peer_address, peer_as, peer_bgp_id, timestamp,
|
||||
version=VERSION, type_=None, len_=None):
|
||||
version=VERSION, type_=None, len_=None, is_adj_rib_out=False):
|
||||
super(BMPPeerMessage, self).__init__(version=version,
|
||||
len_=len_,
|
||||
type_=type_)
|
||||
self.peer_type = peer_type
|
||||
self.is_post_policy = is_post_policy
|
||||
self.is_adj_rib_out = is_adj_rib_out
|
||||
self.peer_distinguisher = peer_distinguisher
|
||||
self.peer_address = peer_address
|
||||
self.peer_as = peer_as
|
||||
@ -200,6 +204,11 @@ class BMPPeerMessage(BMPMessage):
|
||||
|
||||
rest = buf[struct.calcsize(cls._PEER_HDR_PACK_STR):]
|
||||
|
||||
if peer_flags & (1 << 4):
|
||||
is_adj_rib_out = True
|
||||
else:
|
||||
is_adj_rib_out = False
|
||||
|
||||
if peer_flags & (1 << 6):
|
||||
is_post_policy = True
|
||||
else:
|
||||
@ -221,12 +230,16 @@ class BMPPeerMessage(BMPMessage):
|
||||
"peer_address": peer_address,
|
||||
"peer_as": peer_as,
|
||||
"peer_bgp_id": peer_bgp_id,
|
||||
"timestamp": timestamp
|
||||
"timestamp": timestamp,
|
||||
"is_adj_rib_out": is_adj_rib_out,
|
||||
}, rest
|
||||
|
||||
def serialize_tail(self):
|
||||
flags = 0
|
||||
|
||||
if self.is_adj_rib_out:
|
||||
flags |= (1 << 4)
|
||||
|
||||
if self.is_post_policy:
|
||||
flags |= (1 << 6)
|
||||
|
||||
@ -275,7 +288,7 @@ class BMPRouteMonitoring(BMPPeerMessage):
|
||||
def __init__(self, bgp_update, peer_type, is_post_policy,
|
||||
peer_distinguisher, peer_address, peer_as, peer_bgp_id,
|
||||
timestamp, version=VERSION, type_=BMP_MSG_ROUTE_MONITORING,
|
||||
len_=None):
|
||||
len_=None, is_adj_rib_out=False):
|
||||
super(BMPRouteMonitoring,
|
||||
self).__init__(peer_type=peer_type,
|
||||
is_post_policy=is_post_policy,
|
||||
@ -286,7 +299,8 @@ class BMPRouteMonitoring(BMPPeerMessage):
|
||||
timestamp=timestamp,
|
||||
len_=len_,
|
||||
type_=type_,
|
||||
version=version)
|
||||
version=version,
|
||||
is_adj_rib_out=is_adj_rib_out)
|
||||
self.bgp_update = bgp_update
|
||||
|
||||
@classmethod
|
||||
@ -335,7 +349,8 @@ class BMPStatisticsReport(BMPPeerMessage):
|
||||
|
||||
def __init__(self, stats, peer_type, is_post_policy, peer_distinguisher,
|
||||
peer_address, peer_as, peer_bgp_id, timestamp,
|
||||
version=VERSION, type_=BMP_MSG_STATISTICS_REPORT, len_=None):
|
||||
version=VERSION, type_=BMP_MSG_STATISTICS_REPORT, len_=None,
|
||||
is_adj_rib_out=False):
|
||||
super(BMPStatisticsReport,
|
||||
self).__init__(peer_type=peer_type,
|
||||
is_post_policy=is_post_policy,
|
||||
@ -346,7 +361,8 @@ class BMPStatisticsReport(BMPPeerMessage):
|
||||
timestamp=timestamp,
|
||||
len_=len_,
|
||||
type_=type_,
|
||||
version=version)
|
||||
version=version,
|
||||
is_adj_rib_out=is_adj_rib_out)
|
||||
self.stats = stats
|
||||
|
||||
@classmethod
|
||||
@ -381,7 +397,9 @@ class BMPStatisticsReport(BMPPeerMessage):
|
||||
type_ == BMP_STAT_TYPE_INV_UPDATE_DUE_TO_AS_CONFED_LOOP:
|
||||
value, = struct.unpack_from('!I', six.binary_type(value))
|
||||
elif type_ == BMP_STAT_TYPE_ADJ_RIB_IN or \
|
||||
type_ == BMP_STAT_TYPE_LOC_RIB:
|
||||
type_ == BMP_STAT_TYPE_LOC_RIB or \
|
||||
type_ == BMP_STAT_TYPE_ADJ_RIB_OUT or \
|
||||
type_ == BMP_STAT_TYPE_EXPORT_RIB:
|
||||
value, = struct.unpack_from('!Q', six.binary_type(value))
|
||||
|
||||
buf = buf[cls._MIN_LEN + len_:]
|
||||
@ -410,7 +428,9 @@ class BMPStatisticsReport(BMPPeerMessage):
|
||||
t == BMP_STAT_TYPE_INV_UPDATE_DUE_TO_AS_CONFED_LOOP:
|
||||
valuepackstr = 'I'
|
||||
elif t == BMP_STAT_TYPE_ADJ_RIB_IN or \
|
||||
t == BMP_STAT_TYPE_LOC_RIB:
|
||||
t == BMP_STAT_TYPE_LOC_RIB or \
|
||||
t == BMP_STAT_TYPE_ADJ_RIB_OUT or \
|
||||
t == BMP_STAT_TYPE_EXPORT_RIB:
|
||||
valuepackstr = 'Q'
|
||||
else:
|
||||
continue
|
||||
@ -440,7 +460,8 @@ class BMPPeerDownNotification(BMPPeerMessage):
|
||||
def __init__(self, reason, data, peer_type, is_post_policy,
|
||||
peer_distinguisher, peer_address, peer_as, peer_bgp_id,
|
||||
timestamp, version=VERSION,
|
||||
type_=BMP_MSG_PEER_DOWN_NOTIFICATION, len_=None):
|
||||
type_=BMP_MSG_PEER_DOWN_NOTIFICATION, len_=None,
|
||||
is_adj_rib_out=False):
|
||||
|
||||
super(BMPPeerDownNotification,
|
||||
self).__init__(peer_type=peer_type,
|
||||
@ -452,7 +473,8 @@ class BMPPeerDownNotification(BMPPeerMessage):
|
||||
timestamp=timestamp,
|
||||
len_=len_,
|
||||
type_=type_,
|
||||
version=version)
|
||||
version=version,
|
||||
is_adj_rib_out=is_adj_rib_out)
|
||||
|
||||
self.reason = reason
|
||||
self.data = data
|
||||
@ -537,7 +559,7 @@ class BMPPeerUpNotification(BMPPeerMessage):
|
||||
peer_type, is_post_policy, peer_distinguisher,
|
||||
peer_address, peer_as, peer_bgp_id, timestamp,
|
||||
version=VERSION, type_=BMP_MSG_PEER_UP_NOTIFICATION,
|
||||
len_=None):
|
||||
len_=None, is_adj_rib_out=False):
|
||||
super(BMPPeerUpNotification,
|
||||
self).__init__(peer_type=peer_type,
|
||||
is_post_policy=is_post_policy,
|
||||
@ -548,7 +570,8 @@ class BMPPeerUpNotification(BMPPeerMessage):
|
||||
timestamp=timestamp,
|
||||
len_=len_,
|
||||
type_=type_,
|
||||
version=version)
|
||||
version=version,
|
||||
is_adj_rib_out=is_adj_rib_out)
|
||||
self.local_address = local_address
|
||||
self.local_port = local_port
|
||||
self.remote_port = remote_port
|
||||
|
||||
@ -268,7 +268,7 @@ class cc_message(operation):
|
||||
self._opcode = CFM_CC_MESSAGE
|
||||
assert rdi in [0, 1]
|
||||
self.rdi = rdi
|
||||
assert interval is not 0
|
||||
assert interval != 0
|
||||
self.interval = interval
|
||||
self.seq_num = seq_num
|
||||
assert 1 <= mep_id <= 8191
|
||||
|
||||
@ -623,6 +623,50 @@ class nd_option_pi(nd_option):
|
||||
return six.binary_type(hdr)
|
||||
|
||||
|
||||
@nd_router_advert.register_nd_option_type
|
||||
class nd_option_mtu(nd_option):
|
||||
"""ICMPv6 sub encoder/decoder class for Neighbor discovery
|
||||
MTU Option. (RFC 4861)
|
||||
|
||||
This is used with ryu.lib.packet.icmpv6.nd_router_advert.
|
||||
|
||||
An instance has the following attributes at least.
|
||||
Most of them are same to the on-wire counterparts but in host byte order.
|
||||
__init__ takes the corresponding args in this order.
|
||||
|
||||
.. tabularcolumns:: |l|p{35em}|
|
||||
|
||||
============== ====================
|
||||
Attribute Description
|
||||
============== ====================
|
||||
mtu MTU.
|
||||
============== ====================
|
||||
"""
|
||||
|
||||
_PACK_STR = '!BBHI'
|
||||
_LEN = struct.calcsize(_PACK_STR)
|
||||
_OPTION_LEN = _LEN // 8
|
||||
|
||||
@classmethod
|
||||
def option_type(cls):
|
||||
return ND_OPTION_MTU
|
||||
|
||||
def __init__(self, mtu=1500):
|
||||
super(nd_option_mtu, self).__init__(self.option_type(), 0)
|
||||
self.mtu = mtu
|
||||
|
||||
@classmethod
|
||||
def parser(cls, buf, offset):
|
||||
(_, _, _, mtu) = struct.unpack_from(cls._PACK_STR, buf, offset)
|
||||
msg = cls(mtu)
|
||||
return msg
|
||||
|
||||
def serialize(self):
|
||||
buf = bytearray(struct.pack(
|
||||
self._PACK_STR, self.option_type(), self._OPTION_LEN, 0, self.mtu))
|
||||
return six.binary_type(buf)
|
||||
|
||||
|
||||
@icmpv6.register_icmpv6_type(ICMPV6_ECHO_REPLY, ICMPV6_ECHO_REQUEST)
|
||||
class echo(_ICMPv6Payload):
|
||||
"""ICMPv6 sub encoder/decoder class for Echo Request and Echo Reply
|
||||
|
||||
@ -40,8 +40,16 @@ class MessageEncoder(object):
|
||||
|
||||
def __init__(self):
|
||||
super(MessageEncoder, self).__init__()
|
||||
self._packer = msgpack.Packer(encoding='utf-8', use_bin_type=True)
|
||||
self._unpacker = msgpack.Unpacker(encoding='utf-8')
|
||||
if msgpack.version >= (1, 0, 0):
|
||||
self._packer = msgpack.Packer()
|
||||
# The strict_map_key=False option is required to use int keys in
|
||||
# maps; it is disabled by default to prevent hash collision denial
|
||||
# of service attacks (hashdos) in scenarios where an attacker can
|
||||
# control the keys to be hashed.
|
||||
self._unpacker = msgpack.Unpacker(strict_map_key=False)
|
||||
else:
|
||||
self._packer = msgpack.Packer(encoding='utf-8', use_bin_type=True)
|
||||
self._unpacker = msgpack.Unpacker(encoding='utf-8')
|
||||
self._next_msgid = 0
|
||||
|
||||
def _create_msgid(self):
|
||||
|
||||
@ -63,6 +63,8 @@ NXAST_CONTROLLER2 = 37
|
||||
NXAST_SAMPLE2 = 38
|
||||
NXAST_OUTPUT_TRUNC = 39
|
||||
NXAST_CT_CLEAR = 43
|
||||
NXAST_RAW_ENCAP = 46
|
||||
NXAST_RAW_DECAP = 47
|
||||
NXAST_DEC_NSH_TTL = 48
|
||||
|
||||
NX_ACTION_RESUBMIT_PACK_STR = '!HHIHHB3x'
|
||||
|
||||
@ -2999,6 +2999,76 @@ def generate(ofp_name, ofpp_name):
|
||||
self.max_len)
|
||||
return data
|
||||
|
||||
class NXActionEncapEther(NXAction):
|
||||
"""
|
||||
Encap Ether
|
||||
|
||||
This action encaps package with ethernet
|
||||
|
||||
And equivalent to the followings action of ovs-ofctl command.
|
||||
|
||||
::
|
||||
|
||||
encap(ethernet)
|
||||
|
||||
Example::
|
||||
|
||||
actions += [parser.NXActionEncapEther()]
|
||||
"""
|
||||
_subtype = nicira_ext.NXAST_RAW_ENCAP
|
||||
|
||||
_fmt_str = '!HI'
|
||||
|
||||
def __init__(self,
|
||||
type_=None, len_=None, vendor=None, subtype=None):
|
||||
super(NXActionEncapEther, self).__init__()
|
||||
self.hdr_size = 0
|
||||
self.new_pkt_type = 0x00000000
|
||||
|
||||
@classmethod
|
||||
def parser(cls, buf):
|
||||
return cls()
|
||||
|
||||
def serialize_body(self):
|
||||
data = bytearray()
|
||||
msg_pack_into(self._fmt_str, data, 0, self.hdr_size, self.new_pkt_type)
|
||||
return data
|
||||
|
||||
class NXActionEncapNsh(NXAction):
|
||||
"""
|
||||
Encap nsh
|
||||
|
||||
This action encaps package with nsh
|
||||
|
||||
And equivalent to the followings action of ovs-ofctl command.
|
||||
|
||||
::
|
||||
|
||||
encap(nsh(md_type=1))
|
||||
|
||||
Example::
|
||||
|
||||
actions += [parser.NXActionEncapNsh()]
|
||||
"""
|
||||
_subtype = nicira_ext.NXAST_RAW_ENCAP
|
||||
|
||||
_fmt_str = '!HI'
|
||||
|
||||
def __init__(self,
|
||||
type_=None, len_=None, vendor=None, subtype=None):
|
||||
super(NXActionEncapNsh, self).__init__()
|
||||
self.hdr_size = hdr_size
|
||||
self.new_pkt_type = 0x0001894F
|
||||
|
||||
@classmethod
|
||||
def parser(cls, buf):
|
||||
return cls()
|
||||
|
||||
def serialize_body(self):
|
||||
data = bytearray()
|
||||
msg_pack_into(self._fmt_str, data, 0, self.hdr_size, self.new_pkt_type)
|
||||
return data
|
||||
|
||||
class NXActionDecNshTtl(NXAction):
|
||||
"""
|
||||
Decrement NSH TTL action
|
||||
@ -3083,6 +3153,8 @@ def generate(ofp_name, ofpp_name):
|
||||
'NXFlowSpecMatch',
|
||||
'NXFlowSpecLoad',
|
||||
'NXFlowSpecOutput',
|
||||
'NXActionEncapNsh',
|
||||
'NXActionEncapEther',
|
||||
'NXActionDecNshTtl',
|
||||
]
|
||||
vars = locals()
|
||||
|
||||
@ -101,8 +101,16 @@ class RpcSession(Activity):
|
||||
def __init__(self, sock, outgoing_msg_sink_iter):
|
||||
self.peer_name = str(sock.getpeername())
|
||||
super(RpcSession, self).__init__(self.NAME_FMT % self.peer_name)
|
||||
self._packer = msgpack.Packer(encoding='utf-8')
|
||||
self._unpacker = msgpack.Unpacker(encoding='utf-8')
|
||||
if msgpack.version >= (1, 0, 0):
|
||||
self._packer = msgpack.Packer()
|
||||
# The strict_map_key=False option is required to use int keys in
|
||||
# maps; it is disabled by default to prevent hash collision denial
|
||||
# of service attacks (hashdos) in scenarios where an attacker can
|
||||
# control the keys to be hashed.
|
||||
self._unpacker = msgpack.Unpacker(strict_map_key=False)
|
||||
else:
|
||||
self._packer = msgpack.Packer(encoding='utf-8', use_bin_type=True)
|
||||
self._unpacker = msgpack.Unpacker(encoding='utf-8')
|
||||
self._next_msgid = 0
|
||||
self._socket = sock
|
||||
self._outgoing_msg_sink_iter = outgoing_msg_sink_iter
|
||||
|
||||
@ -319,7 +319,7 @@ class RemoteOvsdb(app_manager.RyuApp):
|
||||
|
||||
fsm.connected(now())
|
||||
|
||||
session = jsonrpc.Session(fsm, connection)
|
||||
session = jsonrpc.Session(fsm, connection, fsm.get_name())
|
||||
idl = Idl(session, schemas[0])
|
||||
|
||||
system_id = discover_system_id(idl)
|
||||
|
||||
@ -191,6 +191,7 @@ class TestOpenFlowController(unittest.TestCase):
|
||||
conf_mock.ofp_ssl_listen_port = port
|
||||
conf_mock.ofp_listen_host = "127.0.0.1"
|
||||
conf_mock.ca_certs = None
|
||||
conf_mock.ciphers = None
|
||||
conf_mock.ctl_cert = os.path.join(this_dir, 'cert.crt')
|
||||
conf_mock.ctl_privkey = os.path.join(this_dir, 'cert.key')
|
||||
c = controller.OpenFlowController()
|
||||
|
||||
@ -35,7 +35,7 @@ LOG = logging.getLogger(__name__)
|
||||
DOCKER_IMAGE_MININET = 'osrg/ryu-book'
|
||||
|
||||
OVSDB_MANAGER_ADDR = 'ptcp:6640'
|
||||
OVSDB_SWITCH_ADDR = 'tcp:%s:6640'
|
||||
OVSDB_SWITCH_ADDR = 'tcp:0.0.0.0:6640'
|
||||
|
||||
|
||||
def setUpModule():
|
||||
@ -93,7 +93,7 @@ class TestVSCtl(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def _docker_run(cls, image):
|
||||
return _run('docker run --privileged -t -d %s' % image)[0]
|
||||
return _run('docker run --privileged -t -d -p 6640:6640 %s' % image)[0]
|
||||
|
||||
@classmethod
|
||||
def _docker_stop(cls, container):
|
||||
@ -124,7 +124,7 @@ class TestVSCtl(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def _set_up_vsctl(cls):
|
||||
cls.vsctl = vsctl.VSCtl(OVSDB_SWITCH_ADDR % cls.container_mn_ip)
|
||||
cls.vsctl = vsctl.VSCtl(OVSDB_SWITCH_ADDR)
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
|
||||
@ -413,7 +413,7 @@ class Test_ofctl(unittest.TestCase):
|
||||
# without mask
|
||||
eq_(eth, field_value)
|
||||
return
|
||||
elif key in['nw_src', 'nw_dst', 'arp_spa', 'arp_tpa']:
|
||||
elif key in ['nw_src', 'nw_dst', 'arp_spa', 'arp_tpa']:
|
||||
# IPv4 address
|
||||
if test.ver == ofproto_v1_0.OFP_VERSION:
|
||||
ipv4, mask = _to_match_ip(value)
|
||||
|
||||
@ -54,12 +54,31 @@ class Test_bmp(unittest.TestCase):
|
||||
eq_(msg.to_jsondict(), msg2.to_jsondict())
|
||||
eq_(rest, b'')
|
||||
|
||||
def test_route_monitoring_adj_rib_out(self):
|
||||
update = bgp.BGPUpdate()
|
||||
msg = bmp.BMPRouteMonitoring(bgp_update=update,
|
||||
peer_type=bmp.BMP_PEER_TYPE_GLOBAL,
|
||||
is_post_policy=True,
|
||||
is_adj_rib_out=True,
|
||||
peer_distinguisher=0,
|
||||
peer_address='192.0.2.1',
|
||||
peer_as=30000,
|
||||
peer_bgp_id='192.0.2.1',
|
||||
timestamp=self._time())
|
||||
binmsg = msg.serialize()
|
||||
msg2, rest = bmp.BMPMessage.parser(binmsg)
|
||||
eq_(msg.to_jsondict(), msg2.to_jsondict())
|
||||
eq_(rest, b'')
|
||||
|
||||
def test_statistics_report(self):
|
||||
stats = [{'type': bmp.BMP_STAT_TYPE_REJECTED, 'value': 100},
|
||||
{'type': bmp.BMP_STAT_TYPE_DUPLICATE_PREFIX, 'value': 200},
|
||||
{'type': bmp.BMP_STAT_TYPE_DUPLICATE_WITHDRAW, 'value': 300},
|
||||
{'type': bmp.BMP_STAT_TYPE_ADJ_RIB_IN, 'value': 100000},
|
||||
{'type': bmp.BMP_STAT_TYPE_LOC_RIB, 'value': 500000}]
|
||||
{'type': bmp.BMP_STAT_TYPE_LOC_RIB, 'value': 500000},
|
||||
{'type': bmp.BMP_STAT_TYPE_ADJ_RIB_OUT, 'value': 95000},
|
||||
{'type': bmp.BMP_STAT_TYPE_EXPORT_RIB, 'value': 50000},
|
||||
{'type': bmp.BMP_STAT_TYPE_EXPORT_RIB, 'value': 50000}]
|
||||
msg = bmp.BMPStatisticsReport(stats=stats,
|
||||
peer_type=bmp.BMP_PEER_TYPE_GLOBAL,
|
||||
is_post_policy=True,
|
||||
|
||||
@ -1,85 +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 logging
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
import pkg_resources
|
||||
from six.moves import urllib
|
||||
|
||||
from nose.tools import ok_
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
MOD_DIR = os.path.dirname('file://' + sys.modules[__name__].__file__)
|
||||
_RYU_REQUIREMENTS_FILES = [
|
||||
'../../../tools/pip-requires',
|
||||
'../../../tools/optional-requires',
|
||||
]
|
||||
RYU_REQUIREMENTS_FILES = [
|
||||
os.path.join(MOD_DIR, f) for f in _RYU_REQUIREMENTS_FILES]
|
||||
|
||||
OPENSTACK_REQUIREMENTS_REPO = 'https://github.com/openstack/requirements'
|
||||
OPENSTACK_REQUIREMENTS_URL = (
|
||||
'https://github.com/openstack/requirements/raw/master/')
|
||||
_OPENSTACK_REQUIREMENTS_FILES = [
|
||||
'requirements.txt',
|
||||
'global-requirements.txt',
|
||||
]
|
||||
OPENSTACK_REQUIREMENTS_FILES = [
|
||||
urllib.parse.urljoin(OPENSTACK_REQUIREMENTS_URL, f)
|
||||
for f in _OPENSTACK_REQUIREMENTS_FILES]
|
||||
|
||||
|
||||
def _get_requirements(files):
|
||||
requirements = {}
|
||||
for f in files:
|
||||
response = urllib.request.urlopen(f)
|
||||
contents = response.read().decode('utf-8')
|
||||
for r in pkg_resources.parse_requirements(contents):
|
||||
requirements[r.name] = str(r)
|
||||
|
||||
return requirements
|
||||
|
||||
|
||||
OPENSTACK_REQUIREMENTS = _get_requirements(OPENSTACK_REQUIREMENTS_FILES)
|
||||
RYU_REQUIREMENTS = _get_requirements(RYU_REQUIREMENTS_FILES)
|
||||
|
||||
|
||||
class TestRequirements(unittest.TestCase):
|
||||
"""
|
||||
Test whether the requirements of Ryu has no conflict with that
|
||||
of other projects.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_with_openstack_requirements(self):
|
||||
try:
|
||||
for name, req in OPENSTACK_REQUIREMENTS.items():
|
||||
if name in RYU_REQUIREMENTS:
|
||||
ok_(pkg_resources.require(req))
|
||||
except pkg_resources.VersionConflict as e:
|
||||
LOG.exception(
|
||||
'Some requirements of Ryu are conflicting with that of '
|
||||
'OpenStack project: %s', OPENSTACK_REQUIREMENTS_REPO)
|
||||
raise e
|
||||
@ -4,7 +4,7 @@ summary = Component-based Software-defined Networking Framework
|
||||
license = Apache License 2.0
|
||||
author = Ryu project team
|
||||
author-email = ryu-devel@lists.sourceforge.net
|
||||
home-page = http://osrg.github.io/ryu/
|
||||
home-page = https://ryu-sdn.org
|
||||
description-file = README.rst
|
||||
platform = any
|
||||
classifier =
|
||||
@ -13,12 +13,12 @@ classifier =
|
||||
Topic :: System :: Networking
|
||||
Natural Language :: English
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.4
|
||||
Programming Language :: Python :: 3.5
|
||||
Programming Language :: Python :: 3.6
|
||||
Programming Language :: Python :: 3.7
|
||||
Programming Language :: Python :: 3.8
|
||||
Programming Language :: Python :: 3.9
|
||||
Operating System :: Unix
|
||||
keywords =
|
||||
openflow
|
||||
|
||||
6
setup.py
6
setup.py
@ -14,12 +14,6 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# a bug workaround. http://bugs.python.org/issue15881
|
||||
try:
|
||||
import multiprocessing
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
import setuptools
|
||||
import ryu.hooks
|
||||
|
||||
|
||||
@ -70,29 +70,29 @@ def check_dependencies():
|
||||
if not HAS_VIRTUALENV:
|
||||
raise Exception('Virtualenv not found. ' + \
|
||||
'Try installing python-virtualenv')
|
||||
print 'done.'
|
||||
print('done.') # pylint: disable=print-statement
|
||||
|
||||
|
||||
def create_virtualenv(venv=VENV, install_pip=False):
|
||||
"""Creates the virtual environment and installs PIP only into the
|
||||
virtual environment
|
||||
"""
|
||||
print 'Creating venv...',
|
||||
print('Creating venv...') # pylint: disable=print-statement
|
||||
|
||||
install = ['virtualenv', '-q', venv]
|
||||
run_command(install)
|
||||
|
||||
print 'done.'
|
||||
print 'Installing pip in virtualenv...',
|
||||
print('done.') # pylint: disable=print-statement
|
||||
print('Installing pip in virtualenv...') # pylint: disable=print-statement
|
||||
if install_pip and \
|
||||
not run_command(['tools/with_venv.sh', 'easy_install',
|
||||
'pip>1.0']):
|
||||
die("Failed to install pip.")
|
||||
print 'done.'
|
||||
print('done.') # pylint: disable=print-statement
|
||||
|
||||
|
||||
def install_dependencies(venv=VENV):
|
||||
print 'Installing dependencies with pip (this can take a while)...'
|
||||
print('Installing dependencies with pip (this can take a while)...') # pylint: disable=print-statement
|
||||
run_command(['tools/with_venv.sh', 'pip', 'install', '-r',
|
||||
PIP_REQUIRES], redirect_output=False)
|
||||
run_command(['tools/with_venv.sh', 'pip', 'install', '-r',
|
||||
@ -126,7 +126,7 @@ def print_help():
|
||||
|
||||
Also, make test will automatically use the virtualenv.
|
||||
"""
|
||||
print help
|
||||
print(help) # pylint: disable=print-statement
|
||||
|
||||
|
||||
def main(argv):
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
# NOTE: OpenStack avoids some versions of eventlet, because of the
|
||||
# following issue.
|
||||
# https://github.com/eventlet/eventlet/issues/401
|
||||
eventlet!=0.18.3,>=0.18.2,!=0.20.1,!=0.21.0,!=0.23.0
|
||||
msgpack>=0.3.0 # RPC library, BGP speaker(net_cntl)
|
||||
eventlet==0.30.0
|
||||
msgpack>=0.4.0 # RPC library, BGP speaker(net_cntl)
|
||||
netaddr
|
||||
oslo.config>=2.5.0
|
||||
ovs>=2.6.0 # OVSDB
|
||||
routes # wsgi
|
||||
six>=1.4.0
|
||||
tinyrpc # RPC library, BGP speaker(net_cntl)
|
||||
tinyrpc==0.9.4 # RPC library, BGP speaker(net_cntl)
|
||||
webob>=1.2 # wsgi
|
||||
|
||||
15
tox.ini
15
tox.ini
@ -1,5 +1,13 @@
|
||||
[tox]
|
||||
envlist = py27,py34,py35,py36,py37,pypy,pycodestyle,autopep8
|
||||
envlist = py35,py36,py37,py38,py39,pypy,pycodestyle,autopep8
|
||||
|
||||
[gh-actions]
|
||||
python =
|
||||
3.5: py35
|
||||
3.6: py36, pycodestyle, autopep8
|
||||
3.7: py37
|
||||
3.8: py38
|
||||
3.9: py39
|
||||
|
||||
[testenv]
|
||||
deps =
|
||||
@ -20,11 +28,6 @@ commands =
|
||||
commands =
|
||||
python ryu/tests/integrated/run_test.py
|
||||
|
||||
[testenv:py27]
|
||||
commands =
|
||||
{[testenv]commands}
|
||||
{[testenv:scenario]commands}
|
||||
|
||||
[testenv:py36]
|
||||
commands =
|
||||
{[testenv]commands}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user