Merge "Add OS::Cinder::QoSSpecs"
This commit is contained in:
commit
46a5c78955
|
@ -90,5 +90,6 @@
|
||||||
"resource_types:OS::Manila::ShareType": "rule:project_admin",
|
"resource_types:OS::Manila::ShareType": "rule:project_admin",
|
||||||
"resource_types:OS::Neutron::QoSPolicy": "rule:project_admin",
|
"resource_types:OS::Neutron::QoSPolicy": "rule:project_admin",
|
||||||
"resource_types:OS::Neutron::QoSBandwidthLimitRule": "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