2022-07-01 13:55:41 +00:00

398 lines
14 KiB
Python

# Copyright (c) 2016, Intel Corporation.
# 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 itertools
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
from openstackclient.i18n import _
from openstackclient.network import common
RULE_TYPE_BANDWIDTH_LIMIT = 'bandwidth-limit'
RULE_TYPE_DSCP_MARKING = 'dscp-marking'
RULE_TYPE_MINIMUM_BANDWIDTH = 'minimum-bandwidth'
RULE_TYPE_MINIMUM_PACKET_RATE = 'minimum-packet-rate'
MANDATORY_PARAMETERS = {
RULE_TYPE_MINIMUM_BANDWIDTH: {'min_kbps', 'direction'},
RULE_TYPE_MINIMUM_PACKET_RATE: {'min_kpps', 'direction'},
RULE_TYPE_DSCP_MARKING: {'dscp_mark'},
RULE_TYPE_BANDWIDTH_LIMIT: {'max_kbps'}}
OPTIONAL_PARAMETERS = {
RULE_TYPE_MINIMUM_BANDWIDTH: set(),
RULE_TYPE_MINIMUM_PACKET_RATE: set(),
RULE_TYPE_DSCP_MARKING: set(),
RULE_TYPE_BANDWIDTH_LIMIT: {'direction', 'max_burst_kbps'}}
DIRECTION_EGRESS = 'egress'
DIRECTION_INGRESS = 'ingress'
DIRECTION_ANY = 'any'
DSCP_VALID_MARKS = [0, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32,
34, 36, 38, 40, 46, 48, 56]
ACTION_CREATE = 'create'
ACTION_DELETE = 'delete'
ACTION_FIND = 'find'
ACTION_SET = 'update'
ACTION_SHOW = 'get'
def _get_columns(item):
column_map = {}
hidden_columns = ['location', 'tenant_id']
return utils.get_osc_show_columns_for_sdk_resource(
item,
column_map,
hidden_columns
)
def _check_type_parameters(attrs, type, is_create):
req_params = MANDATORY_PARAMETERS[type]
opt_params = OPTIONAL_PARAMETERS[type]
type_params = req_params | opt_params
notreq_params = set(itertools.chain(
*[v for k, v in MANDATORY_PARAMETERS.items() if k != type]))
notreq_params -= type_params
if is_create and None in map(attrs.get, req_params):
msg = (_('"Create" rule command for type "%(rule_type)s" requires '
'arguments: %(args)s') %
{'rule_type': type, 'args': ", ".join(sorted(req_params))})
raise exceptions.CommandError(msg)
if set(attrs.keys()) & notreq_params:
msg = (_('Rule type "%(rule_type)s" only requires arguments: %(args)s')
% {'rule_type': type, 'args': ", ".join(sorted(type_params))})
raise exceptions.CommandError(msg)
def _get_attrs(network_client, parsed_args, is_create=False):
attrs = {}
qos = network_client.find_qos_policy(parsed_args.qos_policy,
ignore_missing=False)
attrs['qos_policy_id'] = qos.id
if not is_create:
attrs['id'] = parsed_args.id
rule_type = _find_rule_type(qos, parsed_args.id)
if not rule_type:
msg = (_('Rule ID %(rule_id)s not found') %
{'rule_id': parsed_args.id})
raise exceptions.CommandError(msg)
else:
rule_type = parsed_args.type
if parsed_args.max_kbps is not None:
attrs['max_kbps'] = parsed_args.max_kbps
if parsed_args.max_burst_kbits is not None:
# NOTE(ralonsoh): this parameter must be changed in SDK and then in
# Neutron API, from 'max_burst_kbps' to
# 'max_burst_kbits'
attrs['max_burst_kbps'] = parsed_args.max_burst_kbits
if parsed_args.dscp_mark is not None:
attrs['dscp_mark'] = parsed_args.dscp_mark
if parsed_args.min_kbps is not None:
attrs['min_kbps'] = parsed_args.min_kbps
if parsed_args.min_kpps is not None:
attrs['min_kpps'] = parsed_args.min_kpps
if parsed_args.ingress:
attrs['direction'] = DIRECTION_INGRESS
if parsed_args.egress:
attrs['direction'] = DIRECTION_EGRESS
if parsed_args.any:
if rule_type == RULE_TYPE_MINIMUM_PACKET_RATE:
attrs['direction'] = DIRECTION_ANY
else:
msg = (_('Direction "any" can only be used with '
'%(rule_type_min_pps)s rule type') %
{'rule_type_min_pps': RULE_TYPE_MINIMUM_PACKET_RATE})
raise exceptions.CommandError(msg)
_check_type_parameters(attrs, rule_type, is_create)
return attrs
def _get_item_properties(item, fields):
"""Return a tuple containing the item properties."""
row = []
for field in fields:
row.append(item.get(field, ''))
return tuple(row)
def _rule_action_call(client, action, rule_type):
rule_type = rule_type.replace('-', '_')
func_name = '%(action)s_qos_%(rule_type)s_rule' % {'action': action,
'rule_type': rule_type}
return getattr(client, func_name)
def _find_rule_type(qos, rule_id):
for rule in (r for r in qos.rules if r['id'] == rule_id):
return rule['type'].replace('_', '-')
return None
def _add_rule_arguments(parser):
parser.add_argument(
'--max-kbps',
dest='max_kbps',
metavar='<max-kbps>',
type=int,
help=_('Maximum bandwidth in kbps')
)
parser.add_argument(
'--max-burst-kbits',
dest='max_burst_kbits',
metavar='<max-burst-kbits>',
type=int,
help=_('Maximum burst in kilobits, 0 or not specified means '
'automatic, which is 80%% of the bandwidth limit, which works '
'for typical TCP traffic. For details check the QoS user '
'workflow.')
)
parser.add_argument(
'--dscp-mark',
dest='dscp_mark',
metavar='<dscp-mark>',
type=int,
help=_('DSCP mark: value can be 0, even numbers from 8-56, '
'excluding 42, 44, 50, 52, and 54')
)
parser.add_argument(
'--min-kbps',
dest='min_kbps',
metavar='<min-kbps>',
type=int,
help=_('Minimum guaranteed bandwidth in kbps')
)
parser.add_argument(
'--min-kpps',
dest='min_kpps',
metavar='<min-kpps>',
type=int,
help=_('Minimum guaranteed packet rate in kpps')
)
direction_group = parser.add_mutually_exclusive_group()
direction_group.add_argument(
'--ingress',
action='store_true',
help=_("Ingress traffic direction from the project point of view")
)
direction_group.add_argument(
'--egress',
action='store_true',
help=_("Egress traffic direction from the project point of view")
)
direction_group.add_argument(
'--any',
action='store_true',
help=_("Any traffic direction from the project point of view. Can be "
"used only with minimum packet rate rule.")
)
class CreateNetworkQosRule(command.ShowOne,
common.NeutronCommandWithExtraArgs):
_description = _("Create new Network QoS rule")
def get_parser(self, prog_name):
parser = super(CreateNetworkQosRule, self).get_parser(
prog_name)
parser.add_argument(
'qos_policy',
metavar='<qos-policy>',
help=_('QoS policy that contains the rule (name or ID)')
)
parser.add_argument(
'--type',
metavar='<type>',
required=True,
choices=[RULE_TYPE_MINIMUM_BANDWIDTH,
RULE_TYPE_MINIMUM_PACKET_RATE,
RULE_TYPE_DSCP_MARKING,
RULE_TYPE_BANDWIDTH_LIMIT],
help=(_('QoS rule type (%s)') %
", ".join(MANDATORY_PARAMETERS.keys()))
)
_add_rule_arguments(parser)
return parser
def take_action(self, parsed_args):
network_client = self.app.client_manager.network
try:
attrs = _get_attrs(network_client, parsed_args, is_create=True)
attrs.update(
self._parse_extra_properties(parsed_args.extra_properties))
obj = _rule_action_call(
network_client, ACTION_CREATE, parsed_args.type)(
attrs.pop('qos_policy_id'), **attrs)
except Exception as e:
msg = (_('Failed to create Network QoS rule: %(e)s') % {'e': e})
raise exceptions.CommandError(msg)
display_columns, columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns)
return display_columns, data
class DeleteNetworkQosRule(command.Command):
_description = _("Delete Network QoS rule")
def get_parser(self, prog_name):
parser = super(DeleteNetworkQosRule, self).get_parser(prog_name)
parser.add_argument(
'qos_policy',
metavar='<qos-policy>',
help=_('QoS policy that contains the rule (name or ID)')
)
parser.add_argument(
'id',
metavar='<rule-id>',
help=_('Network QoS rule to delete (ID)')
)
return parser
def take_action(self, parsed_args):
network_client = self.app.client_manager.network
rule_id = parsed_args.id
try:
qos = network_client.find_qos_policy(parsed_args.qos_policy,
ignore_missing=False)
rule_type = _find_rule_type(qos, rule_id)
if not rule_type:
raise Exception('Rule %s not found' % rule_id)
_rule_action_call(network_client, ACTION_DELETE, rule_type)(
rule_id, qos.id)
except Exception as e:
msg = (_('Failed to delete Network QoS rule ID "%(rule)s": %(e)s')
% {'rule': rule_id, 'e': e})
raise exceptions.CommandError(msg)
class ListNetworkQosRule(command.Lister):
_description = _("List Network QoS rules")
def get_parser(self, prog_name):
parser = super(ListNetworkQosRule, self).get_parser(prog_name)
parser.add_argument(
'qos_policy',
metavar='<qos-policy>',
help=_('QoS policy that contains the rule (name or ID)')
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.network
columns = (
'id',
'qos_policy_id',
'type',
'max_kbps',
'max_burst_kbps',
'min_kbps',
'min_kpps',
'dscp_mark',
'direction',
)
column_headers = (
'ID',
'QoS Policy ID',
'Type',
'Max Kbps',
'Max Burst Kbits',
'Min Kbps',
'Min Kpps',
'DSCP mark',
'Direction',
)
qos = client.find_qos_policy(parsed_args.qos_policy,
ignore_missing=False)
data = qos.rules
return (column_headers,
(_get_item_properties(s, columns) for s in data))
class SetNetworkQosRule(common.NeutronCommandWithExtraArgs):
_description = _("Set Network QoS rule properties")
def get_parser(self, prog_name):
parser = super(SetNetworkQosRule, self).get_parser(prog_name)
parser.add_argument(
'qos_policy',
metavar='<qos-policy>',
help=_('QoS policy that contains the rule (name or ID)')
)
parser.add_argument(
'id',
metavar='<rule-id>',
help=_('Network QoS rule to delete (ID)')
)
_add_rule_arguments(parser)
return parser
def take_action(self, parsed_args):
network_client = self.app.client_manager.network
try:
qos = network_client.find_qos_policy(parsed_args.qos_policy,
ignore_missing=False)
rule_type = _find_rule_type(qos, parsed_args.id)
if not rule_type:
raise Exception('Rule not found')
attrs = _get_attrs(network_client, parsed_args)
attrs.update(
self._parse_extra_properties(parsed_args.extra_properties))
qos_id = attrs.pop('qos_policy_id')
qos_rule = _rule_action_call(network_client, ACTION_FIND,
rule_type)(attrs.pop('id'), qos_id)
_rule_action_call(network_client, ACTION_SET, rule_type)(
qos_rule, qos_id, **attrs)
except Exception as e:
msg = (_('Failed to set Network QoS rule ID "%(rule)s": %(e)s') %
{'rule': parsed_args.id, 'e': e})
raise exceptions.CommandError(msg)
class ShowNetworkQosRule(command.ShowOne):
_description = _("Display Network QoS rule details")
def get_parser(self, prog_name):
parser = super(ShowNetworkQosRule, self).get_parser(prog_name)
parser.add_argument(
'qos_policy',
metavar='<qos-policy>',
help=_('QoS policy that contains the rule (name or ID)')
)
parser.add_argument(
'id',
metavar='<rule-id>',
help=_('Network QoS rule to delete (ID)')
)
return parser
def take_action(self, parsed_args):
network_client = self.app.client_manager.network
rule_id = parsed_args.id
try:
qos = network_client.find_qos_policy(parsed_args.qos_policy,
ignore_missing=False)
rule_type = _find_rule_type(qos, rule_id)
if not rule_type:
raise Exception('Rule not found')
obj = _rule_action_call(network_client, ACTION_SHOW, rule_type)(
rule_id, qos.id)
except Exception as e:
msg = (_('Failed to set Network QoS rule ID "%(rule)s": %(e)s') %
{'rule': rule_id, 'e': e})
raise exceptions.CommandError(msg)
display_columns, columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns)
return display_columns, data