diff --git a/setup.cfg b/setup.cfg index a8bee52..c04b440 100644 --- a/setup.cfg +++ b/setup.cfg @@ -35,3 +35,4 @@ neutron.db.alembic_migrations = neutronclient.extension = wan_qos = wan_qos.wanqos_client._wanqos wan_tc_device = wan_qos.wanqos_client._wantcdevice + wan_tc_class = wan_qos.wanqos_client._wantcclass diff --git a/wan_qos/common/constants.py b/wan_qos/common/constants.py index e349cfd..f30439a 100644 --- a/wan_qos/common/constants.py +++ b/wan_qos/common/constants.py @@ -18,4 +18,7 @@ WAN_TC = 'wan_tc' WAN_TC_PATH = 'wan-tcs' WAN_TC_DEVICE = 'wan_tc_device' -WAN_TC_DEVICE_PATH = 'wan-tc-devices' \ No newline at end of file +WAN_TC_DEVICE_PATH = 'wan-tc-devices' + +WAN_TC_CLASS = 'wan_tc_class' +WAN_TC_CLASS_PATH = 'wan-tc-classs' \ No newline at end of file diff --git a/wan_qos/db/migration/alembic_migrations/versions/ocata/contract/45194b1d3492_first_rev.py b/wan_qos/db/migration/alembic_migrations/versions/ocata/contract/45194b1d3492_first_rev.py index db25b61..b232ee0 100644 --- a/wan_qos/db/migration/alembic_migrations/versions/ocata/contract/45194b1d3492_first_rev.py +++ b/wan_qos/db/migration/alembic_migrations/versions/ocata/contract/45194b1d3492_first_rev.py @@ -32,30 +32,22 @@ import sqlalchemy as sa def upgrade(): op.create_table('wan_tc_class', sa.Column('id', sa.String(length=36), nullable=False), - sa.Column('parent_class', sa.String(length=36), nullable=False), - sa.Column('device_id', sa.String(length=36), - nullable=False), - sa.Column('project_id', sa.String(length=36), - nullable=False), - sa.Column('network_id', sa.String(length=36), + sa.Column('parent', sa.String(length=36)), + sa.Column('device_id', sa.String(length=36)), + sa.Column('direction', sa.String(length=4), nullable=False), + sa.Column('project_id', sa.String(length=36)), sa.Column('class_ext_id', sa.Integer()), - sa.Column('min_rate', - sa.String(length=15), nullable=False), - sa.Column('max_rate', sa.String(length=15)), + sa.Column('min', sa.String(length=15)), + sa.Column('max', sa.String(length=15)), sa.PrimaryKeyConstraint('id') ) - op.create_foreign_key( - 'fk_wan_tc_class_networks', - 'wan_tc_class', 'networks', - ['network_id'], ['id'], - ) - op.create_table('wan_tc_selector', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('class_id', sa.String(length=36), nullable=False), + sa.Column('network_id', sa.String(length=36)), sa.Column('protocol', sa.String(length=15)), sa.Column('match', sa.String(length=15)), sa.PrimaryKeyConstraint('id') @@ -67,6 +59,12 @@ def upgrade(): ['class_id'], ['id'], ) + op.create_foreign_key( + 'fk_wan_tc_selector_networks', + 'wan_tc_selector', 'networks', + ['network_id'], ['id'], + ) + op.create_table( 'wan_tc_device', sa.Column('id', sa.String(length=36), nullable=False), diff --git a/wan_qos/db/models/wan_tc.py b/wan_qos/db/models/wan_tc.py index c164597..a3d1a11 100644 --- a/wan_qos/db/models/wan_tc.py +++ b/wan_qos/db/models/wan_tc.py @@ -15,7 +15,6 @@ import sqlalchemy as sa - from neutron_lib.db import model_base @@ -30,32 +29,31 @@ class WanTcDevice(model_base.BASEV2, class WanTcClass(model_base.BASEV2, - model_base.HasId, model_base.HasProject): + model_base.HasId, model_base.HasProject): __tablename__ = 'wan_tc_class' device_id = sa.Column(sa.String(36), - sa.ForeignKey('wan_tc_device.id', - ondelete='CASCADE'), - nullable=False) + sa.ForeignKey('wan_tc_device.id', + ondelete='CASCADE'), + nullable=True) + direction = sa.Column(sa.String(4), nullable=False) class_ext_id = sa.Column(sa.Integer) - parent_class = sa.Column(sa.String(36), - sa.ForeignKey('wan_tc_class.id', - ondelete='CASCADE'), - nullable=True) - network_id = sa.Column(sa.String(36), - sa.ForeignKey('networks.id', - ondelete='CASCADE'), - nullable=False) - min_rate = sa.Column(sa.String(15), nullable=False) - max_rate = sa.Column(sa.String(15)) + parent = sa.Column(sa.String(36), + sa.ForeignKey('wan_tc_class.id', + ondelete='CASCADE'), + nullable=True) + min = sa.Column(sa.String(15)) + max = sa.Column(sa.String(15)) class WanTcSelector(model_base.BASEV2, - model_base.HasId, model_base.HasProject): + model_base.HasId, model_base.HasProject): __tablename__ = 'wan_tc_selector' class_id = sa.Column(sa.String(36), sa.ForeignKey('wan_tc_class.id', ondelete='CASCADE'), nullable=False) + network_id = sa.Column(sa.String(36), + sa.ForeignKey('network.id', + ondelete='CASCADE')) protocol = sa.Column(sa.String(15)) match = sa.Column(sa.String(15)) - diff --git a/wan_qos/db/wan_qos_db.py b/wan_qos/db/wan_qos_db.py index a2c4a8b..df18422 100644 --- a/wan_qos/db/wan_qos_db.py +++ b/wan_qos/db/wan_qos_db.py @@ -17,6 +17,8 @@ from oslo_utils import uuidutils from oslo_utils import timeutils from oslo_log import log as logging +from neutron.db.models import segment + from wan_qos.db.models import wan_tc as models from wan_qos.common import constants @@ -41,7 +43,7 @@ class WanTcDb(object): uptime=now, heartbeat_timestamp=now ) - context.session.add(wan_tc_device) + return context.session.add(wan_tc_device) else: LOG.debug('updating uptime for device: %s' % host_info['host']) device.uptime = timeutils.utcnow() @@ -64,11 +66,57 @@ class WanTcDb(object): return device_list_dict - def create_wan_tc_class(self, context, wan_qos_class): - pass + def create_wan_tc_class(self, context, wtc_class): + + wtc_class_db = models.WanTcClass( + id=uuidutils.generate_uuid(), + direction=wtc_class['direction'] + ) + + parent = wtc_class['parent'] + if parent: + wtc_class_db.parent = parent + + with context.session.begin(subtransactions=True): + + if wtc_class['min']: + wtc_class_db.min = wtc_class['min'] + if wtc_class['max']: + wtc_class_db.max = wtc_class['max'] + + context.session.add(wtc_class_db) + return self._class_to_dict(wtc_class_db) + + def delete_wtc_class(self, context, id): + wtc_class_db = context.session.query(models.WanTcClass).filter_by( + id=id).first() + if wtc_class_db: + with context.session.begin(subtransactions=True): + context.session.delete(wtc_class_db) + + def get_class_by_id(self, context, id): + wtc_class = context.session.query(models.WanTcClass).filter_by( + id=id).first() + if wtc_class: + return self._class_to_dict(wtc_class) def get_all_classes(self, context): - return context.session.query(models.WanTcClass).all() + wtc_classes_db = context.session.query(models.WanTcClass).all() + wtc_classes = [] + for wtc_class in wtc_classes_db: + wtc_classes.append(self._class_to_dict(wtc_class)) + return wtc_classes + + def _class_to_dict(self, wtc_class): + class_dict = { + 'id': wtc_class.id, + 'direction': wtc_class.direction, + 'min': wtc_class.min, + 'max': wtc_class.max, + 'parent': wtc_class.parent + } + + return class_dict def _device_to_dict(self, device): device_dict = { diff --git a/wan_qos/extensions/wantcclass.py b/wan_qos/extensions/wantcclass.py new file mode 100644 index 0000000..8602e5d --- /dev/null +++ b/wan_qos/extensions/wantcclass.py @@ -0,0 +1,114 @@ +# 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 + +from neutron_lib.api import extensions +from neutron.api.v2 import resource_helper + +from wan_qos.common import constants + +RESOURCE_ATTRIBUTE_MAP = { + constants.WAN_TC_CLASS_PATH: { + 'id': {'allow_post': False, 'allow_put': False, + 'is_visible': True}, + 'parent': {'allow_post': True, 'allow_put': False, + 'is_visible': True, + 'default': ''}, + 'direction': {'allow_post': True, 'allow_put': False, + 'validate': {'type:string': None}, + 'is_visible': True, + 'default': '' + }, + 'min': {'allow_post': True, 'allow_put': False, + 'validate': {'type:string': None}, + 'is_visible': True, + 'default': '', + }, + 'max': {'allow_post': True, 'allow_put': False, + 'validate': {'type:string': None}, + 'is_visible': True, + 'default': '', + }, + 'project_id': {'allow_post': True, 'allow_put': False, + 'validate': {'type:string': None}, + 'required_by_policy': True, + 'is_visible': True} + }, +} + + +class Wantcclass(extensions.ExtensionDescriptor): + @classmethod + def get_name(cls): + return "WAN Traffic Control class" + + @classmethod + def get_alias(cls): + return "wan-tc-class" + + @classmethod + def get_description(cls): + return "Class for limiting traffic on WAN links" + + @classmethod + def get_updated(cls): + return "2017-01-16T00:00:00-00:00" + + @classmethod + def get_resources(cls): + """Returns Ext Resources.""" + + mem_actions = {} + plural_mappings = resource_helper.build_plural_mappings( + {}, RESOURCE_ATTRIBUTE_MAP) + resources = resource_helper.build_resource_info(plural_mappings, + RESOURCE_ATTRIBUTE_MAP, + constants.WANTC, + action_map=mem_actions, + register_quota=True, + translate_name=True) + + return resources + + def get_extended_resources(self, version): + if version == "2.0": + return RESOURCE_ATTRIBUTE_MAP + else: + return {} + + +class WanTcClassPluginBase(object): + @abc.abstractmethod + def create_wan_tc_class(self, context, wan_tc_class): + pass + + @abc.abstractmethod + def get_wan_tc_class(self, context, id, fields=None): + pass + + @abc.abstractmethod + def get_wan_tc_classs(self, context, filters=None, fields=None, + sorts=None, limit=None, marker=None, + page_reverse=False): + pass + + @abc.abstractmethod + def update_wan_tc_class(self, context, id, wan_tc_class): + pass + + @abc.abstractmethod + def delete_wan_tc_class(self, context, id): + pass diff --git a/wan_qos/services/plugin.py b/wan_qos/services/plugin.py index 60e9eeb..95307f1 100644 --- a/wan_qos/services/plugin.py +++ b/wan_qos/services/plugin.py @@ -26,9 +26,10 @@ import oslo_messaging as messaging from wan_qos.common import api from wan_qos.common import constants from wan_qos.common import topics +from wan_qos.db import wan_qos_db from wan_qos.extensions import wanqos from wan_qos.extensions import wantcdevice -from wan_qos.db import wan_qos_db +from wan_qos.extensions import wantcclass LOG = logging.getLogger(__name__) @@ -50,8 +51,9 @@ class PluginRpcCallback(object): class WanQosPlugin(wanqos.WanQosPluginBase, - wantcdevice.WanTcDevicePluginBase): - supported_extension_aliases = ['wan-tc', 'wan-tc-device'] + wantcdevice.WanTcDevicePluginBase, + wantcclass.WanTcClassPluginBase): + supported_extension_aliases = ['wan-tc', 'wan-tc-device', 'wan-tc-class'] def __init__(self): self.db = wan_qos_db.WanTcDb() @@ -91,7 +93,7 @@ class WanQosPlugin(wanqos.WanQosPluginBase, def get_wan_tcs(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): - return self.db.get_all_classes(context) + pass def delete_wan_tc(self, context, id): pass @@ -105,6 +107,24 @@ class WanQosPlugin(wanqos.WanQosPluginBase, # tenant_id = self._get_tenant_id_for_create(context, wan_qos_class) + def get_wan_tc_class(self, context, id, fields=None): + return self.db.get_class_by_id(context, id) + + def update_wan_tc_class(self, context, id, wan_tc_class): + pass + + def create_wan_tc_class(self, context, wan_tc_class): + LOG.debug('got new class request: %s' % wan_tc_class) + return self.db.create_wan_tc_class(context, + wan_tc_class['wan_tc_class']) + + def delete_wan_tc_class(self, context, id): + self.db.delete_wtc_class(context, id) + + def get_wan_tc_classs(self, context, filters=None, fields=None, sorts=None, + limit=None, marker=None, page_reverse=False): + return self.db.get_all_classes(context) + @staticmethod def _get_tenant_id_for_create(self, context, resource): """Get tenant id for creation of resources.""" diff --git a/wan_qos/wanqos_client/_wanqos.py b/wan_qos/wanqos_client/_wanqos.py index eeb212a..d88322d 100644 --- a/wan_qos/wanqos_client/_wanqos.py +++ b/wan_qos/wanqos_client/_wanqos.py @@ -40,7 +40,7 @@ class WanTcShow(extension.ClientExtensionShow, WanTc): class WanTcList(extension.ClientExtensionList, WanTc): shell_command = 'wan-tc-list' - list_columns = ['id', 'name', 'network'] + list_columns = ['id', 'name', 'network', 'min-rate', 'max-rate'] pagination_support = True sorting_support = True diff --git a/wan_qos/wanqos_client/_wantcclass.py b/wan_qos/wanqos_client/_wantcclass.py new file mode 100644 index 0000000..90a1747 --- /dev/null +++ b/wan_qos/wanqos_client/_wantcclass.py @@ -0,0 +1,99 @@ +# 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 neutronclient._i18n import _ + +from neutronclient.common import extension +from neutronclient.common import exceptions + +from wan_qos.common import constants + + +class WanTcClass(extension.NeutronClientExtension): + resource = constants.WAN_TC_CLASS + resource_plural = '%ss' % constants.WAN_TC_CLASS + path = constants.WAN_TC_CLASS_PATH + object_path = '/%s' % path + resource_path = '/%s/%%s' % path + versions = ['2.0'] + + +class WanTcShow(extension.ClientExtensionShow, WanTcClass): + shell_command = 'wan-tc-class-show' + + +class WanTcList(extension.ClientExtensionList, WanTcClass): + shell_command = 'wan-tc-class-list' + list_columns = ['id', 'parent', 'direction', 'min', 'max'] + pagination_support = True + sorting_support = True + + +class WanTcCreate(extension.ClientExtensionCreate, WanTcClass): + shell_command = 'wan-tc-class-create' + + def add_known_arguments(self, parser): + parser.add_argument( + 'direction', metavar='', + choices=['both', 'in', 'out'], + help=_('The direction for the limiter. Can be both/in/out')) + parser.add_argument( + '--min', + dest='min', + help=_('Set committed rate. (mbit / kbit)')) + parser.add_argument( + '--max', + dest='max', + help=_('Set maximum rate. (mbit / kbit)')) + parser.add_argument( + '--parent', + dest='parent', + help=_('Set the parent class of this class. Omit if root.')) + + def args2body(self, parsed_args): + + body = { + 'direction': parsed_args.direction + } + + if parsed_args.min: + body['min'] = parsed_args.min + else: + if not parsed_args.max: + raise exceptions.BadRequest('Either min or max must be set') + + if parsed_args.max: + body['max'] = parsed_args.max + + if parsed_args.parent: + body['parent'] = parsed_args.parent + + return {self.resource: body} + + +class WanTcDelete(extension.ClientExtensionDelete, WanTcClass): + shell_command = 'wan-tc-class-delete' + + +class WanTcUpdate(extension.ClientExtensionUpdate, WanTcClass): + shell_command = 'wan-tc-class-update' + + def add_known_arguments(self, parser): + pass + + def args2body(self, parsed_args): + body = {} + return {self.resource: body}