Added mock tuskar driver

This patch allows you to simulate the creation and deletion of
a tuskar plan, allowing us to test workflows while the tuskar
API is under development.

It also removes the deployment count validation, as it causes
problems in the new api and is not likely to be used as is.

Change-Id: I20083021e0b55b5de234af68b89786e793ab8f09
This commit is contained in:
Tzu-Mainn Chen 2014-07-29 22:36:02 +02:00
parent daa3c68736
commit 3bb83e2e1b
9 changed files with 112 additions and 52 deletions

View File

@ -21,6 +21,7 @@ from tuskarclient.v1 import client as tuskar_client
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 tuskar_data
from tuskar_ui.test.test_driver import tuskar_driver as mock_tuskar
TEST_DATA = utils.TestDataContainer()
@ -61,19 +62,18 @@ class OvercloudPlan(base.APIDictWrapper):
:return: the created OvercloudPlan object
:rtype: tuskar_ui.api.tuskar.OvercloudPlan
"""
return cls(TEST_DATA.tuskarclient_plans.first(),
request=request)
plan = mock_tuskar.Plan.create(name, description)
return cls(plan, request=request)
@classmethod
def update(cls, request, overcloud_id, name, description):
def update(cls, request, plan_id, name, description):
"""Update an OvercloudPlan in Tuskar
:param request: request object
:type request: django.http.HttpRequest
:param overcloud_id: id of the overcloud we want to update
:type overcloud_id: string
:param plan_id: id of the plan we want to update
:type plan_id: string
:param name: plan name
:type name: string
@ -84,8 +84,8 @@ class OvercloudPlan(base.APIDictWrapper):
:return: the updated OvercloudPlan object
:rtype: tuskar_ui.api.tuskar.OvercloudPlan
"""
return cls(TEST_DATA.tuskarclient_plans.first(),
request=request)
plan = mock_tuskar.Plan.update(plan_id, name, description)
return cls(plan, request=request)
@classmethod
def list(cls, request):
@ -97,7 +97,7 @@ class OvercloudPlan(base.APIDictWrapper):
:return: list of OvercloudPlans, or an empty list if there are none
:rtype: list of tuskar_ui.api.tuskar.OvercloudPlan
"""
plans = TEST_DATA.tuskarclient_plans.list()
plans = mock_tuskar.Plan.list()
return [cls(plan, request=request) for plan in plans]
@ -116,8 +116,7 @@ class OvercloudPlan(base.APIDictWrapper):
the ID
:rtype: tuskar_ui.api.tuskar.OvercloudPlan
"""
# FIXME(lsmola) hack for Icehouse, only one Overcloud is allowed
return cls.get_the_plan(request)
return cls(mock_tuskar.Plan.get(plan_id))
# TODO(lsmola) before will will support multiple overclouds, we
# can work only with overcloud that is named overcloud. Delete
@ -131,8 +130,7 @@ class OvercloudPlan(base.APIDictWrapper):
def get_the_plan(cls, request):
plan_list = cls.list(request)
for plan in plan_list:
if plan.name == 'overcloud':
return plan
return plan
@classmethod
def delete(cls, request, plan_id):
@ -144,7 +142,7 @@ class OvercloudPlan(base.APIDictWrapper):
:param plan_id: plan id
:type plan_id: int
"""
return
mock_tuskar.Plan.delete(plan_id)
@cached_property
def role_list(self):

View File

@ -12,17 +12,22 @@
# License for the specific language governing permissions and limitations
# under the License.
import contextlib
from django.core import urlresolvers
from mock import patch, call # noqa
from openstack_dashboard.test.test_data import utils
from tuskar_ui import api
from tuskar_ui.test import helpers as test
from tuskar_ui.test.test_data import heat_data
from tuskar_ui.test.test_data import tuskar_data
TEST_DATA = utils.TestDataContainer()
heat_data.data(TEST_DATA)
tuskar_data.data(TEST_DATA)
INDEX_URL = urlresolvers.reverse(
'horizon:infrastructure:history:index')
@ -30,10 +35,20 @@ INDEX_URL = urlresolvers.reverse(
class HistoryTest(test.BaseAdminViewTests):
def test_index(self):
plan = api.tuskar.OvercloudPlan(
TEST_DATA.tuskarclient_plans.first())
stack = api.heat.Stack(
TEST_DATA.heatclient_stacks.first())
events = TEST_DATA.heatclient_events.list()
with patch('tuskar_ui.api.heat.Stack.events',
return_value=events):
with contextlib.nested(
patch('tuskar_ui.api.tuskar.OvercloudPlan.get_the_plan',
return_value=plan),
patch('tuskar_ui.api.heat.Stack.get_by_plan',
return_value=stack),
patch('tuskar_ui.api.heat.Stack.events',
return_value=events)
):
res = self.client.get(INDEX_URL)
self.assertTemplateUsed(res, 'infrastructure/history/index.html')

View File

@ -12,20 +12,32 @@
# License for the specific language governing permissions and limitations
# under the License.
import contextlib
from django.core import urlresolvers
from mock import patch, call # noqa
from openstack_dashboard.test.test_data import utils
from tuskar_ui import api
from tuskar_ui.test import helpers as test
from tuskar_ui.test.test_data import tuskar_data
INDEX_URL = urlresolvers.reverse(
'horizon:infrastructure:parameters:index')
TEST_DATA = utils.TestDataContainer()
tuskar_data.data(TEST_DATA)
class ParametersTest(test.BaseAdminViewTests):
def test_index(self):
res = self.client.get(INDEX_URL)
plan = api.tuskar.OvercloudPlan(TEST_DATA.tuskarclient_plans.first())
with contextlib.nested(
patch('tuskar_ui.api.tuskar.OvercloudPlan.get_the_plan',
return_value=plan),
):
res = self.client.get(INDEX_URL)
self.assertTemplateUsed(res, 'infrastructure/parameters/index.html')

View File

@ -14,7 +14,6 @@
import logging
import django.forms
from django.utils import translation
from django.utils.translation import ugettext_lazy as _
import horizon.workflows
@ -26,32 +25,7 @@ from tuskar_ui.infrastructure.plans.workflows import create_overview
LOG = logging.getLogger(__name__)
class DeploymentValidationMixin(object):
def validate(self, context):
requested = sum(context['role_counts'].values())
# TODO(lsmola) change this when we support more overclouds. It
# will have to obtain actual free nodes and compare them to
# number of newly created.
free = len(api.node.Node.list(self.request))
if requested > free:
m1 = translation.ungettext_lazy(
'This configuration requires %(requested)d node, ',
'This configuration requires %(requested)d nodes, ',
requested)
m1 %= {'requested': requested}
m2 = translation.ungettext_lazy(
'but only %(free)d is available.',
'but only %(free)d are available.',
free)
m2 %= {'free': free}
message = unicode(translation.string_concat(m1, m2))
self.add_error_to_step(message, 'create_overview')
self.add_error_to_step(message, 'scale_node_counts')
return False
return super(DeploymentValidationMixin, self).validate(context)
class Workflow(DeploymentValidationMixin, horizon.workflows.Workflow):
class Workflow(horizon.workflows.Workflow):
slug = 'create_plan'
name = _("My OpenStack Deployment Plan")
default_steps = (

View File

@ -18,12 +18,10 @@ from horizon import exceptions
import horizon.workflows
from tuskar_ui import api
from tuskar_ui.infrastructure.plans.workflows import create
from tuskar_ui.infrastructure.plans.workflows import scale_node_counts
class Workflow(create.DeploymentValidationMixin,
horizon.workflows.Workflow):
class Workflow(horizon.workflows.Workflow):
slug = 'scale_overcloud'
name = _("Scale Deployment")
default_steps = (

View File

@ -37,7 +37,11 @@ class HeatAPITests(test.APITestCase):
def test_stack_plan(self):
stack = api.heat.Stack(self.heatclient_stacks.first())
ret_val = stack.plan
plan = self.tuskarclient_plans.first()
with patch('tuskar_ui.test.test_driver.tuskar_driver.Plan.get',
return_value=plan):
ret_val = stack.plan
self.assertIsInstance(ret_val, api.tuskar.OvercloudPlan)
def test_stack_events(self):
@ -100,6 +104,7 @@ class HeatAPITests(test.APITestCase):
def test_stack_dashboard_url(self):
stack = api.heat.Stack(self.heatclient_stacks.first())
stack.plan = api.tuskar.OvercloudPlan(self.tuskarclient_plans.first())
mocked_service = mock.Mock(id='horizon_id')
mocked_service.name = 'horizon'

View File

@ -21,18 +21,29 @@ from tuskar_ui.test import helpers as test
class TuskarAPITests(test.APITestCase):
def test_plan_create(self):
ret_val = api.tuskar.OvercloudPlan.create(self.request, {}, {})
plan = self.tuskarclient_plans.first()
with patch('tuskar_ui.test.test_driver.tuskar_driver.Plan.create',
return_value=plan):
ret_val = api.tuskar.OvercloudPlan.create(self.request, {}, {})
self.assertIsInstance(ret_val, api.tuskar.OvercloudPlan)
def test_plan_list(self):
ret_val = api.tuskar.OvercloudPlan.list(self.request)
plans = self.tuskarclient_plans.list()
with patch('tuskar_ui.test.test_driver.tuskar_driver.Plan.list',
return_value=plans):
ret_val = api.tuskar.OvercloudPlan.list(self.request)
for plan in ret_val:
self.assertIsInstance(plan, api.tuskar.OvercloudPlan)
self.assertEqual(1, len(ret_val))
def test_plan_get(self):
plan = self.tuskarclient_plans.first()
ret_val = api.tuskar.OvercloudPlan.get(self.request, plan['id'])
with patch('tuskar_ui.test.test_driver.tuskar_driver.Plan.get',
return_value=plan):
ret_val = api.tuskar.OvercloudPlan.get(self.request, plan['id'])
self.assertIsInstance(ret_val, api.tuskar.OvercloudPlan)

View File

View File

@ -0,0 +1,47 @@
# 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 tuskar_data
TEST_DATA = utils.TestDataContainer()
tuskar_data.data(TEST_DATA)
class Plan:
_plans = {}
@classmethod
def create(cls, name, description):
plan = TEST_DATA.tuskarclient_plans.first()
cls._plans[plan['id']] = plan
return plan
@classmethod
def update(cls, plan_id, name, description):
plan = cls.get(plan_id)
# no updates for now
return plan
@classmethod
def list(cls):
return cls._plans.values()
@classmethod
def get(cls, plan_id):
return cls._plans.get(plan_id, None)
@classmethod
def delete(cls, plan_id):
cls._plans.pop(plan_id, None)