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 <ramanjieee@gmail.com>
Co-Authored-By: John Schwarz <jschwarz@redhat.com>
Co-Authored-By: vikram.choudhary <vikram.choudhary@huawei.com>
Change-Id: If7460516a3b3e3ccb91f2e7becfe7335ff809bda
This commit is contained in:
Ramanjaneya 2015-07-07 18:40:49 +05:30 committed by John Schwarz
parent c44b57f3f5
commit 002a0c7375
11 changed files with 425 additions and 5 deletions

@ -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

@ -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

@ -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'

@ -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}

@ -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)

@ -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

@ -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'

@ -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'

@ -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)