"Fossies" - the Fresh Open Source Software Archive

Member "neutron-14.0.3/neutron/services/qos/qos_plugin.py" (22 Oct 2019, 26281 Bytes) of package /linux/misc/openstack/neutron-14.0.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "qos_plugin.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.0.2_vs_14.0.3.

    1 # Copyright (c) 2015 Red Hat Inc.
    2 # All Rights Reserved.
    3 #
    4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
    5 #    not use this file except in compliance with the License. You may obtain
    6 #    a copy of the License at
    7 #
    8 #         http://www.apache.org/licenses/LICENSE-2.0
    9 #
   10 #    Unless required by applicable law or agreed to in writing, software
   11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   13 #    License for the specific language governing permissions and limitations
   14 #    under the License.
   15 
   16 from neutron_lib.api.definitions import port as port_def
   17 from neutron_lib.api.definitions import port_resource_request
   18 from neutron_lib.api.definitions import portbindings
   19 from neutron_lib.api.definitions import qos as qos_apidef
   20 from neutron_lib.api.definitions import qos_bw_minimum_ingress
   21 from neutron_lib.api.definitions import qos_rules_alias
   22 from neutron_lib.callbacks import events as callbacks_events
   23 from neutron_lib.callbacks import registry as callbacks_registry
   24 from neutron_lib.callbacks import resources as callbacks_resources
   25 from neutron_lib import constants as nl_constants
   26 from neutron_lib import context
   27 from neutron_lib.db import api as db_api
   28 from neutron_lib.db import resource_extend
   29 from neutron_lib import exceptions as lib_exc
   30 from neutron_lib.exceptions import qos as qos_exc
   31 from neutron_lib.placement import constants as pl_constants
   32 from neutron_lib.placement import utils as pl_utils
   33 from neutron_lib.services.qos import constants as qos_consts
   34 
   35 from neutron._i18n import _
   36 from neutron.db import db_base_plugin_common
   37 from neutron.extensions import qos
   38 from neutron.objects import base as base_obj
   39 from neutron.objects import network as network_object
   40 from neutron.objects import ports as ports_object
   41 from neutron.objects.qos import policy as policy_object
   42 from neutron.objects.qos import qos_policy_validator as checker
   43 from neutron.objects.qos import rule_type as rule_type_object
   44 from neutron.services.qos.drivers import manager
   45 
   46 
   47 @resource_extend.has_resource_extenders
   48 class QoSPlugin(qos.QoSPluginBase):
   49     """Implementation of the Neutron QoS Service Plugin.
   50 
   51     This class implements a Quality of Service plugin that provides quality of
   52     service parameters over ports and networks.
   53 
   54     """
   55     supported_extension_aliases = [qos_apidef.ALIAS,
   56                                    'qos-bw-limit-direction',
   57                                    'qos-default',
   58                                    'qos-rule-type-details',
   59                                    port_resource_request.ALIAS,
   60                                    qos_bw_minimum_ingress.ALIAS,
   61                                    qos_rules_alias.ALIAS]
   62 
   63     __native_pagination_support = True
   64     __native_sorting_support = True
   65     __filter_validation_support = True
   66 
   67     def __init__(self):
   68         super(QoSPlugin, self).__init__()
   69         self.driver_manager = manager.QosServiceDriverManager()
   70 
   71         callbacks_registry.subscribe(
   72             self._validate_create_port_callback,
   73             callbacks_resources.PORT,
   74             callbacks_events.PRECOMMIT_CREATE)
   75         callbacks_registry.subscribe(
   76             self._validate_update_port_callback,
   77             callbacks_resources.PORT,
   78             callbacks_events.PRECOMMIT_UPDATE)
   79         callbacks_registry.subscribe(
   80             self._validate_update_network_callback,
   81             callbacks_resources.NETWORK,
   82             callbacks_events.PRECOMMIT_UPDATE)
   83 
   84     @staticmethod
   85     @resource_extend.extends([port_def.COLLECTION_NAME])
   86     def _extend_port_resource_request(port_res, port_db):
   87         """Add resource request to a port."""
   88         if isinstance(port_db, ports_object.Port):
   89             qos_id = port_db.qos_policy_id or port_db.qos_network_policy_id
   90         else:
   91             qos_id = None
   92             if port_db.get('qos_policy_binding'):
   93                 qos_id = port_db.qos_policy_binding.policy_id
   94             elif port_db.get('qos_network_policy_binding'):
   95                 qos_id = port_db.qos_network_policy_binding.policy_id
   96 
   97         port_res['resource_request'] = None
   98         if not qos_id:
   99             return port_res
  100         qos_policy = policy_object.QosPolicy.get_object(
  101             context.get_admin_context(), id=qos_id)
  102 
  103         resources = {}
  104         # NOTE(ralonsoh): we should move this translation dict to n-lib.
  105         rule_direction_class = {
  106             nl_constants.INGRESS_DIRECTION:
  107                 pl_constants.CLASS_NET_BW_INGRESS_KBPS,
  108             nl_constants.EGRESS_DIRECTION:
  109                 pl_constants.CLASS_NET_BW_EGRESS_KBPS
  110         }
  111         for rule in qos_policy.rules:
  112             if rule.rule_type == qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH:
  113                 resources[rule_direction_class[rule.direction]] = rule.min_kbps
  114         if not resources:
  115             return port_res
  116 
  117         # NOTE(ralonsoh): we should not rely on the current execution order of
  118         # the port extending functions. Although here we have
  119         # port_res[VNIC_TYPE], we should retrieve this value from the port DB
  120         # object instead.
  121         vnic_trait = pl_utils.vnic_type_trait(
  122             port_res[portbindings.VNIC_TYPE])
  123 
  124         # TODO(lajoskatona): Change to handle all segments when any traits
  125         # support will be available. See Placement spec:
  126         # https://review.openstack.org/565730
  127         first_segment = network_object.NetworkSegment.get_objects(
  128             context.get_admin_context(), network_id=port_db.network_id)[0]
  129 
  130         if not first_segment or not first_segment.physical_network:
  131             return port_res
  132         physnet_trait = pl_utils.physnet_trait(
  133             first_segment.physical_network)
  134 
  135         port_res['resource_request'] = {
  136             'required': [physnet_trait, vnic_trait],
  137             'resources': resources}
  138         return port_res
  139 
  140     def _get_ports_with_policy(self, context, policy):
  141         networks_ids = policy.get_bound_networks()
  142         ports_with_net_policy = ports_object.Port.get_objects(
  143             context, network_id=networks_ids)
  144 
  145         # Filter only this ports which don't have overwritten policy
  146         ports_with_net_policy = [
  147             port for port in ports_with_net_policy if
  148             port.qos_policy_id is None
  149         ]
  150 
  151         ports_ids = policy.get_bound_ports()
  152         ports_with_policy = ports_object.Port.get_objects(
  153             context, id=ports_ids)
  154         return list(set(ports_with_policy + ports_with_net_policy))
  155 
  156     def _validate_create_port_callback(self, resource, event, trigger,
  157                                        **kwargs):
  158         context = kwargs['context']
  159         port_id = kwargs['port']['id']
  160         port = ports_object.Port.get_object(context, id=port_id)
  161         network = network_object.Network.get_object(context,
  162                                                     id=port.network_id)
  163 
  164         policy_id = port.qos_policy_id or network.qos_policy_id
  165         if policy_id is None:
  166             return
  167 
  168         policy = policy_object.QosPolicy.get_object(
  169             context.elevated(), id=policy_id)
  170         self.validate_policy_for_port(context, policy, port)
  171 
  172     def _validate_update_port_callback(self, resource, event, trigger,
  173                                        payload=None):
  174         context = payload.context
  175         original_policy_id = payload.states[0].get(
  176             qos_consts.QOS_POLICY_ID)
  177         policy_id = payload.desired_state.get(qos_consts.QOS_POLICY_ID)
  178 
  179         if policy_id is None or policy_id == original_policy_id:
  180             return
  181 
  182         updated_port = ports_object.Port.get_object(
  183             context, id=payload.desired_state['id'])
  184         policy = policy_object.QosPolicy.get_object(
  185             context.elevated(), id=policy_id)
  186 
  187         self.validate_policy_for_port(context, policy, updated_port)
  188 
  189     def _validate_update_network_callback(self, resource, event, trigger,
  190                                           payload=None):
  191         context = payload.context
  192         original_network = payload.states[0]
  193         updated_network = payload.desired_state
  194 
  195         original_policy_id = original_network.get(qos_consts.QOS_POLICY_ID)
  196         policy_id = updated_network.get(qos_consts.QOS_POLICY_ID)
  197 
  198         if policy_id is None or policy_id == original_policy_id:
  199             return
  200 
  201         policy = policy_object.QosPolicy.get_object(
  202             context.elevated(), id=policy_id)
  203         ports = ports_object.Port.get_objects(
  204                 context, network_id=updated_network['id'])
  205         # Filter only this ports which don't have overwritten policy
  206         ports = [
  207             port for port in ports if port.qos_policy_id is None
  208         ]
  209         self.validate_policy_for_ports(context, policy, ports)
  210 
  211     def validate_policy(self, context, policy):
  212         ports = self._get_ports_with_policy(context, policy)
  213         self.validate_policy_for_ports(context, policy, ports)
  214 
  215     def validate_policy_for_ports(self, context, policy, ports):
  216         for port in ports:
  217             self.validate_policy_for_port(context, policy, port)
  218 
  219     def validate_policy_for_port(self, context, policy, port):
  220         for rule in policy.rules:
  221             if not self.driver_manager.validate_rule_for_port(rule, port):
  222                 raise qos_exc.QosRuleNotSupported(rule_type=rule.rule_type,
  223                                                   port_id=port['id'])
  224             # minimum-bandwidth rule is only supported (independently of
  225             # drivers) on networks whose first segment is backed by a physnet
  226             if rule.rule_type == qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH:
  227                 net = network_object.Network.get_object(
  228                     context, id=port.network_id)
  229                 physnet = net.segments[0].physical_network
  230                 if physnet is None:
  231                     raise qos_exc.QosRuleNotSupported(rule_type=rule.rule_type,
  232                                                       port_id=port['id'])
  233 
  234     def reject_min_bw_rule_updates(self, context, policy):
  235         ports = self._get_ports_with_policy(context, policy)
  236         for port in ports:
  237             # NOTE(bence romsics): In some cases the presence of
  238             # 'binding:profile.allocation' is a more precise marker than
  239             # 'device_owner' about when we have to reject min-bw related
  240             # policy/rule updates. However 'binding:profile.allocation' cannot
  241             # be used in a generic way here. Consider the case when the first
  242             # min-bw rule is added to a policy having ports in-use. Those ports
  243             # will not have 'binding:profile.allocation', but this policy
  244             # update must be rejected.
  245             if (port.device_owner is not None and
  246                     port.device_owner.startswith(
  247                         nl_constants.DEVICE_OWNER_COMPUTE_PREFIX)):
  248                 raise NotImplementedError(_(
  249                     'Cannot update QoS policies/rules backed by resources '
  250                     'tracked in Placement'))
  251 
  252     @db_base_plugin_common.convert_result_to_dict
  253     def create_policy(self, context, policy):
  254         """Create a QoS policy.
  255 
  256         :param context: neutron api request context
  257         :type context: neutron_lib.context.Context
  258         :param policy: policy data to be applied
  259         :type policy: dict
  260 
  261         :returns: a QosPolicy object
  262         """
  263         # NOTE(dasm): body 'policy' contains both tenant_id and project_id
  264         # but only latter needs to be used to create QosPolicy object.
  265         # We need to remove redundant keyword.
  266         # This cannot be done in other place of stacktrace, because neutron
  267         # needs to be backward compatible.
  268         tenant_id = policy['policy'].pop('tenant_id', None)
  269         if not policy['policy'].get('project_id'):
  270             policy['policy']['project_id'] = tenant_id
  271         policy_obj = policy_object.QosPolicy(context, **policy['policy'])
  272         with db_api.CONTEXT_WRITER.using(context):
  273             policy_obj.create()
  274             self.driver_manager.call(qos_consts.CREATE_POLICY_PRECOMMIT,
  275                                      context, policy_obj)
  276 
  277         self.driver_manager.call(qos_consts.CREATE_POLICY, context, policy_obj)
  278 
  279         return policy_obj
  280 
  281     @db_base_plugin_common.convert_result_to_dict
  282     def update_policy(self, context, policy_id, policy):
  283         """Update a QoS policy.
  284 
  285         :param context: neutron api request context
  286         :type context: neutron.context.Context
  287         :param policy_id: the id of the QosPolicy to update
  288         :param policy_id: str uuid
  289         :param policy: new policy data to be applied
  290         :type policy: dict
  291 
  292         :returns: a QosPolicy object
  293         """
  294         policy_data = policy['policy']
  295         with db_api.CONTEXT_WRITER.using(context):
  296             policy_obj = policy_object.QosPolicy.get_policy_obj(
  297                 context, policy_id)
  298             policy_obj.update_fields(policy_data, reset_changes=True)
  299             policy_obj.update()
  300             self.driver_manager.call(qos_consts.UPDATE_POLICY_PRECOMMIT,
  301                                      context, policy_obj)
  302 
  303         self.driver_manager.call(qos_consts.UPDATE_POLICY,
  304                                  context, policy_obj)
  305 
  306         return policy_obj
  307 
  308     def delete_policy(self, context, policy_id):
  309         """Delete a QoS policy.
  310 
  311         :param context: neutron api request context
  312         :type context: neutron.context.Context
  313         :param policy_id: the id of the QosPolicy to delete
  314         :type policy_id: str uuid
  315 
  316         :returns: None
  317         """
  318         with db_api.CONTEXT_WRITER.using(context):
  319             policy = policy_object.QosPolicy(context)
  320             policy.id = policy_id
  321             policy.delete()
  322             self.driver_manager.call(qos_consts.DELETE_POLICY_PRECOMMIT,
  323                                      context, policy)
  324 
  325         self.driver_manager.call(qos_consts.DELETE_POLICY,
  326                                  context, policy)
  327 
  328     @db_base_plugin_common.filter_fields
  329     @db_base_plugin_common.convert_result_to_dict
  330     def get_policy(self, context, policy_id, fields=None):
  331         """Get a QoS policy.
  332 
  333         :param context: neutron api request context
  334         :type context: neutron.context.Context
  335         :param policy_id: the id of the QosPolicy to update
  336         :type policy_id: str uuid
  337 
  338         :returns: a QosPolicy object
  339         """
  340         return policy_object.QosPolicy.get_policy_obj(context, policy_id)
  341 
  342     @db_base_plugin_common.filter_fields
  343     @db_base_plugin_common.convert_result_to_dict
  344     def get_policies(self, context, filters=None, fields=None, sorts=None,
  345                      limit=None, marker=None, page_reverse=False):
  346         """Get QoS policies.
  347 
  348         :param context: neutron api request context
  349         :type context: neutron.context.Context
  350         :param filters: search criteria
  351         :type filters: dict
  352 
  353         :returns: QosPolicy objects meeting the search criteria
  354         """
  355         filters = filters or dict()
  356         pager = base_obj.Pager(sorts, limit, page_reverse, marker)
  357         return policy_object.QosPolicy.get_objects(context, _pager=pager,
  358                                                    **filters)
  359 
  360     @db_base_plugin_common.filter_fields
  361     @db_base_plugin_common.convert_result_to_dict
  362     def get_rule_type(self, context, rule_type_name, fields=None):
  363         if not context.is_admin:
  364             raise lib_exc.NotAuthorized()
  365         return rule_type_object.QosRuleType.get_object(rule_type_name)
  366 
  367     @db_base_plugin_common.filter_fields
  368     @db_base_plugin_common.convert_result_to_dict
  369     def get_rule_types(self, context, filters=None, fields=None,
  370                        sorts=None, limit=None,
  371                        marker=None, page_reverse=False):
  372         if not filters:
  373             filters = {}
  374         return rule_type_object.QosRuleType.get_objects(**filters)
  375 
  376     def supported_rule_type_details(self, rule_type_name):
  377         return self.driver_manager.supported_rule_type_details(rule_type_name)
  378 
  379     @property
  380     def supported_rule_types(self):
  381         return self.driver_manager.supported_rule_types
  382 
  383     @db_base_plugin_common.convert_result_to_dict
  384     def create_policy_rule(self, context, rule_cls, policy_id, rule_data):
  385         """Create a QoS policy rule.
  386 
  387         :param context: neutron api request context
  388         :type context: neutron.context.Context
  389         :param rule_cls: the rule object class
  390         :type rule_cls: a class from the rule_object (qos.objects.rule) module
  391         :param policy_id: the id of the QosPolicy for which to create the rule
  392         :type policy_id: str uuid
  393         :param rule_data: the rule data to be applied
  394         :type rule_data: dict
  395 
  396         :returns: a QoS policy rule object
  397         """
  398         rule_type = rule_cls.rule_type
  399         rule_data = rule_data[rule_type + '_rule']
  400 
  401         with db_api.autonested_transaction(context.session):
  402             # Ensure that we have access to the policy.
  403             policy = policy_object.QosPolicy.get_policy_obj(context, policy_id)
  404             checker.check_bandwidth_rule_conflict(policy, rule_data)
  405             rule = rule_cls(context, qos_policy_id=policy_id, **rule_data)
  406             checker.check_rules_conflict(policy, rule)
  407             rule.create()
  408             policy.obj_load_attr('rules')
  409             self.validate_policy(context, policy)
  410             if rule.rule_type == qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH:
  411                 self.reject_min_bw_rule_updates(context, policy)
  412             self.driver_manager.call(qos_consts.UPDATE_POLICY_PRECOMMIT,
  413                                      context, policy)
  414 
  415         self.driver_manager.call(qos_consts.UPDATE_POLICY, context, policy)
  416 
  417         return rule
  418 
  419     @db_base_plugin_common.convert_result_to_dict
  420     def update_policy_rule(self, context, rule_cls, rule_id, policy_id,
  421                            rule_data):
  422         """Update a QoS policy rule.
  423 
  424         :param context: neutron api request context
  425         :type context: neutron.context.Context
  426         :param rule_cls: the rule object class
  427         :type rule_cls: a class from the rule_object (qos.objects.rule) module
  428         :param rule_id: the id of the QoS policy rule to update
  429         :type rule_id: str uuid
  430         :param policy_id: the id of the rule's policy
  431         :type policy_id: str uuid
  432         :param rule_data: the new rule data to update
  433         :type rule_data: dict
  434 
  435         :returns: a QoS policy rule object
  436         """
  437         rule_type = rule_cls.rule_type
  438         rule_data = rule_data[rule_type + '_rule']
  439 
  440         with db_api.autonested_transaction(context.session):
  441             # Ensure we have access to the policy.
  442             policy = policy_object.QosPolicy.get_policy_obj(context, policy_id)
  443             # Ensure the rule belongs to the policy.
  444             checker.check_bandwidth_rule_conflict(policy, rule_data)
  445             rule = policy.get_rule_by_id(rule_id)
  446             rule.update_fields(rule_data, reset_changes=True)
  447             checker.check_rules_conflict(policy, rule)
  448             rule.update()
  449             policy.obj_load_attr('rules')
  450             self.validate_policy(context, policy)
  451             if rule.rule_type == qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH:
  452                 self.reject_min_bw_rule_updates(context, policy)
  453             self.driver_manager.call(qos_consts.UPDATE_POLICY_PRECOMMIT,
  454                                      context, policy)
  455 
  456         self.driver_manager.call(qos_consts.UPDATE_POLICY, context, policy)
  457 
  458         return rule
  459 
  460     def _get_policy_id(self, context, rule_cls, rule_id):
  461         with db_api.autonested_transaction(context.session):
  462             rule_object = rule_cls.get_object(context, id=rule_id)
  463             if not rule_object:
  464                 raise qos_exc.QosRuleNotFound(policy_id="", rule_id=rule_id)
  465         return rule_object.qos_policy_id
  466 
  467     def update_rule(self, context, rule_cls, rule_id, rule_data):
  468         """Update a QoS policy rule alias. This method processes a QoS policy
  469         rule update, where the rule is an API first level resource instead of a
  470         subresource of a policy.
  471 
  472         :param context: neutron api request context
  473         :type context: neutron.context.Context
  474         :param rule_cls: the rule object class
  475         :type rule_cls: a class from the rule_object (qos.objects.rule) module
  476         :param rule_id: the id of the QoS policy rule to update
  477         :type rule_id: str uuid
  478         :param rule_data: the new rule data to update
  479         :type rule_data: dict
  480 
  481         :returns: a QoS policy rule object
  482         :raises: qos_exc.QosRuleNotFound
  483         """
  484         policy_id = self._get_policy_id(context, rule_cls, rule_id)
  485         rule_data_name = rule_cls.rule_type + '_rule'
  486         alias_rule_data_name = 'alias_' + rule_data_name
  487         rule_data[rule_data_name] = rule_data.pop(alias_rule_data_name)
  488         return self.update_policy_rule(context, rule_cls, rule_id, policy_id,
  489                                        rule_data)
  490 
  491     def delete_policy_rule(self, context, rule_cls, rule_id, policy_id):
  492         """Delete a QoS policy rule.
  493 
  494         :param context: neutron api request context
  495         :type context: neutron.context.Context
  496         :param rule_cls: the rule object class
  497         :type rule_cls: a class from the rule_object (qos.objects.rule) module
  498         :param rule_id: the id of the QosPolicy Rule to delete
  499         :type rule_id: str uuid
  500         :param policy_id: the id of the rule's policy
  501         :type policy_id: str uuid
  502 
  503         :returns: None
  504         """
  505         with db_api.autonested_transaction(context.session):
  506             # Ensure we have access to the policy.
  507             policy = policy_object.QosPolicy.get_policy_obj(context, policy_id)
  508             rule = policy.get_rule_by_id(rule_id)
  509             rule.delete()
  510             policy.obj_load_attr('rules')
  511             if rule.rule_type == qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH:
  512                 self.reject_min_bw_rule_updates(context, policy)
  513             self.driver_manager.call(qos_consts.UPDATE_POLICY_PRECOMMIT,
  514                                      context, policy)
  515 
  516         self.driver_manager.call(qos_consts.UPDATE_POLICY, context, policy)
  517 
  518     def delete_rule(self, context, rule_cls, rule_id):
  519         """Delete a QoS policy rule alias. This method processes a QoS policy
  520         rule delete, where the rule is an API first level resource instead of a
  521         subresource of a policy.
  522 
  523         :param context: neutron api request context
  524         :type context: neutron.context.Context
  525         :param rule_cls: the rule object class
  526         :type rule_cls: a class from the rule_object (qos.objects.rule) module
  527         :param rule_id: the id of the QosPolicy Rule to delete
  528         :type rule_id: str uuid
  529 
  530         :returns: None
  531         :raises: qos_exc.QosRuleNotFound
  532         """
  533         policy_id = self._get_policy_id(context, rule_cls, rule_id)
  534         return self.delete_policy_rule(context, rule_cls, rule_id, policy_id)
  535 
  536     @db_base_plugin_common.filter_fields
  537     @db_base_plugin_common.convert_result_to_dict
  538     def get_policy_rule(self, context, rule_cls, rule_id, policy_id,
  539                         fields=None):
  540         """Get a QoS policy rule.
  541 
  542         :param context: neutron api request context
  543         :type context: neutron.context.Context
  544         :param rule_cls: the rule object class
  545         :type rule_cls: a class from the rule_object (qos.objects.rule) module
  546         :param rule_id: the id of the QoS policy rule to get
  547         :type rule_id: str uuid
  548         :param policy_id: the id of the rule's policy
  549         :type policy_id: str uuid
  550 
  551         :returns: a QoS policy rule object
  552         :raises: qos_exc.QosRuleNotFound
  553         """
  554         with db_api.autonested_transaction(context.session):
  555             # Ensure we have access to the policy.
  556             policy_object.QosPolicy.get_policy_obj(context, policy_id)
  557             rule = rule_cls.get_object(context, id=rule_id)
  558         if not rule:
  559             raise qos_exc.QosRuleNotFound(policy_id=policy_id, rule_id=rule_id)
  560         return rule
  561 
  562     def get_rule(self, context, rule_cls, rule_id, fields=None):
  563         """Get a QoS policy rule alias. This method processes a QoS policy
  564         rule get, where the rule is an API first level resource instead of a
  565         subresource of a policy
  566 
  567         :param context: neutron api request context
  568         :type context: neutron.context.Context
  569         :param rule_cls: the rule object class
  570         :type rule_cls: a class from the rule_object (qos.objects.rule) module
  571         :param rule_id: the id of the QoS policy rule to get
  572         :type rule_id: str uuid
  573 
  574         :returns: a QoS policy rule object
  575         :raises: qos_exc.QosRuleNotFound
  576         """
  577         policy_id = self._get_policy_id(context, rule_cls, rule_id)
  578         return self.get_policy_rule(context, rule_cls, rule_id, policy_id)
  579 
  580     # TODO(QoS): enforce rule types when accessing rule objects
  581     @db_base_plugin_common.filter_fields
  582     @db_base_plugin_common.convert_result_to_dict
  583     def get_policy_rules(self, context, rule_cls, policy_id, filters=None,
  584                          fields=None, sorts=None, limit=None, marker=None,
  585                          page_reverse=False):
  586         """Get QoS policy rules.
  587 
  588         :param context: neutron api request context
  589         :type context: neutron.context.Context
  590         :param rule_cls: the rule object class
  591         :type rule_cls: a class from the rule_object (qos.objects.rule) module
  592         :param policy_id: the id of the QosPolicy for which to get rules
  593         :type policy_id: str uuid
  594 
  595         :returns: QoS policy rule objects meeting the search criteria
  596         """
  597         with db_api.autonested_transaction(context.session):
  598             # Ensure we have access to the policy.
  599             policy_object.QosPolicy.get_policy_obj(context, policy_id)
  600             filters = filters or dict()
  601             filters[qos_consts.QOS_POLICY_ID] = policy_id
  602             pager = base_obj.Pager(sorts, limit, page_reverse, marker)
  603             return rule_cls.get_objects(context, _pager=pager, **filters)