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:
parent
e112c25dad
commit
e343725419
|
@ -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.
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue