Merge "Add OS::Cinder::QoSSpecs"
commit
46a5c78955
|
@ -90,5 +90,6 @@
|
|||
"resource_types:OS::Manila::ShareType": "rule:project_admin",
|
||||
"resource_types:OS::Neutron::QoSPolicy": "rule:project_admin",
|
||||
"resource_types:OS::Neutron::QoSBandwidthLimitRule": "rule:project_admin",
|
||||
"resource_types:OS::Nova::HostAggregate": "rule:project_admin"
|
||||
"resource_types:OS::Nova::HostAggregate": "rule:project_admin",
|
||||
"resource_types:OS::Cinder::QoSSpecs": "rule:project_admin"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
#
|
||||
# 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 oslo_log import log as logging
|
||||
|
||||
from heat.common.i18n import _
|
||||
from heat.engine import properties
|
||||
from heat.engine import resource
|
||||
from heat.engine import support
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class QoSSpecs(resource.Resource):
|
||||
"""A resource for creating cinder QoS specs.
|
||||
|
||||
Users can ask for a specific volume type. Part of that volume type is a
|
||||
string that defines the QoS of the volume IO (fast, normal, or slow).
|
||||
Backends that can handle all of the demands of the volume type become
|
||||
candidates for scheduling. Usage of this resource restricted to admins
|
||||
only by default policy.
|
||||
"""
|
||||
|
||||
support_status = support.SupportStatus(version='7.0.0')
|
||||
|
||||
default_client_name = 'cinder'
|
||||
entity = 'qos_specs'
|
||||
required_service_extension = 'qos-specs'
|
||||
|
||||
PROPERTIES = (
|
||||
NAME, SPECS,
|
||||
) = (
|
||||
'name', 'specs',
|
||||
)
|
||||
|
||||
properties_schema = {
|
||||
NAME: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Name of the QoS.'),
|
||||
),
|
||||
SPECS: properties.Schema(
|
||||
properties.Schema.MAP,
|
||||
_('The specs key and value pairs of the QoS.'),
|
||||
required=True,
|
||||
update_allowed=True
|
||||
),
|
||||
}
|
||||
|
||||
def _find_diff(self, update_prps, stored_prps):
|
||||
remove_prps = list(
|
||||
set(stored_prps.keys() or []) - set(update_prps.keys() or [])
|
||||
)
|
||||
add_prps = dict(set(update_prps.items() or []) - set(
|
||||
stored_prps.items() or []))
|
||||
return add_prps, remove_prps
|
||||
|
||||
def handle_create(self):
|
||||
name = (self.properties[self.NAME] or
|
||||
self.physical_resource_name())
|
||||
specs = self.properties[self.SPECS]
|
||||
|
||||
qos = self.client().qos_specs.create(name, specs)
|
||||
self.resource_id_set(qos.id)
|
||||
|
||||
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
|
||||
"""Update the specs for QoS."""
|
||||
|
||||
new_specs = prop_diff.get(self.SPECS)
|
||||
old_specs = self.properties[self.SPECS]
|
||||
add_specs, remove_specs = self._find_diff(new_specs, old_specs)
|
||||
if self.resource_id is not None:
|
||||
# Set new specs to QoS Specs
|
||||
if add_specs:
|
||||
self.client().qos_specs.set_keys(self.resource_id, add_specs)
|
||||
# Unset old specs from QoS Specs
|
||||
if remove_specs:
|
||||
self.client().qos_specs.unset_keys(self.resource_id,
|
||||
remove_specs)
|
||||
|
||||
def handle_delete(self):
|
||||
if self.resource_id is not None:
|
||||
self.client().qos_specs.disassociate_all(self.resource_id)
|
||||
super(QoSSpecs, self).handle_delete()
|
||||
|
||||
|
||||
def resource_mapping():
|
||||
return {
|
||||
'OS::Cinder::QoSSpecs': QoSSpecs,
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
#
|
||||
# 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 mock
|
||||
|
||||
from heat.engine.clients.os import cinder as c_plugin
|
||||
from heat.engine.resources.openstack.cinder import qos_specs
|
||||
from heat.engine import stack
|
||||
from heat.engine import template
|
||||
from heat.tests import common
|
||||
from heat.tests import utils
|
||||
|
||||
QOS_SPECS_TEMPLATE = {
|
||||
'heat_template_version': '2015-10-15',
|
||||
'description': 'Cinder QoS specs creation example',
|
||||
'resources': {
|
||||
'my_qos_specs': {
|
||||
'type': 'OS::Cinder::QoSSpecs',
|
||||
'properties': {
|
||||
'name': 'foobar',
|
||||
'specs': {"foo": "bar", "foo1": "bar1"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class QoSSpecsTest(common.HeatTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(QoSSpecsTest, self).setUp()
|
||||
self.ctx = utils.dummy_context()
|
||||
self.patchobject(c_plugin.CinderClientPlugin, 'has_extension',
|
||||
return_value=True)
|
||||
self.stack = stack.Stack(
|
||||
self.ctx, 'cinder_qos_spec_test_stack',
|
||||
template.Template(QOS_SPECS_TEMPLATE)
|
||||
)
|
||||
self.my_qos_specs = self.stack['my_qos_specs']
|
||||
cinder_client = mock.MagicMock()
|
||||
self.cinderclient = mock.MagicMock()
|
||||
self.my_qos_specs.client = cinder_client
|
||||
cinder_client.return_value = self.cinderclient
|
||||
self.qos_specs = self.cinderclient.qos_specs
|
||||
self.value = mock.MagicMock()
|
||||
self.value.id = '927202df-1afb-497f-8368-9c2d2f26e5db'
|
||||
self.value.name = 'foobar'
|
||||
self.value.specs = {"foo": "bar", "foo1": "bar1"}
|
||||
self.qos_specs.create.return_value = self.value
|
||||
|
||||
def test_resource_mapping(self):
|
||||
mapping = qos_specs.resource_mapping()
|
||||
self.assertEqual(1, len(mapping))
|
||||
self.assertEqual(qos_specs.QoSSpecs,
|
||||
mapping['OS::Cinder::QoSSpecs'])
|
||||
self.assertIsInstance(self.my_qos_specs,
|
||||
qos_specs.QoSSpecs)
|
||||
|
||||
def _set_up_qos_specs_environment(self):
|
||||
self.qos_specs.create.return_value = self.value
|
||||
self.my_qos_specs.handle_create()
|
||||
|
||||
def test_qos_specs_handle_create_specs(self):
|
||||
self._set_up_qos_specs_environment()
|
||||
self.assertEqual(1, self.qos_specs.create.call_count)
|
||||
self.assertEqual(self.value.id, self.my_qos_specs.resource_id)
|
||||
|
||||
def test_qos_specs_handle_update_specs(self):
|
||||
self._set_up_qos_specs_environment()
|
||||
resource_id = self.my_qos_specs.resource_id
|
||||
prop_diff = {'specs': {"foo": "bar", "bar": "bar"}}
|
||||
set_expected = {"bar": "bar"}
|
||||
unset_expected = ["foo1"]
|
||||
|
||||
self.my_qos_specs.handle_update(
|
||||
json_snippet=None, tmpl_diff=None, prop_diff=prop_diff
|
||||
)
|
||||
self.qos_specs.set_keys.assert_called_once_with(
|
||||
resource_id,
|
||||
set_expected
|
||||
)
|
||||
self.qos_specs.unset_keys.assert_called_once_with(
|
||||
resource_id,
|
||||
unset_expected
|
||||
)
|
||||
|
||||
def test_qos_specs_handle_delete_specs(self):
|
||||
self._set_up_qos_specs_environment()
|
||||
resource_id = self.my_qos_specs.resource_id
|
||||
self.my_qos_specs.handle_delete()
|
||||
self.qos_specs.disassociate_all.assert_called_once_with(resource_id)
|
Loading…
Reference in New Issue