vmware-nsx/vmware_nsx/services/qos/nsx_v3/utils.py
Adit Sarfaty 26070b7ac1 NSX-V3| Fix qos switching profile project name
In order to get the real project name on the qos switching profile,
we need to use the original context of the request when building the tags
for the profile.

Change-Id: I483f3a7a23e220d3ce5e61a09f21a95545b7efcc
2017-01-18 08:55:27 +02:00

214 lines
7.9 KiB
Python

# Copyright 2016 VMware, Inc.
#
# 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 neutron.api.rpc.callbacks import events as callbacks_events
from neutron.objects.qos import policy as qos_policy
from neutron.services.qos import qos_consts
from neutron_lib.api import validators
from neutron_lib.plugins import directory
from oslo_config import cfg
from oslo_log import log as logging
from vmware_nsx._i18n import _, _LW
from vmware_nsx.common import exceptions as nsx_exc
from vmware_nsx.db import db as nsx_db
LOG = logging.getLogger(__name__)
MAX_KBPS_MIN_VALUE = 1024
# The max limit is calculated so that the value sent to the backed will
# be smaller than 2**31
MAX_BURST_MAX_VALUE = int((2 ** 31 - 1) / 128)
def handle_qos_notification(context, resource_type, policies_list,
event_type):
for policy_obj in policies_list:
handle_qos_policy_notification(context, policy_obj, event_type)
def handle_qos_policy_notification(context, policy_obj, event_type):
handler = QosNotificationsHandler()
# Reload the policy as admin so we will have a context
if (event_type != callbacks_events.DELETED):
policy = qos_policy.QosPolicy.get_object(context.elevated(),
id=policy_obj.id)
# Check if QoS policy rule was created/deleted/updated
if (event_type == callbacks_events.CREATED):
handler.create_policy(context, policy)
elif (event_type == callbacks_events.UPDATED):
if (hasattr(policy_obj, "rules")):
# Rebuild the QoS data of this policy
# we may have up to 1 rule of each type
bw_rule = None
dscp_rule = None
for rule in policy_obj["rules"]:
if rule.rule_type == qos_consts.RULE_TYPE_BANDWIDTH_LIMIT:
bw_rule = rule
else:
dscp_rule = rule
handler.update_policy_rules(
context, policy_obj.id, bw_rule, dscp_rule)
else:
# Without rules - need to update only name / description
handler.update_policy(context, policy_obj.id, policy)
elif (event_type == callbacks_events.DELETED):
handler.delete_policy(context, policy_obj.id)
else:
msg = _("Unknown QoS notification event %s") % event_type
raise nsx_exc.NsxPluginException(err_msg=msg)
class QosNotificationsHandler(object):
def __init__(self):
super(QosNotificationsHandler, self).__init__()
@property
def _core_plugin(self):
return directory.get_plugin()
@property
def _nsxlib_qos(self):
return self._core_plugin.nsxlib.qos_switching_profile
def _get_tags(self, context, policy):
policy_dict = {'id': policy.id, 'tenant_id': policy.tenant_id}
return self._nsxlib_qos.build_v3_tags_payload(
policy_dict, resource_type='os-neutron-qos-id',
project_name=context.tenant_name)
def create_policy(self, context, policy):
policy_id = policy.id
tags = self._get_tags(context, policy)
result = self._nsxlib_qos.create(
tags=tags, name=policy.name,
description=policy.description)
if not result or not validators.is_attr_set(result.get('id')):
msg = _("Unable to create QoS switching profile on the backend")
raise nsx_exc.NsxPluginException(err_msg=msg)
profile_id = result['id']
# Add the mapping entry of the policy_id <-> profile_id
nsx_db.add_qos_policy_profile_mapping(context.session,
policy_id,
profile_id)
def delete_policy(self, context, policy_id):
profile_id = nsx_db.get_switch_profile_by_qos_policy(
context.session, policy_id)
self._nsxlib_qos.delete(profile_id)
def update_policy(self, context, policy_id, policy):
profile_id = nsx_db.get_switch_profile_by_qos_policy(
context.session, policy_id)
tags = self._get_tags(context, policy)
self._nsxlib_qos.update(
profile_id,
tags=tags,
name=policy.name,
description=policy.description)
def _validate_bw_values(self, bw_rule):
"""Validate that the configured values are allowed by the NSX backend.
Since failing the action from the notification callback
is not possible, just log the warning and use the minimal/maximal
values.
"""
# Validate the max bandwidth value minimum value
# (max value is above what neutron allows so no need to check it)
if (bw_rule.max_kbps < MAX_KBPS_MIN_VALUE):
LOG.warning(_LW("Invalid input for max_kbps. "
"The minimal legal value is %s"),
MAX_KBPS_MIN_VALUE)
bw_rule.max_kbps = MAX_KBPS_MIN_VALUE
# validate the burst size value max value
# (max value is 0, and neutron already validates this)
if (bw_rule.max_burst_kbps > MAX_BURST_MAX_VALUE):
LOG.warning(_LW("Invalid input for burst_size. "
"The maximal legal value is %s"),
MAX_BURST_MAX_VALUE)
bw_rule.max_burst_kbps = MAX_BURST_MAX_VALUE
def _get_bw_values_from_rule(self, bw_rule):
"""Translate the neutron bandwidth_limit_rule values, into the
values expected by the NSX-v3 QoS switch profile,
and validate that those are legal
"""
if bw_rule:
shaping_enabled = True
self._validate_bw_values(bw_rule)
# translate kbps -> bytes
burst_size = int(bw_rule.max_burst_kbps) * 128
# translate kbps -> Mbps
average_bandwidth = int(round(float(bw_rule.max_kbps) / 1024))
# peakBandwidth: a Multiplying on the average BW
# because the neutron qos configuration supports
# only 1 value
peak_bandwidth = int(round(average_bandwidth *
cfg.CONF.NSX.qos_peak_bw_multiplier))
else:
shaping_enabled = False
burst_size = None
peak_bandwidth = None
average_bandwidth = None
return shaping_enabled, burst_size, peak_bandwidth, average_bandwidth
def _get_dscp_values_from_rule(self, dscp_rule):
"""Translate the neutron DSCP marking rule values, into the
values expected by the NSX-v3 QoS switch profile
"""
if dscp_rule:
qos_marking = 'untrusted'
dscp = dscp_rule.dscp_mark
else:
qos_marking = 'trusted'
dscp = 0
return qos_marking, dscp
def update_policy_rules(self, context, policy_id, bw_rule, dscp_rule):
"""Update the QoS switch profile with the BW limitations and
DSCP marking configuration
"""
profile_id = nsx_db.get_switch_profile_by_qos_policy(
context.session, policy_id)
(shaping_enabled, burst_size, peak_bw,
average_bw) = self._get_bw_values_from_rule(bw_rule)
qos_marking, dscp = self._get_dscp_values_from_rule(dscp_rule)
self._nsxlib_qos.update_shaping(
profile_id,
shaping_enabled=shaping_enabled,
burst_size=burst_size,
peak_bandwidth=peak_bw,
average_bandwidth=average_bw,
qos_marking=qos_marking,
dscp=dscp)