summaryrefslogtreecommitdiff
path: root/openstack_dashboard/dashboards/project/stacks/tests.py
diff options
context:
space:
mode:
Diffstat (limited to 'openstack_dashboard/dashboards/project/stacks/tests.py')
-rw-r--r--openstack_dashboard/dashboards/project/stacks/tests.py1003
1 files changed, 0 insertions, 1003 deletions
diff --git a/openstack_dashboard/dashboards/project/stacks/tests.py b/openstack_dashboard/dashboards/project/stacks/tests.py
deleted file mode 100644
index d646a51..0000000
--- a/openstack_dashboard/dashboards/project/stacks/tests.py
+++ /dev/null
@@ -1,1003 +0,0 @@
1# Licensed under the Apache License, Version 2.0 (the "License"); you may
2# not use this file except in compliance with the License. You may obtain
3# a copy of the License at
4#
5# http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10# License for the specific language governing permissions and limitations
11# under the License.
12
13import json
14import re
15
16import django
17from django.conf import settings
18from django.core import exceptions
19from django.core.urlresolvers import reverse
20from django import http
21from django.test.utils import override_settings
22from django.utils import html
23from heatclient.common import template_format as hc_format
24from mox3.mox import IsA
25import six
26
27from openstack_dashboard import api
28from openstack_dashboard.dashboards.project.stacks import api as project_api
29from openstack_dashboard.dashboards.project.stacks import forms
30from openstack_dashboard.dashboards.project.stacks import mappings
31from openstack_dashboard.dashboards.project.stacks import tables
32from openstack_dashboard.test import helpers as test
33
34
35INDEX_TEMPLATE = 'horizon/common/_data_table_view.html'
36INDEX_URL = reverse('horizon:project:stacks:index')
37DETAIL_URL = 'horizon:project:stacks:detail'
38
39
40class MockResource(object):
41 def __init__(self, resource_type, physical_resource_id):
42 self.resource_type = resource_type
43 self.physical_resource_id = physical_resource_id
44
45
46class MappingsTests(test.TestCase):
47
48 def test_mappings(self):
49
50 def assertMappingUrl(url, resource_type, physical_resource_id):
51 mock = MockResource(resource_type, physical_resource_id)
52 mock_url = mappings.resource_to_url(mock)
53 self.assertEqual(url, mock_url)
54
55 assertMappingUrl(
56 '/project/networks/subnets/aaa/detail',
57 'OS::Neutron::Subnet',
58 'aaa')
59 assertMappingUrl(
60 None,
61 'OS::Neutron::Subnet',
62 None)
63 assertMappingUrl(
64 None,
65 None,
66 None)
67 assertMappingUrl(
68 None,
69 'AWS::AutoScaling::LaunchConfiguration',
70 'aaa')
71 assertMappingUrl(
72 '/project/instances/aaa/',
73 'AWS::EC2::Instance',
74 'aaa')
75 assertMappingUrl(
76 '/project/containers/container/aaa/',
77 'OS::Swift::Container',
78 'aaa')
79 assertMappingUrl(
80 None,
81 'Foo::Bar::Baz',
82 'aaa')
83 assertMappingUrl(
84 '/project/instances/aaa/',
85 'OS::Nova::Server',
86 'aaa')
87 assertMappingUrl(
88 '/project/stacks/stack/aaa/',
89 'OS::Heat::ResourceGroup',
90 'aaa')
91
92 def test_stack_output(self):
93 self.assertEqual(u'<pre>foo</pre>', mappings.stack_output('foo'))
94 self.assertEqual(u'', mappings.stack_output(None))
95
96 outputs = ['one', 'two', 'three']
97 # On Python 3, the pretty JSON output doesn't add space before newline
98 if six.PY3:
99 expected_text = """[\n "one",\n "two",\n "three"\n]"""
100 else:
101 expected_text = """[\n "one", \n "two", \n "three"\n]"""
102
103 self.assertEqual(u'<pre>%s</pre>' % html.escape(expected_text),
104 mappings.stack_output(outputs))
105
106 outputs = {'foo': 'bar'}
107 expected_text = """{\n "foo": "bar"\n}"""
108 self.assertEqual(u'<pre>%s</pre>' % html.escape(expected_text),
109 mappings.stack_output(outputs))
110
111 self.assertEqual(
112 u'<a href="http://www.example.com/foo" target="_blank">'
113 'http://www.example.com/foo</a>',
114 mappings.stack_output('http://www.example.com/foo'))
115
116
117class StackTests(test.TestCase):
118
119 @override_settings(API_RESULT_PAGE_SIZE=2)
120 @test.create_stubs({api.heat: ('stacks_list',)})
121 def test_index_paginated(self):
122 stacks = self.stacks.list()[:5]
123 filters = {}
124 api.heat.stacks_list(IsA(http.HttpRequest),
125 marker=None,
126 paginate=True,
127 sort_dir='desc',
128 filters=filters) \
129 .AndReturn([stacks, True, True])
130 api.heat.stacks_list(IsA(http.HttpRequest),
131 marker=None,
132 paginate=True,
133 sort_dir='desc',
134 filters=filters) \
135 .AndReturn([stacks[:2], True, True])
136 api.heat.stacks_list(IsA(http.HttpRequest),
137 marker=stacks[2].id,
138 paginate=True,
139 sort_dir='desc',
140 filters=filters) \
141 .AndReturn([stacks[2:4], True, True])
142 api.heat.stacks_list(IsA(http.HttpRequest),
143 marker=stacks[4].id,
144 paginate=True,
145 sort_dir='desc',
146 filters=filters) \
147 .AndReturn([stacks[4:], True, True])
148 self.mox.ReplayAll()
149
150 url = reverse('horizon:project:stacks:index')
151 res = self.client.get(url)
152 # get all
153 self.assertEqual(len(res.context['stacks_table'].data),
154 len(stacks))
155 self.assertTemplateUsed(res, INDEX_TEMPLATE)
156
157 res = self.client.get(url)
158 # get first page with 2 items
159 self.assertEqual(len(res.context['stacks_table'].data),
160 settings.API_RESULT_PAGE_SIZE)
161
162 url = "%s?%s=%s" % (reverse('horizon:project:stacks:index'),
163 tables.StacksTable._meta.pagination_param,
164 stacks[2].id)
165 res = self.client.get(url)
166 # get second page (items 2-4)
167 self.assertEqual(len(res.context['stacks_table'].data),
168 settings.API_RESULT_PAGE_SIZE)
169
170 url = "%s?%s=%s" % (reverse('horizon:project:stacks:index'),
171 tables.StacksTable._meta.pagination_param,
172 stacks[4].id)
173 res = self.client.get(url)
174 # get third page (item 5)
175 self.assertEqual(len(res.context['stacks_table'].data),
176 1)
177
178 @override_settings(API_RESULT_PAGE_SIZE=2)
179 @test.create_stubs({api.heat: ('stacks_list',)})
180 def test_index_prev_paginated(self):
181 stacks = self.stacks.list()[:3]
182 filters = {}
183 api.heat.stacks_list(IsA(http.HttpRequest),
184 marker=None,
185 paginate=True,
186 sort_dir='desc',
187 filters=filters) \
188 .AndReturn([stacks, True, False])
189 api.heat.stacks_list(IsA(http.HttpRequest),
190 marker=None,
191 paginate=True,
192 sort_dir='desc',
193 filters=filters) \
194 .AndReturn([stacks[:2], True, True])
195 api.heat.stacks_list(IsA(http.HttpRequest),
196 marker=stacks[2].id,
197 paginate=True,
198 sort_dir='desc',
199 filters=filters) \
200 .AndReturn([stacks[2:], True, True])
201 api.heat.stacks_list(IsA(http.HttpRequest),
202 marker=stacks[2].id,
203 paginate=True,
204 sort_dir='asc',
205 filters=filters) \
206 .AndReturn([stacks[:2], True, True])
207 self.mox.ReplayAll()
208
209 url = reverse('horizon:project:stacks:index')
210 res = self.client.get(url)
211 # get all
212 self.assertEqual(len(res.context['stacks_table'].data),
213 len(stacks))
214 self.assertTemplateUsed(res, INDEX_TEMPLATE)
215
216 res = self.client.get(url)
217 # get first page with 2 items
218 self.assertEqual(len(res.context['stacks_table'].data),
219 settings.API_RESULT_PAGE_SIZE)
220
221 url = "%s?%s=%s" % (reverse('horizon:project:stacks:index'),
222 tables.StacksTable._meta.pagination_param,
223 stacks[2].id)
224 res = self.client.get(url)
225 # get second page (item 3)
226 self.assertEqual(len(res.context['stacks_table'].data), 1)
227
228 url = "%s?%s=%s" % (reverse('horizon:project:stacks:index'),
229 tables.StacksTable._meta.prev_pagination_param,
230 stacks[2].id)
231 res = self.client.get(url)
232 # prev back to get first page with 2 pages
233 self.assertEqual(len(res.context['stacks_table'].data),
234 settings.API_RESULT_PAGE_SIZE)
235
236 @test.create_stubs({api.heat: ('stack_create', 'template_validate'),
237 api.neutron: ('network_list_for_tenant', )})
238 def test_launch_stack(self):
239 template = self.stack_templates.first()
240 stack = self.stacks.first()
241
242 api.heat.template_validate(IsA(http.HttpRequest),
243 files={},
244 template=hc_format.parse(template.data)) \
245 .AndReturn(json.loads(template.validate))
246
247 api.heat.stack_create(IsA(http.HttpRequest),
248 stack_name=stack.stack_name,
249 timeout_mins=60,
250 disable_rollback=True,
251 template=None,
252 parameters=IsA(dict),
253 password='password',
254 files=None)
255 api.neutron.network_list_for_tenant(IsA(http.HttpRequest),
256 self.tenant.id) \
257 .AndReturn(self.networks.list())
258 api.neutron.network_list_for_tenant(IsA(http.HttpRequest),
259 self.tenant.id) \
260 .AndReturn(self.networks.list())
261
262 self.mox.ReplayAll()
263
264 url = reverse('horizon:project:stacks:select_template')
265 res = self.client.get(url)
266 self.assertTemplateUsed(res, 'project/stacks/select_template.html')
267
268 form_data = {'template_source': 'raw',
269 'template_data': template.data,
270 'method': forms.TemplateForm.__name__}
271 res = self.client.post(url, form_data)
272 self.assertTemplateUsed(res, 'project/stacks/create.html')
273
274 url = reverse('horizon:project:stacks:launch')
275 form_data = {'template_source': 'raw',
276 'template_data': template.data,
277 'password': 'password',
278 'parameters': template.validate,
279 'stack_name': stack.stack_name,
280 "timeout_mins": 60,
281 "disable_rollback": True,
282 "__param_DBUsername": "admin",
283 "__param_LinuxDistribution": "F17",
284 "__param_InstanceType": "m1.small",
285 "__param_KeyName": "test",
286 "__param_DBPassword": "admin",
287 "__param_DBRootPassword": "admin",
288 "__param_DBName": "wordpress",
289 "__param_Network": self.networks.list()[0]['id'],
290 'method': forms.CreateStackForm.__name__}
291 res = self.client.post(url, form_data)
292 self.assertRedirectsNoFollow(res, INDEX_URL)
293
294 @test.create_stubs({api.heat: ('stack_create', 'template_validate'),
295 api.neutron: ('network_list_for_tenant', )})
296 def test_launch_stack_with_environment(self):
297 template = self.stack_templates.first()
298 environment = self.stack_environments.first()
299 stack = self.stacks.first()
300
301 api.heat.template_validate(IsA(http.HttpRequest),
302 files={},
303 template=hc_format.parse(template.data),
304 environment=environment.data) \
305 .AndReturn(json.loads(template.validate))
306
307 api.heat.stack_create(IsA(http.HttpRequest),
308 stack_name=stack.stack_name,
309 timeout_mins=60,
310 disable_rollback=True,
311 template=None,
312 environment=environment.data,
313 parameters=IsA(dict),
314 password='password',
315 files=None)
316 api.neutron.network_list_for_tenant(IsA(http.HttpRequest),
317 self.tenant.id) \
318 .AndReturn(self.networks.list())
319 api.neutron.network_list_for_tenant(IsA(http.HttpRequest),
320 self.tenant.id) \
321 .AndReturn(self.networks.list())
322
323 self.mox.ReplayAll()
324
325 url = reverse('horizon:project:stacks:select_template')
326 res = self.client.get(url)
327 self.assertTemplateUsed(res, 'project/stacks/select_template.html')
328
329 form_data = {'template_source': 'raw',
330 'template_data': template.data,
331 'environment_source': 'raw',
332 'environment_data': environment.data,
333 'method': forms.TemplateForm.__name__}
334 res = self.client.post(url, form_data)
335 self.assertTemplateUsed(res, 'project/stacks/create.html')
336
337 url = reverse('horizon:project:stacks:launch')
338 form_data = {'template_source': 'raw',
339 'template_data': template.data,
340 'environment_source': 'raw',
341 'environment_data': environment.data,
342 'password': 'password',
343 'parameters': template.validate,
344 'stack_name': stack.stack_name,
345 "timeout_mins": 60,
346 "disable_rollback": True,
347 "__param_DBUsername": "admin",
348 "__param_LinuxDistribution": "F17",
349 "__param_InstanceType": "m1.small",
350 "__param_KeyName": "test",
351 "__param_DBPassword": "admin",
352 "__param_DBRootPassword": "admin",
353 "__param_DBName": "wordpress",
354 "__param_Network": self.networks.list()[0]['id'],
355 'method': forms.CreateStackForm.__name__}
356 res = self.client.post(url, form_data)
357 self.assertRedirectsNoFollow(res, INDEX_URL)
358
359 @test.create_stubs({api.heat: ('template_validate',)})
360 def test_launch_stack_with_hidden_parameters(self):
361 template = {
362 'data': ('heat_template_version: 2013-05-23\n'
363 'parameters:\n'
364 ' public_string:\n'
365 ' type: string\n'
366 ' secret_string:\n'
367 ' type: string\n'
368 ' hidden: true\n'),
369 'validate': {
370 'Description': 'No description',
371 'Parameters': {
372 'public_string': {
373 'Label': 'public_string',
374 'Description': '',
375 'Type': 'String',
376 'NoEcho': 'false'
377 },
378 'secret_string': {
379 'Label': 'secret_string',
380 'Description': '',
381 'Type': 'String',
382 'NoEcho': 'true'
383 }
384 }
385 }
386 }
387 api.heat.template_validate(IsA(http.HttpRequest),
388 files={},
389 template=hc_format.parse(template['data'])) \
390 .AndReturn(template['validate'])
391
392 self.mox.ReplayAll()
393
394 url = reverse('horizon:project:stacks:select_template')
395 res = self.client.get(url)
396 self.assertTemplateUsed(res, 'project/stacks/select_template.html')
397
398 form_data = {'template_source': 'raw',
399 'template_data': template['data'],
400 'method': forms.TemplateForm.__name__}
401 res = self.client.post(url, form_data)
402 self.assertTemplateUsed(res, 'project/stacks/create.html')
403
404 # ensure the fields were rendered correctly
405 if django.VERSION >= (1, 10):
406 pattern = ('<input class="form-control" '
407 'id="id___param_public_string" '
408 'name="__param_public_string" type="text" required/>')
409 secret = ('<input class="form-control" '
410 'id="id___param_secret_string" '
411 'name="__param_secret_string" '
412 'type="password" required>')
413 else:
414 pattern = ('<input class="form-control" '
415 'id="id___param_public_string" '
416 'name="__param_public_string" type="text" />')
417 secret = ('<input class="form-control" '
418 'id="id___param_secret_string" '
419 'name="__param_secret_string" '
420 'type="password" />')
421
422 self.assertContains(res, pattern, html=True)
423 self.assertContains(res, secret, html=True)
424
425 @test.create_stubs({api.heat: ('template_validate',)})
426 def test_launch_stack_with_parameter_group(self):
427 template = {
428 'data': ('heat_template_version: 2013-05-23\n'
429 'parameters:\n'
430 ' last_param:\n'
431 ' type: string\n'
432 ' first_param:\n'
433 ' type: string\n'
434 ' middle_param:\n'
435 ' type: string\n'
436 'parameter_groups:\n'
437 '- parameters:\n'
438 ' - first_param\n'
439 ' - middle_param\n'
440 ' - last_param\n'),
441 'validate': {
442 'Description': 'No description',
443 'Parameters': {
444 'last_param': {
445 'Label': 'last_param',
446 'Description': '',
447 'Type': 'String',
448 'NoEcho': 'false'
449 },
450 'first_param': {
451 'Label': 'first_param',
452 'Description': '',
453 'Type': 'String',
454 'NoEcho': 'false'
455 },
456 'middle_param': {
457 'Label': 'middle_param',
458 'Description': '',
459 'Type': 'String',
460 'NoEcho': 'true'
461 }
462 },
463 'ParameterGroups': [
464 {
465 'parameters': [
466 'first_param',
467 'middle_param',
468 'last_param'
469 ]
470 }
471 ]
472 }
473 }
474 api.heat.template_validate(IsA(http.HttpRequest),
475 files={},
476 template=hc_format.parse(template['data'])) \
477 .AndReturn(template['validate'])
478
479 self.mox.ReplayAll()
480
481 url = reverse('horizon:project:stacks:select_template')
482 res = self.client.get(url)
483 self.assertTemplateUsed(res, 'project/stacks/select_template.html')
484
485 form_data = {'template_source': 'raw',
486 'template_data': template['data'],
487 'method': forms.TemplateForm.__name__}
488 res = self.client.post(url, form_data)
489 self.assertTemplateUsed(res, 'project/stacks/create.html')
490
491 # ensure the fields were rendered in the correct order
492 regex = re.compile('^.*>first_param<.*>middle_param<.*>last_param<.*$',
493 flags=re.DOTALL)
494 self.assertRegexpMatches(res.content.decode('utf-8'), regex)
495
496 @test.create_stubs({api.heat: ('stack_create', 'template_validate')})
497 def test_launch_stack_parameter_types(self):
498 template = {
499 'data': ('heat_template_version: 2013-05-23\n'
500 'parameters:\n'
501 ' param1:\n'
502 ' type: string\n'
503 ' param2:\n'
504 ' type: number\n'
505 ' param3:\n'
506 ' type: json\n'
507 ' param4:\n'
508 ' type: comma_delimited_list\n'
509 ' param5:\n'
510 ' type: boolean\n'),
511 'validate': {
512 "Description": "No description",
513 "Parameters": {
514 "param1": {
515 "Type": "String",
516 "NoEcho": "false",
517 "Description": "",
518 "Label": "param1"
519 },
520 "param2": {
521 "Type": "Number",
522 "NoEcho": "false",
523 "Description": "",
524 "Label": "param2"
525 },
526 "param3": {
527 "Type": "Json",
528 "NoEcho": "false",
529 "Description": "",
530 "Label": "param3"
531 },
532 "param4": {
533 "Type": "CommaDelimitedList",
534 "NoEcho": "false",
535 "Description": "",
536 "Label": "param4"
537 },
538 "param5": {
539 "Type": "Boolean",
540 "NoEcho": "false",
541 "Description": "",
542 "Label": "param5"
543 }
544 }
545 }
546 }
547 stack = self.stacks.first()
548
549 api.heat.template_validate(IsA(http.HttpRequest),
550 files={},
551 template=hc_format.parse(template['data'])) \
552 .AndReturn(template['validate'])
553
554 api.heat.stack_create(IsA(http.HttpRequest),
555 stack_name=stack.stack_name,
556 timeout_mins=60,
557 disable_rollback=True,
558 template=hc_format.parse(template['data']),
559 parameters={'param1': 'some string',
560 'param2': 42,
561 'param3': '{"key": "value"}',
562 'param4': 'a,b,c',
563 'param5': True},
564 password='password',
565 files={})
566
567 self.mox.ReplayAll()
568
569 url = reverse('horizon:project:stacks:select_template')
570 res = self.client.get(url)
571 self.assertTemplateUsed(res, 'project/stacks/select_template.html')
572
573 form_data = {'template_source': 'raw',
574 'template_data': template['data'],
575 'method': forms.TemplateForm.__name__}
576 res = self.client.post(url, form_data)
577 self.assertTemplateUsed(res, 'project/stacks/create.html')
578
579 # ensure the fields were rendered correctly
580 if django.VERSION >= (1, 10):
581 input_str = ('<input class="form-control" '
582 'id="id___param_param{0}" '
583 'name="__param_param{0}" type="{1}" required/>')
584 else:
585 input_str = ('<input class="form-control" '
586 'id="id___param_param{0}" '
587 'name="__param_param{0}" type="{1}"/>')
588
589 self.assertContains(res, input_str.format(1, 'text'), html=True)
590 # the custom number spinner produces an input element
591 # that doesn't match the input_strs above
592 # validate with id alone
593 self.assertContains(res, 'id="id___param_param2"')
594 self.assertContains(res, input_str.format(3, 'text'), html=True)
595 self.assertContains(res, input_str.format(4, 'text'), html=True)
596 self.assertContains(
597 res,
598 '<input id="id___param_param5" name="__param_param5" '
599 'type="checkbox">',
600 html=True)
601
602 # post some sample data and make sure it validates
603 url = reverse('horizon:project:stacks:launch')
604 form_data = {'template_source': 'raw',
605 'template_data': template['data'],
606 'password': 'password',
607 'parameters': json.dumps(template['validate']),
608 'stack_name': stack.stack_name,
609 "timeout_mins": 60,
610 "disable_rollback": True,
611 "__param_param1": "some string",
612 "__param_param2": 42,
613 "__param_param3": '{"key": "value"}',
614 "__param_param4": "a,b,c",
615 "__param_param5": True,
616 'method': forms.CreateStackForm.__name__}
617 res = self.client.post(url, form_data)
618 self.assertRedirectsNoFollow(res, INDEX_URL)
619
620 @test.create_stubs({api.heat: ('stack_update', 'stack_get', 'template_get',
621 'template_validate'),
622 api.neutron: ('network_list_for_tenant', )})
623 def test_edit_stack_template(self):
624 template = self.stack_templates.first()
625 stack = self.stacks.first()
626
627 # GET to template form
628 api.heat.stack_get(IsA(http.HttpRequest),
629 stack.id).AndReturn(stack)
630 # POST template form, validation
631 api.heat.template_validate(IsA(http.HttpRequest),
632 files={},
633 template=hc_format.parse(template.data)) \
634 .AndReturn(json.loads(template.validate))
635
636 # GET to edit form
637 api.heat.stack_get(IsA(http.HttpRequest),
638 stack.id).AndReturn(stack)
639 api.heat.template_get(IsA(http.HttpRequest),
640 stack.id) \
641 .AndReturn(json.loads(template.validate))
642
643 # POST to edit form
644 api.heat.stack_get(IsA(http.HttpRequest),
645 stack.id).AndReturn(stack)
646
647 fields = {
648 'stack_name': stack.stack_name,
649 'disable_rollback': True,
650 'timeout_mins': 61,
651 'password': 'password',
652 'template': None,
653 'parameters': IsA(dict),
654 'files': None
655 }
656 api.heat.stack_update(IsA(http.HttpRequest),
657 stack_id=stack.id,
658 **fields)
659 api.neutron.network_list_for_tenant(IsA(http.HttpRequest),
660 self.tenant.id) \
661 .AndReturn(self.networks.list())
662
663 self.mox.ReplayAll()
664
665 url = reverse('horizon:project:stacks:change_template',
666 args=[stack.id])
667 res = self.client.get(url)
668 self.assertTemplateUsed(res, 'project/stacks/change_template.html')
669
670 form_data = {'template_source': 'raw',
671 'template_data': template.data,
672 'method': forms.ChangeTemplateForm.__name__}
673 res = self.client.post(url, form_data)
674
675 url = reverse('horizon:project:stacks:edit_stack',
676 args=[stack.id, ])
677 form_data = {'template_source': 'raw',
678 'template_data': template.data,
679 'password': 'password',
680 'parameters': template.validate,
681 'stack_name': stack.stack_name,
682 'stack_id': stack.id,
683 "timeout_mins": 61,
684 "disable_rollback": True,
685 "__param_DBUsername": "admin",
686 "__param_LinuxDistribution": "F17",
687 "__param_InstanceType": "m1.small",
688 "__param_KeyName": "test",
689 "__param_DBPassword": "admin",
690 "__param_DBRootPassword": "admin",
691 "__param_DBName": "wordpress",
692 "__param_Network": self.networks.list()[0]['id'],
693 'method': forms.EditStackForm.__name__}
694 res = self.client.post(url, form_data)
695 self.assertRedirectsNoFollow(res, INDEX_URL)
696
697 def test_launch_stack_form_invalid_name_digit(self):
698 self._test_launch_stack_invalid_name('2_StartWithDigit')
699
700 def test_launch_stack_form_invalid_name_underscore(self):
701 self._test_launch_stack_invalid_name('_StartWithUnderscore')
702
703 def test_launch_stack_form_invalid_name_point(self):
704 self._test_launch_stack_invalid_name('.StartWithPoint')
705
706 @test.create_stubs({api.neutron: ('network_list_for_tenant', )})
707 def _test_launch_stack_invalid_name(self, name):
708 api.neutron.network_list_for_tenant(IsA(http.HttpRequest),
709 self.tenant.id) \
710 .AndReturn(self.networks.list())
711 self.mox.ReplayAll()
712
713 template = self.stack_templates.first()
714 url = reverse('horizon:project:stacks:launch')
715 form_data = {'template_source': 'raw',
716 'template_data': template.data,
717 'password': 'password',
718 'parameters': template.validate,
719 'stack_name': name,
720 "timeout_mins": 60,
721 "disable_rollback": True,
722 "__param_DBUsername": "admin",
723 "__param_LinuxDistribution": "F17",
724 "__param_InstanceType": "m1.small",
725 "__param_KeyName": "test",
726 "__param_DBPassword": "admin",
727 "__param_DBRootPassword": "admin",
728 "__param_DBName": "wordpress",
729 "__param_Network": self.networks.list()[0]['id'],
730 'method': forms.CreateStackForm.__name__}
731
732 res = self.client.post(url, form_data)
733 error = ('Name must start with a letter and may only contain letters, '
734 'numbers, underscores, periods and hyphens.')
735
736 self.assertFormErrors(res, 1)
737 self.assertFormError(res, "form", 'stack_name', error)
738
739 def _test_stack_action(self, action):
740 stack = self.stacks.first()
741 filters = {}
742 api.heat.stacks_list(IsA(http.HttpRequest),
743 marker=None,
744 paginate=True,
745 sort_dir='desc',
746 filters=filters) \
747 .AndReturn([self.stacks.list(), True, True])
748
749 getattr(api.heat, 'action_%s' % action)(IsA(http.HttpRequest),
750 stack.id).AndReturn(stack)
751
752 self.mox.ReplayAll()
753
754 form_data = {"action": "stacks__%s__%s" % (action, stack.id)}
755 res = self.client.post(INDEX_URL, form_data)
756
757 self.assertNoFormErrors(res)
758 self.assertRedirectsNoFollow(res, INDEX_URL)
759
760 @test.create_stubs({api.heat: ('stacks_list', 'action_check',)})
761 def test_check_stack(self):
762 self._test_stack_action('check')
763
764 @test.create_stubs({api.heat: ('stacks_list', 'action_suspend',)})
765 def test_suspend_stack(self):
766 self._test_stack_action('suspend')
767
768 @test.create_stubs({api.heat: ('stacks_list', 'action_resume',)})
769 def test_resume_stack(self):
770 self._test_stack_action('resume')
771
772 @test.create_stubs({api.heat: ('stack_preview', 'template_validate')})
773 def test_preview_stack(self):
774 template = self.stack_templates.first()
775 stack = self.stacks.first()
776
777 api.heat.template_validate(IsA(http.HttpRequest),
778 files={},
779 template=hc_format.parse(template.data)) \
780 .AndReturn(json.loads(template.validate))
781
782 api.heat.stack_preview(IsA(http.HttpRequest),
783 stack_name=stack.stack_name,
784 timeout_mins=60,
785 disable_rollback=True,
786 template=None,
787 parameters=IsA(dict),
788 files=None).AndReturn(stack)
789
790 self.mox.ReplayAll()
791
792 url = reverse('horizon:project:stacks:preview_template')
793 res = self.client.get(url)
794 self.assertTemplateUsed(res, 'project/stacks/preview_template.html')
795
796 form_data = {'template_source': 'raw',
797 'template_data': template.data,
798 'method': forms.PreviewTemplateForm.__name__}
799 res = self.client.post(url, form_data)
800 self.assertTemplateUsed(res, 'project/stacks/preview.html')
801
802 url = reverse('horizon:project:stacks:preview')
803 form_data = {'template_source': 'raw',
804 'template_data': template.data,
805 'parameters': template.validate,
806 'stack_name': stack.stack_name,
807 "timeout_mins": 60,
808 "disable_rollback": True,
809 "__param_DBUsername": "admin",
810 "__param_LinuxDistribution": "F17",
811 "__param_InstanceType": "m1.small",
812 "__param_KeyName": "test",
813 "__param_DBPassword": "admin",
814 "__param_DBRootPassword": "admin",
815 "__param_DBName": "wordpress",
816 'method': forms.PreviewStackForm.__name__}
817 res = self.client.post(url, form_data)
818 self.assertTemplateUsed(res, 'project/stacks/preview_details.html')
819 self.assertEqual(res.context['stack_preview']['stack_name'],
820 stack.stack_name)
821
822 @test.create_stubs({api.heat: ('stack_get', 'template_get',
823 'resources_list')})
824 def test_detail_stack_topology(self):
825 stack = self.stacks.first()
826 template = self.stack_templates.first()
827 api.heat.stack_get(IsA(http.HttpRequest), stack.id) \
828 .MultipleTimes().AndReturn(stack)
829 api.heat.template_get(IsA(http.HttpRequest), stack.id) \
830 .AndReturn(json.loads(template.validate))
831 api.heat.resources_list(IsA(http.HttpRequest), stack.stack_name) \
832 .AndReturn([])
833 self.mox.ReplayAll()
834
835 url = '?'.join([reverse(DETAIL_URL, args=[stack.id]),
836 '='.join(['tab', 'stack_details__stack_topology'])])
837 res = self.client.get(url)
838 tab = res.context['tab_group'].get_tab('topology')
839 d3_data = tab.data['d3_data']
840 self.assertEqual(tab.template_name,
841 'project/stacks/_detail_topology.html')
842 # status is CREATE_COMPLETE, so we expect the topology to display it
843 self.assertIn('info_box', d3_data)
844 self.assertIn('stack-green.svg', d3_data)
845 self.assertIn('Create Complete', d3_data)
846
847 @test.create_stubs({api.heat: ('stack_get', 'template_get'),
848 project_api: ('d3_data',)})
849 def test_detail_stack_overview(self):
850 stack = self.stacks.first()
851 template = self.stack_templates.first()
852 api.heat.stack_get(IsA(http.HttpRequest), stack.id) \
853 .MultipleTimes().AndReturn(stack)
854 api.heat.template_get(IsA(http.HttpRequest), stack.id) \
855 .AndReturn(json.loads(template.validate))
856 project_api.d3_data(IsA(http.HttpRequest), stack_id=stack.id) \
857 .AndReturn(json.dumps({"nodes": [], "stack": {}}))
858 self.mox.ReplayAll()
859
860 url = '?'.join([reverse(DETAIL_URL, args=[stack.id]),
861 '='.join(['tab', 'stack_details__stack_overview'])])
862 res = self.client.get(url)
863 tab = res.context['tab_group'].get_tab('overview')
864 overview_data = tab.data['stack']
865 self.assertEqual(tab.template_name,
866 'project/stacks/_detail_overview.html')
867 self.assertEqual(stack.stack_name, overview_data.stack_name)
868
869 @test.create_stubs({api.heat: ('stack_get', 'template_get'),
870 project_api: ('d3_data',)})
871 def test_detail_stack_resources(self):
872 stack = self.stacks.first()
873 template = self.stack_templates.first()
874 api.heat.stack_get(IsA(http.HttpRequest), stack.id) \
875 .MultipleTimes().AndReturn(stack)
876 api.heat.template_get(IsA(http.HttpRequest), stack.id) \
877 .AndReturn(json.loads(template.validate))
878 project_api.d3_data(IsA(http.HttpRequest), stack_id=stack.id) \
879 .AndReturn(json.dumps({"nodes": [], "stack": {}}))
880 self.mox.ReplayAll()
881
882 url = '?'.join([reverse(DETAIL_URL, args=[stack.id]),
883 '='.join(['tab', 'stack_details__resource_overview'])])
884 res = self.client.get(url)
885 tab = res.context['tab_group'].get_tab('resources')
886 self.assertEqual(tab.template_name,
887 'project/stacks/_detail_resources.html')
888
889 @test.create_stubs({api.heat: ('stack_get', 'template_get')})
890 def test_detail_stack_template(self):
891 stack = self.stacks.first()
892 template = self.stack_templates.first()
893 api.heat.stack_get(IsA(http.HttpRequest), stack.id) \
894 .AndReturn(stack)
895 api.heat.template_get(IsA(http.HttpRequest), stack.id) \
896 .AndReturn(json.loads(template.validate))
897 self.mox.ReplayAll()
898
899 url = '?'.join([reverse(DETAIL_URL, args=[stack.id]),
900 '='.join(['tab', 'stack_details__stack_template'])])
901 res = self.client.get(url)
902 tab = res.context['tab_group'].get_tab('stack_template')
903 template_data = tab.data['stack_template']
904 self.assertEqual(tab.template_name,
905 'project/stacks/_stack_template.html')
906 self.assertIn(json.loads(template.validate)['Description'],
907 template_data)
908
909 @test.create_stubs({api.heat: ('resource_get', 'resource_metadata_get')})
910 def test_resource_view(self):
911 stack = self.stacks.first()
912 resource = self.heat_resources.first()
913 metadata = {}
914 api.heat.resource_get(
915 IsA(http.HttpRequest), stack.id, resource.resource_name) \
916 .AndReturn(resource)
917 api.heat.resource_metadata_get(
918 IsA(http.HttpRequest), stack.id, resource.resource_name) \
919 .AndReturn(metadata)
920 self.mox.ReplayAll()
921
922 url = reverse('horizon:project:stacks:resource',
923 args=[stack.id, resource.resource_name])
924 res = self.client.get(url)
925 self.assertTemplateUsed(res, 'horizon/common/_detail.html')
926 self.assertTemplateUsed(res, 'project/stacks/_resource_overview.html')
927 self.assertEqual(res.context['resource'].logical_resource_id,
928 resource.logical_resource_id)
929
930
931class TemplateFormTests(test.TestCase):
932
933 class SimpleFile(object):
934 def __init__(self, name, data):
935 self.name = name
936 self.data = data
937
938 def read(self):
939 return self.data
940
941 def test_create_upload_form_attributes(self):
942 attrs = forms.create_upload_form_attributes(
943 'env', 'url', 'Environment')
944 self.assertEqual(attrs['data-envsource-url'], 'Environment')
945
946 def test_clean_file_upload_form_url(self):
947 kwargs = {'next_view': 'Launch Stack'}
948 t = forms.TemplateForm({}, **kwargs)
949 precleaned = {
950 'template_url': 'http://templateurl.com',
951 }
952 t.clean_uploaded_files('template', 'template', precleaned, {})
953
954 self.assertEqual(precleaned['template_url'], 'http://templateurl.com')
955
956 def test_clean_file_upload_form_multiple(self):
957 kwargs = {'next_view': 'Launch Stack'}
958 t = forms.TemplateForm({}, **kwargs)
959 precleaned = {
960 'template_url': 'http://templateurl.com',
961 'template_data': 'http://templateurl.com',
962 }
963 self.assertRaises(
964 exceptions.ValidationError,
965 t.clean_uploaded_files,
966 'template',
967 'template',
968 precleaned,
969 {})
970
971 def test_clean_file_upload_form_invalid_json(self):
972 kwargs = {'next_view': 'Launch Stack'}
973 t = forms.TemplateForm({}, **kwargs)
974 precleaned = {
975 'template_data': 'http://templateurl.com',
976 }
977 json_str = '{notvalidjson::::::json/////json'
978 files = {'template_upload':
979 self.SimpleFile('template_name', json_str)}
980
981 self.assertRaises(
982 exceptions.ValidationError,
983 t.clean_uploaded_files,
984 'template',
985 'template',
986 precleaned,
987 files)
988
989 def test_clean_file_upload_form_valid_data(self):
990 kwargs = {'next_view': 'Launch Stack'}
991 t = forms.TemplateForm({}, **kwargs)
992 precleaned = {
993 'template_data': 'http://templateurl.com',
994 }
995
996 json_str = '{"isvalid":"json"}'
997 files = {'template_upload':
998 self.SimpleFile('template_name', json_str)}
999
1000 t.clean_uploaded_files('template', 'template', precleaned, files)
1001 self.assertEqual(
1002 json_str,
1003 precleaned['template_data'])