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:
parent
daa3c68736
commit
3bb83e2e1b
@ -21,6 +21,7 @@ from tuskarclient.v1 import client as tuskar_client
|
|||||||
from tuskar_ui.cached_property import cached_property # noqa
|
from tuskar_ui.cached_property import cached_property # noqa
|
||||||
from tuskar_ui.handle_errors import handle_errors # noqa
|
from tuskar_ui.handle_errors import handle_errors # noqa
|
||||||
from tuskar_ui.test.test_data import tuskar_data
|
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()
|
TEST_DATA = utils.TestDataContainer()
|
||||||
@ -61,19 +62,18 @@ class OvercloudPlan(base.APIDictWrapper):
|
|||||||
:return: the created OvercloudPlan object
|
:return: the created OvercloudPlan object
|
||||||
:rtype: tuskar_ui.api.tuskar.OvercloudPlan
|
:rtype: tuskar_ui.api.tuskar.OvercloudPlan
|
||||||
"""
|
"""
|
||||||
|
plan = mock_tuskar.Plan.create(name, description)
|
||||||
return cls(TEST_DATA.tuskarclient_plans.first(),
|
return cls(plan, request=request)
|
||||||
request=request)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def update(cls, request, overcloud_id, name, description):
|
def update(cls, request, plan_id, name, description):
|
||||||
"""Update an OvercloudPlan in Tuskar
|
"""Update an OvercloudPlan in Tuskar
|
||||||
|
|
||||||
:param request: request object
|
:param request: request object
|
||||||
:type request: django.http.HttpRequest
|
:type request: django.http.HttpRequest
|
||||||
|
|
||||||
:param overcloud_id: id of the overcloud we want to update
|
:param plan_id: id of the plan we want to update
|
||||||
:type overcloud_id: string
|
:type plan_id: string
|
||||||
|
|
||||||
:param name: plan name
|
:param name: plan name
|
||||||
:type name: string
|
:type name: string
|
||||||
@ -84,8 +84,8 @@ class OvercloudPlan(base.APIDictWrapper):
|
|||||||
:return: the updated OvercloudPlan object
|
:return: the updated OvercloudPlan object
|
||||||
:rtype: tuskar_ui.api.tuskar.OvercloudPlan
|
:rtype: tuskar_ui.api.tuskar.OvercloudPlan
|
||||||
"""
|
"""
|
||||||
return cls(TEST_DATA.tuskarclient_plans.first(),
|
plan = mock_tuskar.Plan.update(plan_id, name, description)
|
||||||
request=request)
|
return cls(plan, request=request)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def list(cls, request):
|
def list(cls, request):
|
||||||
@ -97,7 +97,7 @@ class OvercloudPlan(base.APIDictWrapper):
|
|||||||
:return: list of OvercloudPlans, or an empty list if there are none
|
:return: list of OvercloudPlans, or an empty list if there are none
|
||||||
:rtype: list of tuskar_ui.api.tuskar.OvercloudPlan
|
: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]
|
return [cls(plan, request=request) for plan in plans]
|
||||||
|
|
||||||
@ -116,8 +116,7 @@ class OvercloudPlan(base.APIDictWrapper):
|
|||||||
the ID
|
the ID
|
||||||
:rtype: tuskar_ui.api.tuskar.OvercloudPlan
|
:rtype: tuskar_ui.api.tuskar.OvercloudPlan
|
||||||
"""
|
"""
|
||||||
# FIXME(lsmola) hack for Icehouse, only one Overcloud is allowed
|
return cls(mock_tuskar.Plan.get(plan_id))
|
||||||
return cls.get_the_plan(request)
|
|
||||||
|
|
||||||
# TODO(lsmola) before will will support multiple overclouds, we
|
# TODO(lsmola) before will will support multiple overclouds, we
|
||||||
# can work only with overcloud that is named overcloud. Delete
|
# can work only with overcloud that is named overcloud. Delete
|
||||||
@ -131,8 +130,7 @@ class OvercloudPlan(base.APIDictWrapper):
|
|||||||
def get_the_plan(cls, request):
|
def get_the_plan(cls, request):
|
||||||
plan_list = cls.list(request)
|
plan_list = cls.list(request)
|
||||||
for plan in plan_list:
|
for plan in plan_list:
|
||||||
if plan.name == 'overcloud':
|
return plan
|
||||||
return plan
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def delete(cls, request, plan_id):
|
def delete(cls, request, plan_id):
|
||||||
@ -144,7 +142,7 @@ class OvercloudPlan(base.APIDictWrapper):
|
|||||||
:param plan_id: plan id
|
:param plan_id: plan id
|
||||||
:type plan_id: int
|
:type plan_id: int
|
||||||
"""
|
"""
|
||||||
return
|
mock_tuskar.Plan.delete(plan_id)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def role_list(self):
|
def role_list(self):
|
||||||
|
@ -12,17 +12,22 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import contextlib
|
||||||
|
|
||||||
from django.core import urlresolvers
|
from django.core import urlresolvers
|
||||||
|
|
||||||
from mock import patch, call # noqa
|
from mock import patch, call # noqa
|
||||||
|
|
||||||
from openstack_dashboard.test.test_data import utils
|
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 import helpers as test
|
||||||
from tuskar_ui.test.test_data import heat_data
|
from tuskar_ui.test.test_data import heat_data
|
||||||
|
from tuskar_ui.test.test_data import tuskar_data
|
||||||
|
|
||||||
|
|
||||||
TEST_DATA = utils.TestDataContainer()
|
TEST_DATA = utils.TestDataContainer()
|
||||||
heat_data.data(TEST_DATA)
|
heat_data.data(TEST_DATA)
|
||||||
|
tuskar_data.data(TEST_DATA)
|
||||||
INDEX_URL = urlresolvers.reverse(
|
INDEX_URL = urlresolvers.reverse(
|
||||||
'horizon:infrastructure:history:index')
|
'horizon:infrastructure:history:index')
|
||||||
|
|
||||||
@ -30,10 +35,20 @@ INDEX_URL = urlresolvers.reverse(
|
|||||||
class HistoryTest(test.BaseAdminViewTests):
|
class HistoryTest(test.BaseAdminViewTests):
|
||||||
|
|
||||||
def test_index(self):
|
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()
|
events = TEST_DATA.heatclient_events.list()
|
||||||
|
|
||||||
with patch('tuskar_ui.api.heat.Stack.events',
|
with contextlib.nested(
|
||||||
return_value=events):
|
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)
|
res = self.client.get(INDEX_URL)
|
||||||
|
|
||||||
self.assertTemplateUsed(res, 'infrastructure/history/index.html')
|
self.assertTemplateUsed(res, 'infrastructure/history/index.html')
|
||||||
|
@ -12,20 +12,32 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import contextlib
|
||||||
|
|
||||||
from django.core import urlresolvers
|
from django.core import urlresolvers
|
||||||
|
|
||||||
from mock import patch, call # noqa
|
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 import helpers as test
|
||||||
|
from tuskar_ui.test.test_data import tuskar_data
|
||||||
|
|
||||||
|
|
||||||
INDEX_URL = urlresolvers.reverse(
|
INDEX_URL = urlresolvers.reverse(
|
||||||
'horizon:infrastructure:parameters:index')
|
'horizon:infrastructure:parameters:index')
|
||||||
|
|
||||||
|
TEST_DATA = utils.TestDataContainer()
|
||||||
|
tuskar_data.data(TEST_DATA)
|
||||||
|
|
||||||
|
|
||||||
class ParametersTest(test.BaseAdminViewTests):
|
class ParametersTest(test.BaseAdminViewTests):
|
||||||
|
|
||||||
def test_index(self):
|
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')
|
self.assertTemplateUsed(res, 'infrastructure/parameters/index.html')
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
import django.forms
|
import django.forms
|
||||||
from django.utils import translation
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
import horizon.workflows
|
import horizon.workflows
|
||||||
|
|
||||||
@ -26,32 +25,7 @@ from tuskar_ui.infrastructure.plans.workflows import create_overview
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class DeploymentValidationMixin(object):
|
class Workflow(horizon.workflows.Workflow):
|
||||||
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):
|
|
||||||
slug = 'create_plan'
|
slug = 'create_plan'
|
||||||
name = _("My OpenStack Deployment Plan")
|
name = _("My OpenStack Deployment Plan")
|
||||||
default_steps = (
|
default_steps = (
|
||||||
|
@ -18,12 +18,10 @@ from horizon import exceptions
|
|||||||
import horizon.workflows
|
import horizon.workflows
|
||||||
|
|
||||||
from tuskar_ui import api
|
from tuskar_ui import api
|
||||||
from tuskar_ui.infrastructure.plans.workflows import create
|
|
||||||
from tuskar_ui.infrastructure.plans.workflows import scale_node_counts
|
from tuskar_ui.infrastructure.plans.workflows import scale_node_counts
|
||||||
|
|
||||||
|
|
||||||
class Workflow(create.DeploymentValidationMixin,
|
class Workflow(horizon.workflows.Workflow):
|
||||||
horizon.workflows.Workflow):
|
|
||||||
slug = 'scale_overcloud'
|
slug = 'scale_overcloud'
|
||||||
name = _("Scale Deployment")
|
name = _("Scale Deployment")
|
||||||
default_steps = (
|
default_steps = (
|
||||||
|
@ -37,7 +37,11 @@ class HeatAPITests(test.APITestCase):
|
|||||||
|
|
||||||
def test_stack_plan(self):
|
def test_stack_plan(self):
|
||||||
stack = api.heat.Stack(self.heatclient_stacks.first())
|
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)
|
self.assertIsInstance(ret_val, api.tuskar.OvercloudPlan)
|
||||||
|
|
||||||
def test_stack_events(self):
|
def test_stack_events(self):
|
||||||
@ -100,6 +104,7 @@ class HeatAPITests(test.APITestCase):
|
|||||||
|
|
||||||
def test_stack_dashboard_url(self):
|
def test_stack_dashboard_url(self):
|
||||||
stack = api.heat.Stack(self.heatclient_stacks.first())
|
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 = mock.Mock(id='horizon_id')
|
||||||
mocked_service.name = 'horizon'
|
mocked_service.name = 'horizon'
|
||||||
|
@ -21,18 +21,29 @@ from tuskar_ui.test import helpers as test
|
|||||||
|
|
||||||
class TuskarAPITests(test.APITestCase):
|
class TuskarAPITests(test.APITestCase):
|
||||||
def test_plan_create(self):
|
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)
|
self.assertIsInstance(ret_val, api.tuskar.OvercloudPlan)
|
||||||
|
|
||||||
def test_plan_list(self):
|
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:
|
for plan in ret_val:
|
||||||
self.assertIsInstance(plan, api.tuskar.OvercloudPlan)
|
self.assertIsInstance(plan, api.tuskar.OvercloudPlan)
|
||||||
self.assertEqual(1, len(ret_val))
|
self.assertEqual(1, len(ret_val))
|
||||||
|
|
||||||
def test_plan_get(self):
|
def test_plan_get(self):
|
||||||
plan = self.tuskarclient_plans.first()
|
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)
|
self.assertIsInstance(ret_val, api.tuskar.OvercloudPlan)
|
||||||
|
|
||||||
|
0
tuskar_ui/test/test_driver/__init__.py
Normal file
0
tuskar_ui/test/test_driver/__init__.py
Normal file
47
tuskar_ui/test/test_driver/tuskar_driver.py
Normal file
47
tuskar_ui/test/test_driver/tuskar_driver.py
Normal 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)
|
Loading…
x
Reference in New Issue
Block a user