In future, Horizon team is planning to remove heat relevant logic like,
- Heat GUI part - API wrapper client - API rest endpoint creator (like api/rest/heat) According to above changes, I've fixed all unittest that can work without Heat relevant logic in Horizon. Change-Id: I7b8c61275e1fef9e27a34d7e65693ee00d07110d
This commit is contained in:
parent
1fafd501e1
commit
7201c1835f
@ -31,7 +31,7 @@ In other words, Horizon developers not working on openstack_dashboard.api
|
||||
shouldn't need to understand the finer details of APIs for
|
||||
Keystone/Nova/Glance/Swift et. al.
|
||||
"""
|
||||
from openstack_dashboard.api import heat
|
||||
from heat_dashboard.api import heat
|
||||
|
||||
|
||||
__all__ = [
|
||||
|
@ -21,27 +21,9 @@ It does not promise to adhere to the general OpenStack API Guidelines set out
|
||||
in https://wiki.openstack.org/wiki/APIChangeGuidelines.
|
||||
"""
|
||||
|
||||
from openstack_dashboard.api.rest import cinder
|
||||
from openstack_dashboard.api.rest import config
|
||||
from openstack_dashboard.api.rest import glance
|
||||
from openstack_dashboard.api.rest import heat
|
||||
from openstack_dashboard.api.rest import keystone
|
||||
from openstack_dashboard.api.rest import network
|
||||
from openstack_dashboard.api.rest import neutron
|
||||
from openstack_dashboard.api.rest import nova
|
||||
from openstack_dashboard.api.rest import policy
|
||||
from openstack_dashboard.api.rest import swift
|
||||
from heat_dashboard.api.rest import heat
|
||||
|
||||
|
||||
__all__ = [
|
||||
'cinder',
|
||||
'config',
|
||||
'glance',
|
||||
'heat',
|
||||
'keystone',
|
||||
'network',
|
||||
'neutron',
|
||||
'nova',
|
||||
'policy',
|
||||
'swift',
|
||||
]
|
||||
|
@ -13,8 +13,9 @@
|
||||
|
||||
from django.views import generic
|
||||
|
||||
# from openstack_dashboard import api
|
||||
from heat_dashboard import api
|
||||
|
||||
from openstack_dashboard import api as dashboard_api
|
||||
from openstack_dashboard.api.rest import urls
|
||||
from openstack_dashboard.api.rest import utils as rest_utils
|
||||
|
||||
@ -45,7 +46,7 @@ class Services(generic.View):
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get a list of heat services."""
|
||||
if api.base.is_service_enabled(request, 'orchestration'):
|
||||
if dashboard_api.base.is_service_enabled(request, 'orchestration'):
|
||||
result = api.heat.service_list(request)
|
||||
return {'items': [u.to_dict() for u in result]}
|
||||
else:
|
||||
|
@ -19,7 +19,6 @@ from horizon import tabs
|
||||
class ResourceTypeOverviewTab(tabs.Tab):
|
||||
name = _("Overview")
|
||||
slug = "resource_type_overview"
|
||||
# template_name = "project/stacks.resource_types/_details.html"
|
||||
template_name = "project/resource_types/_details.html"
|
||||
|
||||
def get_context_data(self, request):
|
||||
|
@ -20,11 +20,6 @@ from horizon import exceptions
|
||||
from horizon import tables
|
||||
from horizon import tabs
|
||||
|
||||
# from openstack_dashboard import api
|
||||
# import openstack_dashboard.dashboards.project.stacks.resource_types.tables \
|
||||
# as project_tables
|
||||
# import openstack_dashboard.dashboards.project.stacks.resource_types.tabs \
|
||||
# as project_tabs
|
||||
from heat_dashboard import api
|
||||
import heat_dashboard.content.resource_types.tables as project_tables
|
||||
import heat_dashboard.content.resource_types.tabs as project_tabs
|
||||
|
@ -12,10 +12,10 @@
|
||||
|
||||
import json
|
||||
|
||||
from openstack_dashboard.api import heat
|
||||
from heat_dashboard.api import heat
|
||||
|
||||
from openstack_dashboard.dashboards.project.stacks import mappings
|
||||
from openstack_dashboard.dashboards.project.stacks import sro
|
||||
from heat_dashboard.content.stacks import mappings
|
||||
from heat_dashboard.content.stacks import sro
|
||||
|
||||
|
||||
class Stack(object):
|
||||
|
@ -26,7 +26,6 @@ from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import messages
|
||||
|
||||
# from openstack_dashboard import api
|
||||
from heat_dashboard import api
|
||||
from openstack_dashboard.dashboards.project.images \
|
||||
import utils as image_utils
|
||||
|
@ -23,8 +23,6 @@ from horizon.utils import filters
|
||||
|
||||
from heatclient import exc
|
||||
|
||||
# from openstack_dashboard import api
|
||||
# from openstack_dashboard.dashboards.project.stacks import mappings
|
||||
from heat_dashboard import api
|
||||
from heat_dashboard.content.stacks import mappings
|
||||
|
||||
@ -160,7 +158,6 @@ class DeleteStack(tables.DeleteAction):
|
||||
policy_rules = (("orchestration", "stacks:delete"),)
|
||||
|
||||
def delete(self, request, stack_id):
|
||||
# api.heat.stack_delete(request, stack_id)
|
||||
api.heat.stack_delete(request, stack_id)
|
||||
|
||||
def allowed(self, request, stack):
|
||||
@ -177,7 +174,6 @@ class StacksUpdateRow(tables.Row):
|
||||
|
||||
def get_data(self, request, stack_id):
|
||||
try:
|
||||
# stack = api.heat.stack_get(request, stack_id)
|
||||
stack = api.heat.stack_get(request, stack_id)
|
||||
if stack.stack_status == 'DELETE_COMPLETE':
|
||||
# returning 404 to the ajax call removes the
|
||||
|
@ -55,7 +55,6 @@ class StackOverviewTab(tabs.Tab):
|
||||
name = _("Overview")
|
||||
slug = "overview"
|
||||
template_name = "project/stacks/_detail_overview.html"
|
||||
# template_name = "stacks/_detail_overview.html"
|
||||
|
||||
def allowed(self, request):
|
||||
return policy.check(
|
||||
@ -72,7 +71,6 @@ class ResourceOverviewTab(tabs.Tab):
|
||||
name = _("Overview")
|
||||
slug = "resource_overview"
|
||||
template_name = "project/stacks/_resource_overview.html"
|
||||
# template_name = "stacks/_resource_overview.html"
|
||||
|
||||
def get_context_data(self, request):
|
||||
resource = self.tab_group.kwargs['resource']
|
||||
@ -87,7 +85,6 @@ class StackEventsTab(tabs.Tab):
|
||||
name = _("Events")
|
||||
slug = "events"
|
||||
template_name = "project/stacks/_detail_events.html"
|
||||
# template_name = "stacks/_detail_events.html"
|
||||
preload = False
|
||||
|
||||
def allowed(self, request):
|
||||
@ -119,7 +116,6 @@ class StackResourcesTab(tabs.Tab):
|
||||
name = _("Resources")
|
||||
slug = "resources"
|
||||
template_name = "project/stacks/_detail_resources.html"
|
||||
# template_name = "stacks/_detail_resources.html"
|
||||
preload = False
|
||||
|
||||
def allowed(self, request):
|
||||
@ -152,7 +148,6 @@ class StackTemplateTab(tabs.Tab):
|
||||
name = _("Template")
|
||||
slug = "stack_template"
|
||||
template_name = "project/stacks/_stack_template.html"
|
||||
# template_name = "stacks/_stack_template.html"
|
||||
|
||||
def allowed(self, request):
|
||||
return policy.check(
|
||||
|
@ -15,13 +15,9 @@
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
# from openstack_dashboard.dashboards.project import dashboard
|
||||
|
||||
|
||||
class TemplateGenerator(horizon.Panel):
|
||||
name = _("Template Generator")
|
||||
slug = 'template_generator'
|
||||
permissions = ('openstack.services.orchestration',)
|
||||
|
||||
|
||||
# dashboard.Project.register(TemplateGenerator)
|
||||
|
@ -15,11 +15,10 @@ from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import messages
|
||||
from horizon import tabs
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard import policy
|
||||
|
||||
# from openstack_dashboard.dashboards.project.stacks.template_versions \
|
||||
# import tables as project_tables
|
||||
from heat_dashboard import api
|
||||
|
||||
from heat_dashboard.content.template_versions import tables as project_tables
|
||||
|
||||
|
||||
|
@ -13,8 +13,6 @@
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
# from openstack_dashboard.dashboards.project.stacks.template_versions \
|
||||
# import views
|
||||
from heat_dashboard.content.template_versions import views
|
||||
|
||||
|
||||
|
@ -18,11 +18,6 @@ from horizon import exceptions
|
||||
from horizon import tables
|
||||
from horizon import tabs
|
||||
|
||||
# from openstack_dashboard import api
|
||||
# import openstack_dashboard.dashboards.project.\
|
||||
# stacks.template_versions.tables as project_tables
|
||||
# import openstack_dashboard.dashboards.project.\
|
||||
# stacks.template_versions.tabs as project_tabs
|
||||
from heat_dashboard import api
|
||||
import heat_dashboard.content.template_versions.tables as project_tables
|
||||
import heat_dashboard.content.template_versions.tabs as project_tabs
|
||||
|
@ -22,5 +22,3 @@ ADD_PANEL = 'heat_dashboard.content.stacks.panel.Stacks'
|
||||
|
||||
# Automatically discover static resources in installed apps
|
||||
AUTO_DISCOVER_STATIC_FILES = True
|
||||
|
||||
# ADD_INSTALLED_APPS = ['heat_dashboard.content.stacks', ]
|
||||
|
@ -22,5 +22,3 @@ ADD_PANEL = 'heat_dashboard.content.template_versions.panel.TemplateVersions'
|
||||
|
||||
# Automatically discover static resources in installed apps
|
||||
AUTO_DISCOVER_STATIC_FILES = True
|
||||
|
||||
# ADD_INSTALLED_APPS = ['heat_dashboard.content.template_versions', ]
|
||||
|
@ -16,8 +16,6 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import collections
|
||||
import copy
|
||||
from functools import wraps
|
||||
from importlib import import_module
|
||||
import os
|
||||
@ -28,34 +26,27 @@ import django
|
||||
from django.conf import settings
|
||||
from django.contrib.messages.storage import default_storage
|
||||
from django.core.handlers import wsgi
|
||||
from django.core import urlresolvers
|
||||
from django.test.client import RequestFactory
|
||||
from django.test import utils as django_test_utils
|
||||
from django.utils import http
|
||||
|
||||
from cinderclient import client as cinder_client
|
||||
import glanceclient
|
||||
from heatclient import client as heat_client
|
||||
from keystoneclient.v2_0 import client as keystone_client
|
||||
import mock
|
||||
from mox3 import mox
|
||||
from neutronclient.v2_0 import client as neutron_client
|
||||
from novaclient import api_versions as nova_api_versions
|
||||
from novaclient.v2 import client as nova_client
|
||||
from openstack_auth import user
|
||||
from openstack_auth import utils
|
||||
from requests.packages.urllib3.connection import HTTPConnection
|
||||
|
||||
import six
|
||||
from six import moves
|
||||
from swiftclient import client as swift_client
|
||||
|
||||
from horizon import base
|
||||
from horizon import conf
|
||||
from horizon.test import helpers as horizon_helpers
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard import context_processors
|
||||
from openstack_dashboard.test.test_data import utils as test_utils
|
||||
|
||||
from openstack_dashboard import api as project_api
|
||||
from openstack_dashboard import context_processors
|
||||
|
||||
from heat_dashboard import api
|
||||
from heat_dashboard.test.test_data import utils as test_utils
|
||||
|
||||
# Makes output of failing mox tests much easier to read.
|
||||
wsgi.WSGIRequest.__repr__ = lambda self: "<class 'django.http.HttpRequest'>"
|
||||
@ -412,63 +403,20 @@ class APITestCase(TestCase):
|
||||
"""
|
||||
return self.stub_keystoneclient()
|
||||
|
||||
def fake_glanceclient(request, version='1'):
|
||||
"""Returns the stub glanceclient.
|
||||
|
||||
Only necessary because the function takes too many arguments to
|
||||
conveniently be a lambda.
|
||||
"""
|
||||
return self.stub_glanceclient()
|
||||
|
||||
def fake_novaclient(request, version=None):
|
||||
return self.stub_novaclient()
|
||||
|
||||
# Store the original clients
|
||||
self._original_glanceclient = api.glance.glanceclient
|
||||
self._original_keystoneclient = api.keystone.keystoneclient
|
||||
self._original_novaclient = api.nova.novaclient
|
||||
self._original_neutronclient = api.neutron.neutronclient
|
||||
self._original_cinderclient = api.cinder.cinderclient
|
||||
self._original_keystoneclient = project_api.keystone.keystoneclient
|
||||
self._original_heatclient = api.heat.heatclient
|
||||
|
||||
# Replace the clients with our stubs.
|
||||
api.glance.glanceclient = fake_glanceclient
|
||||
api.keystone.keystoneclient = fake_keystoneclient
|
||||
api.nova.novaclient = fake_novaclient
|
||||
api.neutron.neutronclient = lambda request: self.stub_neutronclient()
|
||||
api.cinder.cinderclient = lambda request: self.stub_cinderclient()
|
||||
project_api.keystone.keystoneclient = fake_keystoneclient
|
||||
api.heat.heatclient = (lambda request, password=None:
|
||||
self.stub_heatclient())
|
||||
|
||||
def tearDown(self):
|
||||
super(APITestCase, self).tearDown()
|
||||
api.glance.glanceclient = self._original_glanceclient
|
||||
api.nova.novaclient = self._original_novaclient
|
||||
api.keystone.keystoneclient = self._original_keystoneclient
|
||||
api.neutron.neutronclient = self._original_neutronclient
|
||||
api.cinder.cinderclient = self._original_cinderclient
|
||||
project_api.keystone.keystoneclient = self._original_keystoneclient
|
||||
api.heat.heatclient = self._original_heatclient
|
||||
|
||||
def stub_novaclient(self):
|
||||
if not hasattr(self, "novaclient"):
|
||||
self.mox.StubOutWithMock(nova_client, 'Client')
|
||||
# mock the api_version since MockObject.__init__ ignores it.
|
||||
# The preferred version in the api.nova code is 2 but that's
|
||||
# equivalent to 2.1 now and is the base version that's backward
|
||||
# compatible to 2.0 anyway.
|
||||
api_version = nova_api_versions.APIVersion('2.1')
|
||||
nova_client.Client.api_version = api_version
|
||||
nova_client.Client.projectid = 'fake_project'
|
||||
nova_client.Client.tenant_id = 'fake_tenant'
|
||||
self.novaclient = self.mox.CreateMock(nova_client.Client)
|
||||
return self.novaclient
|
||||
|
||||
def stub_cinderclient(self):
|
||||
if not hasattr(self, "cinderclient"):
|
||||
self.mox.StubOutWithMock(cinder_client, 'Client')
|
||||
self.cinderclient = self.mox.CreateMock(cinder_client.Client)
|
||||
return self.cinderclient
|
||||
|
||||
def stub_keystoneclient(self):
|
||||
if not hasattr(self, "keystoneclient"):
|
||||
self.mox.StubOutWithMock(keystone_client, 'Client')
|
||||
@ -482,35 +430,12 @@ class APITestCase(TestCase):
|
||||
self.keystoneclient = self.mox.CreateMock(keystone_client.Client)
|
||||
return self.keystoneclient
|
||||
|
||||
def stub_glanceclient(self):
|
||||
if not hasattr(self, "glanceclient"):
|
||||
self.mox.StubOutWithMock(glanceclient, 'Client')
|
||||
self.glanceclient = self.mox.CreateMock(glanceclient.Client)
|
||||
return self.glanceclient
|
||||
|
||||
def stub_neutronclient(self):
|
||||
if not hasattr(self, "neutronclient"):
|
||||
self.mox.StubOutWithMock(neutron_client, 'Client')
|
||||
self.neutronclient = self.mox.CreateMock(neutron_client.Client)
|
||||
return self.neutronclient
|
||||
|
||||
def stub_swiftclient(self, expected_calls=1):
|
||||
if not hasattr(self, "swiftclient"):
|
||||
self.mox.StubOutWithMock(swift_client, 'Connection')
|
||||
self.swiftclient = self.mox.CreateMock(swift_client.Connection)
|
||||
while expected_calls:
|
||||
swift_client.Connection(None,
|
||||
mox.IgnoreArg(),
|
||||
None,
|
||||
preauthtoken=mox.IgnoreArg(),
|
||||
preauthurl=mox.IgnoreArg(),
|
||||
cacert=None,
|
||||
insecure=False,
|
||||
auth_version="2.0") \
|
||||
.AndReturn(self.swiftclient)
|
||||
expected_calls -= 1
|
||||
return self.swiftclient
|
||||
|
||||
def stub_heatclient(self):
|
||||
if not hasattr(self, "heatclient"):
|
||||
self.mox.StubOutWithMock(heat_client, 'Client')
|
||||
@ -522,161 +447,8 @@ class APITestCase(TestCase):
|
||||
class ResetImageAPIVersionMixin(object):
|
||||
def setUp(self):
|
||||
super(ResetImageAPIVersionMixin, self).setUp()
|
||||
api.glance.VERSIONS.clear_active_cache()
|
||||
project_api.glance.VERSIONS.clear_active_cache()
|
||||
|
||||
def tearDown(self):
|
||||
api.glance.VERSIONS.clear_active_cache()
|
||||
project_api.glance.VERSIONS.clear_active_cache()
|
||||
super(ResetImageAPIVersionMixin, self).tearDown()
|
||||
|
||||
|
||||
@unittest.skipUnless(os.environ.get('WITH_SELENIUM', False),
|
||||
"The WITH_SELENIUM env variable is not set.")
|
||||
class SeleniumTestCase(horizon_helpers.SeleniumTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(SeleniumTestCase, self).setUp()
|
||||
|
||||
test_utils.load_test_data(self)
|
||||
self.mox = mox.Mox()
|
||||
|
||||
self._real_get_user = utils.get_user
|
||||
self.setActiveUser(id=self.user.id,
|
||||
token=self.token,
|
||||
username=self.user.name,
|
||||
tenant_id=self.tenant.id,
|
||||
service_catalog=self.service_catalog,
|
||||
authorized_tenants=self.tenants.list())
|
||||
self.patchers = _apply_panel_mocks()
|
||||
os.environ["HORIZON_TEST_RUN"] = "True"
|
||||
|
||||
def tearDown(self):
|
||||
self.mox.UnsetStubs()
|
||||
utils.get_user = self._real_get_user
|
||||
mock.patch.stopall()
|
||||
self.mox.VerifyAll()
|
||||
del os.environ["HORIZON_TEST_RUN"]
|
||||
|
||||
def setActiveUser(self, id=None, token=None, username=None, tenant_id=None,
|
||||
service_catalog=None, tenant_name=None, roles=None,
|
||||
authorized_tenants=None, enabled=True):
|
||||
def get_user(request):
|
||||
return user.User(id=id,
|
||||
token=token,
|
||||
user=username,
|
||||
tenant_id=tenant_id,
|
||||
service_catalog=service_catalog,
|
||||
roles=roles,
|
||||
enabled=enabled,
|
||||
authorized_tenants=authorized_tenants,
|
||||
endpoint=settings.OPENSTACK_KEYSTONE_URL)
|
||||
utils.get_user = get_user
|
||||
|
||||
|
||||
class SeleniumAdminTestCase(SeleniumTestCase):
|
||||
"""Version of AdminTestCase for Selenium.
|
||||
|
||||
Sets an active user with the "admin" role for testing admin-only views and
|
||||
functionality.
|
||||
"""
|
||||
def setActiveUser(self, *args, **kwargs):
|
||||
if "roles" not in kwargs:
|
||||
kwargs['roles'] = [self.roles.admin._info]
|
||||
super(SeleniumAdminTestCase, self).setActiveUser(*args, **kwargs)
|
||||
|
||||
|
||||
def my_custom_sort(flavor):
|
||||
sort_order = {
|
||||
'm1.secret': 0,
|
||||
'm1.tiny': 1,
|
||||
'm1.massive': 2,
|
||||
'm1.metadata': 3,
|
||||
}
|
||||
return sort_order[flavor.name]
|
||||
|
||||
|
||||
class PluginTestCase(TestCase):
|
||||
"""Test case for testing plugin system of Horizon.
|
||||
|
||||
For use with tests which deal with the pluggable dashboard and panel
|
||||
configuration, it takes care of backing up and restoring the Horizon
|
||||
configuration.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(PluginTestCase, self).setUp()
|
||||
self.old_horizon_config = conf.HORIZON_CONFIG
|
||||
conf.HORIZON_CONFIG = conf.LazySettings()
|
||||
base.Horizon._urls()
|
||||
# Store our original dashboards
|
||||
self._discovered_dashboards = base.Horizon._registry.keys()
|
||||
# Gather up and store our original panels for each dashboard
|
||||
self._discovered_panels = {}
|
||||
for dash in self._discovered_dashboards:
|
||||
panels = base.Horizon._registry[dash]._registry.keys()
|
||||
self._discovered_panels[dash] = panels
|
||||
|
||||
def tearDown(self):
|
||||
super(PluginTestCase, self).tearDown()
|
||||
conf.HORIZON_CONFIG = self.old_horizon_config
|
||||
# Destroy our singleton and re-create it.
|
||||
base.HorizonSite._instance = None
|
||||
del base.Horizon
|
||||
base.Horizon = base.HorizonSite()
|
||||
# Reload the convenience references to Horizon stored in __init__
|
||||
moves.reload_module(import_module("horizon"))
|
||||
# Re-register our original dashboards and panels.
|
||||
# This is necessary because autodiscovery only works on the first
|
||||
# import, and calling reload introduces innumerable additional
|
||||
# problems. Manual re-registration is the only good way for testing.
|
||||
for dash in self._discovered_dashboards:
|
||||
base.Horizon.register(dash)
|
||||
for panel in self._discovered_panels[dash]:
|
||||
dash.register(panel)
|
||||
self._reload_urls()
|
||||
|
||||
def _reload_urls(self):
|
||||
"""CLeans up URLs.
|
||||
|
||||
Clears out the URL caches, reloads the root urls module, and
|
||||
re-triggers the autodiscovery mechanism for Horizon. Allows URLs
|
||||
to be re-calculated after registering new dashboards. Useful
|
||||
only for testing and should never be used on a live site.
|
||||
"""
|
||||
urlresolvers.clear_url_caches()
|
||||
moves.reload_module(import_module(settings.ROOT_URLCONF))
|
||||
base.Horizon._urls()
|
||||
|
||||
|
||||
class update_settings(django_test_utils.override_settings):
|
||||
"""override_settings which allows override an item in dict.
|
||||
|
||||
django original override_settings replaces a dict completely,
|
||||
however OpenStack dashboard setting has many dictionary configuration
|
||||
and there are test case where we want to override only one item in
|
||||
a dictionary and keep other items in the dictionary.
|
||||
This version of override_settings allows this if keep_dict is True.
|
||||
|
||||
If keep_dict False is specified, the original behavior of
|
||||
Django override_settings is used.
|
||||
"""
|
||||
|
||||
def __init__(self, keep_dict=True, **kwargs):
|
||||
if keep_dict:
|
||||
for key, new_value in kwargs.items():
|
||||
value = getattr(settings, key, None)
|
||||
if (isinstance(new_value, collections.Mapping) and
|
||||
isinstance(value, collections.Mapping)):
|
||||
copied = copy.copy(value)
|
||||
copied.update(new_value)
|
||||
kwargs[key] = copied
|
||||
super(update_settings, self).__init__(**kwargs)
|
||||
|
||||
|
||||
def mock_obj_to_dict(r):
|
||||
return mock.Mock(**{'to_dict.return_value': r})
|
||||
|
||||
|
||||
def mock_factory(r):
|
||||
"""mocks all the attributes as well as the to_dict """
|
||||
mocked = mock_obj_to_dict(r)
|
||||
mocked.configure_mock(**r)
|
||||
return mocked
|
||||
|
@ -11,14 +11,29 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from horizon.test.settings import * # noqa
|
||||
from openstack_dashboard.test.settings import * # noqa
|
||||
# Default to Horizons test settings to avoid any missing keys
|
||||
import heat_dashboard.enabled
|
||||
|
||||
from openstack_dashboard.test.settings import * # noqa: F403,H303
|
||||
|
||||
from openstack_dashboard.utils import settings
|
||||
|
||||
|
||||
INSTALLED_APPS = list(INSTALLED_APPS)
|
||||
INSTALLED_APPS.append('heat_dashboard.content.stacks')
|
||||
INSTALLED_APPS.append('heat_dashboard.content.resource_types')
|
||||
INSTALLED_APPS.append('heat_dashboard.content.template_versions')
|
||||
# pop these keys to avoid log warnings about deprecation
|
||||
# update_dashboards will populate them anyway
|
||||
HORIZON_CONFIG.pop('dashboards', None)
|
||||
HORIZON_CONFIG.pop('default_dashboard', None)
|
||||
|
||||
# further implementation
|
||||
# INSTALLED_APPS.append('heat_dashboard.content.template_generator')
|
||||
# Update the dashboards with heat_dashboard enabled files
|
||||
# and current INSTALLED_APPS
|
||||
settings.update_dashboards(
|
||||
[
|
||||
openstack_dashboard.enabled,
|
||||
heat_dashboard.enabled,
|
||||
],
|
||||
HORIZON_CONFIG,
|
||||
INSTALLED_APPS
|
||||
)
|
||||
|
||||
# Remove duplicated apps
|
||||
INSTALLED_APPS = list(set(INSTALLED_APPS))
|
||||
|
0
heat_dashboard/test/test_data/__init__.py
Normal file
0
heat_dashboard/test/test_data/__init__.py
Normal file
56
heat_dashboard/test/test_data/exceptions.py
Normal file
56
heat_dashboard/test/test_data/exceptions.py
Normal file
@ -0,0 +1,56 @@
|
||||
# Copyright 2012 Nebula, Inc.
|
||||
#
|
||||
# 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 heatclient.exc as heat_exceptions
|
||||
import six
|
||||
|
||||
from heat_dashboard.test.test_data import utils
|
||||
|
||||
|
||||
def create_stubbed_exception(cls, status_code=500):
|
||||
msg = "Expected failure."
|
||||
|
||||
def fake_init_exception(self, code=None, message=None, **kwargs):
|
||||
if code is not None:
|
||||
if hasattr(self, 'http_status'):
|
||||
self.http_status = code
|
||||
else:
|
||||
self.code = code
|
||||
self.message = message or self.__class__.message
|
||||
|
||||
try:
|
||||
# Neutron sometimes updates the message with additional
|
||||
# information, like a reason.
|
||||
self.message = self.message % kwargs
|
||||
except Exception:
|
||||
pass # We still have the main error message.
|
||||
|
||||
def fake_str(self):
|
||||
return str(self.message)
|
||||
|
||||
def fake_unicode(self):
|
||||
return six.text_type(self.message)
|
||||
|
||||
cls.__init__ = fake_init_exception
|
||||
cls.__str__ = fake_str
|
||||
cls.__unicode__ = fake_unicode
|
||||
cls.silence_logging = True
|
||||
return cls(status_code, msg)
|
||||
|
||||
|
||||
def data(TEST):
|
||||
TEST.exceptions = utils.TestDataContainer()
|
||||
|
||||
heat_exception = heat_exceptions.HTTPException
|
||||
TEST.exceptions.heat = create_stubbed_exception(heat_exception)
|
617
heat_dashboard/test/test_data/heat_data.py
Normal file
617
heat_dashboard/test/test_data/heat_data.py
Normal file
@ -0,0 +1,617 @@
|
||||
# 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 heatclient.v1 import resource_types
|
||||
from heatclient.v1 import resources
|
||||
from heatclient.v1 import services
|
||||
from heatclient.v1 import stacks
|
||||
from heatclient.v1 import template_versions
|
||||
|
||||
from heat_dashboard.test.test_data import utils
|
||||
|
||||
# A slightly hacked up copy of a sample cloudformation template for testing.
|
||||
TEMPLATE = """
|
||||
{
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
"Description": "AWS CloudFormation Sample Template.",
|
||||
"Parameters": {
|
||||
"KeyName": {
|
||||
"Description": "Name of an EC2 Key Pair to enable SSH access to the instances",
|
||||
"Type": "String"
|
||||
},
|
||||
"InstanceType": {
|
||||
"Description": "WebServer EC2 instance type",
|
||||
"Type": "String",
|
||||
"Default": "m1.small",
|
||||
"AllowedValues": [
|
||||
"m1.tiny",
|
||||
"m1.small",
|
||||
"m1.medium",
|
||||
"m1.large",
|
||||
"m1.xlarge"
|
||||
],
|
||||
"ConstraintDescription": "must be a valid EC2 instance type."
|
||||
},
|
||||
"DBName": {
|
||||
"Default": "wordpress",
|
||||
"Description": "The WordPress database name",
|
||||
"Type": "String",
|
||||
"MinLength": "1",
|
||||
"MaxLength": "64",
|
||||
"AllowedPattern": "[a-zA-Z][a-zA-Z0-9]*",
|
||||
"ConstraintDescription": "must begin with a letter and..."
|
||||
},
|
||||
"DBUsername": {
|
||||
"Default": "admin",
|
||||
"NoEcho": "true",
|
||||
"Description": "The WordPress database admin account username",
|
||||
"Type": "String",
|
||||
"MinLength": "1",
|
||||
"MaxLength": "16",
|
||||
"AllowedPattern": "[a-zA-Z][a-zA-Z0-9]*",
|
||||
"ConstraintDescription": "must begin with a letter and..."
|
||||
},
|
||||
"DBPassword": {
|
||||
"Default": "admin",
|
||||
"NoEcho": "true",
|
||||
"Description": "The WordPress database admin account password",
|
||||
"Type": "String",
|
||||
"MinLength": "1",
|
||||
"MaxLength": "41",
|
||||
"AllowedPattern": "[a-zA-Z0-9]*",
|
||||
"ConstraintDescription": "must contain only alphanumeric characters."
|
||||
},
|
||||
"DBRootPassword": {
|
||||
"Default": "admin",
|
||||
"NoEcho": "true",
|
||||
"Description": "Root password for MySQL",
|
||||
"Type": "String",
|
||||
"MinLength": "1",
|
||||
"MaxLength": "41",
|
||||
"AllowedPattern": "[a-zA-Z0-9]*",
|
||||
"ConstraintDescription": "must contain only alphanumeric characters."
|
||||
},
|
||||
"LinuxDistribution": {
|
||||
"Default": "F17",
|
||||
"Description": "Distribution of choice",
|
||||
"Type": "String",
|
||||
"AllowedValues": [
|
||||
"F18",
|
||||
"F17",
|
||||
"U10",
|
||||
"RHEL-6.1",
|
||||
"RHEL-6.2",
|
||||
"RHEL-6.3"
|
||||
]
|
||||
},
|
||||
"Network": {
|
||||
"Type": "String",
|
||||
"CustomConstraint": "neutron.network"
|
||||
}
|
||||
},
|
||||
"Mappings": {
|
||||
"AWSInstanceType2Arch": {
|
||||
"m1.tiny": {
|
||||
"Arch": "32"
|
||||
},
|
||||
"m1.small": {
|
||||
"Arch": "64"
|
||||
},
|
||||
"m1.medium": {
|
||||
"Arch": "64"
|
||||
},
|
||||
"m1.large": {
|
||||
"Arch": "64"
|
||||
},
|
||||
"m1.xlarge": {
|
||||
"Arch": "64"
|
||||
}
|
||||
},
|
||||
"DistroArch2AMI": {
|
||||
"F18": {
|
||||
"32": "F18-i386-cfntools",
|
||||
"64": "F18-x86_64-cfntools"
|
||||
},
|
||||
"F17": {
|
||||
"32": "F17-i386-cfntools",
|
||||
"64": "F17-x86_64-cfntools"
|
||||
},
|
||||
"U10": {
|
||||
"32": "U10-i386-cfntools",
|
||||
"64": "U10-x86_64-cfntools"
|
||||
},
|
||||
"RHEL-6.1": {
|
||||
"32": "rhel61-i386-cfntools",
|
||||
"64": "rhel61-x86_64-cfntools"
|
||||
},
|
||||
"RHEL-6.2": {
|
||||
"32": "rhel62-i386-cfntools",
|
||||
"64": "rhel62-x86_64-cfntools"
|
||||
},
|
||||
"RHEL-6.3": {
|
||||
"32": "rhel63-i386-cfntools",
|
||||
"64": "rhel63-x86_64-cfntools"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Resources": {
|
||||
"WikiDatabase": {
|
||||
"Type": "AWS::EC2::Instance",
|
||||
"Metadata": {
|
||||
"AWS::CloudFormation::Init": {
|
||||
"config": {
|
||||
"packages": {
|
||||
"yum": {
|
||||
"mysql": [],
|
||||
"mysql-server": [],
|
||||
"httpd": [],
|
||||
"wordpress": []
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"systemd": {
|
||||
"mysqld": {
|
||||
"enabled": "true",
|
||||
"ensureRunning": "true"
|
||||
},
|
||||
"httpd": {
|
||||
"enabled": "true",
|
||||
"ensureRunning": "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Properties": {
|
||||
"ImageId": {
|
||||
"Fn::FindInMap": [
|
||||
"DistroArch2AMI",
|
||||
{
|
||||
"Ref": "LinuxDistribution"
|
||||
},
|
||||
{
|
||||
"Fn::FindInMap": [
|
||||
"AWSInstanceType2Arch",
|
||||
{
|
||||
"Ref": "InstanceType"
|
||||
},
|
||||
"Arch"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"InstanceType": {
|
||||
"Ref": "InstanceType"
|
||||
},
|
||||
"KeyName": {
|
||||
"Ref": "KeyName"
|
||||
},
|
||||
"UserData": {
|
||||
"Fn::Base64": {
|
||||
"Fn::Join": [
|
||||
"",
|
||||
[
|
||||
"#!/bin/bash -v\\n",
|
||||
"/opt/aws/bin/cfn-init\\n"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Outputs": {
|
||||
"WebsiteURL": {
|
||||
"Value": {
|
||||
"Fn::Join": [
|
||||
"",
|
||||
[
|
||||
"http://",
|
||||
{
|
||||
"Fn::GetAtt": [
|
||||
"WikiDatabase",
|
||||
"PublicIp"
|
||||
]
|
||||
},
|
||||
"/wordpress"
|
||||
]
|
||||
]
|
||||
},
|
||||
"Description": "URL for Wordpress wiki"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
VALIDATE = """
|
||||
{
|
||||
"Description": "AWS CloudFormation Sample Template.",
|
||||
"Parameters": {
|
||||
"DBUsername": {
|
||||
"Type": "String",
|
||||
"Description": "The WordPress database admin account username",
|
||||
"Default": "admin",
|
||||
"MinLength": "1",
|
||||
"AllowedPattern": "[a-zA-Z][a-zA-Z0-9]*",
|
||||
"NoEcho": "true",
|
||||
"MaxLength": "16",
|
||||
"ConstraintDescription": "must begin with a letter and..."
|
||||
},
|
||||
"LinuxDistribution": {
|
||||
"Default": "F17",
|
||||
"Type": "String",
|
||||
"Description": "Distribution of choice",
|
||||
"AllowedValues": [
|
||||
"F18",
|
||||
"F17",
|
||||
"U10",
|
||||
"RHEL-6.1",
|
||||
"RHEL-6.2",
|
||||
"RHEL-6.3"
|
||||
]
|
||||
},
|
||||
"DBRootPassword": {
|
||||
"Type": "String",
|
||||
"Description": "Root password for MySQL",
|
||||
"Default": "admin",
|
||||
"MinLength": "1",
|
||||
"AllowedPattern": "[a-zA-Z0-9]*",
|
||||
"NoEcho": "true",
|
||||
"MaxLength": "41",
|
||||
"ConstraintDescription": "must contain only alphanumeric characters."
|
||||
},
|
||||
"KeyName": {
|
||||
"Type": "String",
|
||||
"Description": "Name of an EC2 Key Pair to enable SSH access to the instances"
|
||||
},
|
||||
"DBName": {
|
||||
"Type": "String",
|
||||
"Description": "The WordPress database name",
|
||||
"Default": "wordpress",
|
||||
"MinLength": "1",
|
||||
"AllowedPattern": "[a-zA-Z][a-zA-Z0-9]*",
|
||||
"MaxLength": "64",
|
||||
"ConstraintDescription": "must begin with a letter and..."
|
||||
},
|
||||
"DBPassword": {
|
||||
"Type": "String",
|
||||
"Description": "The WordPress database admin account password",
|
||||
"Default": "admin",
|
||||
"MinLength": "1",
|
||||
"AllowedPattern": "[a-zA-Z0-9]*",
|
||||
"NoEcho": "true",
|
||||
"MaxLength": "41",
|
||||
"ConstraintDescription": "must contain only alphanumeric characters."
|
||||
},
|
||||
"InstanceType": {
|
||||
"Default": "m1.small",
|
||||
"Type": "String",
|
||||
"ConstraintDescription": "must be a valid EC2 instance type.",
|
||||
"Description": "WebServer EC2 instance type",
|
||||
"AllowedValues": [
|
||||
"m1.tiny",
|
||||
"m1.small",
|
||||
"m1.medium",
|
||||
"m1.large",
|
||||
"m1.xlarge"
|
||||
]
|
||||
},
|
||||
"Network": {
|
||||
"Type": "String",
|
||||
"CustomConstraint": "neutron.network"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
ENVIRONMENT = """
|
||||
parameters:
|
||||
InstanceType: m1.xsmall
|
||||
db_password: verybadpass
|
||||
KeyName: heat_key
|
||||
"""
|
||||
|
||||
SNAPSHOT_CREATE = """
|
||||
{
|
||||
"status": "IN_PROGRESS",
|
||||
"name": "None",
|
||||
"data": "None",
|
||||
"creation_time": "2016-02-19T07:25:23.494152",
|
||||
"status_reason": "None",
|
||||
"id": "8af90c07-b788-44ee-a8ab-5990197f5e32"
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
class Environment(object):
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
|
||||
|
||||
class Template(object):
|
||||
def __init__(self, data, validate):
|
||||
self.data = data
|
||||
self.validate = validate
|
||||
|
||||
|
||||
class Snapshot(object):
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
|
||||
|
||||
def data(TEST):
|
||||
TEST.stacks = utils.TestDataContainer()
|
||||
TEST.stack_templates = utils.TestDataContainer()
|
||||
TEST.stack_environments = utils.TestDataContainer()
|
||||
TEST.stack_snapshot_create = utils.TestDataContainer()
|
||||
TEST.stack_snapshot = utils.TestDataContainer()
|
||||
TEST.resource_types = utils.TestDataContainer()
|
||||
TEST.heat_resources = utils.TestDataContainer()
|
||||
TEST.heat_services = utils.TestDataContainer()
|
||||
TEST.template_versions = utils.TestDataContainer()
|
||||
TEST.template_functions = utils.TestDataContainer()
|
||||
|
||||
# Services
|
||||
service_1 = services.Service(services.ServiceManager(None), {
|
||||
"status": "up",
|
||||
"binary": "heat-engine",
|
||||
"report_interval": 60,
|
||||
"engine_id": "2f7b5a9b-c50b-4b01-8248-f89f5fb338d1",
|
||||
"created_at": "2015-02-06T03:23:32.000000",
|
||||
"hostname": "mrkanag",
|
||||
"updated_at": "2015-02-20T09:49:52.000000",
|
||||
"topic": "engine",
|
||||
"host": "engine-1",
|
||||
"deleted_at": None,
|
||||
"id": "1efd7015-5016-4caa-b5c8-12438af7b100"
|
||||
})
|
||||
|
||||
service_2 = services.Service(services.ServiceManager(None), {
|
||||
"status": "up",
|
||||
"binary": "heat-engine",
|
||||
"report_interval": 60,
|
||||
"engine_id": "2f7b5a9b-c50b-4b01-8248-f89f5fb338d2",
|
||||
"created_at": "2015-02-06T03:23:32.000000",
|
||||
"hostname": "mrkanag",
|
||||
"updated_at": "2015-02-20T09:49:52.000000",
|
||||
"topic": "engine",
|
||||
"host": "engine-2",
|
||||
"deleted_at": None,
|
||||
"id": "1efd7015-5016-4caa-b5c8-12438af7b100"
|
||||
})
|
||||
|
||||
TEST.heat_services.add(service_1)
|
||||
TEST.heat_services.add(service_2)
|
||||
|
||||
# Data return by heatclient.
|
||||
TEST.api_resource_types = utils.TestDataContainer()
|
||||
|
||||
for i in range(10):
|
||||
stack_data = {
|
||||
"description": "No description",
|
||||
"links": [{
|
||||
"href": "http://192.168.1.70:8004/v1/"
|
||||
"051c727ee67040d6a7b7812708485a97/"
|
||||
"stacks/stack-test{0}/"
|
||||
"05b4f39f-ea96-4d91-910c-e758c078a089{0}".format(i),
|
||||
"rel": "self"
|
||||
}],
|
||||
"parameters": {
|
||||
'DBUsername': '******',
|
||||
'InstanceType': 'm1.small',
|
||||
'AWS::StackId': (
|
||||
'arn:openstack:heat::2ce287:stacks/teststack/88553ec'),
|
||||
'DBRootPassword': '******',
|
||||
'AWS::StackName': "teststack{0}".format(i),
|
||||
'DBPassword': '******',
|
||||
'AWS::Region': 'ap-southeast-1',
|
||||
'DBName': u'wordpress'
|
||||
},
|
||||
"stack_status_reason": "Stack successfully created",
|
||||
"stack_name": "stack-test{0}".format(i),
|
||||
"creation_time": "2013-04-22T00:11:39Z",
|
||||
"updated_time": "2013-04-22T00:11:39Z",
|
||||
"stack_status": "CREATE_COMPLETE",
|
||||
"id": "05b4f39f-ea96-4d91-910c-e758c078a089{0}".format(i)
|
||||
}
|
||||
stack = stacks.Stack(stacks.StackManager(None), stack_data)
|
||||
TEST.stacks.add(stack)
|
||||
|
||||
for i in range(10):
|
||||
snapshot_data = {
|
||||
"status": "COMPLETE",
|
||||
"name": 'null',
|
||||
"data": {
|
||||
"files": {},
|
||||
"status": "COMPLETE",
|
||||
"name": "zhao3",
|
||||
"tags": ["a", " 123", " b", " 456"],
|
||||
"stack_user_project_id": "3cba4460875444049a2a7cc5420ccddb",
|
||||
"environment": {
|
||||
"encrypted_param_names": [],
|
||||
"parameter_defaults": {},
|
||||
"event_sinks": [],
|
||||
"parameters": {},
|
||||
"resource_registry": {
|
||||
"resources": {}
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"heat_template_version": "2013-05-23",
|
||||
"description":
|
||||
"HOT template for Test.",
|
||||
"resources": {
|
||||
"private_subnet": {
|
||||
"type": "OS::Neutron::Subnet",
|
||||
"properties": {
|
||||
"network_id": {"get_resource": "private_net"},
|
||||
"cidr": "172.16.2.0/24",
|
||||
"gateway_ip": "172.16.2.1"
|
||||
}
|
||||
},
|
||||
"private_net": {
|
||||
"type": "OS::Neutron::Net",
|
||||
"properties": {"name": "private-net"}
|
||||
}
|
||||
}
|
||||
},
|
||||
"action": "SNAPSHOT",
|
||||
"project_id": "1acd0026829f4d28bb2eff912d7aad0d",
|
||||
"id": "70650725-bdbd-419f-b53f-5707767bfe0e",
|
||||
"resources": {
|
||||
"private_subnet": {
|
||||
"status": "COMPLETE",
|
||||
"name": "private_subnet",
|
||||
"resource_data": {},
|
||||
"resource_id": "9c7211b3-31c7-41f6-b92a-442ad3f71ef0",
|
||||
"action": "SNAPSHOT",
|
||||
"type": "OS::Neutron::Subnet",
|
||||
"metadata": {}
|
||||
},
|
||||
"private_net": {
|
||||
"status": "COMPLETE",
|
||||
"name": "private_net",
|
||||
"resource_data": {},
|
||||
"resource_id": "ff4fd287-31b2-4d00-bc96-c409bc1db027",
|
||||
"action": "SNAPSHOT",
|
||||
"type": "OS::Neutron::Net",
|
||||
"metadata": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"creation_time": "2016-02-21T04:02:54",
|
||||
"status_reason": "Stack SNAPSHOT completed successfully",
|
||||
"id": "01558a3b-ba05-4427-bbb4-1e4ab71cfca{0}".format(i)
|
||||
}
|
||||
TEST.stack_snapshot.add(snapshot_data)
|
||||
|
||||
TEST.stack_templates.add(Template(TEMPLATE, VALIDATE))
|
||||
TEST.stack_environments.add(Environment(ENVIRONMENT))
|
||||
TEST.stack_snapshot_create.add(Snapshot(SNAPSHOT_CREATE))
|
||||
|
||||
# Resource types list
|
||||
r_type_1 = {
|
||||
"resource_type": "AWS::CloudFormation::Stack",
|
||||
"attributes": {},
|
||||
"properties": {
|
||||
"Parameters": {
|
||||
"description":
|
||||
"The set of parameters passed to this nested stack.",
|
||||
"immutable": False,
|
||||
"required": False,
|
||||
"type": "map",
|
||||
"update_allowed": True},
|
||||
"TemplateURL": {
|
||||
"description": "The URL of a template that specifies"
|
||||
" the stack to be created as a resource.",
|
||||
"immutable": False,
|
||||
"required": True,
|
||||
"type": "string",
|
||||
"update_allowed": True},
|
||||
"TimeoutInMinutes": {
|
||||
"description": "The length of time, in minutes,"
|
||||
" to wait for the nested stack creation.",
|
||||
"immutable": False,
|
||||
"required": False,
|
||||
"type": "number",
|
||||
"update_allowed": True}
|
||||
}
|
||||
}
|
||||
|
||||
r_type_2 = {
|
||||
"resource_type": "OS::Heat::CloudConfig",
|
||||
"attributes": {
|
||||
"config": {
|
||||
"description": "The config value of the software config."}
|
||||
},
|
||||
"properties": {
|
||||
"cloud_config": {
|
||||
"description": "Map representing the cloud-config data"
|
||||
" structure which will be formatted as YAML.",
|
||||
"immutable": False,
|
||||
"required": False,
|
||||
"type": "map",
|
||||
"update_allowed": False}
|
||||
}
|
||||
}
|
||||
|
||||
r_types_list = [r_type_1, r_type_2]
|
||||
|
||||
for rt in r_types_list:
|
||||
r_type = resource_types.ResourceType(
|
||||
resource_types.ResourceTypeManager(None), rt['resource_type'])
|
||||
TEST.resource_types.add(r_type)
|
||||
TEST.api_resource_types.add(rt)
|
||||
|
||||
# Resources
|
||||
resource_1 = resources.Resource(resources.ResourceManager(None), {
|
||||
"logical_resource_id": "my_resource",
|
||||
"physical_resource_id": "7b5e29b1-c94d-402d-b69c-df9ac6dfc0ce",
|
||||
"resource_name": "my_resource",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://192.168.1.70:8004/v1/"
|
||||
"051c727ee67040d6a7b7812708485a97/"
|
||||
"stacks/%s/%s/resources/my_resource" %
|
||||
(TEST.stacks.first().stack_name,
|
||||
TEST.stacks.first().id),
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "http://192.168.1.70:8004/v1/"
|
||||
"051c727ee67040d6a7b7812708485a97/"
|
||||
"stacks/%s/%s" %
|
||||
(TEST.stacks.first().stack_name,
|
||||
TEST.stacks.first().id),
|
||||
"rel": "stack"
|
||||
}
|
||||
],
|
||||
"attributes": {
|
||||
"metadata": {}
|
||||
}
|
||||
})
|
||||
|
||||
TEST.heat_resources.add(resource_1)
|
||||
|
||||
# Template versions
|
||||
template_version_1 = template_versions.TemplateVersion(
|
||||
template_versions.TemplateVersionManager(None), {
|
||||
"version": "HeatTemplateFormatVersion.2012-12-12",
|
||||
"type": "cfn"
|
||||
})
|
||||
|
||||
template_version_2 = template_versions.TemplateVersion(
|
||||
template_versions.TemplateVersionManager(None), {
|
||||
"version": "heat_template_version.2013-05-23",
|
||||
"type": "hot"
|
||||
})
|
||||
|
||||
TEST.template_versions.add(template_version_1)
|
||||
TEST.template_versions.add(template_version_2)
|
||||
|
||||
# Template functions
|
||||
template_function_1 = template_versions.TemplateVersion(
|
||||
template_versions.TemplateVersionManager(None), {
|
||||
"functions": "Fn::GetAZs",
|
||||
"description": "A function for retrieving the availability zones."
|
||||
})
|
||||
|
||||
template_function_2 = template_versions.TemplateVersion(
|
||||
template_versions.TemplateVersionManager(None), {
|
||||
"functions": "Fn::Join",
|
||||
"description": "A function for joining strings."
|
||||
})
|
||||
|
||||
TEST.template_functions.add(template_function_1)
|
||||
TEST.template_functions.add(template_function_2)
|
339
heat_dashboard/test/test_data/keystone_data.py
Normal file
339
heat_dashboard/test/test_data/keystone_data.py
Normal file
@ -0,0 +1,339 @@
|
||||
# Copyright 2012 Nebula, Inc.
|
||||
#
|
||||
# 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
|
||||
from datetime import timedelta
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils import datetime_safe
|
||||
|
||||
from keystoneclient import access
|
||||
from keystoneclient.v2_0 import tenants
|
||||
from keystoneclient.v2_0 import users
|
||||
from keystoneclient.v3.contrib.federation import identity_providers
|
||||
from keystoneclient.v3.contrib.federation import mappings
|
||||
from keystoneclient.v3.contrib.federation import protocols
|
||||
from keystoneclient.v3 import domains
|
||||
|
||||
from openstack_auth import user as auth_user
|
||||
|
||||
from heat_dashboard.test.test_data import utils
|
||||
|
||||
|
||||
# Dummy service catalog with all service.
|
||||
# All endpoint URLs should point to example.com.
|
||||
# Try to keep them as accurate to real data as possible (ports, URIs, etc.)
|
||||
SERVICE_CATALOG = [
|
||||
{"type": "compute",
|
||||
"name": "nova",
|
||||
"endpoints_links": [],
|
||||
"endpoints": [
|
||||
{"region": "RegionOne",
|
||||
"adminURL": "http://admin.nova.example.com:8774/v2",
|
||||
"internalURL": "http://int.nova.example.com:8774/v2",
|
||||
"publicURL": "http://public.nova.example.com:8774/v2"},
|
||||
{"region": "RegionTwo",
|
||||
"adminURL": "http://admin.nova2.example.com:8774/v2",
|
||||
"internalURL": "http://int.nova2.example.com:8774/v2",
|
||||
"publicURL": "http://public.nova2.example.com:8774/v2"}]},
|
||||
{"type": "volumev2",
|
||||
"name": "cinderv2",
|
||||
"endpoints_links": [],
|
||||
"endpoints": [
|
||||
{"region": "RegionOne",
|
||||
"adminURL": "http://admin.nova.example.com:8776/v2",
|
||||
"internalURL": "http://int.nova.example.com:8776/v2",
|
||||
"publicURL": "http://public.nova.example.com:8776/v2"},
|
||||
{"region": "RegionTwo",
|
||||
"adminURL": "http://admin.nova.example.com:8776/v2",
|
||||
"internalURL": "http://int.nova.example.com:8776/v2",
|
||||
"publicURL": "http://public.nova.example.com:8776/v2"}]},
|
||||
{"type": "image",
|
||||
"name": "glance",
|
||||
"endpoints_links": [],
|
||||
"endpoints": [
|
||||
{"region": "RegionOne",
|
||||
"adminURL": "http://admin.glance.example.com:9292",
|
||||
"internalURL": "http://int.glance.example.com:9292",
|
||||
"publicURL": "http://public.glance.example.com:9292"}]},
|
||||
{"type": "identity",
|
||||
"name": "keystone",
|
||||
"endpoints_links": [],
|
||||
"endpoints": [
|
||||
{"region": "RegionOne",
|
||||
"adminURL": "http://admin.keystone.example.com:35357/v2.0",
|
||||
"internalURL": "http://int.keystone.example.com:5000/v2.0",
|
||||
"publicURL": "http://public.keystone.example.com:5000/v2.0"}]},
|
||||
{"type": "object-store",
|
||||
"name": "swift",
|
||||
"endpoints_links": [],
|
||||
"endpoints": [
|
||||
{"region": "RegionOne",
|
||||
"adminURL": "http://admin.swift.example.com:8080/",
|
||||
"internalURL": "http://int.swift.example.com:8080/",
|
||||
"publicURL": "http://public.swift.example.com:8080/"}]},
|
||||
{"type": "network",
|
||||
"name": "neutron",
|
||||
"endpoints_links": [],
|
||||
"endpoints": [
|
||||
{"region": "RegionOne",
|
||||
"adminURL": "http://admin.neutron.example.com:9696/",
|
||||
"internalURL": "http://int.neutron.example.com:9696/",
|
||||
"publicURL": "http://public.neutron.example.com:9696/"}]},
|
||||
{"type": "ec2",
|
||||
"name": "EC2 Service",
|
||||
"endpoints_links": [],
|
||||
"endpoints": [
|
||||
{"region": "RegionOne",
|
||||
"adminURL": "http://admin.nova.example.com:8773/services/Admin",
|
||||
"publicURL": "http://public.nova.example.com:8773/services/Cloud",
|
||||
"internalURL": "http://int.nova.example.com:8773/services/Cloud"}]},
|
||||
{"type": "orchestration",
|
||||
"name": "Heat",
|
||||
"endpoints_links": [],
|
||||
"endpoints": [
|
||||
{"region": "RegionOne",
|
||||
"adminURL": "http://admin.heat.example.com:8004/v1",
|
||||
"publicURL": "http://public.heat.example.com:8004/v1",
|
||||
"internalURL": "http://int.heat.example.com:8004/v1"}]}
|
||||
]
|
||||
|
||||
|
||||
def data(TEST):
|
||||
# Make a deep copy of the catalog to avoid persisting side-effects
|
||||
# when tests modify the catalog.
|
||||
TEST.service_catalog = copy.deepcopy(SERVICE_CATALOG)
|
||||
TEST.tokens = utils.TestDataContainer()
|
||||
TEST.domains = utils.TestDataContainer()
|
||||
TEST.users = utils.TestDataContainer()
|
||||
# TEST.groups = utils.TestDataContainer()
|
||||
TEST.tenants = utils.TestDataContainer()
|
||||
# TEST.role_assignments = utils.TestDataContainer()
|
||||
# TEST.roles = utils.TestDataContainer()
|
||||
# TEST.ec2 = utils.TestDataContainer()
|
||||
|
||||
TEST.identity_providers = utils.TestDataContainer()
|
||||
TEST.idp_mappings = utils.TestDataContainer()
|
||||
TEST.idp_protocols = utils.TestDataContainer()
|
||||
|
||||
# admin_role_dict = {'id': '1',
|
||||
# 'name': 'admin'}
|
||||
# admin_role = roles.Role(roles.RoleManager, admin_role_dict, loaded=True)
|
||||
member_role_dict = {'id': "2",
|
||||
'name': settings.OPENSTACK_KEYSTONE_DEFAULT_ROLE}
|
||||
# member_role = roles.Role(roles.RoleManager,
|
||||
# member_role_dict, loaded=True)
|
||||
# TEST.roles.add(admin_role, member_role)
|
||||
# TEST.roles.admin = admin_role
|
||||
# TEST.roles.member = member_role
|
||||
|
||||
domain_dict = {'id': "1",
|
||||
'name': 'test_domain',
|
||||
'description': "a test domain.",
|
||||
'enabled': True}
|
||||
domain_dict_2 = {'id': "2",
|
||||
'name': 'disabled_domain',
|
||||
'description': "a disabled test domain.",
|
||||
'enabled': False}
|
||||
domain_dict_3 = {'id': "3",
|
||||
'name': 'another_test_domain',
|
||||
'description': "another test domain.",
|
||||
'enabled': True}
|
||||
domain = domains.Domain(domains.DomainManager, domain_dict)
|
||||
disabled_domain = domains.Domain(domains.DomainManager, domain_dict_2)
|
||||
another_domain = domains.Domain(domains.DomainManager, domain_dict_3)
|
||||
TEST.domains.add(domain, disabled_domain, another_domain)
|
||||
TEST.domain = domain # Your "current" domain
|
||||
|
||||
user_dict = {'id': "1",
|
||||
'name': 'test_user',
|
||||
'description': 'test_description',
|
||||
'email': 'test@example.com',
|
||||
'password': 'password',
|
||||
'token': 'test_token',
|
||||
'project_id': '1',
|
||||
'enabled': True,
|
||||
'domain_id': "1"}
|
||||
user = users.User(None, user_dict)
|
||||
user_dict = {'id': "2",
|
||||
'name': 'user_two',
|
||||
'description': 'test_description',
|
||||
'email': 'two@example.com',
|
||||
'password': 'password',
|
||||
'token': 'test_token',
|
||||
'project_id': '1',
|
||||
'enabled': True,
|
||||
'domain_id': "1"}
|
||||
user2 = users.User(None, user_dict)
|
||||
user_dict = {'id': "3",
|
||||
'name': 'user_three',
|
||||
'description': 'test_description',
|
||||
'email': 'three@example.com',
|
||||
'password': 'password',
|
||||
'token': 'test_token',
|
||||
'project_id': '1',
|
||||
'enabled': True,
|
||||
'domain_id': "1"}
|
||||
user3 = users.User(None, user_dict)
|
||||
user_dict = {'id': "4",
|
||||
'name': 'user_four',
|
||||
'description': 'test_description',
|
||||
'email': 'four@example.com',
|
||||
'password': 'password',
|
||||
'token': 'test_token',
|
||||
'project_id': '2',
|
||||
'enabled': True,
|
||||
'domain_id': "2"}
|
||||
user4 = users.User(None, user_dict)
|
||||
user_dict = {'id': "5",
|
||||
'name': 'user_five',
|
||||
'description': 'test_description',
|
||||
'email': None,
|
||||
'password': 'password',
|
||||
'token': 'test_token',
|
||||
'project_id': '2',
|
||||
'enabled': True,
|
||||
'domain_id': "1"}
|
||||
user5 = users.User(None, user_dict)
|
||||
TEST.users.add(user, user2, user3, user4, user5)
|
||||
TEST.user = user # Your "current" user
|
||||
TEST.user.service_catalog = copy.deepcopy(SERVICE_CATALOG)
|
||||
|
||||
tenant_dict = {'id': "1",
|
||||
'name': 'test_tenant',
|
||||
'description': "a test tenant.",
|
||||
'enabled': True,
|
||||
'domain_id': '1',
|
||||
'domain_name': 'test_domain'}
|
||||
tenant_dict_2 = {'id': "2",
|
||||
'name': 'disabled_tenant',
|
||||
'description': "a disabled test tenant.",
|
||||
'enabled': False,
|
||||
'domain_id': '2',
|
||||
'domain_name': 'disabled_domain'}
|
||||
tenant_dict_3 = {'id': "3",
|
||||
'name': u'\u4e91\u89c4\u5219',
|
||||
'description': "an unicode-named tenant.",
|
||||
'enabled': True,
|
||||
'domain_id': '2',
|
||||
'domain_name': 'disabled_domain'}
|
||||
tenant = tenants.Tenant(tenants.TenantManager, tenant_dict)
|
||||
disabled_tenant = tenants.Tenant(tenants.TenantManager, tenant_dict_2)
|
||||
tenant_unicode = tenants.Tenant(tenants.TenantManager, tenant_dict_3)
|
||||
|
||||
TEST.tenants.add(tenant, disabled_tenant, tenant_unicode)
|
||||
TEST.tenant = tenant # Your "current" tenant
|
||||
|
||||
tomorrow = datetime_safe.datetime.now() + timedelta(days=1)
|
||||
expiration = tomorrow.isoformat()
|
||||
|
||||
scoped_token_dict = {
|
||||
'access': {
|
||||
'token': {
|
||||
'id': "test_token_id",
|
||||
'expires': expiration,
|
||||
'tenant': tenant_dict,
|
||||
'tenants': [tenant_dict]},
|
||||
'user': {
|
||||
'id': "test_user_id",
|
||||
'name': "test_user",
|
||||
'roles': [member_role_dict]},
|
||||
'serviceCatalog': TEST.service_catalog
|
||||
}
|
||||
}
|
||||
|
||||
scoped_access_info = access.AccessInfo.factory(resp=None,
|
||||
body=scoped_token_dict)
|
||||
|
||||
unscoped_token_dict = {
|
||||
'access': {
|
||||
'token': {
|
||||
'id': "test_token_id",
|
||||
'expires': expiration},
|
||||
'user': {
|
||||
'id': "test_user_id",
|
||||
'name': "test_user",
|
||||
'roles': [member_role_dict]},
|
||||
'serviceCatalog': TEST.service_catalog
|
||||
}
|
||||
}
|
||||
unscoped_access_info = access.AccessInfo.factory(resp=None,
|
||||
body=unscoped_token_dict)
|
||||
|
||||
scoped_token = auth_user.Token(scoped_access_info)
|
||||
unscoped_token = auth_user.Token(unscoped_access_info)
|
||||
TEST.tokens.add(scoped_token, unscoped_token)
|
||||
TEST.token = scoped_token # your "current" token.
|
||||
TEST.tokens.scoped_token = scoped_token
|
||||
TEST.tokens.unscoped_token = unscoped_token
|
||||
|
||||
idp_dict_1 = {'id': 'idp_1',
|
||||
'description': 'identity provider 1',
|
||||
'enabled': True,
|
||||
'remote_ids': ['rid_1', 'rid_2']}
|
||||
idp_1 = identity_providers.IdentityProvider(
|
||||
identity_providers.IdentityProviderManager,
|
||||
idp_dict_1, loaded=True)
|
||||
idp_dict_2 = {'id': 'idp_2',
|
||||
'description': 'identity provider 2',
|
||||
'enabled': True,
|
||||
'remote_ids': ['rid_3', 'rid_4']}
|
||||
idp_2 = identity_providers.IdentityProvider(
|
||||
identity_providers.IdentityProviderManager,
|
||||
idp_dict_2, loaded=True)
|
||||
TEST.identity_providers.add(idp_1, idp_2)
|
||||
|
||||
idp_mapping_dict = {
|
||||
"id": "mapping_1",
|
||||
"rules": [
|
||||
{
|
||||
"local": [
|
||||
{
|
||||
"user": {
|
||||
"name": "{0}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"group": {
|
||||
"id": "0cd5e9"
|
||||
}
|
||||
}
|
||||
],
|
||||
"remote": [
|
||||
{
|
||||
"type": "UserName"
|
||||
},
|
||||
{
|
||||
"type": "orgPersonType",
|
||||
"not_any_of": [
|
||||
"Contractor",
|
||||
"Guest"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
idp_mapping = mappings.Mapping(
|
||||
mappings.MappingManager(None),
|
||||
idp_mapping_dict)
|
||||
TEST.idp_mappings.add(idp_mapping)
|
||||
|
||||
idp_protocol_dict_1 = {'id': 'protocol_1',
|
||||
'mapping_id': 'mapping_1'}
|
||||
idp_protocol = protocols.Protocol(
|
||||
protocols.ProtocolManager,
|
||||
idp_protocol_dict_1,
|
||||
loaded=True)
|
||||
TEST.idp_protocols.add(idp_protocol)
|
77
heat_dashboard/test/test_data/neutron_data.py
Normal file
77
heat_dashboard/test/test_data/neutron_data.py
Normal file
@ -0,0 +1,77 @@
|
||||
# Copyright 2012 Nebula, Inc.
|
||||
#
|
||||
# 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
|
||||
|
||||
from openstack_dashboard.api import neutron
|
||||
|
||||
from heat_dashboard.test.test_data import utils
|
||||
|
||||
|
||||
def data(TEST):
|
||||
# Data returned by openstack_dashboard.api.neutron wrapper.
|
||||
TEST.networks = utils.TestDataContainer()
|
||||
|
||||
# Data return by neutronclient.
|
||||
TEST.api_networks = utils.TestDataContainer()
|
||||
TEST.api_subnets = utils.TestDataContainer()
|
||||
|
||||
# 1st network.
|
||||
network_dict = {'admin_state_up': True,
|
||||
'id': '82288d84-e0a5-42ac-95be-e6af08727e42',
|
||||
'name': 'net1',
|
||||
'status': 'ACTIVE',
|
||||
'subnets': ['e8abc972-eb0c-41f1-9edd-4bc6e3bcd8c9',
|
||||
'41e53a49-442b-4307-9e9a-88967a6b6657'],
|
||||
'tenant_id': '1',
|
||||
'router:external': False,
|
||||
'shared': False}
|
||||
subnet_dict = {'allocation_pools': [{'end': '10.0.0.254',
|
||||
'start': '10.0.0.2'}],
|
||||
'dns_nameservers': [],
|
||||
'host_routes': [],
|
||||
'cidr': '10.0.0.0/24',
|
||||
'enable_dhcp': True,
|
||||
'gateway_ip': '10.0.0.1',
|
||||
'id': network_dict['subnets'][0],
|
||||
'ip_version': 4,
|
||||
'name': 'mysubnet1',
|
||||
'network_id': network_dict['id'],
|
||||
'tenant_id': network_dict['tenant_id']}
|
||||
subnetv6_dict = {
|
||||
'allocation_pools': [{'start': 'fdb6:b88a:488e::2',
|
||||
'end': 'fdb6:b88a:488e:0:ffff:ffff:ffff:ffff'}],
|
||||
'dns_nameservers': [],
|
||||
'host_routes': [],
|
||||
'cidr': 'fdb6:b88a:488e::/64',
|
||||
'enable_dhcp': True,
|
||||
'gateway_ip': 'fdb6:b88a:488e::1',
|
||||
'id': network_dict['subnets'][1],
|
||||
'ip_version': 6,
|
||||
'name': 'myv6subnet',
|
||||
'network_id': network_dict['id'],
|
||||
'tenant_id': network_dict['tenant_id'],
|
||||
'ipv6_ra_mode': 'slaac',
|
||||
'ipv6_address_mode': 'slaac'
|
||||
}
|
||||
|
||||
TEST.api_networks.add(network_dict)
|
||||
TEST.api_subnets.add(subnet_dict)
|
||||
TEST.api_subnets.add(subnetv6_dict)
|
||||
|
||||
network = copy.deepcopy(network_dict)
|
||||
subnet = neutron.Subnet(subnet_dict)
|
||||
subnetv6 = neutron.Subnet(subnetv6_dict)
|
||||
network['subnets'] = [subnet, subnetv6]
|
||||
TEST.networks.add(neutron.Network(network))
|
128
heat_dashboard/test/test_data/utils.py
Normal file
128
heat_dashboard/test/test_data/utils.py
Normal file
@ -0,0 +1,128 @@
|
||||
# Copyright 2012 Nebula, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
def load_test_data(load_onto=None):
|
||||
from heat_dashboard.test.test_data import exceptions
|
||||
from heat_dashboard.test.test_data import heat_data
|
||||
from heat_dashboard.test.test_data import keystone_data
|
||||
from heat_dashboard.test.test_data import neutron_data
|
||||
|
||||
# The order of these loaders matters, some depend on others.
|
||||
loaders = (
|
||||
exceptions.data,
|
||||
keystone_data.data,
|
||||
# glance_data.data,
|
||||
# nova_data.data,
|
||||
# cinder_data.data,
|
||||
neutron_data.data,
|
||||
# swift_data.data,
|
||||
heat_data.data,
|
||||
)
|
||||
if load_onto:
|
||||
for data_func in loaders:
|
||||
data_func(load_onto)
|
||||
return load_onto
|
||||
else:
|
||||
return TestData(*loaders)
|
||||
|
||||
|
||||
class TestData(object):
|
||||
"""Holder object for test data.
|
||||
|
||||
Any functions passed to the init method will be called with the
|
||||
``TestData`` object as their only argument.
|
||||
They can then load data onto the object as desired.
|
||||
|
||||
The idea is to use the instantiated object like this::
|
||||
|
||||
>>> import glance_data
|
||||
>>> TEST = TestData(glance_data.data)
|
||||
>>> TEST.images.list()
|
||||
[<Image: visible_image>, <Image: invisible_image>]
|
||||
>>> TEST.images.first()
|
||||
<Image: visible_image>
|
||||
|
||||
You can load as little or as much data as you like as long as the loaders
|
||||
don't conflict with each other.
|
||||
|
||||
See the
|
||||
:class:`~openstack_dashboard.test.test_data.utils.TestDataContainer`
|
||||
class for a list of available methods.
|
||||
"""
|
||||
def __init__(self, *args):
|
||||
for data_func in args:
|
||||
data_func(self)
|
||||
|
||||
|
||||
class TestDataContainer(object):
|
||||
"""A container for test data objects.
|
||||
|
||||
The behavior of this class is meant to mimic a "manager" class, which
|
||||
has convenient shortcuts for common actions like "list", "filter", "get",
|
||||
and "add".
|
||||
"""
|
||||
def __init__(self):
|
||||
self._objects = []
|
||||
|
||||
def add(self, *args):
|
||||
"""Add a new object to this container.
|
||||
|
||||
Generally this method should only be used during data loading, since
|
||||
adding data during a test can affect the results of other tests.
|
||||
"""
|
||||
for obj in args:
|
||||
if obj not in self._objects:
|
||||
self._objects.append(obj)
|
||||
|
||||
def list(self):
|
||||
"""Returns a list of all objects in this container."""
|
||||
return self._objects
|
||||
|
||||
def filter(self, filtered=None, **kwargs):
|
||||
"""Returns objects whose attributes match the given kwargs."""
|
||||
if filtered is None:
|
||||
filtered = self._objects
|
||||
try:
|
||||
key, value = kwargs.popitem()
|
||||
except KeyError:
|
||||
# We're out of filters, return
|
||||
return filtered
|
||||
|
||||
def get_match(obj):
|
||||
return hasattr(obj, key) and getattr(obj, key) == value
|
||||
|
||||
filtered = [obj for obj in filtered if get_match(obj)]
|
||||
return self.filter(filtered=filtered, **kwargs)
|
||||
|
||||
def get(self, **kwargs):
|
||||
"""Returns a single object whose attributes match the given kwargs.
|
||||
|
||||
An error will be raised if the arguments
|
||||
provided don't return exactly one match.
|
||||
"""
|
||||
matches = self.filter(**kwargs)
|
||||
if not matches:
|
||||
raise Exception("No matches found.")
|
||||
elif len(matches) > 1:
|
||||
raise Exception("Multiple matches found.")
|
||||
else:
|
||||
return matches.pop()
|
||||
|
||||
def first(self):
|
||||
"""Returns the first object from this container."""
|
||||
return self._objects[0]
|
||||
|
||||
def count(self):
|
||||
return len(self._objects)
|
@ -15,8 +15,6 @@ import json
|
||||
|
||||
import mock
|
||||
|
||||
# from openstack_dashboard.api.rest import heat
|
||||
# from openstack_dashboard.test import helpers as test
|
||||
from heat_dashboard.api.rest import heat
|
||||
from heat_dashboard.test import helpers as test
|
||||
from openstack_dashboard import api
|
||||
|
@ -14,8 +14,6 @@ import six
|
||||
from django.conf import settings
|
||||
from django.test.utils import override_settings
|
||||
|
||||
# from openstack_dashboard import api
|
||||
# from openstack_dashboard.test import helpers as test
|
||||
from heat_dashboard import api
|
||||
from heat_dashboard.test import helpers as test
|
||||
|
||||
|
@ -16,7 +16,6 @@ from django import http
|
||||
|
||||
from mox3.mox import IsA
|
||||
|
||||
# from openstack_dashboard import api
|
||||
from heat_dashboard import api
|
||||
from heat_dashboard.test import helpers as test
|
||||
|
||||
@ -32,7 +31,7 @@ class ResourceTypesTests(test.TestCase):
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(
|
||||
reverse('horizon:project:stacks.resource_types:index'))
|
||||
reverse('horizon:project:resource_types:index'))
|
||||
self.assertTemplateUsed(
|
||||
res, 'horizon/common/_data_table_view.html')
|
||||
self.assertContains(res, 'AWS::CloudFormation::Stack')
|
||||
@ -45,7 +44,7 @@ class ResourceTypesTests(test.TestCase):
|
||||
IsA(http.HttpRequest), rt['resource_type']).AndReturn(rt)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
url = reverse('horizon:project:stacks.resource_types:details',
|
||||
url = reverse('horizon:project:resource_types:details',
|
||||
args=[rt['resource_type']])
|
||||
res = self.client.get(url)
|
||||
|
||||
|
@ -30,10 +30,6 @@ from heat_dashboard import api
|
||||
from heat_dashboard.test import helpers as test
|
||||
from openstack_dashboard import api as dashboard_api
|
||||
|
||||
# from openstack_dashboard.dashboards.project.stacks import api as project_api
|
||||
# from openstack_dashboard.dashboards.project.stacks import forms
|
||||
# from openstack_dashboard.dashboards.project.stacks import mappings
|
||||
# from openstack_dashboard.dashboards.project.stacks import tables
|
||||
from heat_dashboard.content.stacks import forms
|
||||
from heat_dashboard.content.stacks import mappings
|
||||
from heat_dashboard.content.stacks import tables
|
||||
|
@ -16,14 +16,13 @@ from django import http
|
||||
|
||||
from mox3.mox import IsA
|
||||
|
||||
# from openstack_dashboard import api
|
||||
# from openstack_dashboard.test import helpers as test
|
||||
from heat_dashboard import api
|
||||
from heat_dashboard.test import helpers as test
|
||||
|
||||
|
||||
class TemplateVersionsTests(test.TestCase):
|
||||
INDEX_URL = reverse('horizon:project:stacks.template_versions:index')
|
||||
|
||||
INDEX_URL = reverse('horizon:project:template_versions:index')
|
||||
|
||||
@test.create_stubs({api.heat: ('template_version_list',)})
|
||||
def test_index(self):
|
||||
@ -33,7 +32,7 @@ class TemplateVersionsTests(test.TestCase):
|
||||
|
||||
res = self.client.get(self.INDEX_URL)
|
||||
self.assertTemplateUsed(
|
||||
res, 'project/stacks.template_versions/index.html')
|
||||
res, 'project/template_versions/index.html')
|
||||
self.assertContains(res, 'HeatTemplateFormatVersion.2012-12-12')
|
||||
|
||||
@test.create_stubs({api.heat: ('template_version_list',)})
|
||||
@ -44,7 +43,7 @@ class TemplateVersionsTests(test.TestCase):
|
||||
|
||||
res = self.client.get(self.INDEX_URL)
|
||||
self.assertTemplateUsed(
|
||||
res, 'project/stacks.template_versions/index.html')
|
||||
res, 'project/template_versions/index.html')
|
||||
self.assertEqual(len(res.context['table'].data), 0)
|
||||
self.assertMessageCount(res, error=1)
|
||||
|
||||
@ -57,7 +56,7 @@ class TemplateVersionsTests(test.TestCase):
|
||||
IsA(http.HttpRequest), t_version).AndReturn(t_functions)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
url = reverse('horizon:project:stacks.template_versions:details',
|
||||
url = reverse('horizon:project:template_versions:details',
|
||||
args=[t_version])
|
||||
res = self.client.get(url)
|
||||
|
||||
@ -69,10 +68,11 @@ class TemplateVersionsTests(test.TestCase):
|
||||
t_version = self.template_versions.first().version
|
||||
|
||||
api.heat.template_function_list(
|
||||
IsA(http.HttpRequest), t_version).AndRaise(self.exceptions.heat)
|
||||
IsA(http.HttpRequest), t_version).\
|
||||
AndRaise(self.exceptions.heat)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
url = reverse('horizon:project:stacks.template_versions:details',
|
||||
url = reverse('horizon:project:template_versions:details',
|
||||
args=[t_version])
|
||||
res = self.client.get(url)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user