Add TC Agent manager

This commit is contained in:
Ofer Ben-Yacov 2016-12-21 15:07:37 +02:00
parent a75662ca92
commit 8235239a82
18 changed files with 518 additions and 0 deletions

7
etc/wan_qos_agent.ini Normal file
View File

@ -0,0 +1,7 @@
[DEFAULT]
# Show debugging output in log (sets DEBUG log level output)
# debug = False
[WANQOS]
lan_port = 'enp0s1f0'
wan_port = 'enp0s1f1'

0
etc/wan_qos_plugin.ini Normal file
View File

36
setup.cfg Normal file
View File

@ -0,0 +1,36 @@
[metadata]
name = wan-qos
summary = API's and implementations to support WAN QoS
description-file =
README.rst
author = OpenStack
author-email = openstack-dev@lists.openstack.org
home-page = http://docs.openstack.org/developer/wan-qos
classifier =
Environment :: OpenStack
Intended Audience :: Information Technology
Intended Audience :: System Administrators
License :: OSI Approved :: Apache Software License
Operating System :: POSIX :: Linux
Programming Language :: Python
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.3
Programming Language :: Python :: 3.4
[files]
packages =
wan-qos
data_files =
etc/neutron =
etc/wan_qos_agent.ini
etc/wan_qos_plugin.ini
[entry_points]
console_scripts =
neutron-wan-qos-agent = wan_qos.cmd.eventlet.agent:main
neutron.db.alembic_migrations =
wan-qos = wan_qos.db.migration:alembic_migrations
neutronclient.extension =
wan_qos =wan_qos.wanqos_client._wan_qos

View File

@ -0,0 +1,53 @@
# Copyright 2016 Huawei corp.
# All Rights Reserved.
#
# 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 abc
import six
@six.add_metaclass(abc.ABCMeta)
class AgentInterface(object):
""" Base class that defines the contract for TC agent"""
@abc.abstractmethod
def clear_all(self):
""" delete all traffic control configurations """
@abc.abstractmethod
def set_ports(self, in_port, out_port):
""" set the names of the LAN and WAN facing ports """
@abc.abstractmethod
def set_root_queue(self, tc_dict):
""" sets the root qdisc with its max rate of the WAN link to be set
as upper limit"""
@abc.abstractmethod
def create_traffic_limiter(self, tc_dict):
""" Add traffic limiter using traffic information from the
dictionary. """
@abc.abstractmethod
def update_traffic_limiter(self, tc_dict):
""" update traffic control using information from tc dictionary. """
@abc.abstractmethod
def remove_traffic_limiter(self, tc_dict):
""" update traffic control using information from tc dictionary. """
@abc.abstractmethod
def create_filter(self, tc_dict):
""" create traffic filter that is used to route packets to the
right queue"""

View File

@ -0,0 +1,63 @@
# Copyright (c) 2015 OpenStack Foundation
#
# 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 neutron.agent.ovsdb import api
from neutron.agent.ovsdb import impl_idl
from neutron.agent.ovsdb.native import commands as cmd
from neutron.agent.ovsdb.native import connection
from neutron.agent.ovsdb.native import idlutils
from neutron.agent.ovsdb.native import vlog
def _get_queue_id_list(api, port_name):
port_row = idlutils.row_by_value(api.idl, 'Port', 'name', port_name)
if port_row and port_row.qos:
qos_row = api._tables['QoS'].rows[port_row.qos[0].uuid]
if qos_row:
queues = idlutils.get_column_value(qos_row, 'queues')
return queues.keys()
class GetQueueIdList(cmd.BaseCommand):
def __init__(self, api, port_name):
super(GetQueueIdList, self).__init__(api)
self.port_name = port_name
def run_idl(self, txn):
self.result = _get_queue_id_list(self.api, self.port_name)
class AddQueue(cmd.BaseCommand):
def __init__(self, api, port_name, queue_id, min_rate, max_rate):
super(AddQueue, self).__init__(api)
self.port_name = port_name
self.queue_id = queue_id
self.min_rate = min_rate
self.max_rate = max_rate
def run_idl(self, txn):
port_row = idlutils.row_by_value(self.api.idl, 'Port', 'name',
self.port_name)
qos_row = self.api._tables['QoS'].rows[port_row.qos[0].uuid]
queues = getattr(qos_row, 'queues', [])
if self.queue_id in queues.keys():
raise Exception
queue_row = txn.insert(self.api._tables['Queue'])
queue_row.other_config = {'min-rate': self.min_rate,
'max-rate': self.max_rate}
queues[self.queue_id] = queue_row
qos_row.verify('queues')
qos_row.queues = queues
self.result = 'Done'

49
wan_qos/agent/tc_agent.py Normal file
View File

@ -0,0 +1,49 @@
# Copyright 2016 Huawei corp.
# All Rights Reserved.
#
# 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 sys
from oslo_config import cfg
from oslo_service import service
from neutron.agent.common import config
from neutron.common import config as common_config
from neutron import service as neutron_service
from wan_qos.common import topics
WANQOS_OPTS = [
cfg.StrOpt('lan_port',
default='eth0',
help="The name of the port facing teh LAN"),
cfg.StrOpt('wan_port',
default='eth1',
help="The name of the port facing the WAN")
]
def main():
cfg.CONF.register_opts(WANQOS_OPTS,'WANQOS')
common_config.init(sys.argv[1:])
config.setup_logging()
server = neutron_service.Service.create(
binary='neutron-wan-qos-agent',
topic=topics.TC_AGENT,
report_interval=10,
manager='wan_qos.agent.tc_manager.TcAgentManager')
service.launch(cfg.CONF, server).wait()
if __name__ == '__main__':
main()

101
wan_qos/agent/tc_driver.py Normal file
View File

@ -0,0 +1,101 @@
# Copyright 2016 Huawei corp.
# All Rights Reserved.
#
# 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 subprocess import check_call
from oslo_config import cfg
from oslo_log import log as logging
import agent_api
LOG = logging.getLogger(__name__)
class TcDriver(agent_api.AgentInterface):
def __init__(self):
self.ports = {}
def set_ports(self, lan_port, wan_port):
self.ports['lan_port'] = lan_port
self.ports['wan_port'] = wan_port
def clear_all(self):
for port in self.ports.values():
check_call('sudo tc qdisc del dev %s root' % port, shell=True)
def set_root_queue(self, tc_dict):
check_call('sudo tc qdisc add dev %s handle 1: root htb' %
self.ports[tc_dict['port_side']], shell=True)
class_str = 'sudo tc class add dev %s parent 1: classid 1:1 ' \
'htb rate %s ceil %s'
check_call(class_str % (self.ports[tc_dict['port_side']],
str(tc_dict['max_rate']),
str(tc_dict['max_rate'])), shell=True)
def create_traffic_limiter(self, tc_dict):
""" Create new traffic limiter.
Parameters:
port_side - lan_port / wan_port
parent - the parent class
child - the limiter id
min - minimum traffic rate (CIR)
max - maximum traffic rate. if not provide, the maximum rate will
be limitted by parent maximum rate.
"""
tc_dict['command'] = 'add'
self._create_or_update_limiter(tc_dict)
def update_traffic_limiter(self, tc_dict):
tc_dict['command'] = 'change'
self._create_or_update_limiter(tc_dict)
def remove_traffic_limiter(self, tc_dict):
self._delete_filter(tc_dict)
cmd = 'sudo tc class del dev %s classid %s:%s' % (
self.ports[tc_dict['port_side']],
tc_dict['parent'],
tc_dict['child']
)
check_call(cmd, shell=True)
def _create_or_update_limiter(self, tc_dict):
cmd = 'sudo tc class %s dev %s parent 1:%s classid 1:%s htb' \
' rate %s' % (
tc_dict['command'],
self.ports[tc_dict['port_side']],
tc_dict['parent'], tc_dict['child'],
tc_dict['min']
)
if tc_dict['max']:
cmd += ' ceil %s' % tc_dict['max']
check_call(cmd, shell=True)
def create_filter(self, tc_dict):
cmd = 'sudo tc filter add dev %s parent 1:0' % (
self.ports[tc_dict['port_side']])
cmd += ' protocol ip prio 1 u32'
if tc_dict['protocol'] and tc_dict['protocol'] == 'vxlan':
cmd += ' match ip protocol 17 0xFF' # UDP
cmd += ' match u16 0x12B5 0xFFFF at 22' # VxLAN port
cmd += ' match u32 0x%0.6X00 0xFFFFFF00 at 32' % tc_dict['vni']
cmd += ' flowid 1:%s' % tc_dict['child']
LOG.debug('creating filter: %s' % cmd)
check_call(cmd, shell=True)
def _delete_filter(self, tc_dict):
cmd = 'sudo tc filter del dev %s ' % self.ports[tc_dict['port_side']]
cmd += ' parent 1:0 protocol ip prio 1 u32'
cmd += ' flowid %s:%s' % (tc_dict['parent'], tc_dict['child'])
check_call(cmd, shell=True)

View File

@ -0,0 +1,43 @@
# Copyright 2016 Huawei corp.
# All Rights Reserved.
#
# 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 oslo_config import cfg
from oslo_log import log as logging
from wan_qos.agent import tc_driver
LOG = logging.getLogger(__name__)
class TcAgentManager:
def __init__(self, host, conf=None):
self.agent = tc_driver.TcDriver()
if not conf:
conf = cfg.CONF
lan_port = conf.WANQOS.lan_port
wan_port = conf.WANQOS.wan_port
self.agent.set_ports(lan_port, wan_port)
def init_host(self):
"""Handle initialization if this is a standalone service.
"""
pass
def after_start(self):
LOG.info("WAN QoS agent started")
def periodic_tasks(self, context, raise_on_error=False):
pass

0
wan_qos/cmd/__init__.py Normal file
View File

View File

View File

@ -0,0 +1,20 @@
# Copyright 2016 Huawei corp.
# All Rights Reserved.
#
# 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 wan_qos.agent import tc_agent
def main():
tc_agent.main()

View File

17
wan_qos/common/topics.py Normal file
View File

@ -0,0 +1,17 @@
# Copyright 2016 Huawei corp.
# All Rights Reserved.
#
# 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.
TC_AGENT = 'wan_tc_agent'
TC_PLUGIN = 'wan_tc_plugin'

View File

View File

View File

@ -0,0 +1,114 @@
# Copyright (c) 2016 Huawei, Inc.
#
# 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 wan_qos.agent import tc_driver
from neutron.tests import base
class TestTcDriver(base.BaseTestCase):
def setUp(self):
super(TestTcDriver, self).setUp()
self.tc_agent = tc_driver.TcDriver()
self.tc_agent.set_ports('enp1s0f0', 'enp1s0f1')
def test_clear_all(self):
self.tc_agent.clear_all()
def test_set_root_queue(self):
tc_dict = {
'port_side': 'lan_port',
'max_rate': '100mbit'
}
self.tc_agent.set_root_queue(tc_dict)
tc_dict = {
'port_side': 'wan_port',
'max_rate': '100mbit'
}
self.tc_agent.set_root_queue(tc_dict)
def test_add_limiter(self):
tc_dict = {
'port_side': 'lan_port',
'parent': '1',
'child': '10',
'min': '200kbit',
'max': '512kbit'
}
self.tc_agent.create_traffic_limiter(tc_dict)
tc_dict = {
'port_side': 'wan_port',
'parent': '1',
'child': '10',
'min': '2mbit',
'max': '2mbit'
}
self.tc_agent.create_traffic_limiter(tc_dict)
tc_dict = {
'port_side': 'wan_port',
'parent': '10',
'child': '100',
'min': '200kbit',
'max': '512kbit'
}
self.tc_agent.create_traffic_limiter(tc_dict)
def test_update_limiter(self):
tc_dict = {
'port_side': 'lan_port',
'parent': '1',
'child': '10',
'min': '300kbit',
'max': '400kbit'
}
self.tc_agent.update_traffic_limiter(tc_dict)
tc_dict = {
'port_side': 'wan_port',
'parent': '10',
'child': '100',
'min': '250kbit',
'max': '600kbit'
}
self.tc_agent.update_traffic_limiter(tc_dict)
def test_add_filter(self):
tc_dict = {
'port_side': 'lan_port',
'protocol': 'vxlan',
'vni': 100,
'child': '10'
}
self.tc_agent.create_filter(tc_dict)
tc_dict = {
'port_side': 'wan_port',
'protocol': 'vxlan',
'vni': 100,
'child': '100'
}
self.tc_agent.create_filter(tc_dict)
def test_remove_limiter(self):
tc_dict = {
'port_side': 'lan_port',
'parent': '1',
'child': '10'
}
self.tc_agent.remove_traffic_limiter(tc_dict)
tc_dict = {
'port_side': 'wan_port',
'parent': '1',
'child': '10'
}
self.tc_agent.remove_traffic_limiter(tc_dict)

View File

View File

@ -0,0 +1,15 @@
# Copyright 2016 Huawei corp.
# All Rights Reserved.
#
# 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.