Merge "Add Bay to Magnum resources"

This commit is contained in:
Jenkins 2015-10-21 03:05:35 +00:00 committed by Gerrit Code Review
commit fcb771feca
2 changed files with 301 additions and 0 deletions

View File

@ -0,0 +1,155 @@
#
# 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 six
from heat.common import exception
from heat.common.i18n import _
from heat.engine import constraints
from heat.engine import properties
from heat.engine import resource
from heat.engine import support
class Bay(resource.Resource):
"""A resource that creates a Magnum Bay.
This resource creates a Magnum bay, which is a
collection of node objects where work is scheduled.
"""
support_status = support.SupportStatus(version='6.0.0')
PROPERTIES = (
NAME, BAYMODEL, NODE_COUNT, MASTER_COUNT, DISCOVERY_URL,
BAY_CREATE_TIMEOUT
) = (
'name', 'baymodel', 'node_count', 'master_count',
'discovery_url', 'bay_create_timeout'
)
properties_schema = {
NAME: properties.Schema(
properties.Schema.STRING,
_('The bay name.')
),
BAYMODEL: properties.Schema(
properties.Schema.STRING,
_('The ID of the bay model.'),
required=True
),
NODE_COUNT: properties.Schema(
properties.Schema.INTEGER,
_('The node count for this bay.'),
constraints=[constraints.Range(min=1)],
update_allowed=True
),
MASTER_COUNT: properties.Schema(
properties.Schema.INTEGER,
_('The number of master nodes for this bay.'),
constraints=[constraints.Range(min=1)],
update_allowed=True
),
DISCOVERY_URL: properties.Schema(
properties.Schema.STRING,
_('Url used for bay node discovery.')
),
BAY_CREATE_TIMEOUT: properties.Schema(
properties.Schema.INTEGER,
_('Timeout for creating the bay in minutes. '
'Set to 0 for no timeout.'),
constraints=[constraints.Range(min=0)],
default=0
)
}
default_client_name = 'magnum'
entity = 'bays'
def handle_create(self):
args = {
'name': self.properties[self.NAME],
'baymodel_id': self.properties[self.BAYMODEL],
'node_count': self.properties[self.NODE_COUNT],
'master_count': self.properties[self.NODE_COUNT],
'discovery_url': self.properties[self.DISCOVERY_URL],
'bay_create_timeout': self.properties[self.BAY_CREATE_TIMEOUT]
}
bay = self.client().bays.create(**args)
self.resource_id_set(bay.uuid)
return bay.uuid
def check_create_complete(self, id):
bay = self.client().bays.get(id)
if bay.status == 'CREATE_IN_PROGRESS':
return False
elif bay.status is None:
return False
elif bay.status == 'CREATE_COMPLETE':
return True
elif bay.status == 'CREATE_FAILED':
msg = (_("Failed to create Bay '%(name)s' - %(reason)s")
% {'name': self.name, 'reason': bay.status_reason})
raise exception.ResourceInError(status_reason=msg,
resource_status=bay.status)
else:
msg = (_("Unknown status creating Bay '%(name)s' - %(reason)s")
% {'name': self.name, 'reason': bay.status_reason})
raise exception.ResourceUnknownStatus(status_reason=msg,
resource_status=bay.status)
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
if prop_diff:
patch = [{'op': 'replace', 'path': '/' + k, 'value': v}
for k, v in six.iteritems(prop_diff)]
self.client().bays.update(self.resource_id, patch)
return self.resource_id
def check_update_complete(self, id):
bay = self.client().bays.get(id)
if bay.status == 'UPDATE_IN_PROGRESS':
return False
# check for None due to Magnum bug
# https://bugs.launchpad.net/magnum/+bug/1507598
elif bay.status is None:
return False
elif bay.status == 'UPDATE_COMPLETE':
return True
elif bay.status == 'UPDATE_FAILED':
msg = (_("Failed to update Bay '%(name)s' - %(reason)s")
% {'name': self.name, 'reason': bay.status_reason})
raise exception.ResourceInError(status_reason=msg,
resource_status=bay.status)
else:
msg = (_("Unknown status updating Bay '%(name)s' - %(reason)s")
% {'name': self.name, 'reason': bay.status_reason})
raise exception.ResourceUnknownStatus(status_reason=msg,
resource_status=bay.status)
def check_delete_complete(self, id):
if not id:
return True
try:
self.client().bays.get(id)
except Exception as exc:
self.client_plugin().ignore_not_found(exc)
return True
return False
def resource_mapping():
return {
'OS::Magnum::Bay': Bay
}

View File

@ -0,0 +1,146 @@
#
# 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 copy
import mock
from oslo_config import cfg
import six
from heat.common import exception
from heat.common import template_format
from heat.engine import resource
from heat.engine.resources.openstack.magnum import bay
from heat.engine import scheduler
from heat.engine import template
from heat.tests import common
from heat.tests import utils
magnum_template = '''
heat_template_version: 2015-04-30
resources:
test_bay:
type: OS::Magnum::Bay
properties:
name: test_bay
baymodel: 123456
node_count: 5
master_count: 1
discovery_url: https://discovery.etcd.io
bay_create_timeout: 15
'''
RESOURCE_TYPE = 'OS::Magnum::Bay'
class TestMagnumBay(common.HeatTestCase):
def setUp(self):
super(TestMagnumBay, self).setUp()
self.ctx = utils.dummy_context()
resource._register_class(RESOURCE_TYPE, bay.Bay)
t = template_format.parse(magnum_template)
self.stack = utils.parse_stack(t)
resource_defns = self.stack.t.resource_definitions(self.stack)
self.rsrc_defn = resource_defns['test_bay']
self.client = mock.Mock()
self.patchobject(bay.Bay, 'client', return_value=self.client)
def _create_resource(self, name, snippet, stack, stat='CREATE_COMPLETE'):
self.resource_id = '12345'
value = mock.MagicMock(uuid=self.resource_id)
self.client.bays.create.return_value = value
get_rv = mock.MagicMock(status=stat)
self.client.bays.get.return_value = get_rv
b = bay.Bay(name, snippet, stack)
return b
def test_bay_create(self):
b = self._create_resource('bay', self.rsrc_defn, self.stack)
scheduler.TaskRunner(b.create)()
self.assertEqual(self.resource_id, b.resource_id)
self.assertEqual((b.CREATE, b.COMPLETE), b.state)
def test_bay_create_failed(self):
cfg.CONF.set_override('action_retry_limit', 0)
b = self._create_resource('bay', self.rsrc_defn, self.stack,
stat='CREATE_FAILED')
exc = self.assertRaises(
exception.ResourceFailure,
scheduler.TaskRunner(b.create))
self.assertIn("Failed to create Bay", six.text_type(exc))
def test_bay_create_unknown_status(self):
b = self._create_resource('bay', self.rsrc_defn, self.stack,
stat='CREATE_FOO')
exc = self.assertRaises(
exception.ResourceFailure,
scheduler.TaskRunner(b.create))
self.assertIn("Unknown status creating Bay", six.text_type(exc))
def test_bay_update(self):
b = self._create_resource('bay', self.rsrc_defn, self.stack)
scheduler.TaskRunner(b.create)()
status = mock.MagicMock(status='UPDATE_COMPLETE')
self.client.bays.get.return_value = status
t = template_format.parse(magnum_template)
new_t = copy.deepcopy(t)
new_t['resources']['test_bay']['properties']['node_count'] = 10
rsrc_defns = template.Template(new_t).resource_definitions(self.stack)
new_bm = rsrc_defns['test_bay']
scheduler.TaskRunner(b.update, new_bm)()
self.assertEqual((b.UPDATE, b.COMPLETE), b.state)
def test_bay_update_failed(self):
b = self._create_resource('bay', self.rsrc_defn, self.stack)
scheduler.TaskRunner(b.create)()
status = mock.MagicMock(status='UPDATE_FAILED')
self.client.bays.get.return_value = status
t = template_format.parse(magnum_template)
new_t = copy.deepcopy(t)
new_t['resources']['test_bay']['properties']['node_count'] = 10
rsrc_defns = template.Template(new_t).resource_definitions(self.stack)
new_bm = rsrc_defns['test_bay']
exc = self.assertRaises(
exception.ResourceFailure,
scheduler.TaskRunner(b.update, new_bm))
self.assertIn("Failed to update Bay", six.text_type(exc))
def test_bay_update_unknown_status(self):
b = self._create_resource('bay', self.rsrc_defn, self.stack)
scheduler.TaskRunner(b.create)()
status = mock.MagicMock(status='UPDATE_BAR')
self.client.bays.get.return_value = status
t = template_format.parse(magnum_template)
new_t = copy.deepcopy(t)
new_t['resources']['test_bay']['properties']['node_count'] = 10
rsrc_defns = template.Template(new_t).resource_definitions(self.stack)
new_bm = rsrc_defns['test_bay']
exc = self.assertRaises(
exception.ResourceFailure,
scheduler.TaskRunner(b.update, new_bm))
self.assertIn("Unknown status updating Bay", six.text_type(exc))
def test_bay_delete(self):
b = self._create_resource('bay', self.rsrc_defn, self.stack)
scheduler.TaskRunner(b.create)()
b.client_plugin = mock.MagicMock()
self.client.bays.get.side_effect = Exception('Not Found')
self.client.get.reset_mock()
scheduler.TaskRunner(b.delete)()
self.assertEqual((b.DELETE, b.COMPLETE), b.state)
self.assertEqual(2, self.client.bays.get.call_count)
def test_resource_mapping(self):
mapping = bay.resource_mapping()
self.assertEqual(1, len(mapping))
self.assertEqual(mapping[RESOURCE_TYPE], bay.Bay)