Un-mock heat and remaining mocked calls
This also updates Stack.resources to deal with the nested stacks introduced by the updated tuskar-api. The tests should eventually be updated to include nested stacks. There are some new Plan properties that have 'todos' indicating that they will be updated once the tuskar-api pythonclient is updated. Testing was done with hardcoded values there. Change-Id: I8ba071bf586bb2350ed4838a9cf9199f195cbe17
This commit is contained in:
parent
da896c2241
commit
919678da84
@ -21,20 +21,14 @@ from horizon.utils import memoized
|
||||
from openstack_dashboard.api import base
|
||||
from openstack_dashboard.api import heat
|
||||
from openstack_dashboard.api import keystone
|
||||
from openstack_dashboard.test.test_data import utils as test_utils
|
||||
|
||||
from tuskar_ui.api import node
|
||||
from tuskar_ui.api import tuskar
|
||||
from tuskar_ui.cached_property import cached_property # noqa
|
||||
from tuskar_ui.handle_errors import handle_errors # noqa
|
||||
from tuskar_ui.test.test_data import heat_data
|
||||
from tuskar_ui.test.test_driver import heat_driver as mock_heat
|
||||
from tuskar_ui.utils import utils
|
||||
|
||||
|
||||
TEST_DATA = test_utils.TestDataContainer()
|
||||
heat_data.data(TEST_DATA)
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -86,11 +80,16 @@ class Stack(base.APIResourceWrapper):
|
||||
|
||||
@classmethod
|
||||
@handle_errors(_("Unable to create Heat stack"), [])
|
||||
def create(cls, request, stack_name, template, parameters):
|
||||
stack = mock_heat.Stack.create(
|
||||
stack_name=stack_name,
|
||||
template=template,
|
||||
parameters=parameters)
|
||||
def create(cls, request, stack_name, template, environment,
|
||||
provider_resource_templates):
|
||||
fields = {
|
||||
'stack_name': stack_name,
|
||||
'template': template,
|
||||
'environment': environment,
|
||||
'files': provider_resource_templates,
|
||||
'password': 'password',
|
||||
}
|
||||
stack = heat.stack_create(request, **fields)
|
||||
return cls(stack, request=request)
|
||||
|
||||
@classmethod
|
||||
@ -146,11 +145,11 @@ class Stack(base.APIResourceWrapper):
|
||||
@classmethod
|
||||
@handle_errors(_("Unable to delete Heat stack"), [])
|
||||
def delete(cls, request, stack_id):
|
||||
mock_heat.Stack.delete(stack_id)
|
||||
heat.stack_delete(request, stack_id)
|
||||
|
||||
@memoized.memoized
|
||||
def resources(self, with_joins=True):
|
||||
"""Return a list of all Resources associated with the Stack
|
||||
def resources(self, with_joins=True, role=None):
|
||||
"""Return list of OS::Nova::Server Resources associated with the Stack
|
||||
|
||||
:param with_joins: should we also retrieve objects associated with each
|
||||
retrieved Resource?
|
||||
@ -159,48 +158,49 @@ class Stack(base.APIResourceWrapper):
|
||||
:return: list of all Resources or an empty list if there are none
|
||||
:rtype: list of tuskar_ui.api.heat.Resource
|
||||
"""
|
||||
resources = [r for r in TEST_DATA.heatclient_resources.list() if
|
||||
r.stack_id == self.id]
|
||||
top_level_resources = heat.resources_list(self._request,
|
||||
self.stack_name)
|
||||
resource_dicts = []
|
||||
for resource in top_level_resources:
|
||||
if resource.resource_type == 'OS::Nova::Server':
|
||||
if role is None:
|
||||
resource_dicts.append({"resource": resource, "role": None})
|
||||
elif resource.resource_type == 'OS::Heat::ResourceGroup':
|
||||
# we need to dig through the resource group to reach the nova
|
||||
# resources
|
||||
group_resources = heat.resources_list(
|
||||
self._request, resource.physical_resource_id)
|
||||
for group_resource in group_resources:
|
||||
tuskar_role = tuskar.OvercloudRole.get_by_resource_type(
|
||||
self._request, group_resource.resource_type)
|
||||
nova_resources = heat.resources_list(
|
||||
self._request,
|
||||
group_resource.physical_resource_id)
|
||||
if role is None or role.uuid == tuskar_role.uuid:
|
||||
resource_dicts.extend([{"resource": resource,
|
||||
"role": tuskar_role}
|
||||
for resource in nova_resources])
|
||||
|
||||
if not with_joins:
|
||||
return [Resource(r, request=self._request, stack=self)
|
||||
for r in resources]
|
||||
return [Resource(rd['resource'], request=self._request,
|
||||
stack=self, role=rd['role'])
|
||||
for rd in resource_dicts]
|
||||
|
||||
nodes_dict = utils.list_to_dict(node.Node.list(self._request,
|
||||
associated=True),
|
||||
key_attribute='instance_uuid')
|
||||
joined_resources = []
|
||||
for r in resources:
|
||||
for rd in resource_dicts:
|
||||
resource = rd['resource']
|
||||
joined_resources.append(
|
||||
Resource(r, node=nodes_dict.get(r.physical_resource_id, None),
|
||||
request=self._request, stack=self))
|
||||
Resource(resource,
|
||||
node=nodes_dict.get(resource.physical_resource_id,
|
||||
None),
|
||||
request=self._request, stack=self, role=rd['role']))
|
||||
# TODO(lsmola) I want just resources with nova instance
|
||||
# this could be probably filtered a better way, investigate
|
||||
return [r for r in joined_resources if r.node is not None]
|
||||
|
||||
@memoized.memoized
|
||||
def resources_by_role(self, overcloud_role, with_joins=True):
|
||||
"""Return a list of Resources that match an OvercloudRole
|
||||
|
||||
:param overcloud_role: role of resources to be returned
|
||||
:type overcloud_role: tuskar_ui.api.tuskar.OvercloudRole
|
||||
|
||||
:param with_joins: should we also retrieve objects associated with each
|
||||
retrieved Resource?
|
||||
:type with_joins: bool
|
||||
|
||||
:return: list of Resources that match the OvercloudRole, or an empty
|
||||
list if there are none
|
||||
:rtype: list of tuskar_ui.api.heat.Resource
|
||||
"""
|
||||
# FIXME(lsmola) with_joins is not necessary here, I need at least
|
||||
# nova instance
|
||||
resources = self.resources(with_joins)
|
||||
filtered_resources = [resource for resource in resources if
|
||||
(resource.has_role(overcloud_role))]
|
||||
|
||||
return filtered_resources
|
||||
|
||||
@memoized.memoized
|
||||
def resources_count(self, overcloud_role=None):
|
||||
"""Return count of associated Resources
|
||||
@ -218,7 +218,7 @@ class Stack(base.APIResourceWrapper):
|
||||
if overcloud_role is None:
|
||||
resources = self.resources()
|
||||
else:
|
||||
resources = self.resources_by_role(overcloud_role)
|
||||
resources = self.resources(role=overcloud_role)
|
||||
return len(resources)
|
||||
|
||||
@cached_property
|
||||
@ -384,27 +384,8 @@ class Resource(base.APIResourceWrapper):
|
||||
self._node = kwargs['node']
|
||||
if 'stack' in kwargs:
|
||||
self._stack = kwargs['stack']
|
||||
|
||||
@classmethod
|
||||
def get(cls, request, stack, resource_name):
|
||||
"""Return the specified Heat Resource within a Stack
|
||||
|
||||
:param request: request object
|
||||
:type request: django.http.HttpRequest
|
||||
|
||||
:param overcloud: the Stack from which to retrieve the resource
|
||||
:type overcloud: tuskar_ui.api.heat.OvercloudStack
|
||||
|
||||
:param resource_name: name of the Resource to retrieve
|
||||
:type resource_name: str
|
||||
|
||||
:return: matching Resource, or None if no Resource in the Stack
|
||||
matches the resource name
|
||||
:rtype: tuskar_ui.api.heat.Resource
|
||||
"""
|
||||
for r in TEST_DATA.heatclient_resources.list():
|
||||
if r.stack_id == stack.id and r.resource_name == resource_name:
|
||||
return cls(r, request=request, stack=stack)
|
||||
if 'role' in kwargs:
|
||||
self._role = kwargs['role']
|
||||
|
||||
@classmethod
|
||||
def get_by_node(cls, request, node):
|
||||
@ -438,21 +419,8 @@ class Resource(base.APIResourceWrapper):
|
||||
OvercloudRole is associated
|
||||
:rtype: tuskar_ui.api.tuskar.OvercloudRole
|
||||
"""
|
||||
roles = tuskar.OvercloudRole.list(self._request)
|
||||
for role in roles:
|
||||
if self.has_role(role):
|
||||
return role
|
||||
|
||||
def has_role(self, role):
|
||||
"""Determine whether a resources matches an overcloud role
|
||||
|
||||
:param role: role to check against
|
||||
:type role: tuskar_ui.api.tuskar.OvercloudRole
|
||||
|
||||
:return: does this resource match the overcloud_role?
|
||||
:rtype: bool
|
||||
"""
|
||||
return self.resource_type == role.provider_resource_type
|
||||
if hasattr(self, '_role'):
|
||||
return self._role
|
||||
|
||||
@cached_property
|
||||
def node(self):
|
||||
|
@ -19,19 +19,12 @@ from novaclient.v1_1.contrib import baremetal
|
||||
from openstack_dashboard.api import base
|
||||
from openstack_dashboard.api import glance
|
||||
from openstack_dashboard.api import nova
|
||||
from openstack_dashboard.test.test_data import utils as test_utils
|
||||
|
||||
from tuskar_ui.cached_property import cached_property # noqa
|
||||
from tuskar_ui.handle_errors import handle_errors # noqa
|
||||
from tuskar_ui.test.test_data import heat_data
|
||||
from tuskar_ui.test.test_data import node_data
|
||||
from tuskar_ui.utils import utils
|
||||
|
||||
|
||||
TEST_DATA = test_utils.TestDataContainer()
|
||||
node_data.data(TEST_DATA)
|
||||
heat_data.data(TEST_DATA)
|
||||
|
||||
ERROR_STATES = set(['deploy failed', 'error'])
|
||||
POWER_ON_STATES = set(['on', 'power on'])
|
||||
|
||||
@ -432,25 +425,17 @@ class Node(base.APIResourceWrapper):
|
||||
def get(cls, request, uuid):
|
||||
node = NodeClient(request).node_class.get(request, uuid)
|
||||
if node.instance_uuid is not None:
|
||||
for server in TEST_DATA.novaclient_servers.list():
|
||||
if server.id == node.instance_uuid:
|
||||
break
|
||||
else:
|
||||
server = None
|
||||
return cls(node, instance=server, request=request)
|
||||
|
||||
return cls(node)
|
||||
server = nova.server_get(request, node.instance_uuid)
|
||||
else:
|
||||
server = None
|
||||
return cls(node, instance=server, request=request)
|
||||
|
||||
@classmethod
|
||||
@handle_errors(_("Unable to retrieve node"))
|
||||
def get_by_instance_uuid(cls, request, instance_uuid):
|
||||
node = NodeClient(request).node_class.get_by_instance_uuid(
|
||||
request, instance_uuid)
|
||||
for server in TEST_DATA.novaclient_servers.list():
|
||||
if server.id == node.instance_uuid:
|
||||
break
|
||||
else:
|
||||
server = None
|
||||
server = nova.server_get(request, instance_uuid)
|
||||
return cls(node, instance=server, request=request)
|
||||
|
||||
@classmethod
|
||||
@ -459,7 +444,7 @@ class Node(base.APIResourceWrapper):
|
||||
nodes = NodeClient(request).node_class.list(
|
||||
request, associated=associated)
|
||||
if associated is None or associated:
|
||||
servers = TEST_DATA.novaclient_servers.list()
|
||||
servers = nova.server_list(request)[0]
|
||||
servers_dict = utils.list_to_dict(servers)
|
||||
nodes_with_instance = []
|
||||
for n in nodes:
|
||||
@ -511,9 +496,8 @@ class Node(base.APIResourceWrapper):
|
||||
"""
|
||||
if self.instance is None:
|
||||
return
|
||||
for image in TEST_DATA.glanceclient_images.list():
|
||||
if image.id == self.instance.image['id']:
|
||||
return image.name
|
||||
image = image_get(self._request, self.instance.image['id'])
|
||||
return image.name
|
||||
|
||||
@cached_property
|
||||
def instance_status(self):
|
||||
|
@ -36,7 +36,7 @@ def tuskarclient(request):
|
||||
|
||||
class OvercloudPlan(base.APIResourceWrapper):
|
||||
_attrs = ('id', 'name', 'description', 'created_at', 'modified_at',
|
||||
'roles', 'parameters', 'template')
|
||||
'roles', 'parameters')
|
||||
|
||||
def __init__(self, apiresource, request=None):
|
||||
super(OvercloudPlan, self).__init__(apiresource)
|
||||
@ -151,6 +151,24 @@ class OvercloudPlan(base.APIResourceWrapper):
|
||||
return [OvercloudRole.get(self._request, role['uuid'])
|
||||
for role in self.roles]
|
||||
|
||||
@cached_property
|
||||
def template(self):
|
||||
#TODO(tzumainn): replace with actual call to tuskar api
|
||||
#once tuskar pythonclient is updated
|
||||
return ""
|
||||
|
||||
@cached_property
|
||||
def environment(self):
|
||||
#TODO(tzumainn): replace with actual call to tuskar api
|
||||
#once tuskar pythonclient is updated
|
||||
return ""
|
||||
|
||||
@cached_property
|
||||
def provider_resource_templates(self):
|
||||
#TODO(tzumainn): replace with actual call to tuskar api
|
||||
#once tuskar pythonclient is updated
|
||||
return {}
|
||||
|
||||
def parameter_list(self, include_key_parameters=True):
|
||||
params = self.parameters
|
||||
if not include_key_parameters:
|
||||
@ -240,11 +258,18 @@ class OvercloudRole(base.APIResourceWrapper):
|
||||
if image_id_from_plan == image.id:
|
||||
return role
|
||||
|
||||
@classmethod
|
||||
@handle_errors(_("Unable to retrieve overcloud role"))
|
||||
def get_by_resource_type(cls, request, resource_type):
|
||||
for role in OvercloudRole.list(request):
|
||||
if role.provider_resource_type == resource_type:
|
||||
return role
|
||||
|
||||
# TODO(tzumainn): fix this once we know how a role corresponds to
|
||||
# its provider resource type
|
||||
@property
|
||||
def provider_resource_type(self):
|
||||
return self.name
|
||||
return 'Tuskar::' + self.name + '-' + str(self.version)
|
||||
|
||||
@property
|
||||
def parameter_prefix(self):
|
||||
|
@ -50,7 +50,6 @@ class NodesTests(test.BaseAdminViewTests, helpers.APITestCase):
|
||||
raise horizon_exceptions.NotFound
|
||||
|
||||
def test_index_get(self):
|
||||
|
||||
with patch('tuskar_ui.api.node.Node', **{
|
||||
'spec_set': ['list'], # Only allow these attributes
|
||||
'list.return_value': [],
|
||||
|
@ -67,7 +67,8 @@ class DeployOvercloud(horizon.forms.SelfHandlingForm):
|
||||
api.heat.Stack.create(request,
|
||||
plan.name,
|
||||
plan.template,
|
||||
plan.parameters)
|
||||
plan.environment,
|
||||
plan.provider_resource_templates)
|
||||
except Exception:
|
||||
LOG.exception()
|
||||
return False
|
||||
|
@ -126,6 +126,8 @@ class OverviewTests(test.BaseAdminViewTests):
|
||||
return_value=True),
|
||||
patch('tuskar_ui.api.heat.Stack.is_deployed',
|
||||
return_value=False),
|
||||
patch('tuskar_ui.api.heat.Stack.resources',
|
||||
return_value=[]),
|
||||
patch('tuskar_ui.api.heat.Stack.events',
|
||||
return_value=[]),
|
||||
):
|
||||
|
@ -41,7 +41,7 @@ def _get_role_data(plan, stack, role, field):
|
||||
}
|
||||
|
||||
if stack:
|
||||
resources = stack.resources_by_role(role, with_joins=True)
|
||||
resources = stack.resources(role=role, with_joins=True)
|
||||
nodes = [r.node for r in resources]
|
||||
node_count = len(nodes)
|
||||
|
||||
|
@ -74,9 +74,8 @@ class DetailView(horizon_tables.DataTableView, OvercloudRoleMixin, StackMixin):
|
||||
|
||||
@memoized.memoized
|
||||
def _get_nodes(self, stack, role):
|
||||
resources = stack.resources_by_role(role, with_joins=True)
|
||||
resources = stack.resources(role=role, with_joins=True)
|
||||
nodes = [r.node for r in resources]
|
||||
|
||||
for node in nodes:
|
||||
# TODO(tzumainn): this could probably be done more efficiently
|
||||
# by getting the resource for all nodes at once
|
||||
|
@ -40,7 +40,6 @@ class HeatAPITests(test.APITestCase):
|
||||
with patch('openstack_dashboard.api.heat.stack_get',
|
||||
return_value=stack):
|
||||
ret_val = api.heat.Stack.get(self.request, stack.id)
|
||||
|
||||
self.assertIsInstance(ret_val, api.heat.Stack)
|
||||
|
||||
def test_stack_plan(self):
|
||||
@ -70,8 +69,8 @@ class HeatAPITests(test.APITestCase):
|
||||
self.assertFalse(ret_val)
|
||||
|
||||
def test_stack_resources(self):
|
||||
stack = api.heat.Stack(self.heatclient_stacks.first())
|
||||
|
||||
stack = api.heat.Stack(self.heatclient_stacks.first(),
|
||||
request=self.request)
|
||||
resources = self.heatclient_resources.list()
|
||||
nodes = self.baremetalclient_nodes.list()
|
||||
instances = []
|
||||
@ -89,26 +88,7 @@ class HeatAPITests(test.APITestCase):
|
||||
|
||||
for i in ret_val:
|
||||
self.assertIsInstance(i, api.heat.Resource)
|
||||
self.assertEqual(3, len(ret_val))
|
||||
|
||||
def test_stack_resources_no_ironic(self):
|
||||
stack = api.heat.Stack(self.heatclient_stacks.first())
|
||||
role = api.tuskar.OvercloudRole(
|
||||
self.tuskarclient_roles.first())
|
||||
nodes = self.baremetalclient_nodes.list()
|
||||
|
||||
# FIXME(lsmola) only resources and image_name should be tested
|
||||
# here, anybody has idea how to do that?
|
||||
with patch('openstack_dashboard.api.base.is_service_enabled',
|
||||
return_value=False):
|
||||
with patch('novaclient.v1_1.contrib.baremetal.'
|
||||
'BareMetalNodeManager.list',
|
||||
return_value=nodes):
|
||||
ret_val = stack.resources_by_role(role)
|
||||
|
||||
for i in ret_val:
|
||||
self.assertIsInstance(i, api.heat.Resource)
|
||||
self.assertEqual(1, len(ret_val))
|
||||
self.assertEqual(4, len(ret_val))
|
||||
|
||||
def test_stack_keystone_ip(self):
|
||||
stack = api.heat.Stack(self.heatclient_stacks.first())
|
||||
@ -142,26 +122,6 @@ class HeatAPITests(test.APITestCase):
|
||||
stack.dashboard_urls)
|
||||
self.assertEqual(client_get.call_count, 1)
|
||||
|
||||
def test_resource_get(self):
|
||||
stack = self.heatclient_stacks.first()
|
||||
resource = self.heatclient_resources.first()
|
||||
|
||||
ret_val = api.heat.Resource.get(None, stack,
|
||||
resource.resource_name)
|
||||
self.assertIsInstance(ret_val, api.heat.Resource)
|
||||
|
||||
def test_resource_role(self):
|
||||
# The api needs to get the role, and getting the role means listing
|
||||
# all roles, so we mock that.
|
||||
roles = self.tuskarclient_roles.list()
|
||||
with patch('tuskarclient.v2.roles.RoleManager.list',
|
||||
return_value=roles):
|
||||
resource = api.heat.Resource(self.heatclient_resources.first(),
|
||||
self.request)
|
||||
ret_val = resource.role
|
||||
self.assertIsInstance(ret_val, api.tuskar.OvercloudRole)
|
||||
self.assertEqual('Compute', ret_val.name)
|
||||
|
||||
def test_resource_node_no_ironic(self):
|
||||
resource = self.heatclient_resources.first()
|
||||
nodes = self.baremetalclient_nodes.list()
|
||||
|
@ -125,7 +125,7 @@ class NodeAPITests(test.APITestCase):
|
||||
with patch('openstack_dashboard.api.nova.server_list',
|
||||
return_value=([instance], False)):
|
||||
ret_val = api.node.Node(node).image_name
|
||||
self.assertEqual(ret_val, 'overcloud-compute')
|
||||
self.assertEqual(ret_val, 'overcloud-control')
|
||||
|
||||
def test_node_addresses_no_ironic(self):
|
||||
node = self.baremetalclient_nodes.first()
|
||||
|
@ -118,7 +118,7 @@ def data(TEST):
|
||||
'logical_resource_id': 'Compute0',
|
||||
'physical_resource_id': 'aa',
|
||||
'resource_status': 'CREATE_COMPLETE',
|
||||
'resource_type': 'Compute'})
|
||||
'resource_type': 'OS::Nova::Server'})
|
||||
resource_2 = resources.Resource(
|
||||
resources.ResourceManager(None),
|
||||
{'id': '2-resource-id',
|
||||
@ -127,7 +127,7 @@ def data(TEST):
|
||||
'logical_resource_id': 'Controller',
|
||||
'physical_resource_id': 'bb',
|
||||
'resource_status': 'CREATE_COMPLETE',
|
||||
'resource_type': 'Controller'})
|
||||
'resource_type': 'OS::Nova::Server'})
|
||||
resource_3 = resources.Resource(
|
||||
resources.ResourceManager(None),
|
||||
{'id': '3-resource-id',
|
||||
@ -136,7 +136,7 @@ def data(TEST):
|
||||
'logical_resource_id': 'Compute1',
|
||||
'physical_resource_id': 'cc',
|
||||
'resource_status': 'CREATE_COMPLETE',
|
||||
'resource_type': 'Compute'})
|
||||
'resource_type': 'OS::Nova::Server'})
|
||||
resource_4 = resources.Resource(
|
||||
resources.ResourceManager(None),
|
||||
{'id': '4-resource-id',
|
||||
@ -145,7 +145,7 @@ def data(TEST):
|
||||
'logical_resource_id': 'Compute2',
|
||||
'physical_resource_id': 'dd',
|
||||
'resource_status': 'CREATE_COMPLETE',
|
||||
'resource_type': 'Compute'})
|
||||
'resource_type': 'OS::Nova::Server'})
|
||||
TEST.heatclient_resources.add(resource_1,
|
||||
resource_2,
|
||||
resource_3,
|
||||
|
@ -1,41 +0,0 @@
|
||||
# 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 openstack_dashboard.test.test_data import utils
|
||||
|
||||
from tuskar_ui.test.test_data import heat_data
|
||||
|
||||
TEST_DATA = utils.TestDataContainer()
|
||||
heat_data.data(TEST_DATA)
|
||||
|
||||
|
||||
class Stack:
|
||||
|
||||
_stacks = {}
|
||||
|
||||
@classmethod
|
||||
def create(cls, **kwargs):
|
||||
stack = TEST_DATA.heatclient_stacks.first()
|
||||
cls._stacks[stack.id] = stack
|
||||
return stack
|
||||
|
||||
@classmethod
|
||||
def list(cls):
|
||||
return cls._stacks.values()
|
||||
|
||||
@classmethod
|
||||
def get(cls, stack_id):
|
||||
return cls._stacks.get(stack_id, None)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, stack_id):
|
||||
cls._stacks.pop(stack_id, None)
|
Loading…
x
Reference in New Issue
Block a user