From 002a0c73759d86d3b93d9d53ce5f832a1bb434b3 Mon Sep 17 00:00:00 2001 From: Ramanjaneya Date: Tue, 7 Jul 2015 18:40:49 +0530 Subject: [PATCH] Support QoS neutron-client (1/2). This patch adds the command line support for 1. QoS policy 2. QoS policy association with the neutron network 3. QoS policy association with the neutron port Note: Neutronclient support for QoS rule-types will be addressed via a separate patch (https://review.openstack.org/#/c/198277/) DocImpact APIImpact Partially-Implements: blueprint quantum-qos-api Co-Authored-By: Ramanjaneya Reddy Palleti Co-Authored-By: John Schwarz Co-Authored-By: vikram.choudhary Change-Id: If7460516a3b3e3ccb91f2e7becfe7335ff809bda --- neutronclient/neutron/v2_0/network.py | 18 +- neutronclient/neutron/v2_0/port.py | 11 +- neutronclient/neutron/v2_0/qos/__init__.py | 0 neutronclient/neutron/v2_0/qos/policy.py | 140 +++++++++++++++ neutronclient/shell.py | 6 + neutronclient/tests/unit/qos/__init__.py | 0 .../tests/unit/qos/test_cli20_policy.py | 160 ++++++++++++++++++ neutronclient/tests/unit/test_cli20.py | 3 +- .../tests/unit/test_cli20_network.py | 29 ++++ neutronclient/tests/unit/test_cli20_port.py | 30 ++++ neutronclient/v2_0/client.py | 33 ++++ 11 files changed, 425 insertions(+), 5 deletions(-) create mode 100755 neutronclient/neutron/v2_0/qos/__init__.py create mode 100755 neutronclient/neutron/v2_0/qos/policy.py create mode 100755 neutronclient/tests/unit/qos/__init__.py create mode 100755 neutronclient/tests/unit/qos/test_cli20_policy.py diff --git a/neutronclient/neutron/v2_0/network.py b/neutronclient/neutron/v2_0/network.py index 8884c79..8dc963c 100644 --- a/neutronclient/neutron/v2_0/network.py +++ b/neutronclient/neutron/v2_0/network.py @@ -20,6 +20,7 @@ from neutronclient.common import exceptions from neutronclient.common import utils from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 +from neutronclient.neutron.v2_0.qos import policy as qos_policy def _format_subnets(network): @@ -101,7 +102,7 @@ class ShowNetwork(neutronV20.ShowCommand): resource = 'network' -class CreateNetwork(neutronV20.CreateCommand): +class CreateNetwork(neutronV20.CreateCommand, qos_policy.CreateQosPolicyMixin): """Create a network for a given tenant.""" resource = 'network' @@ -144,6 +145,8 @@ class CreateNetwork(neutronV20.CreateCommand): 'name', metavar='NAME', help=_('Name of network to create.')) + self.add_arguments_qos_policy(parser) + def args2body(self, parsed_args): body = {'network': { 'name': parsed_args.name, @@ -154,6 +157,9 @@ class CreateNetwork(neutronV20.CreateCommand): 'provider:network_type', 'provider:physical_network', 'provider:segmentation_id']) + + self.args2body_qos_policy(parsed_args, body['network']) + return body @@ -163,7 +169,15 @@ class DeleteNetwork(neutronV20.DeleteCommand): resource = 'network' -class UpdateNetwork(neutronV20.UpdateCommand): +class UpdateNetwork(neutronV20.UpdateCommand, qos_policy.UpdateQosPolicyMixin): """Update network's information.""" resource = 'network' + + def add_known_arguments(self, parser): + self.add_arguments_qos_policy(parser) + + def args2body(self, parsed_args): + body = {'network': {}} + self.args2body_qos_policy(parsed_args, body['network']) + return body diff --git a/neutronclient/neutron/v2_0/port.py b/neutronclient/neutron/v2_0/port.py index a983317..42d6714 100644 --- a/neutronclient/neutron/v2_0/port.py +++ b/neutronclient/neutron/v2_0/port.py @@ -22,6 +22,7 @@ from neutronclient.common import exceptions from neutronclient.common import utils from neutronclient.i18n import _ from neutronclient.neutron import v2_0 as neutronV20 +from neutronclient.neutron.v2_0.qos import policy as qos_policy def _format_fixed_ips(port): @@ -193,7 +194,7 @@ class UpdateExtraDhcpOptMixin(object): class CreatePort(neutronV20.CreateCommand, UpdatePortSecGroupMixin, - UpdateExtraDhcpOptMixin): + UpdateExtraDhcpOptMixin, qos_policy.CreateQosPolicyMixin): """Create a port for a given tenant.""" resource = 'port' @@ -230,6 +231,7 @@ class CreatePort(neutronV20.CreateCommand, UpdatePortSecGroupMixin, help=argparse.SUPPRESS) self.add_arguments_secgroup(parser) self.add_arguments_extradhcpopt(parser) + self.add_arguments_qos_policy(parser) parser.add_argument( 'network_id', metavar='NETWORK', @@ -254,6 +256,7 @@ class CreatePort(neutronV20.CreateCommand, UpdatePortSecGroupMixin, self.args2body_secgroup(parsed_args, body['port']) self.args2body_extradhcpopt(parsed_args, body['port']) + self.args2body_qos_policy(parsed_args, body['port']) return body @@ -265,7 +268,7 @@ class DeletePort(neutronV20.DeleteCommand): class UpdatePort(neutronV20.UpdateCommand, UpdatePortSecGroupMixin, - UpdateExtraDhcpOptMixin): + UpdateExtraDhcpOptMixin, qos_policy.UpdateQosPolicyMixin): """Update port's information.""" resource = 'port' @@ -282,6 +285,7 @@ class UpdatePort(neutronV20.UpdateCommand, UpdatePortSecGroupMixin, help=argparse.SUPPRESS) self.add_arguments_secgroup(parser) self.add_arguments_extradhcpopt(parser) + self.add_arguments_qos_policy(parser) def args2body(self, parsed_args): body = {'port': {}} @@ -290,6 +294,9 @@ class UpdatePort(neutronV20.UpdateCommand, UpdatePortSecGroupMixin, if parsed_args.admin_state_up: body['port'].update({'admin_state_up': parsed_args.admin_state_up}) + self.args2body_secgroup(parsed_args, body['port']) self.args2body_extradhcpopt(parsed_args, body['port']) + self.args2body_qos_policy(parsed_args, body['port']) + return body diff --git a/neutronclient/neutron/v2_0/qos/__init__.py b/neutronclient/neutron/v2_0/qos/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/neutronclient/neutron/v2_0/qos/policy.py b/neutronclient/neutron/v2_0/qos/policy.py new file mode 100755 index 0000000..fd6ffb1 --- /dev/null +++ b/neutronclient/neutron/v2_0/qos/policy.py @@ -0,0 +1,140 @@ +# Copyright 2015 Huawei Technologies India Pvt Ltd, 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 neutronclient.i18n import _ +from neutronclient.neutron import v2_0 as neutronv20 + + +def get_qos_policy_id(client, policy_id_or_name): + _policy_id = neutronv20.find_resourceid_by_name_or_id( + client, 'policy', policy_id_or_name, cmd_resource='qos_policy') + return _policy_id + + +class CreateQosPolicyMixin(object): + def add_arguments_qos_policy(self, parser): + qos_policy_args = parser.add_mutually_exclusive_group() + qos_policy_args.add_argument( + '--qos-policy', + help=_('Attach QoS policy ID or name to the resource.')) + return qos_policy_args + + def args2body_qos_policy(self, parsed_args, resource): + if parsed_args.qos_policy: + _policy_id = get_qos_policy_id(self.get_client(), + parsed_args.qos_policy) + resource['qos_policy_id'] = _policy_id + + +class UpdateQosPolicyMixin(CreateQosPolicyMixin): + def add_arguments_qos_policy(self, parser): + qos_policy_args = (super(UpdateQosPolicyMixin, self). + add_arguments_qos_policy(parser)) + qos_policy_args.add_argument( + '--no-qos-policy', + action='store_true', + help=_('Detach QoS policy from the resource.')) + return qos_policy_args + + def args2body_qos_policy(self, parsed_args, resource): + super(UpdateQosPolicyMixin, self).args2body_qos_policy(parsed_args, + resource) + if parsed_args.no_qos_policy: + resource['qos_policy_id'] = None + + +class ListQoSPolicy(neutronv20.ListCommand): + """List QoS policies that belong to a given tenant connection.""" + + resource = 'policy' + shadow_resource = 'qos_policy' + list_columns = ['id', 'name'] + pagination_support = True + sorting_support = True + + +class ShowQoSPolicy(neutronv20.ShowCommand): + """Show information of a given qos policy.""" + + resource = 'policy' + shadow_resource = 'qos_policy' + + +class CreateQoSPolicy(neutronv20.CreateCommand): + """Create a qos policy.""" + + resource = 'policy' + shadow_resource = 'qos_policy' + + def add_known_arguments(self, parser): + parser.add_argument( + 'name', metavar='NAME', + help=_('Name of QoS policy to create.')) + parser.add_argument( + '--description', + help=_('Description of the QoS policy.')) + parser.add_argument( + '--shared', + action='store_true', + help=_('Accessible by other tenants. ' + 'Set shared to True (default is False).')) + + def args2body(self, parsed_args): + body = {self.resource: {'name': parsed_args.name}, } + if parsed_args.description: + body[self.resource]['description'] = parsed_args.description + if parsed_args.shared: + body[self.resource]['shared'] = parsed_args.shared + if parsed_args.tenant_id: + body[self.resource]['tenant_id'] = parsed_args.tenant_id + return body + + +class UpdateQoSPolicy(neutronv20.UpdateCommand): + """Update a given qos policy.""" + + resource = 'policy' + shadow_resource = 'qos_policy' + + def add_known_arguments(self, parser): + parser.add_argument( + '--name', + help=_('Name of QoS policy.')) + parser.add_argument( + '--description', + help=_('Description of the QoS policy.')) + parser.add_argument( + '--shared', + action='store_true', + help=_('Accessible by other tenants. ' + 'Set shared to True (default is False).')) + + def args2body(self, parsed_args): + body = {self.resource: {}, } + if parsed_args.name: + body[self.resource]['name'] = parsed_args.name + if parsed_args.description: + body[self.resource]['description'] = parsed_args.description + if parsed_args.shared: + body[self.resource]['shared'] = parsed_args.shared + return body + + +class DeleteQoSPolicy(neutronv20.DeleteCommand): + """Delete a given qos policy.""" + + resource = 'policy' + shadow_resource = 'qos_policy' diff --git a/neutronclient/shell.py b/neutronclient/shell.py index 7f80404..8554b28 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -72,6 +72,7 @@ from neutronclient.neutron.v2_0.nsx import networkgateway from neutronclient.neutron.v2_0.nsx import qos_queue from neutronclient.neutron.v2_0 import policyprofile from neutronclient.neutron.v2_0 import port +from neutronclient.neutron.v2_0.qos import policy as qos_policy from neutronclient.neutron.v2_0 import quota from neutronclient.neutron.v2_0 import rbac from neutronclient.neutron.v2_0 import router @@ -372,6 +373,11 @@ COMMAND_V2 = { 'address-scope-create': address_scope.CreateAddressScope, 'address-scope-delete': address_scope.DeleteAddressScope, 'address-scope-update': address_scope.UpdateAddressScope, + 'qos-policy-list': qos_policy.ListQoSPolicy, + 'qos-policy-show': qos_policy.ShowQoSPolicy, + 'qos-policy-create': qos_policy.CreateQoSPolicy, + 'qos-policy-update': qos_policy.UpdateQoSPolicy, + 'qos-policy-delete': qos_policy.DeleteQoSPolicy, } COMMANDS = {'2.0': COMMAND_V2} diff --git a/neutronclient/tests/unit/qos/__init__.py b/neutronclient/tests/unit/qos/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/neutronclient/tests/unit/qos/test_cli20_policy.py b/neutronclient/tests/unit/qos/test_cli20_policy.py new file mode 100755 index 0000000..9e4bf6d --- /dev/null +++ b/neutronclient/tests/unit/qos/test_cli20_policy.py @@ -0,0 +1,160 @@ +# Copyright 2015 Huawei Technologies India Pvt Ltd. +# 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 neutronclient.neutron.v2_0.qos import policy as policy +from neutronclient.tests.unit import test_cli20 + + +class CLITestV20QoSPolicyJSON(test_cli20.CLITestV20Base): + def setUp(self): + super(CLITestV20QoSPolicyJSON, self).setUp() + self.res = 'policy' + self.cmd_res = 'qos_policy' + self.ress = "policies" + self.cmd_ress = 'qos_policies' + + def test_create_policy_with_only_keyattributes(self): + """Create qos policy abc.""" + cmd = policy.CreateQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + myid = 'myid' + name = 'abc' + args = [name] + position_names = ['name'] + position_values = [name] + self._test_create_resource(self.res, cmd, name, myid, args, + position_names, position_values, + cmd_resource=self.cmd_res) + + def test_create_policy_with_description(self): + """Create qos policy xyz --description abc.""" + cmd = policy.CreateQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + myid = 'myid' + name = 'abc' + description = 'policy_abc' + args = [name, '--description', description] + position_names = ['name', 'description'] + position_values = [name, description] + self._test_create_resource(self.res, cmd, name, myid, args, + position_names, position_values, + cmd_resource=self.cmd_res) + + def test_create_policy_with_shared(self): + """Create qos policy abc shared across tenants""" + cmd = policy.CreateQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + myid = 'myid' + name = 'abc' + description = 'policy_abc' + args = [name, '--description', description, '--shared'] + position_names = ['name', 'description', 'shared'] + position_values = [name, description, True] + self._test_create_resource(self.res, cmd, name, myid, args, + position_names, position_values, + cmd_resource=self.cmd_res) + + def test_create_policy_with_unicode(self): + """Create qos policy u'\u7f51\u7edc'.""" + cmd = policy.CreateQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + myid = 'myid' + name = u'\u7f51\u7edc' + description = u'\u7f51\u7edc' + args = [name, '--description', description] + position_names = ['name', 'description'] + position_values = [name, description] + self._test_create_resource(self.res, cmd, name, myid, args, + position_names, position_values, + cmd_resource=self.cmd_res) + + def test_update_policy(self): + """policy-update myid --name newname.""" + cmd = policy.UpdateQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + self._test_update_resource(self.res, cmd, 'myid', + ['myid', '--name', 'newname'], + {'name': 'newname', }, + cmd_resource=self.cmd_res) + + def test_update_policy_description(self): + """policy-update myid --name newname --description newdesc""" + cmd = policy.UpdateQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + self._test_update_resource(self.res, cmd, 'myid', + ['myid', '--description', 'newdesc'], + {'description': 'newdesc', }, + cmd_resource=self.cmd_res) + + def test_list_policies(self): + """qos-policy-list.""" + cmd = policy.ListQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(self.ress, cmd, True, + cmd_resources=self.cmd_ress) + + def test_list_policies_pagination(self): + """qos-policy-list for pagination.""" + cmd = policy.ListQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources_with_pagination(self.ress, cmd, + cmd_resources=self.cmd_ress) + + def test_list_policies_sort(self): + """sorted list: qos-policy-list --sort-key name --sort-key id + --sort-key asc --sort-key desc + """ + cmd = policy.ListQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(self.ress, cmd, + sort_key=["name", "id"], + sort_dir=["asc", "desc"], + cmd_resources=self.cmd_ress) + + def test_list_policies_limit(self): + """size (1000) limited list: qos-policy-list -P.""" + cmd = policy.ListQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(self.ress, cmd, page_size=1000, + cmd_resources=self.cmd_ress) + + def test_show_policy_id(self): + """qos-policy-show test_id.""" + cmd = policy.ShowQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + args = ['--fields', 'id', self.test_id] + self._test_show_resource(self.res, cmd, self.test_id, args, + ['id'], cmd_resource=self.cmd_res) + + def test_show_policy_name(self): + """qos-policy-show.""" + cmd = policy.ShowQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + args = ['--fields', 'id', '--fields', 'name', self.test_id] + self._test_show_resource(self.res, cmd, self.test_id, + args, ['id', 'name'], + cmd_resource=self.cmd_res) + + def test_delete_policy(self): + """qos-policy-delete my-id.""" + cmd = policy.DeleteQoSPolicy(test_cli20.MyApp(sys.stdout), + None) + my_id = 'myid1' + args = [my_id] + self._test_delete_resource(self.res, cmd, my_id, args, + cmd_resource=self.cmd_res) diff --git a/neutronclient/tests/unit/test_cli20.py b/neutronclient/tests/unit/test_cli20.py index 448a48a..9e09d7a 100644 --- a/neutronclient/tests/unit/test_cli20.py +++ b/neutronclient/tests/unit/test_cli20.py @@ -47,7 +47,8 @@ non_admin_status_resources = ['subnet', 'floatingip', 'security_group', 'ipsecpolicy', 'metering_label', 'metering_label_rule', 'net_partition', 'fox_socket', 'subnetpool', - 'rbac_policy', 'address_scope'] + 'rbac_policy', 'address_scope', + 'policy'] @contextlib.contextmanager diff --git a/neutronclient/tests/unit/test_cli20_network.py b/neutronclient/tests/unit/test_cli20_network.py index 2d6edc5..2f10b80 100644 --- a/neutronclient/tests/unit/test_cli20_network.py +++ b/neutronclient/tests/unit/test_cli20_network.py @@ -136,6 +136,19 @@ class CLITestV20NetworkJSON(test_cli20.CLITestV20Base): position_names, position_values, **vlantrans) + def test_create_network_with_qos_policy(self): + """Create net: --qos-policy mypolicy.""" + resource = 'network' + cmd = network.CreateNetwork(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + qos_policy_name = 'mypolicy' + args = [name, '--qos-policy', qos_policy_name] + position_names = ['name', 'qos_policy_id'] + position_values = [name, qos_policy_name] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + def test_list_nets_empty_with_column(self): resources = "networks" cmd = network.ListNetwork(test_cli20.MyApp(sys.stdout), None) @@ -486,6 +499,22 @@ class CLITestV20NetworkJSON(test_cli20.CLITestV20Base): 'tags': ['a', 'b'], } ) + def test_update_network_with_qos_policy(self): + """Update net: myid --qos-policy mypolicy.""" + resource = 'network' + cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--qos-policy', 'mypolicy'], + {'qos_policy_id': 'mypolicy', }) + + def test_update_network_with_no_qos_policy(self): + """Update net: myid --no-qos-policy.""" + resource = 'network' + cmd = network.UpdateNetwork(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--no-qos-policy'], + {'qos_policy_id': None, }) + def test_show_network(self): """Show net: --fields id --fields name myid.""" resource = 'network' diff --git a/neutronclient/tests/unit/test_cli20_port.py b/neutronclient/tests/unit/test_cli20_port.py index 76eb105..7717753 100644 --- a/neutronclient/tests/unit/test_cli20_port.py +++ b/neutronclient/tests/unit/test_cli20_port.py @@ -288,6 +288,20 @@ class CLITestV20PortJSON(test_cli20.CLITestV20Base): self._test_create_resource(resource, cmd, name, myid, args, position_names, position_values) + def test_create_port_with_qos_policy(self): + """Create port: --qos-policy mypolicy.""" + resource = 'port' + cmd = port.CreatePort(test_cli20.MyApp(sys.stdout), None) + name = 'myname' + myid = 'myid' + netid = 'netid' + qos_policy_name = 'mypolicy' + args = [netid, '--qos-policy', qos_policy_name] + position_names = ['network_id', 'qos_policy_id'] + position_values = [netid, qos_policy_name] + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) + def test_list_ports(self): """List ports: -D.""" resources = "ports" @@ -539,6 +553,22 @@ class CLITestV20PortJSON(test_cli20.CLITestV20Base): cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) self._test_update_resource(resource, cmd, myid, args, updatedfields) + def test_update_port_with_qos_policy(self): + """Update port: myid --qos-policy mypolicy.""" + resource = 'port' + cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--qos-policy', 'mypolicy'], + {'qos_policy_id': 'mypolicy', }) + + def test_update_port_with_no_qos_policy(self): + """Update port: myid --no-qos-policy.""" + resource = 'port' + cmd = port.UpdatePort(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--no-qos-policy'], + {'qos_policy_id': None, }) + def test_delete_extra_dhcp_opts_from_port(self): resource = 'port' myid = 'myid' diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index c77b998..e521420 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -431,6 +431,8 @@ class Client(ClientBase): net_partition_path = "/net-partitions/%s" rbac_policies_path = "/rbac-policies" rbac_policy_path = "/rbac-policies/%s" + qos_policies_path = "/qos/policies" + qos_policy_path = "/qos/policies/%s" # API has no way to report plurals, so we have to hard code them EXTED_PLURALS = {'routers': 'router', @@ -464,6 +466,8 @@ class Client(ClientBase): 'healthmonitors': 'healthmonitor', 'rbac_policies': 'rbac_policy', 'address_scopes': 'address_scope', + 'qos_policies': 'qos_policy', + 'policies': 'policy', } @APIParamsCall @@ -1660,6 +1664,35 @@ class Client(ClientBase): """Delete the specified RBAC policy.""" return self.delete(self.rbac_policy_path % rbac_policy_id) + @APIParamsCall + def list_qos_policies(self, retrieve_all=True, **_params): + """Fetches a list of all qos policies for a tenant.""" + # Pass filters in "params" argument to do_request + return self.list('policies', self.qos_policies_path, + retrieve_all, **_params) + + @APIParamsCall + def show_qos_policy(self, qos_policy, **_params): + """Fetches information of a certain qos policy.""" + return self.get(self.qos_policy_path % qos_policy, + params=_params) + + @APIParamsCall + def create_qos_policy(self, body=None): + """Creates a new qos policy.""" + return self.post(self.qos_policies_path, body=body) + + @APIParamsCall + def update_qos_policy(self, qos_policy, body=None): + """Updates a qos policy.""" + return self.put(self.qos_policy_path % qos_policy, + body=body) + + @APIParamsCall + def delete_qos_policy(self, qos_policy): + """Deletes the specified qos policy.""" + return self.delete(self.qos_policy_path % qos_policy) + def __init__(self, **kwargs): """Initialize a new client for the Neutron v2.0 API.""" super(Client, self).__init__(**kwargs)