Support neutron QoSBandwidthLimitRule resource plugin

Provide some resources to support Neutron QoS capability.
This patch add OS::Neutron::QoSBandwidthLimitRule
resource plugin.

Change-Id: I0b28d990f9fd086eba089601c42f74bc2a9bf667
Blueprint: support-neutron-qos
This commit is contained in:
huangtianhua 2015-10-15 14:56:47 +08:00
parent e112c25dad
commit e343725419
6 changed files with 295 additions and 13 deletions

View File

@ -104,6 +104,15 @@ class NeutronClientPlugin(client_plugin.ClientPlugin):
subnet_info = self.client().show_subnet(subnet_id)
return subnet_info['subnet']['network_id']
def get_qos_policy_id(self, policy):
"""Returns the id of QoS policy.
Args:
policy: ID or name of the policy.
"""
return neutronV20.find_resourceid_by_name_or_id(
self.client(), 'policy', policy, cmd_resource='qos_policy')
def get_secgroup_uuids(self, security_groups):
'''Returns a list of security group UUIDs.

View File

@ -85,3 +85,13 @@ class AddressScopeConstraint(constraints.BaseCustomConstraint):
neutron_client = client.client('neutron')
neutronV20.find_resourceid_by_name_or_id(
neutron_client, 'address_scope', value)
class QoSPolicyConstraint(constraints.BaseCustomConstraint):
expected_exceptions = (exceptions.NeutronClientException,)
def validate_with_client(self, client, value):
neutron_client = client.client('neutron')
neutronV20.find_resourceid_by_name_or_id(
neutron_client, 'policy', value, cmd_resource='qos_policy')

View File

@ -15,6 +15,7 @@ from oslo_log import log as logging
from heat.common.i18n import _
from heat.engine import attributes
from heat.engine import constraints
from heat.engine import properties
from heat.engine.resources.openstack.neutron import neutron
from heat.engine import support
@ -106,7 +107,118 @@ class QoSPolicy(neutron.NeutronResource):
self.resource_id)['policy']
class QoSRule(neutron.NeutronResource):
"""A resource for Neutron QoS base rule."""
required_service_extension = 'qos'
support_status = support.SupportStatus(version='6.0.0')
PROPERTIES = (
POLICY, TENANT_ID,
) = (
'policy', 'tenant_id',
)
properties_schema = {
POLICY: properties.Schema(
properties.Schema.STRING,
_('ID or name of the QoS policy.'),
required=True,
constraints=[constraints.CustomConstraint('neutron.qos_policy')]
),
TENANT_ID: properties.Schema(
properties.Schema.STRING,
_('The owner tenant ID of this rule.')
),
}
def __init__(self, name, json_snippet, stack):
super(QoSRule, self).__init__(name, json_snippet, stack)
self._policy_id = None
@property
def policy_id(self):
if not self._policy_id:
self._policy_id = self.client_plugin().get_qos_policy_id(
self.properties[self.POLICY])
return self._policy_id
class QoSBandwidthLimitRule(QoSRule):
"""A resource for Neutron QoS bandwidth limit rule.
This rule can be associated with QoS policy, and then the policy
can be used by neutron port and network, to provide bandwidth limit
QoS capabilities.
The default policy usage of this resource is limited to
administrators only.
"""
PROPERTIES = (
MAX_BANDWIDTH, MAX_BURST_BANDWIDTH,
) = (
'max_kbps', 'max_burst_kbps',
)
properties_schema = {
MAX_BANDWIDTH: properties.Schema(
properties.Schema.INTEGER,
_('Max bandwidth in kbps.'),
required=True,
update_allowed=True,
constraints=[
constraints.Range(min=0)
]
),
MAX_BURST_BANDWIDTH: properties.Schema(
properties.Schema.INTEGER,
_('Max burst bandwidth in kbps.'),
update_allowed=True,
constraints=[
constraints.Range(min=0)
],
default=0
)
}
properties_schema.update(QoSRule.properties_schema)
def handle_create(self):
props = self.prepare_properties(self.properties,
self.physical_resource_name())
props.pop(self.POLICY)
rule = self.client().create_bandwidth_limit_rule(
self.policy_id,
{'bandwidth_limit_rule': props})['bandwidth_limit_rule']
self.resource_id_set(rule['id'])
def handle_delete(self):
if self.resource_id is None:
return
with self.client_plugin().ignore_not_found:
self.client().delete_bandwidth_limit_rule(
self.resource_id, self.policy_id)
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
if prop_diff:
self.client().update_bandwidth_limit_rule(
self.resource_id,
self.policy_id,
{'bandwidth_limit_rule': prop_diff})
def _show_resource(self):
return self.client().show_bandwidth_limit_rule(
self.resource_id, self.policy_id)['bandwidth_limit_rule']
def resource_mapping():
return {
'OS::Neutron::QoSPolicy': QoSPolicy,
'OS::Neutron::QoSBandwidthLimitRule': QoSBandwidthLimitRule
}

View File

@ -157,31 +157,44 @@ class NeutronConstraintsValidate(common.HeatTestCase):
scenarios = [
('validate_network',
dict(constraint_class=nc.NetworkConstraint,
resource_type='network')),
resource_type='network',
cmd_resource=None)),
('validate_port',
dict(constraint_class=nc.PortConstraint,
resource_type='port')),
resource_type='port',
cmd_resource=None)),
('validate_router',
dict(constraint_class=nc.RouterConstraint,
resource_type='router')),
resource_type='router',
cmd_resource=None)),
('validate_subnet',
dict(constraint_class=nc.SubnetConstraint,
resource_type='subnet')),
resource_type='subnet',
cmd_resource=None)),
('validate_subnetpool',
dict(constraint_class=nc.SubnetPoolConstraint,
resource_type='subnetpool')),
resource_type='subnetpool',
cmd_resource=None)),
('validate_address_scope',
dict(constraint_class=nc.AddressScopeConstraint,
resource_type='address_scope')),
resource_type='address_scope',
cmd_resource=None)),
('validate_loadbalancer',
dict(constraint_class=lc.LoadbalancerConstraint,
resource_type='loadbalancer')),
resource_type='loadbalancer',
cmd_resource=None)),
('validate_listener',
dict(constraint_class=lc.ListenerConstraint,
resource_type='listener')),
resource_type='listener',
cmd_resource=None)),
('validate_pool',
dict(constraint_class=lc.PoolConstraint,
resource_type='lbaas_pool'))
resource_type='lbaas_pool',
cmd_resource=None)),
('validate_qos_policy',
dict(constraint_class=nc.QoSPolicyConstraint,
resource_type='policy',
cmd_resource='qos_policy'))
]
def test_validate(self):
@ -197,8 +210,15 @@ class NeutronConstraintsValidate(common.HeatTestCase):
ctx = utils.dummy_context()
self.assertTrue(constraint.validate("foo", ctx))
self.assertFalse(constraint.validate("bar", ctx))
mock_find.assert_has_calls([mock.call(nc, self.resource_type, 'foo'),
mock.call(nc, self.resource_type, 'bar')])
if self.cmd_resource:
mock_calls = [mock.call(nc, self.resource_type, 'foo',
cmd_resource=self.cmd_resource),
mock.call(nc, self.resource_type, 'bar',
cmd_resource=self.cmd_resource)]
else:
mock_calls = [mock.call(nc, self.resource_type, 'foo'),
mock.call(nc, self.resource_type, 'bar')]
mock_find.assert_has_calls(mock_calls)
class NeutronClientPluginExtensionsTests(NeutronClientPluginTestCase):

View File

@ -35,6 +35,19 @@ resources:
tenant_id: d66c74c01d6c41b9846088c1ad9634d0
'''
bandwidth_limit_rule_template = '''
heat_template_version: 2016-04-08
description: This template to define a neutron bandwidth limit rule.
resources:
my_bandwidth_limit_rule:
type: OS::Neutron::QoSBandwidthLimitRule
properties:
policy: 477e8273-60a7-4c41-b683-fdb0bc7cd151
max_kbps: 1000
max_burst_kbps: 1000
tenant_id: d66c74c01d6c41b9846088c1ad9634d0
'''
class NeutronQoSPolicyTest(common.HeatTestCase):
def setUp(self):
@ -59,7 +72,6 @@ class NeutronQoSPolicyTest(common.HeatTestCase):
def test_resource_mapping(self):
mapping = qos.resource_mapping()
self.assertEqual(1, len(mapping))
self.assertEqual(qos.QoSPolicy, mapping['OS::Neutron::QoSPolicy'])
self.assertIsInstance(self.my_qos_policy, qos.QoSPolicy)
@ -80,7 +92,6 @@ class NeutronQoSPolicyTest(common.HeatTestCase):
'tenant_id': 'd66c74c01d6c41b9846088c1ad9634d0'}
self.neutronclient.create_qos_policy.return_value = policy
self.my_qos_policy.handle_create()
self.assertEqual('9c1eb3fe-7bba-479d-bd43-1d497e53c384',
self.my_qos_policy.resource_id)
@ -153,3 +164,122 @@ class NeutronQoSPolicyTest(common.HeatTestCase):
self.my_qos_policy.FnGetAtt('show'))
self.neutronclient.show_qos_policy.assert_has_calls(
[mock.call(self.my_qos_policy.resource_id)] * 2)
class NeutronQoSBandwidthLimitRuleTest(common.HeatTestCase):
def setUp(self):
super(NeutronQoSBandwidthLimitRuleTest, self).setUp()
utils.setup_dummy_db()
self.ctx = utils.dummy_context()
tpl = template_format.parse(bandwidth_limit_rule_template)
self.stack = stack.Stack(
self.ctx,
'neutron_bandwidth_limit_rule_test',
template.Template(tpl)
)
self.neutronclient = mock.MagicMock()
self.patchobject(neutron.NeutronClientPlugin, 'has_extension',
return_value=True)
self.bandwidth_limit_rule = self.stack['my_bandwidth_limit_rule']
self.bandwidth_limit_rule.client = mock.MagicMock(
return_value=self.neutronclient)
self.find_mock = self.patchobject(
neutron.neutronV20,
'find_resourceid_by_name_or_id')
self.policy_id = '477e8273-60a7-4c41-b683-fdb0bc7cd151'
self.find_mock.return_value = self.policy_id
def test_resource_mapping(self):
mapping = qos.resource_mapping()
self.assertEqual(2, len(mapping))
self.assertEqual(qos.QoSBandwidthLimitRule,
mapping['OS::Neutron::QoSBandwidthLimitRule'])
self.assertIsInstance(self.bandwidth_limit_rule,
qos.QoSBandwidthLimitRule)
def test_rule_handle_create(self):
rule = {
'bandwidth_limit_rule': {
'id': 'cf0eab12-ef8b-4a62-98d0-70576583c17a',
'max_kbps': 1000,
'max_burst_kbps': 1000,
'tenant_id': 'd66c74c01d6c41b9846088c1ad9634d0'
}
}
create_props = {'max_kbps': 1000,
'max_burst_kbps': 1000,
'tenant_id': 'd66c74c01d6c41b9846088c1ad9634d0'}
self.neutronclient.create_bandwidth_limit_rule.return_value = rule
self.bandwidth_limit_rule.handle_create()
self.assertEqual('cf0eab12-ef8b-4a62-98d0-70576583c17a',
self.bandwidth_limit_rule.resource_id)
self.neutronclient.create_bandwidth_limit_rule.assert_called_once_with(
self.policy_id,
{'bandwidth_limit_rule': create_props})
def test_rule_handle_delete(self):
rule_id = 'cf0eab12-ef8b-4a62-98d0-70576583c17a'
self.bandwidth_limit_rule.resource_id = rule_id
self.neutronclient.delete_bandwidth_limit_rule.return_value = None
self.assertIsNone(self.bandwidth_limit_rule.handle_delete())
self.neutronclient.delete_bandwidth_limit_rule.assert_called_once_with(
rule_id, self.policy_id)
def test_rule_handle_delete_not_found(self):
rule_id = 'cf0eab12-ef8b-4a62-98d0-70576583c17a'
self.bandwidth_limit_rule.resource_id = rule_id
not_found = self.neutronclient.NotFound
self.neutronclient.delete_bandwidth_limit_rule.side_effect = not_found
self.assertIsNone(self.bandwidth_limit_rule.handle_delete())
self.neutronclient.delete_bandwidth_limit_rule.assert_called_once_with(
rule_id, self.policy_id)
def test_rule_handle_delete_resource_id_is_none(self):
self.bandwidth_limit_rule.resource_id = None
self.assertIsNone(self.bandwidth_limit_rule.handle_delete())
self.assertEqual(0,
self.neutronclient.bandwidth_limit_rule.call_count)
def test_rule_handle_update(self):
rule_id = 'cf0eab12-ef8b-4a62-98d0-70576583c17a'
self.bandwidth_limit_rule.resource_id = rule_id
prop_diff = {
'max_kbps': 500,
'max_burst_kbps': 400
}
self.bandwidth_limit_rule.handle_update(
json_snippet={},
tmpl_diff={},
prop_diff=prop_diff)
self.neutronclient.update_bandwidth_limit_rule.assert_called_once_with(
rule_id,
self.policy_id,
{'bandwidth_limit_rule': prop_diff})
def test_rule_get_attr(self):
self.bandwidth_limit_rule.resource_id = 'test rule'
rule = {
'bandwidth_limit_rule': {
'id': 'cf0eab12-ef8b-4a62-98d0-70576583c17a',
'max_kbps': 1000,
'max_burst_kbps': 1000,
'tenant_id': 'd66c74c01d6c41b9846088c1ad9634d0'
}
}
self.neutronclient.show_bandwidth_limit_rule.return_value = rule
self.assertEqual(rule['bandwidth_limit_rule'],
self.bandwidth_limit_rule.FnGetAtt('show'))
self.neutronclient.show_bandwidth_limit_rule.assert_called_once_with(
self.bandwidth_limit_rule.resource_id, self.policy_id)

View File

@ -82,6 +82,7 @@ heat.constraints =
neutron.router = heat.engine.clients.os.neutron.neutron_constraints:RouterConstraint
neutron.subnet = heat.engine.clients.os.neutron.neutron_constraints:SubnetConstraint
neutron.subnetpool = heat.engine.clients.os.neutron.neutron_constraints:SubnetPoolConstraint
neutron.qos_policy = heat.engine.clients.os.neutron.neutron_constraints:QoSPolicyConstraint
neutron.lbaas.loadbalancer = heat.engine.clients.os.neutron.lbaas_constraints:LoadbalancerConstraint
neutron.lbaas.listener = heat.engine.clients.os.neutron.lbaas_constraints:ListenerConstraint
neutron.lbaas.pool = heat.engine.clients.os.neutron.lbaas_constraints:PoolConstraint