"Fossies" - the Fresh Open Source Software Archive 
Member "horizon-20.1.2/openstack_dashboard/dashboards/project/instances/tests.py" (29 Apr 2022, 243255 Bytes) of package /linux/misc/openstack/horizon-20.1.2.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style:
standard) with prefixed line numbers.
Alternatively you can here
view or
download the uninterpreted source code file.
See also the latest
Fossies "Diffs" side-by-side code changes report for "tests.py":
20.1.1_vs_20.1.2.
1 # Copyright 2012 United States Government as represented by the
2 # Administrator of the National Aeronautics and Space Administration.
3 # All Rights Reserved.
4 #
5 # Copyright 2012 Nebula, Inc.
6 #
7 # Licensed under the Apache License, Version 2.0 (the "License"); you may
8 # not use this file except in compliance with the License. You may obtain
9 # a copy of the License at
10 #
11 # http://www.apache.org/licenses/LICENSE-2.0
12 #
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16 # License for the specific language governing permissions and limitations
17 # under the License.
18
19 import collections
20 import json
21 import logging
22 import sys
23 from unittest import mock
24
25 from django.conf import settings
26 from django.forms import widgets
27 from django import http
28 import django.test
29 from django.test.utils import override_settings
30 from django.urls import reverse
31 from django.utils.http import urlencode
32 from novaclient import api_versions
33
34 from horizon import exceptions
35 from horizon import forms
36 from horizon.workflows import views
37 from openstack_dashboard import api
38 from openstack_dashboard.api import cinder
39 from openstack_dashboard.dashboards.project.instances import console
40 from openstack_dashboard.dashboards.project.instances import tables
41 from openstack_dashboard.dashboards.project.instances import tabs
42 from openstack_dashboard.dashboards.project.instances import workflows
43 from openstack_dashboard.test import helpers
44 from openstack_dashboard.usage import quotas
45 from openstack_dashboard.views import get_url_with_pagination
46
47
48 INDEX_TEMPLATE = 'horizon/common/_data_table_view.html'
49 INDEX_URL = reverse('horizon:project:instances:index')
50 SEC_GROUP_ROLE_PREFIX = \
51 workflows.update_instance.INSTANCE_SEC_GROUP_SLUG + "_role_"
52 AVAILABLE = api.cinder.VOLUME_STATE_AVAILABLE
53 VOLUME_SEARCH_OPTS = dict(status=AVAILABLE, bootable=True)
54 VOLUME_BOOTABLE_SEARCH_OPTS = dict(bootable=True)
55 SNAPSHOT_SEARCH_OPTS = dict(status=AVAILABLE)
56
57
58 class InstanceTestBase(helpers.ResetImageAPIVersionMixin,
59 helpers.TestCase):
60 def _assert_mock_image_list_detailed_calls(self):
61 expected_calls = [
62 mock.call(helpers.IsHttpRequest()),
63 mock.call(
64 helpers.IsHttpRequest(),
65 filters={'visibility': 'community'}
66 )
67 ]
68 self.mock_image_list_detailed.assert_has_calls(expected_calls, False)
69
70 def _assert_mock_image_list_detailed_calls_double(self):
71 expected_calls = [
72 mock.call(helpers.IsHttpRequest()),
73 mock.call(
74 helpers.IsHttpRequest(),
75 filters={'visibility': 'community'}
76 ),
77 mock.call(helpers.IsHttpRequest()),
78 mock.call(
79 helpers.IsHttpRequest(),
80 filters={'visibility': 'community'}
81 )
82 ]
83 self.mock_image_list_detailed.assert_has_calls(expected_calls, False)
84
85 def setUp(self):
86 super().setUp()
87 if api.glance.VERSIONS.active < 2:
88 self.versioned_images = self.images
89 self.versioned_snapshots = self.snapshots
90 else:
91 self.versioned_images = self.imagesV2
92 self.versioned_snapshots = self.snapshotsV2
93
94
95 class InstanceTableTestMixin(object):
96
97 def _mock_glance_image_list_detailed(self, image_list):
98 self.mock_image_list_detailed.side_effect = [
99 [image_list, False, False],
100 [[], False, False],
101 ]
102
103 def _check_glance_image_list_detailed(self, count=None):
104 if count is None:
105 count = 2
106 self.mock_image_list_detailed.assert_has_calls([
107 mock.call(helpers.IsHttpRequest(),
108 filters={'is_public': True, 'status': 'active'}),
109 mock.call(helpers.IsHttpRequest(),
110 filters={'property-owner_id': self.tenant.id,
111 'status': 'active'}),
112 ])
113 self.assertEqual(count, self.mock_image_list_detailed.call_count)
114
115 def _mock_neutron_network_and_port_list(self):
116 self.mock_network_list.side_effect = [
117 self.networks.list()[:1],
118 self.networks.list()[1:],
119 self.networks.list()[:1],
120 self.networks.list()[1:],
121 ]
122 self.mock_port_list_with_trunk_types.return_value = self.ports.list()
123
124 def _check_neutron_network_and_port_list(self):
125 self.assertEqual(4, self.mock_network_list.call_count)
126 self.mock_network_list.assert_has_calls([
127 mock.call(helpers.IsHttpRequest(), tenant_id=self.tenant.id,
128 shared=False),
129 mock.call(helpers.IsHttpRequest(), shared=True),
130 mock.call(helpers.IsHttpRequest(), tenant_id=self.tenant.id,
131 shared=False),
132 mock.call(helpers.IsHttpRequest(), shared=True),
133 ])
134 self.assertEqual(len(self.networks.list()),
135 self.mock_port_list_with_trunk_types.call_count)
136 self.mock_port_list_with_trunk_types(
137 [mock.call(helpers.IsHttpRequest(),
138 network_id=net.id,
139 tenant_id=self.tenant.id)
140 for net in self.networks.list()])
141
142 def _mock_nova_lists(self):
143 self.mock_flavor_list.return_value = self.flavors.list()
144 self.mock_keypair_list.return_value = self.keypairs.list()
145 self.mock_security_group_list.return_value = \
146 self.security_groups.list()
147 self.mock_availability_zone_list.return_value = \
148 self.availability_zones.list()
149 self.mock_server_group_list.return_value = self.server_groups.list()
150
151 def _check_nova_lists(self, flavor_count=None):
152 if flavor_count is None:
153 flavor_count = 1
154 self.assert_mock_multiple_calls_with_same_arguments(
155 self.mock_flavor_list, flavor_count,
156 mock.call(helpers.IsHttpRequest()))
157 self.mock_keypair_list.assert_called_once_with(helpers.IsHttpRequest())
158 self.mock_security_group_list.assert_called_once_with(
159 helpers.IsHttpRequest())
160 self.mock_availability_zone_list.assert_called_once_with(
161 helpers.IsHttpRequest())
162 self.mock_server_group_list.assert_called_once_with(
163 helpers.IsHttpRequest())
164
165 def _mock_nova_glance_neutron_lists(self):
166 self._mock_nova_lists()
167 self._mock_glance_image_list_detailed(
168 self.versioned_images.list())
169 self._mock_neutron_network_and_port_list()
170
171 def _check_nova_glance_neutron_lists(self, return_value=True,
172 flavor_count=None,
173 image_count=None):
174 self._check_nova_lists(flavor_count=flavor_count)
175 self._check_glance_image_list_detailed(count=image_count)
176 self._check_neutron_network_and_port_list()
177
178
179 class InstanceTableTests(InstanceTestBase, InstanceTableTestMixin):
180
181 @helpers.create_mocks({
182 api.nova: (
183 'flavor_list',
184 'server_list_paged',
185 'tenant_absolute_limits',
186 'is_feature_available',
187 ),
188 api.glance: ('image_list_detailed',),
189 api.neutron: (
190 'floating_ip_simple_associate_supported',
191 'floating_ip_supported',
192 ),
193 api.network: (
194 'servers_update_addresses',
195 ),
196 api.cinder: ('volume_list',),
197 }, stop_mock=False)
198 # NOTE: _get_index() and _check_get_index() are used as pair
199 # and the test logic will be placed between these calls,
200 # so we cannot stop mocking when exiting this method.
201 def _get_index(self, use_servers_update_address=True):
202 servers = self.servers.list()
203 self.mock_is_feature_available.return_value = True
204 self.mock_flavor_list.return_value = self.flavors.list()
205 self.mock_image_list_detailed.return_value = \
206 (self.images.list(), False, False)
207 self.mock_server_list_paged.return_value = [servers, False, False]
208 self.mock_servers_update_addresses.return_value = None
209 self.mock_tenant_absolute_limits.return_value = \
210 self.limits['absolute']
211 self.mock_floating_ip_supported.return_value = True
212 self.mock_floating_ip_simple_associate_supported.return_value = True
213 return self.client.get(INDEX_URL)
214
215 def _check_get_index(self, use_servers_update_address=True,
216 multiplier=5):
217 expected_feature_count = 2 * multiplier
218 expected_fip_supported_count = 2 * multiplier
219 expected_simple_fip_supported = 1 * multiplier
220
221 self.assert_mock_multiple_calls_with_same_arguments(
222 self.mock_is_feature_available, expected_feature_count,
223 mock.call(helpers.IsHttpRequest(), 'locked_attribute'))
224 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
225 self._assert_mock_image_list_detailed_calls()
226
227 search_opts = {'marker': None, 'paginate': True}
228 self.mock_server_list_paged.assert_called_once_with(
229 helpers.IsHttpRequest(),
230 sort_dir='desc',
231 search_opts=search_opts)
232 if use_servers_update_address:
233 servers = self.servers.list()
234 self.mock_servers_update_addresses.assert_called_once_with(
235 helpers.IsHttpRequest(), servers)
236 else:
237 self.assertEqual(0, self.mock_servers_update_addresses.call_count)
238 self.assert_mock_multiple_calls_with_same_arguments(
239 self.mock_tenant_absolute_limits, 2,
240 mock.call(helpers.IsHttpRequest(), reserved=True))
241 if expected_fip_supported_count is None:
242 expected_fip_supported_count = 8
243 self.assert_mock_multiple_calls_with_same_arguments(
244 self.mock_floating_ip_supported, expected_fip_supported_count,
245 mock.call(helpers.IsHttpRequest()))
246 self.assert_mock_multiple_calls_with_same_arguments(
247 self.mock_floating_ip_simple_associate_supported,
248 expected_simple_fip_supported,
249 mock.call(helpers.IsHttpRequest()))
250
251 def test_index(self):
252
253 res = self._get_index()
254
255 self.assertTemplateUsed(res, INDEX_TEMPLATE)
256 instances = res.context['instances_table'].data
257
258 self.assertCountEqual(instances, self.servers.list())
259 self.assertNotContains(res, "Launch Instance (Quota exceeded)")
260 self._check_get_index()
261
262 @override_settings(OPENSTACK_INSTANCE_RETRIEVE_IP_ADDRESSES=False)
263 def test_index_without_servers_update_addresses(self):
264 res = self._get_index(use_servers_update_address=False)
265
266 self.assertTemplateUsed(res, INDEX_TEMPLATE)
267 instances = res.context['instances_table'].data
268
269 self.assertCountEqual(instances, self.servers.list())
270 self.assertNotContains(res, "Launch Instance (Quota exceeded)")
271 self._check_get_index(use_servers_update_address=False)
272
273 @helpers.create_mocks({
274 api.nova: ('server_list_paged',
275 'tenant_absolute_limits',
276 'flavor_list'),
277 api.glance: ('image_list_detailed',),
278 api.cinder: ('volume_list',),
279 })
280 def test_index_server_list_exception(self):
281 search_opts = {'marker': None, 'paginate': True}
282 flavors = self.flavors.list()
283 images = self.images.list()
284 self.mock_flavor_list.return_value = flavors
285 self.mock_image_list_detailed.return_value = (images, False, False)
286 self.mock_server_list_paged.side_effect = self.exceptions.nova
287 self.mock_tenant_absolute_limits.return_value = self.limits['absolute']
288
289 res = self.client.get(INDEX_URL)
290
291 self.assertTemplateUsed(res, INDEX_TEMPLATE)
292 self.assertEqual(len(res.context['instances_table'].data), 0)
293 self.assertMessageCount(res, error=1)
294 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
295 self._assert_mock_image_list_detailed_calls()
296
297 self.mock_server_list_paged.assert_called_once_with(
298 helpers.IsHttpRequest(),
299 sort_dir='desc',
300 search_opts=search_opts)
301 self.assert_mock_multiple_calls_with_same_arguments(
302 self.mock_tenant_absolute_limits, 2,
303 mock.call(helpers.IsHttpRequest(), reserved=True))
304
305 @helpers.create_mocks({
306 api.nova: ('flavor_list',
307 'server_list_paged',
308 'flavor_get',
309 'tenant_absolute_limits',
310 'is_feature_available',),
311 api.glance: ('image_list_detailed',),
312 api.neutron: ('floating_ip_simple_associate_supported',
313 'floating_ip_supported',),
314 api.network: ('servers_update_addresses',),
315 api.cinder: ('volume_list',),
316 })
317 def test_index_flavor_list_exception(self):
318 servers = self.servers.list()
319 search_opts = {'marker': None, 'paginate': True}
320
321 self.mock_is_feature_available.return_value = True
322 self.mock_server_list_paged.return_value = [servers, False, False]
323 self.mock_servers_update_addresses.return_value = None
324 self.mock_flavor_list.side_effect = self.exceptions.nova
325 self.mock_image_list_detailed.return_value = (self.images.list(),
326 False, False)
327 self.mock_tenant_absolute_limits.return_value = self.limits['absolute']
328 self.mock_floating_ip_supported.return_value = True
329 self.mock_floating_ip_simple_associate_supported.return_value = True
330
331 res = self.client.get(INDEX_URL)
332
333 self.assertTemplateUsed(res, INDEX_TEMPLATE)
334 instances = res.context['instances_table'].data
335
336 self.assertCountEqual(instances, self.servers.list())
337
338 self.assert_mock_multiple_calls_with_same_arguments(
339 self.mock_is_feature_available, 10,
340 mock.call(helpers.IsHttpRequest(), 'locked_attribute'))
341 self.mock_server_list_paged.assert_called_once_with(
342 helpers.IsHttpRequest(),
343 sort_dir='desc',
344 search_opts=search_opts)
345 self.mock_servers_update_addresses.assert_called_once_with(
346 helpers.IsHttpRequest(), servers)
347 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
348 self._assert_mock_image_list_detailed_calls()
349
350 self.assert_mock_multiple_calls_with_same_arguments(
351 self.mock_tenant_absolute_limits, 2,
352 mock.call(helpers.IsHttpRequest(), reserved=True))
353 self.assert_mock_multiple_calls_with_same_arguments(
354 self.mock_floating_ip_supported, 10,
355 mock.call(helpers.IsHttpRequest()))
356 self.assert_mock_multiple_calls_with_same_arguments(
357 self.mock_floating_ip_simple_associate_supported, 5,
358 mock.call(helpers.IsHttpRequest()))
359
360 @helpers.create_mocks({
361 api.nova: ('flavor_list',
362 'server_list_paged',
363 'tenant_absolute_limits',
364 'is_feature_available',),
365 api.glance: ('image_list_detailed',),
366 api.neutron: ('floating_ip_simple_associate_supported',
367 'floating_ip_supported',),
368 api.network: ('servers_update_addresses',),
369 api.cinder: ('volume_list',),
370 })
371 def _test_index_with_instance_booted_from_volume(
372 self, volume_image_metadata, expected_image_name):
373 servers = self.servers.list()
374 volume_server = servers[0]
375 # Override the server is booted from a volume.
376 volume_server.image = ""
377 # NOTE(amotoki): openstack_dashboard.api.nova.server_list should return
378 # a list of api.nova.Server instances, but the current test code
379 # returns a list of novaclient.v2.servers.Server instances.
380 # This leads to a situation that image_name property of api.nova.Server
381 # is not handled in our test case properly.
382 # TODO(amotoki): Refactor test_data/nova_data.py to use api.nova.Server
383 # (horizon API wrapper class).
384 volume_server = api.nova.Server(volume_server, self.request)
385 servers[0] = volume_server
386
387 volumes = self.cinder_volumes.list()
388 # 3rd volume in the list is attached to server with ID 1.
389 volume = volumes[2]
390 volume.volume_image_metadata = volume_image_metadata
391
392 self.mock_is_feature_available.return_value = True
393 self.mock_server_list_paged.return_value = [servers, False, False]
394 self.mock_servers_update_addresses.return_value = None
395 self.mock_flavor_list.return_value = self.flavors.list()
396 self.mock_image_list_detailed.return_value = (self.images.list(),
397 False, False)
398 self.mock_tenant_absolute_limits.return_value = self.limits['absolute']
399 self.mock_floating_ip_supported.return_value = True
400 self.mock_floating_ip_simple_associate_supported.return_value = True
401 self.mock_volume_list.return_value = volumes
402
403 res = self.client.get(INDEX_URL)
404
405 self.assertTemplateUsed(res, INDEX_TEMPLATE)
406 instances = res.context['instances_table'].data
407 self.assertEqual(len(instances), len(servers))
408 if expected_image_name:
409 self.assertContains(res, expected_image_name)
410
411 self.assert_mock_multiple_calls_with_same_arguments(
412 self.mock_is_feature_available, 10,
413 mock.call(helpers.IsHttpRequest(), 'locked_attribute'))
414 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
415 self._assert_mock_image_list_detailed_calls()
416
417 search_opts = {'marker': None, 'paginate': True}
418 self.mock_server_list_paged.assert_called_once_with(
419 helpers.IsHttpRequest(),
420 sort_dir='desc',
421 search_opts=search_opts)
422 self.mock_servers_update_addresses.assert_called_once_with(
423 helpers.IsHttpRequest(), servers)
424 self.assert_mock_multiple_calls_with_same_arguments(
425 self.mock_tenant_absolute_limits, 2,
426 mock.call(helpers.IsHttpRequest(), reserved=True))
427 self.assert_mock_multiple_calls_with_same_arguments(
428 self.mock_floating_ip_supported, 10,
429 mock.call(helpers.IsHttpRequest()))
430 self.assert_mock_multiple_calls_with_same_arguments(
431 self.mock_floating_ip_simple_associate_supported, 5,
432 mock.call(helpers.IsHttpRequest()))
433 self.mock_volume_list.assert_called_once_with(helpers.IsHttpRequest())
434
435 return instances
436
437 def test_index_with_instance_booted_from_volume(self):
438 base_image = self.images.get(name='private_image')
439 image_metadata = {
440 "image_id": base_image.id
441 }
442 servers = self._test_index_with_instance_booted_from_volume(
443 image_metadata, expected_image_name=base_image.name)
444 self.assertEqual(base_image.name, servers[0].image.name)
445
446 def test_index_with_instance_booted_from_volume_no_image_info(self):
447 # Borrowed from bug #1834747
448 image_metadata = {
449 'hw_qemu_guest_agent': 'yes',
450 'hw_vif_multiqueue_enabled': 'true',
451 'os_require_quiesce': 'yes',
452 }
453 servers = self._test_index_with_instance_booted_from_volume(
454 image_metadata, expected_image_name=None)
455 self.assertEqual('', servers[0].image)
456
457 def test_index_with_console_link(self):
458 res = self._get_index()
459
460 instances_table = res.context['instances_table']
461 instances = res.context['instances_table'].data
462 console_link_rendered = False
463 for instance in instances:
464 for action in instances_table.get_row_actions(instance):
465 if isinstance(action, tables.ConsoleLink):
466 console_link_rendered = True
467 break
468 if console_link_rendered:
469 break
470 self.assertTrue(console_link_rendered)
471 self._check_get_index(multiplier=6)
472
473 @django.test.utils.override_settings(CONSOLE_TYPE=None)
474 def test_index_without_console_link(self):
475 res = self._get_index()
476
477 instances_table = res.context['instances_table']
478 instances = res.context['instances_table'].data
479 for instance in instances:
480 for action in instances_table.get_row_actions(instance):
481 self.assertNotIsInstance(action, tables.ConsoleLink)
482 self._check_get_index(multiplier=10)
483
484 @helpers.create_mocks({api.nova: ('server_list_paged',
485 'flavor_list',
486 'server_delete',),
487 api.glance: ('image_list_detailed',),
488 api.network: ('servers_update_addresses',),
489 api.cinder: ('volume_list',)})
490 def test_delete_instance(self):
491 servers = self.servers.list()
492 server = servers[0]
493
494 self.mock_server_list_paged.return_value = [servers, False, False]
495 self.mock_servers_update_addresses.return_value = None
496 self.mock_flavor_list.return_value = self.flavors.list()
497 self.mock_image_list_detailed.return_value = (self.images.list(),
498 False, False)
499 self.mock_server_delete.return_value = None
500 formData = {'action': 'instances__delete__%s' % server.id}
501 res = self.client.post(INDEX_URL, formData)
502
503 self.assertRedirectsNoFollow(res, INDEX_URL)
504
505 search_opts = {'marker': None, 'paginate': True}
506 self.mock_server_list_paged.assert_called_once_with(
507 helpers.IsHttpRequest(),
508 sort_dir='desc',
509 search_opts=search_opts)
510 self.mock_servers_update_addresses.assert_called_once_with(
511 helpers.IsHttpRequest(), servers)
512 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
513 self._assert_mock_image_list_detailed_calls()
514
515 self.mock_server_delete.assert_called_once_with(
516 helpers.IsHttpRequest(), server.id)
517
518 @helpers.create_mocks({api.nova: ('server_list_paged',
519 'flavor_list',
520 'server_delete',),
521 api.glance: ('image_list_detailed',),
522 api.network: ('servers_update_addresses',),
523 api.cinder: ('volume_list',)})
524 def test_delete_instance_error_state(self):
525 servers = self.servers.list()
526 server = servers[0]
527 server.status = 'ERROR'
528
529 self.mock_server_list_paged.return_value = [servers, False, False]
530 self.mock_servers_update_addresses.return_value = None
531 self.mock_flavor_list.return_value = self.flavors.list()
532 self.mock_image_list_detailed.return_value = (self.images.list(),
533 False, False)
534 self.mock_server_delete.return_value = None
535
536 formData = {'action': 'instances__delete__%s' % server.id}
537 res = self.client.post(INDEX_URL, formData)
538
539 self.assertRedirectsNoFollow(res, INDEX_URL)
540
541 search_opts = {'marker': None, 'paginate': True}
542 self.mock_server_list_paged.assert_called_once_with(
543 helpers.IsHttpRequest(),
544 sort_dir='desc',
545 search_opts=search_opts)
546 self.mock_servers_update_addresses.assert_called_once_with(
547 helpers.IsHttpRequest(), servers)
548 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
549 self._assert_mock_image_list_detailed_calls()
550 self.mock_server_delete.assert_called_once_with(
551 helpers.IsHttpRequest(), server.id)
552
553 @helpers.create_mocks({api.nova: ('server_list_paged',
554 'flavor_list',
555 'server_delete',),
556 api.glance: ('image_list_detailed',),
557 api.network: ('servers_update_addresses',),
558 api.cinder: ('volume_list',)})
559 def test_delete_instance_exception(self):
560 servers = self.servers.list()
561 server = servers[0]
562
563 self.mock_server_list_paged.return_value = [servers, False, False]
564 self.mock_servers_update_addresses.return_value = None
565 self.mock_flavor_list.return_value = self.flavors.list()
566 self.mock_image_list_detailed.return_value = (self.images.list(),
567 False, False)
568 self.mock_server_delete.side_effect = self.exceptions.nova
569
570 formData = {'action': 'instances__delete__%s' % server.id}
571 res = self.client.post(INDEX_URL, formData)
572
573 self.assertRedirectsNoFollow(res, INDEX_URL)
574
575 search_opts = {'marker': None, 'paginate': True}
576 self.mock_server_list_paged.assert_called_once_with(
577 helpers.IsHttpRequest(),
578 sort_dir='desc',
579 search_opts=search_opts)
580 self.mock_servers_update_addresses.assert_called_once_with(
581 helpers.IsHttpRequest(), servers)
582 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
583 self._assert_mock_image_list_detailed_calls()
584 self.mock_server_delete.assert_called_once_with(
585 helpers.IsHttpRequest(), server.id)
586
587 @helpers.create_mocks({api.nova: ('server_pause',
588 'server_list_paged',
589 'flavor_list',
590 'is_feature_available',),
591 api.glance: ('image_list_detailed',),
592 api.network: ('servers_update_addresses',),
593 api.cinder: ('volume_list',)})
594 def test_pause_instance(self):
595 servers = self.servers.list()
596 server = servers[0]
597
598 self.mock_flavor_list.return_value = self.flavors.list()
599 self.mock_image_list_detailed.return_value = (self.images.list(),
600 False, False)
601 self.mock_server_list_paged.return_value = [servers, False, False]
602 self.mock_servers_update_addresses.return_value = None
603 self.mock_server_pause.return_value = None
604
605 formData = {'action': 'instances__pause__%s' % server.id}
606 res = self.client.post(INDEX_URL, formData)
607
608 self.assertRedirectsNoFollow(res, INDEX_URL)
609
610 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
611 self._assert_mock_image_list_detailed_calls()
612 search_opts = {'marker': None, 'paginate': True}
613 self.mock_server_list_paged.assert_called_once_with(
614 helpers.IsHttpRequest(),
615 sort_dir='desc',
616 search_opts=search_opts)
617 self.mock_servers_update_addresses.assert_called_once_with(
618 helpers.IsHttpRequest(), servers)
619 self.mock_server_pause.assert_called_once_with(
620 helpers.IsHttpRequest(), server.id)
621
622 @helpers.create_mocks({api.nova: ('server_pause',
623 'server_list_paged',
624 'flavor_list',
625 'is_feature_available',),
626 api.glance: ('image_list_detailed',),
627 api.network: ('servers_update_addresses',),
628 api.cinder: ('volume_list',)})
629 def test_pause_instance_exception(self):
630 servers = self.servers.list()
631 server = servers[0]
632
633 self.mock_flavor_list.return_value = self.flavors.list()
634 self.mock_image_list_detailed.return_value = (self.images.list(),
635 False, False)
636 self.mock_server_list_paged.return_value = [servers, False, False]
637 self.mock_servers_update_addresses.return_value = None
638 self.mock_server_pause.side_effect = self.exceptions.nova
639
640 formData = {'action': 'instances__pause__%s' % server.id}
641 res = self.client.post(INDEX_URL, formData)
642
643 self.assertRedirectsNoFollow(res, INDEX_URL)
644
645 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
646 self._assert_mock_image_list_detailed_calls()
647 search_opts = {'marker': None, 'paginate': True}
648 self.mock_server_list_paged.assert_called_once_with(
649 helpers.IsHttpRequest(),
650 sort_dir='desc',
651 search_opts=search_opts)
652 self.mock_servers_update_addresses.assert_called_once_with(
653 helpers.IsHttpRequest(), servers)
654 self.mock_server_pause.assert_called_once_with(
655 helpers.IsHttpRequest(), server.id)
656
657 @helpers.create_mocks({api.nova: ('server_unpause',
658 'server_list_paged',
659 'flavor_list',
660 'is_feature_available',),
661 api.glance: ('image_list_detailed',),
662 api.network: ('servers_update_addresses',),
663 api.cinder: ('volume_list',)})
664 def test_unpause_instance(self):
665 servers = self.servers.list()
666 server = servers[0]
667 server.status = "PAUSED"
668 self.mock_flavor_list.return_value = self.flavors.list()
669 self.mock_image_list_detailed.return_value = (self.images.list(),
670 False, False)
671 self.mock_server_list_paged.return_value = [servers, False, False]
672 self.mock_servers_update_addresses.return_value = None
673 self.mock_server_unpause.return_value = None
674
675 formData = {'action': 'instances__pause__%s' % server.id}
676 res = self.client.post(INDEX_URL, formData)
677
678 self.assertRedirectsNoFollow(res, INDEX_URL)
679
680 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
681 self._assert_mock_image_list_detailed_calls()
682
683 search_opts = {'marker': None, 'paginate': True}
684 self.mock_server_list_paged.assert_called_once_with(
685 helpers.IsHttpRequest(),
686 sort_dir='desc',
687 search_opts=search_opts)
688 self.mock_servers_update_addresses.assert_called_once_with(
689 helpers.IsHttpRequest(), servers)
690 self.mock_server_unpause.assert_called_once_with(
691 helpers.IsHttpRequest(), server.id)
692
693 @helpers.create_mocks({api.nova: ('server_unpause',
694 'server_list_paged',
695 'flavor_list',
696 'is_feature_available',),
697 api.glance: ('image_list_detailed',),
698 api.network: ('servers_update_addresses',),
699 api.cinder: ('volume_list',)})
700 def test_unpause_instance_exception(self):
701 servers = self.servers.list()
702 server = servers[0]
703 server.status = "PAUSED"
704
705 self.mock_flavor_list.return_value = self.flavors.list()
706 self.mock_image_list_detailed.return_value = (self.images.list(),
707 False, False)
708 self.mock_server_list_paged.return_value = [servers, False, False]
709 self.mock_servers_update_addresses.return_value = None
710 self.mock_server_unpause.side_effect = self.exceptions.nova
711
712 formData = {'action': 'instances__pause__%s' % server.id}
713 res = self.client.post(INDEX_URL, formData)
714
715 self.assertRedirectsNoFollow(res, INDEX_URL)
716
717 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
718 self._assert_mock_image_list_detailed_calls()
719
720 search_opts = {'marker': None, 'paginate': True}
721 self.mock_server_list_paged.assert_called_once_with(
722 helpers.IsHttpRequest(),
723 sort_dir='desc',
724 search_opts=search_opts)
725 self.mock_servers_update_addresses.assert_called_once_with(
726 helpers.IsHttpRequest(), servers)
727 self.mock_server_unpause.assert_called_once_with(
728 helpers.IsHttpRequest(), server.id)
729
730 @helpers.create_mocks({api.nova: ('server_reboot',
731 'server_list_paged',
732 'flavor_list',),
733 api.glance: ('image_list_detailed',),
734 api.network: ('servers_update_addresses',),
735 api.cinder: ('volume_list',)})
736 def test_reboot_instance(self):
737 servers = self.servers.list()
738 server = servers[0]
739
740 self.mock_flavor_list.return_value = self.flavors.list()
741 self.mock_image_list_detailed.return_value = (self.images.list(),
742 False, False)
743 self.mock_server_list_paged.return_value = [servers, False, False]
744 self.mock_servers_update_addresses.return_value = None
745 self.mock_server_reboot.return_value = None
746
747 formData = {'action': 'instances__reboot__%s' % server.id}
748 res = self.client.post(INDEX_URL, formData)
749
750 self.assertRedirectsNoFollow(res, INDEX_URL)
751
752 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
753 self._assert_mock_image_list_detailed_calls()
754
755 search_opts = {'marker': None, 'paginate': True}
756 self.mock_server_list_paged.assert_called_once_with(
757 helpers.IsHttpRequest(),
758 sort_dir='desc',
759 search_opts=search_opts)
760 self.mock_servers_update_addresses.assert_called_once_with(
761 helpers.IsHttpRequest(), servers)
762 self.mock_server_reboot.assert_called_once_with(
763 helpers.IsHttpRequest(), server.id, soft_reboot=False)
764
765 @helpers.create_mocks({api.nova: ('server_reboot',
766 'server_list_paged',
767 'flavor_list',),
768 api.glance: ('image_list_detailed',),
769 api.network: ('servers_update_addresses',),
770 api.cinder: ('volume_list',)})
771 def test_reboot_instance_exception(self):
772 servers = self.servers.list()
773 server = servers[0]
774
775 self.mock_flavor_list.return_value = self.flavors.list()
776 self.mock_image_list_detailed.return_value = (self.images.list(),
777 False, False)
778 self.mock_server_list_paged.return_value = [servers, False, False]
779 self.mock_servers_update_addresses.return_value = None
780 self.mock_server_reboot.side_effect = self.exceptions.nova
781
782 formData = {'action': 'instances__reboot__%s' % server.id}
783 res = self.client.post(INDEX_URL, formData)
784
785 self.assertRedirectsNoFollow(res, INDEX_URL)
786
787 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
788 self._assert_mock_image_list_detailed_calls()
789
790 search_opts = {'marker': None, 'paginate': True}
791 self.mock_server_list_paged.assert_called_once_with(
792 helpers.IsHttpRequest(),
793 sort_dir='desc',
794 search_opts=search_opts)
795 self.mock_servers_update_addresses.assert_called_once_with(
796 helpers.IsHttpRequest(), servers)
797 self.mock_server_reboot.assert_called_once_with(
798 helpers.IsHttpRequest(), server.id, soft_reboot=False)
799
800 @helpers.create_mocks({api.nova: ('server_reboot',
801 'server_list_paged',
802 'flavor_list',),
803 api.glance: ('image_list_detailed',),
804 api.network: ('servers_update_addresses',),
805 api.cinder: ('volume_list',)})
806 def test_soft_reboot_instance(self):
807 servers = self.servers.list()
808 server = servers[0]
809
810 self.mock_flavor_list.return_value = self.flavors.list()
811 self.mock_image_list_detailed.return_value = (self.images.list(),
812 False, False)
813 self.mock_server_list_paged.return_value = [servers, False, False]
814 self.mock_servers_update_addresses.return_value = None
815 self.mock_server_reboot.return_value = None
816
817 formData = {'action': 'instances__soft_reboot__%s' % server.id}
818 res = self.client.post(INDEX_URL, formData)
819
820 self.assertRedirectsNoFollow(res, INDEX_URL)
821
822 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
823 self._assert_mock_image_list_detailed_calls()
824
825 search_opts = {'marker': None, 'paginate': True}
826 self.mock_server_list_paged.assert_called_once_with(
827 helpers.IsHttpRequest(),
828 sort_dir='desc',
829 search_opts=search_opts)
830 self.mock_servers_update_addresses.assert_called_once_with(
831 helpers.IsHttpRequest(), servers)
832 self.mock_server_reboot.assert_called_once_with(
833 helpers.IsHttpRequest(), server.id, soft_reboot=True)
834
835 @helpers.create_mocks({api.nova: ('server_suspend',
836 'server_list_paged',
837 'flavor_list',
838 'is_feature_available',),
839 api.glance: ('image_list_detailed',),
840 api.network: ('servers_update_addresses',),
841 api.cinder: ('volume_list',)})
842 def test_suspend_instance(self):
843 servers = self.servers.list()
844 server = servers[0]
845
846 self.mock_flavor_list.return_value = self.flavors.list()
847 self.mock_image_list_detailed.return_value = (self.images.list(),
848 False, False)
849 self.mock_server_list_paged.return_value = [servers, False, False]
850 self.mock_servers_update_addresses.return_value = None
851 self.mock_server_suspend.return_value = None
852
853 formData = {'action': 'instances__suspend__%s' % server.id}
854 url = get_url_with_pagination(
855 self.request, 'next', 'prev', 'horizon:project:instances:index')
856 res = self.client.post(url, formData)
857
858 self.assertRedirectsNoFollow(res, INDEX_URL)
859
860 self.mock_flavor_list.assert_called_once_with(
861 helpers.IsHttpRequest())
862 self._assert_mock_image_list_detailed_calls()
863
864 search_opts = {'marker': None, 'paginate': True}
865 self.mock_server_list_paged.assert_called_once_with(
866 helpers.IsHttpRequest(),
867 sort_dir='desc',
868 search_opts=search_opts)
869 self.mock_servers_update_addresses.assert_called_once_with(
870 helpers.IsHttpRequest(), servers)
871 self.mock_server_suspend.assert_called_once_with(
872 helpers.IsHttpRequest(), server.id)
873
874 @django.test.utils.override_settings(API_RESULT_PAGE_SIZE=2)
875 @helpers.create_mocks({api.nova: ('server_suspend',
876 'server_list_paged',
877 'flavor_list',
878 'is_feature_available',),
879 api.glance: ('image_list_detailed',),
880 api.network: ('servers_update_addresses',),
881 api.cinder: ('volume_list',)})
882 def test_suspend_instance_if_placed_on_2nd_page(self):
883 page_size = settings.API_RESULT_PAGE_SIZE
884 servers = self.servers.list()[:3]
885
886 self.mock_flavor_list.return_value = self.flavors.list()
887 self.mock_image_list_detailed.return_value = (self.images.list(),
888 False, False)
889 self.mock_server_list_paged.return_value = [
890 servers[page_size:], False, True]
891 self.mock_servers_update_addresses.return_value = None
892 self.mock_server_suspend.return_value = None
893
894 self.request.GET['marker'] = servers[-2].id
895 params = "=".join([tables.InstancesTable._meta.pagination_param,
896 servers[page_size - 1].id])
897 url = "?".join([reverse('horizon:project:instances:index'),
898 params])
899 formData = {'action': 'instances__suspend__%s' % servers[-1].id}
900
901 self.client.post(url, formData)
902
903 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
904 self._assert_mock_image_list_detailed_calls()
905
906 self.mock_server_list_paged.assert_called_once_with(
907 helpers.IsHttpRequest(),
908 sort_dir='desc',
909 search_opts={'marker': servers[page_size - 1].id,
910 'paginate': True})
911 self.mock_servers_update_addresses.assert_called_once_with(
912 helpers.IsHttpRequest(), servers[page_size:])
913 self.mock_server_suspend.assert_called_once_with(
914 helpers.IsHttpRequest(), servers[-1].id)
915
916 @helpers.create_mocks({api.nova: ('server_suspend',
917 'server_list_paged',
918 'flavor_list',
919 'is_feature_available',),
920 api.glance: ('image_list_detailed',),
921 api.network: ('servers_update_addresses',),
922 api.cinder: ('volume_list',)})
923 def test_suspend_instance_exception(self):
924 servers = self.servers.list()
925 server = servers[0]
926
927 self.mock_flavor_list.return_value = self.flavors.list()
928 self.mock_image_list_detailed.return_value = (self.images.list(),
929 False, False)
930 self.mock_server_list_paged.return_value = [servers, False, False]
931 self.mock_servers_update_addresses.return_value = None
932 self.mock_server_suspend.side_effect = self.exceptions.nova
933
934 formData = {'action': 'instances__suspend__%s' % server.id}
935 res = self.client.post(INDEX_URL, formData)
936
937 self.assertRedirectsNoFollow(res, INDEX_URL)
938
939 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
940 self._assert_mock_image_list_detailed_calls()
941
942 search_opts = {'marker': None, 'paginate': True}
943 self.mock_server_list_paged.assert_called_once_with(
944 helpers.IsHttpRequest(),
945 sort_dir='desc',
946 search_opts=search_opts)
947 self.mock_servers_update_addresses.assert_called_once_with(
948 helpers.IsHttpRequest(), servers)
949 self.mock_server_suspend.assert_called_once_with(
950 helpers.IsHttpRequest(), server.id)
951
952 @helpers.create_mocks({api.nova: ('server_resume',
953 'server_list_paged',
954 'flavor_list',
955 'is_feature_available',),
956 api.glance: ('image_list_detailed',),
957 api.network: ('servers_update_addresses',),
958 api.cinder: ('volume_list',)})
959 def test_resume_instance(self):
960 servers = self.servers.list()
961 server = servers[0]
962 server.status = "SUSPENDED"
963
964 self.mock_flavor_list.return_value = self.flavors.list()
965 self.mock_image_list_detailed.return_value = (self.images.list(),
966 False, False)
967 self.mock_server_list_paged.return_value = [servers, False, False]
968 self.mock_servers_update_addresses.return_value = None
969 self.mock_server_resume.return_value = None
970
971 formData = {'action': 'instances__suspend__%s' % server.id}
972 res = self.client.post(INDEX_URL, formData)
973
974 self.assertRedirectsNoFollow(res, INDEX_URL)
975
976 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
977 self._assert_mock_image_list_detailed_calls()
978
979 search_opts = {'marker': None, 'paginate': True}
980 self.mock_server_list_paged.assert_called_once_with(
981 helpers.IsHttpRequest(),
982 sort_dir='desc',
983 search_opts=search_opts)
984 self.mock_servers_update_addresses.assert_called_once_with(
985 helpers.IsHttpRequest(), servers)
986 self.mock_server_resume.assert_called_once_with(
987 helpers.IsHttpRequest(), server.id)
988
989 @helpers.create_mocks({api.nova: ('server_resume',
990 'server_list_paged',
991 'flavor_list',
992 'is_feature_available'),
993 api.glance: ('image_list_detailed',),
994 api.network: ('servers_update_addresses',),
995 api.cinder: ('volume_list',)})
996 def test_resume_instance_exception(self):
997 servers = self.servers.list()
998 server = servers[0]
999 server.status = "SUSPENDED"
1000
1001 self.mock_flavor_list.return_value = self.flavors.list()
1002 self.mock_image_list_detailed.return_value = (self.images.list(),
1003 False, False)
1004 self.mock_server_list_paged.return_value = [servers, False, False]
1005 self.mock_servers_update_addresses.return_value = None
1006 self.mock_server_resume.side_effect = self.exceptions.nova
1007
1008 formData = {'action': 'instances__suspend__%s' % server.id}
1009 res = self.client.post(INDEX_URL, formData)
1010
1011 self.assertRedirectsNoFollow(res, INDEX_URL)
1012
1013 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
1014 self._assert_mock_image_list_detailed_calls()
1015
1016 search_opts = {'marker': None, 'paginate': True}
1017 self.mock_server_list_paged.assert_called_once_with(
1018 helpers.IsHttpRequest(),
1019 sort_dir='desc',
1020 search_opts=search_opts)
1021 self.mock_servers_update_addresses.assert_called_once_with(
1022 helpers.IsHttpRequest(), servers)
1023 self.mock_server_resume.assert_called_once_with(
1024 helpers.IsHttpRequest(), server.id)
1025
1026 @helpers.create_mocks({api.nova: ('server_shelve',
1027 'server_list_paged',
1028 'flavor_list',
1029 'is_feature_available',),
1030 api.glance: ('image_list_detailed',),
1031 api.network: ('servers_update_addresses',),
1032 api.cinder: ('volume_list',)})
1033 def test_shelve_instance(self):
1034 servers = self.servers.list()
1035 server = servers[0]
1036
1037 self.mock_flavor_list.return_value = self.flavors.list()
1038 self.mock_image_list_detailed.return_value = (self.images.list(),
1039 False, False)
1040 self.mock_server_list_paged.return_value = [servers, False, False]
1041 self.mock_servers_update_addresses.return_value = None
1042 self.mock_server_shelve.return_value = None
1043 formData = {'action': 'instances__shelve__%s' % server.id}
1044 res = self.client.post(INDEX_URL, formData)
1045
1046 self.assertRedirectsNoFollow(res, INDEX_URL)
1047
1048 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
1049 self._assert_mock_image_list_detailed_calls()
1050
1051 search_opts = {'marker': None, 'paginate': True}
1052 self.mock_server_list_paged.assert_called_once_with(
1053 helpers.IsHttpRequest(),
1054 sort_dir='desc',
1055 search_opts=search_opts)
1056 self.mock_servers_update_addresses.assert_called_once_with(
1057 helpers.IsHttpRequest(), servers)
1058 self.mock_server_shelve.assert_called_once_with(
1059 helpers.IsHttpRequest(), server.id)
1060
1061 @helpers.create_mocks({api.nova: ('server_shelve',
1062 'server_list_paged',
1063 'flavor_list',
1064 'is_feature_available',),
1065 api.glance: ('image_list_detailed',),
1066 api.network: ('servers_update_addresses',),
1067 api.cinder: ('volume_list',)})
1068 def test_shelve_instance_exception(self):
1069 servers = self.servers.list()
1070 server = servers[0]
1071
1072 self.mock_flavor_list.return_value = self.flavors.list()
1073 self.mock_image_list_detailed.return_value = (self.images.list(),
1074 False, False)
1075 self.mock_server_list_paged.return_value = [servers, False, False]
1076 self.mock_servers_update_addresses.return_value = None
1077 self.mock_server_shelve.side_effect = self.exceptions.nova
1078
1079 formData = {'action': 'instances__shelve__%s' % server.id}
1080 res = self.client.post(INDEX_URL, formData)
1081
1082 self.assertRedirectsNoFollow(res, INDEX_URL)
1083
1084 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
1085 self._assert_mock_image_list_detailed_calls()
1086
1087 search_opts = {'marker': None, 'paginate': True}
1088 self.mock_server_list_paged.assert_called_once_with(
1089 helpers.IsHttpRequest(),
1090 sort_dir='desc',
1091 search_opts=search_opts)
1092 self.mock_servers_update_addresses.assert_called_once_with(
1093 helpers.IsHttpRequest(), servers)
1094 self.mock_server_shelve.assert_called_once_with(
1095 helpers.IsHttpRequest(), server.id)
1096
1097 @helpers.create_mocks({api.nova: ('server_unshelve',
1098 'server_list_paged',
1099 'flavor_list',
1100 'is_feature_available',),
1101 api.glance: ('image_list_detailed',),
1102 api.network: ('servers_update_addresses',),
1103 api.cinder: ('volume_list',)})
1104 def test_unshelve_instance(self):
1105 servers = self.servers.list()
1106 server = servers[0]
1107 server.status = "SHELVED_OFFLOADED"
1108
1109 self.mock_flavor_list.return_value = self.flavors.list()
1110 self.mock_image_list_detailed.return_value = (self.images.list(),
1111 False, False)
1112 self.mock_server_list_paged.return_value = [servers, False, False]
1113 self.mock_servers_update_addresses.return_value = None
1114 self.mock_server_unshelve.return_value = None
1115
1116 formData = {'action': 'instances__shelve__%s' % server.id}
1117 res = self.client.post(INDEX_URL, formData)
1118
1119 self.assertRedirectsNoFollow(res, INDEX_URL)
1120
1121 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
1122 self._assert_mock_image_list_detailed_calls()
1123
1124 search_opts = {'marker': None, 'paginate': True}
1125 self.mock_server_list_paged.assert_called_once_with(
1126 helpers.IsHttpRequest(),
1127 sort_dir='desc',
1128 search_opts=search_opts)
1129 self.mock_servers_update_addresses.assert_called_once_with(
1130 helpers.IsHttpRequest(), servers)
1131 self.mock_server_unshelve.assert_called_once_with(
1132 helpers.IsHttpRequest(), server.id)
1133
1134 @helpers.create_mocks({api.nova: ('server_unshelve',
1135 'server_list_paged',
1136 'flavor_list',
1137 'is_feature_available',),
1138 api.glance: ('image_list_detailed',),
1139 api.network: ('servers_update_addresses',),
1140 api.cinder: ('volume_list',)})
1141 def test_unshelve_instance_exception(self):
1142 servers = self.servers.list()
1143 server = servers[0]
1144 server.status = "SHELVED_OFFLOADED"
1145
1146 self.mock_flavor_list.return_value = self.flavors.list()
1147 self.mock_image_list_detailed.return_value = (self.images.list(),
1148 False, False)
1149 self.mock_server_list_paged.return_value = [servers, False, False]
1150 self.mock_servers_update_addresses.return_value = None
1151 self.mock_server_unshelve.side_effect = self.exceptions.nova
1152
1153 formData = {'action': 'instances__shelve__%s' % server.id}
1154 res = self.client.post(INDEX_URL, formData)
1155
1156 self.assertRedirectsNoFollow(res, INDEX_URL)
1157
1158 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
1159 self._assert_mock_image_list_detailed_calls()
1160
1161 search_opts = {'marker': None, 'paginate': True}
1162 self.mock_server_list_paged.assert_called_once_with(
1163 helpers.IsHttpRequest(),
1164 sort_dir='desc',
1165 search_opts=search_opts)
1166 self.mock_servers_update_addresses.assert_called_once_with(
1167 helpers.IsHttpRequest(), servers)
1168 self.mock_server_unshelve.assert_called_once_with(
1169 helpers.IsHttpRequest(), server.id)
1170
1171 @helpers.create_mocks({api.nova: ('server_lock',
1172 'server_list_paged',
1173 'flavor_list',
1174 'is_feature_available',),
1175 api.glance: ('image_list_detailed',),
1176 api.network: ('servers_update_addresses',),
1177 api.cinder: ('volume_list',)})
1178 def test_lock_instance(self):
1179 servers = self.servers.list()
1180 server = servers[0]
1181
1182 self.mock_is_feature_available.return_value = True
1183 self.mock_flavor_list.return_value = self.flavors.list()
1184 self.mock_image_list_detailed.return_value = (self.images.list(),
1185 False, False)
1186 self.mock_server_list_paged.return_value = [servers, False, False]
1187 self.mock_servers_update_addresses.return_value = None
1188 self.mock_server_lock.return_value = None
1189 formData = {'action': 'instances__lock__%s' % server.id}
1190 res = self.client.post(INDEX_URL, formData)
1191
1192 self.assertRedirectsNoFollow(res, INDEX_URL)
1193
1194 self.mock_is_feature_available.assert_called_once_with(
1195 helpers.IsHttpRequest(), 'locked_attribute')
1196 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
1197 self._assert_mock_image_list_detailed_calls()
1198
1199 search_opts = {'marker': None, 'paginate': True}
1200 self.mock_server_list_paged.assert_called_once_with(
1201 helpers.IsHttpRequest(),
1202 sort_dir='desc',
1203 search_opts=search_opts)
1204 self.mock_servers_update_addresses.assert_called_once_with(
1205 helpers.IsHttpRequest(), servers)
1206 self.mock_server_lock.assert_called_once_with(
1207 helpers.IsHttpRequest(), server.id)
1208
1209 @helpers.create_mocks({api.nova: ('server_lock',
1210 'server_list_paged',
1211 'flavor_list',
1212 'is_feature_available',),
1213 api.glance: ('image_list_detailed',),
1214 api.network: ('servers_update_addresses',),
1215 api.cinder: ('volume_list',)})
1216 def test_lock_instance_exception(self):
1217 servers = self.servers.list()
1218 server = servers[0]
1219
1220 self.mock_is_feature_available.return_value = True
1221 self.mock_flavor_list.return_value = self.flavors.list()
1222 self.mock_image_list_detailed.return_value = (self.images.list(),
1223 False, False)
1224 self.mock_server_list_paged.return_value = [servers, False, False]
1225 self.mock_servers_update_addresses.return_value = None
1226 self.mock_server_lock.side_effect = self.exceptions.nova
1227
1228 formData = {'action': 'instances__lock__%s' % server.id}
1229 res = self.client.post(INDEX_URL, formData)
1230
1231 self.assertRedirectsNoFollow(res, INDEX_URL)
1232
1233 self.mock_is_feature_available.assert_called_once_with(
1234 helpers.IsHttpRequest(), 'locked_attribute')
1235 self.mock_flavor_list.assert_called_once_with(
1236 helpers.IsHttpRequest())
1237 self._assert_mock_image_list_detailed_calls()
1238
1239 search_opts = {'marker': None, 'paginate': True}
1240 self.mock_server_list_paged.assert_called_once_with(
1241 helpers.IsHttpRequest(),
1242 sort_dir='desc',
1243 search_opts=search_opts)
1244 self.mock_servers_update_addresses.assert_called_once_with(
1245 helpers.IsHttpRequest(), servers)
1246 self.mock_server_lock.assert_called_once_with(
1247 helpers.IsHttpRequest(), server.id)
1248
1249 @helpers.create_mocks({api.nova: ('server_unlock',
1250 'server_list_paged',
1251 'flavor_list',
1252 'is_feature_available'),
1253 api.glance: ('image_list_detailed',),
1254 api.network: ('servers_update_addresses',),
1255 api.cinder: ('volume_list',)})
1256 def test_unlock_instance(self):
1257 servers = self.servers.list()
1258 server = servers[0]
1259 self.mock_is_feature_available.return_value = True
1260 self.mock_flavor_list.return_value = self.flavors.list()
1261 self.mock_image_list_detailed.return_value = (self.images.list(),
1262 False, False)
1263 self.mock_server_list_paged.return_value = [servers, False, False]
1264 self.mock_servers_update_addresses.return_value = None
1265 self.mock_server_unlock.return_value = None
1266 formData = {'action': 'instances__unlock__%s' % server.id}
1267 res = self.client.post(INDEX_URL, formData)
1268
1269 self.assertRedirectsNoFollow(res, INDEX_URL)
1270
1271 self.mock_is_feature_available.assert_called_once_with(
1272 helpers.IsHttpRequest(), 'locked_attribute')
1273 self.mock_flavor_list.assert_called_once_with(
1274 helpers.IsHttpRequest())
1275 self._assert_mock_image_list_detailed_calls()
1276
1277 search_opts = {'marker': None, 'paginate': True}
1278 self.mock_server_list_paged.assert_called_once_with(
1279 helpers.IsHttpRequest(),
1280 sort_dir='desc',
1281 search_opts=search_opts)
1282 self.mock_servers_update_addresses.assert_called_once_with(
1283 helpers.IsHttpRequest(), servers)
1284 self.mock_server_unlock.assert_called_once_with(
1285 helpers.IsHttpRequest(), server.id)
1286
1287 @helpers.create_mocks({api.nova: ('server_unlock',
1288 'server_list_paged',
1289 'flavor_list',
1290 'is_feature_available'),
1291 api.glance: ('image_list_detailed',),
1292 api.network: ('servers_update_addresses',),
1293 api.cinder: ('volume_list',)})
1294 def test_unlock_instance_exception(self):
1295 servers = self.servers.list()
1296 server = servers[0]
1297 self.mock_is_feature_available.return_value = True
1298 self.mock_flavor_list.return_value = self.flavors.list()
1299 self.mock_image_list_detailed.return_value = (self.images.list(),
1300 False, False)
1301 self.mock_server_list_paged.return_value = [servers, False, False]
1302 self.mock_servers_update_addresses.return_value = None
1303 self.mock_server_unlock.side_effect = self.exceptions.nova
1304
1305 formData = {'action': 'instances__unlock__%s' % server.id}
1306 res = self.client.post(INDEX_URL, formData)
1307
1308 self.assertRedirectsNoFollow(res, INDEX_URL)
1309
1310 self.mock_is_feature_available.assert_called_once_with(
1311 helpers.IsHttpRequest(), 'locked_attribute')
1312 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
1313 self._assert_mock_image_list_detailed_calls()
1314
1315 search_opts = {'marker': None, 'paginate': True}
1316 self.mock_server_list_paged.assert_called_once_with(
1317 helpers.IsHttpRequest(),
1318 sort_dir='desc',
1319 search_opts=search_opts)
1320 self.mock_servers_update_addresses.assert_called_once_with(
1321 helpers.IsHttpRequest(), servers)
1322 self.mock_server_unlock.assert_called_once_with(
1323 helpers.IsHttpRequest(), server.id)
1324
1325
1326 class InstanceDetailTests(InstanceTestBase):
1327
1328 @helpers.create_mocks({
1329 api.nova: (
1330 "server_get",
1331 "instance_volumes_list",
1332 "flavor_get",
1333 'is_feature_available',
1334 ),
1335 api.neutron: (
1336 "server_security_groups",
1337 "floating_ip_simple_associate_supported",
1338 "floating_ip_supported"
1339 ),
1340 api.network: ('servers_update_addresses',),
1341 })
1342 def _get_instance_details(self, server, qs=None,
1343 flavor_return=None, volumes_return=None,
1344 security_groups_return=None,
1345 flavor_exception=False):
1346
1347 url = reverse('horizon:project:instances:detail', args=[server.id])
1348 if qs:
1349 url += qs
1350
1351 if flavor_return is None:
1352 flavor_return = self.flavors.first()
1353
1354 if volumes_return is None:
1355 volumes_return = []
1356
1357 if security_groups_return is None:
1358 security_groups_return = self.security_groups.list()
1359
1360 self.mock_server_get.return_value = server
1361 self.mock_servers_update_addresses.return_value = None
1362 self.mock_instance_volumes_list.return_value = volumes_return
1363 if flavor_exception:
1364 self.mock_flavor_get.side_effect = self.exceptions.nova
1365 else:
1366 self.mock_flavor_get.return_value = flavor_return
1367 self.mock_server_security_groups.return_value = security_groups_return
1368 self.mock_floating_ip_simple_associate_supported.return_value = True
1369 self.mock_floating_ip_supported.return_value = True
1370 self.mock_is_feature_available.return_value = True
1371
1372 res = self.client.get(url)
1373
1374 self.mock_server_get.assert_called_once_with(
1375 helpers.IsHttpRequest(), server.id)
1376 self.mock_servers_update_addresses.assert_called_once_with(
1377 helpers.IsHttpRequest(), mock.ANY)
1378 self.mock_instance_volumes_list.assert_called_once_with(
1379 helpers.IsHttpRequest(), server.id)
1380 self.mock_flavor_get.assert_called_once_with(
1381 helpers.IsHttpRequest(), server.flavor['id'])
1382 self.mock_server_security_groups.assert_called_once_with(
1383 helpers.IsHttpRequest(), server.id)
1384 self.mock_floating_ip_simple_associate_supported \
1385 .assert_called_once_with(helpers.IsHttpRequest())
1386 self.assert_mock_multiple_calls_with_same_arguments(
1387 self.mock_floating_ip_supported, 2,
1388 mock.call(helpers.IsHttpRequest()))
1389 self.assert_mock_multiple_calls_with_same_arguments(
1390 self.mock_is_feature_available, 2,
1391 mock.call(helpers.IsHttpRequest(), 'locked_attribute'))
1392
1393 return res
1394
1395 @helpers.create_mocks({api.neutron: ['is_extension_supported']})
1396 def test_instance_details_volumes(self):
1397 server = self.servers.first()
1398 volumes = [self.nova_volumes.list()[1]]
1399 security_groups = self.security_groups.list()
1400
1401 self.mock_is_extension_supported.return_value = False
1402
1403 res = self._get_instance_details(
1404 server, volumes_return=volumes,
1405 security_groups_return=security_groups)
1406
1407 self.assertCountEqual(res.context['instance'].volumes, volumes)
1408 self.mock_is_extension_supported.assert_called_once_with(
1409 helpers.IsHttpRequest(), 'mac-learning')
1410
1411 @helpers.create_mocks({api.neutron: ['is_extension_supported']})
1412 def test_instance_details_volume_sorting(self):
1413 server = self.servers.first()
1414 volumes = self.nova_volumes.list()[1:3]
1415 security_groups = self.security_groups.list()
1416
1417 self.mock_is_extension_supported.return_value = False
1418
1419 res = self._get_instance_details(
1420 server, volumes_return=volumes,
1421 security_groups_return=security_groups)
1422
1423 self.assertCountEqual(res.context['instance'].volumes, volumes)
1424 self.assertEqual(res.context['instance'].volumes[0].device,
1425 "/dev/hda")
1426 self.assertEqual(res.context['instance'].volumes[1].device,
1427 "/dev/hdk")
1428 self.mock_is_extension_supported.assert_called_once_with(
1429 helpers.IsHttpRequest(), 'mac-learning')
1430
1431 @helpers.create_mocks({api.neutron: ['is_extension_supported']})
1432 def test_instance_details_metadata(self):
1433 server = self.servers.first()
1434
1435 self.mock_is_extension_supported.return_value = False
1436
1437 tg = tabs.InstanceDetailTabs(self.request, instance=server)
1438 qs = "?%s=%s" % (tg.param_name, tg.get_tab("overview").get_id())
1439 res = self._get_instance_details(server, qs)
1440
1441 self.assertContains(res, '<dd class="word-wrap">keyName</dd>', 1)
1442 self.assertContains(res, "<dt>someMetaLabel</dt>", 1)
1443 self.assertContains(res, "<dd>someMetaData</dd>", 1)
1444 self.assertContains(res, "<dt>some<b>html</b>label</dt>",
1445 1)
1446 self.assertContains(res, "<dd><!--</dd>", 1)
1447 self.assertContains(res, "<dt>empty</dt>", 1)
1448 self.assertContains(res, "<dd><em>N/A</em></dd>", 1)
1449
1450 self.assert_mock_multiple_calls_with_same_arguments(
1451 self.mock_is_extension_supported, 2,
1452 mock.call(helpers.IsHttpRequest(), 'mac-learning'))
1453
1454 @helpers.create_mocks({api.neutron: ['is_extension_supported']})
1455 def test_instance_details_fault(self):
1456 server = self.servers.first()
1457
1458 self.mock_is_extension_supported.return_value = False
1459
1460 server.status = 'ERROR'
1461 server.fault = {"message": "NoValidHost",
1462 "code": 500,
1463 "details": "No valid host was found. \n "
1464 "File \"/mnt/stack/nova/nova/"
1465 "scheduler/filter_scheduler.py\", "
1466 "line 105, in schedule_run_instance\n "
1467 "raise exception.NoValidHost"
1468 "(reason=\"\")\n",
1469 "created": "2013-10-07T00:08:32Z"}
1470
1471 res = self._get_instance_details(server)
1472 self.assertCountEqual(res.context['instance'].fault, server.fault)
1473 self.mock_is_extension_supported.assert_called_once_with(
1474 helpers.IsHttpRequest(), 'mac-learning')
1475
1476 @helpers.create_mocks({console: ['get_console'],
1477 api.neutron: ['is_extension_supported']})
1478 def test_instance_details_console_tab(self):
1479 server = self.servers.first()
1480 CONSOLE_OUTPUT = '/vncserver'
1481 CONSOLE_TITLE = '&title=%s(%s)' % (server.name, server.id)
1482 CONSOLE_URL = CONSOLE_OUTPUT + CONSOLE_TITLE
1483
1484 self.mock_get_console.return_value = ('VNC', CONSOLE_URL)
1485 self.mock_is_extension_supported.return_value = False
1486
1487 tg = tabs.InstanceDetailTabs(self.request, instance=server)
1488 qs = "?%s=%s" % (tg.param_name, tg.get_tab("console").get_id())
1489 res = self._get_instance_details(server, qs)
1490 self.assertIn(tabs.ConsoleTab, res.context_data['tab_group'].tabs)
1491 self.assertTemplateUsed(res,
1492 'project/instances/_detail_console.html')
1493 console_tab_rendered = False
1494 for tab in res.context_data['tab_group'].get_loaded_tabs():
1495 if isinstance(tab, tabs.ConsoleTab):
1496 console_tab_rendered = True
1497 break
1498 self.assertTrue(console_tab_rendered)
1499 self.mock_get_console.assert_called_once_with(
1500 mock.ANY, 'AUTO', server)
1501 self.assert_mock_multiple_calls_with_same_arguments(
1502 self.mock_is_extension_supported, 2,
1503 mock.call(helpers.IsHttpRequest(), 'mac-learning'))
1504
1505 @django.test.utils.override_settings(CONSOLE_TYPE=None)
1506 @helpers.create_mocks({api.neutron: ['is_extension_supported']})
1507 def test_instance_details_console_tab_deactivated(self):
1508 server = self.servers.first()
1509
1510 self.mock_is_extension_supported.return_value = False
1511
1512 tg = tabs.InstanceDetailTabs(self.request, instance=server)
1513 self.assertIsNone(tg.get_tab("console"))
1514 res = self._get_instance_details(server)
1515 self.assertTemplateNotUsed(res,
1516 'project/instances/_detail_console.html')
1517 for tab in res.context_data['tab_group'].get_loaded_tabs():
1518 self.assertNotIsInstance(tab, tabs.ConsoleTab)
1519
1520 self.assert_mock_multiple_calls_with_same_arguments(
1521 self.mock_is_extension_supported, 2,
1522 mock.call(helpers.IsHttpRequest(), 'mac-learning'))
1523
1524 @helpers.create_mocks({api.nova: ('server_get',)})
1525 def test_instance_details_exception(self):
1526 server = self.servers.first()
1527
1528 self.mock_server_get.side_effect = self.exceptions.nova
1529
1530 url = reverse('horizon:project:instances:detail',
1531 args=[server.id])
1532 res = self.client.get(url)
1533
1534 self.assertRedirectsNoFollow(res, INDEX_URL)
1535 self.mock_server_get.assert_called_once_with(helpers.IsHttpRequest(),
1536 server.id)
1537
1538 @helpers.create_mocks({api.nova: ("server_get",)})
1539 def test_instance_details_unauthorized(self):
1540 server = self.servers.first()
1541
1542 url = reverse('horizon:admin:instances:detail',
1543 args=[server.id])
1544
1545 # Avoid the log message in the test
1546 # when unauthorized exception will be logged
1547 logging.disable(logging.ERROR)
1548 res = self.client.get(url)
1549 logging.disable(logging.NOTSET)
1550
1551 self.assertEqual(403, res.status_code)
1552 self.assertEqual(0, self.mock_server_get.call_count)
1553
1554 @helpers.create_mocks({api.neutron: ['is_extension_supported']})
1555 def test_instance_details_flavor_not_found(self):
1556 server = self.servers.first()
1557 self.mock_is_extension_supported.return_value = False
1558 res = self._get_instance_details(server, flavor_exception=True)
1559 self.assertTemplateUsed(res,
1560 'project/instances/_detail_overview.html')
1561 self.assertContains(res, "Not available")
1562 self.mock_is_extension_supported.assert_called_once_with(
1563 helpers.IsHttpRequest(), 'mac-learning')
1564
1565 @helpers.create_mocks({api.nova: ['server_console_output'],
1566 api.neutron: ['is_extension_supported']})
1567 def test_instance_log(self):
1568 server = self.servers.first()
1569 CONSOLE_OUTPUT = 'output'
1570
1571 self.mock_server_console_output.return_value = CONSOLE_OUTPUT
1572 self.mock_is_extension_supported.return_value = False
1573
1574 url = reverse('horizon:project:instances:console',
1575 args=[server.id])
1576 tg = tabs.InstanceDetailTabs(self.request, instance=server)
1577 qs = "?%s=%s" % (tg.param_name, tg.get_tab("log").get_id())
1578 res = self.client.get(url + qs)
1579
1580 self.assertNoMessages()
1581 self.assertIsInstance(res, http.HttpResponse)
1582 self.assertContains(res, CONSOLE_OUTPUT)
1583 self.mock_server_console_output.assert_called_once_with(
1584 helpers.IsHttpRequest(), server.id, tail_length=None)
1585 self.mock_is_extension_supported.assert_called_once_with(
1586 helpers.IsHttpRequest(), 'mac-learning')
1587
1588 @helpers.create_mocks({api.nova: ['server_console_output'],
1589 api.neutron: ['is_extension_supported']})
1590 def test_instance_log_exception(self):
1591 server = self.servers.first()
1592
1593 self.mock_server_console_output.side_effect = self.exceptions.nova
1594 self.mock_is_extension_supported.return_value = False
1595
1596 url = reverse('horizon:project:instances:console',
1597 args=[server.id])
1598 tg = tabs.InstanceDetailTabs(self.request, instance=server)
1599 qs = "?%s=%s" % (tg.param_name, tg.get_tab("log").get_id())
1600 res = self.client.get(url + qs)
1601
1602 self.assertContains(res, "Unable to get log for")
1603 self.mock_server_console_output.assert_called_once_with(
1604 helpers.IsHttpRequest(), server.id, tail_length=None)
1605 self.mock_is_extension_supported.assert_called_once_with(
1606 helpers.IsHttpRequest(), 'mac-learning')
1607
1608 @helpers.create_mocks({api.neutron: ['is_extension_supported']})
1609 def test_instance_log_invalid_input(self):
1610 server = self.servers.first()
1611 self.mock_is_extension_supported.return_value = False
1612
1613 url = reverse('horizon:project:instances:console',
1614 args=[server.id])
1615 tg = tabs.InstanceDetailTabs(self.request, instance=server)
1616 for length in ["-5", "x"]:
1617 qs = "?%s=%s&length=%s" % (tg.param_name,
1618 tg.get_tab("log").get_id(),
1619 length)
1620 res = self.client.get(url + qs)
1621
1622 self.assertContains(res, "Unable to get log for")
1623
1624 self.mock_is_extension_supported.assert_called_once_with(
1625 helpers.IsHttpRequest(), 'mac-learning')
1626
1627 @helpers.create_mocks({api.nova: ['server_get'],
1628 console: ['get_console']})
1629 def test_instance_auto_console(self):
1630 server = self.servers.first()
1631 CONSOLE_OUTPUT = '/vncserver'
1632 CONSOLE_TITLE = '&title=%s(%s)' % (server.name, server.id)
1633 CONSOLE_URL = CONSOLE_OUTPUT + CONSOLE_TITLE
1634
1635 self.mock_server_get.return_value = server
1636 self.mock_get_console.return_value = ('VNC', CONSOLE_URL)
1637
1638 url = reverse('horizon:project:instances:auto_console',
1639 args=[server.id])
1640 res = self.client.get(url)
1641 redirect = CONSOLE_URL
1642 self.assertRedirectsNoFollow(res, redirect)
1643
1644 self.mock_server_get.assert_called_once_with(
1645 helpers.IsHttpRequest(), server.id)
1646 self.mock_get_console.assert_called_once_with(
1647 mock.ANY, 'AUTO', server)
1648
1649 @helpers.create_mocks({api.nova: ['server_get'],
1650 console: ['get_console']})
1651 def test_instance_vnc_error(self):
1652 server = self.servers.first()
1653 self.mock_server_get.return_value = server
1654 self.mock_get_console.side_effect = exceptions.NotAvailable('console')
1655
1656 url = reverse('horizon:project:instances:vnc',
1657 args=[server.id])
1658 res = self.client.get(url)
1659
1660 self.assertRedirectsNoFollow(res, INDEX_URL)
1661
1662 self.mock_server_get.assert_called_once_with(
1663 helpers.IsHttpRequest(), server.id)
1664 self.mock_get_console.assert_called_once_with(
1665 mock.ANY, 'VNC', server)
1666
1667 @helpers.create_mocks({api.nova: ['server_get'],
1668 console: ['get_console']})
1669 def test_instance_spice(self):
1670 server = self.servers.first()
1671 CONSOLE_OUTPUT = '/spiceserver'
1672 CONSOLE_TITLE = '&title=%s(%s)' % (server.name, server.id)
1673 CONSOLE_URL = CONSOLE_OUTPUT + CONSOLE_TITLE
1674
1675 self.mock_server_get.return_value = server
1676 self.mock_get_console.return_value = ('SPICE', CONSOLE_URL)
1677
1678 url = reverse('horizon:project:instances:spice',
1679 args=[server.id])
1680 res = self.client.get(url)
1681 redirect = CONSOLE_URL
1682 self.assertRedirectsNoFollow(res, redirect)
1683
1684 self.mock_server_get.assert_called_once_with(
1685 helpers.IsHttpRequest(), server.id)
1686 self.mock_get_console.assert_called_once_with(
1687 mock.ANY, 'SPICE', server)
1688
1689 @helpers.create_mocks({api.nova: ['server_get'],
1690 console: ['get_console']})
1691 def test_instance_spice_exception(self):
1692 server = self.servers.first()
1693 self.mock_server_get.return_value = server
1694 self.mock_get_console.side_effect = exceptions.NotAvailable('console')
1695
1696 url = reverse('horizon:project:instances:spice',
1697 args=[server.id])
1698 res = self.client.get(url)
1699
1700 self.assertRedirectsNoFollow(res, INDEX_URL)
1701
1702 self.mock_server_get.assert_called_once_with(
1703 helpers.IsHttpRequest(), server.id)
1704 self.mock_get_console.assert_called_once_with(
1705 mock.ANY, 'SPICE', server)
1706
1707 @helpers.create_mocks({api.nova: ['server_get'],
1708 console: ['get_console']})
1709 def test_instance_rdp(self):
1710 server = self.servers.first()
1711 CONSOLE_OUTPUT = '/rdpserver'
1712 CONSOLE_TITLE = '&title=%s(%s)' % (server.name, server.id)
1713 CONSOLE_URL = CONSOLE_OUTPUT + CONSOLE_TITLE
1714
1715 self.mock_server_get.return_value = server
1716 self.mock_get_console.return_value = ('RDP', CONSOLE_URL)
1717
1718 url = reverse('horizon:project:instances:rdp',
1719 args=[server.id])
1720 res = self.client.get(url)
1721 redirect = CONSOLE_URL
1722 self.assertRedirectsNoFollow(res, redirect)
1723
1724 self.mock_server_get.assert_called_once_with(
1725 helpers.IsHttpRequest(), server.id)
1726 self.mock_get_console.assert_called_once_with(
1727 mock.ANY, 'RDP', server)
1728
1729 @helpers.create_mocks({api.nova: ['server_get'],
1730 console: ['get_console']})
1731 def test_instance_rdp_exception(self):
1732 server = self.servers.first()
1733
1734 self.mock_server_get.return_value = server
1735 self.mock_get_console.side_effect = exceptions.NotAvailable('console')
1736
1737 url = reverse('horizon:project:instances:rdp',
1738 args=[server.id])
1739 res = self.client.get(url)
1740
1741 self.assertRedirectsNoFollow(res, INDEX_URL)
1742
1743 self.mock_server_get.assert_called_once_with(
1744 helpers.IsHttpRequest(), server.id)
1745 self.mock_get_console.assert_called_once_with(
1746 mock.ANY, 'RDP', server)
1747
1748
1749 class InstanceTests(InstanceTestBase):
1750
1751 @helpers.create_mocks({api.nova: ('server_get',
1752 'snapshot_create'),
1753 api.glance: ('image_list_detailed',)})
1754 def test_create_instance_snapshot(self):
1755 server = self.servers.first()
1756
1757 self.mock_server_get.return_value = server
1758 self.mock_snapshot_create.return_value = self.snapshots.first()
1759 self.mock_image_list_detailed.return_value = \
1760 (self.images.list(), False, False)
1761
1762 formData = {'instance_id': server.id,
1763 'method': 'CreateSnapshot',
1764 'name': 'snapshot1'}
1765 url = reverse('horizon:project:images:snapshots:create',
1766 args=[server.id])
1767 redir_url = reverse('horizon:project:images:index')
1768 res = self.client.post(url, formData)
1769 self.assertRedirects(res, redir_url)
1770
1771 self.mock_server_get.assert_called_once_with(
1772 helpers.IsHttpRequest(), server.id)
1773 self.mock_snapshot_create.assert_called_once_with(
1774 helpers.IsHttpRequest(), server.id, "snapshot1")
1775 self.mock_image_list_detailed.assert_called_once_with(
1776 helpers.IsHttpRequest(), marker=None, paginate=True,
1777 reversed_order=False, sort_dir='asc', sort_key='name')
1778
1779 @django.test.utils.override_settings(
1780 OPENSTACK_ENABLE_PASSWORD_RETRIEVE=False)
1781 def test_instances_index_retrieve_password_action_disabled(self):
1782 self. _test_instances_index_retrieve_password_action()
1783
1784 @django.test.utils.override_settings(
1785 OPENSTACK_ENABLE_PASSWORD_RETRIEVE=True)
1786 def test_instances_index_retrieve_password_action_enabled(self):
1787 self._test_instances_index_retrieve_password_action()
1788
1789 @helpers.create_mocks({
1790 api.nova: ('flavor_list',
1791 'server_list_paged',
1792 'tenant_absolute_limits',
1793 'is_feature_available',),
1794 api.glance: ('image_list_detailed',),
1795 api.neutron: ('floating_ip_simple_associate_supported',
1796 'floating_ip_supported',),
1797 api.network: ('servers_update_addresses',),
1798 api.cinder: ('volume_list',),
1799 })
1800 def _test_instances_index_retrieve_password_action(self):
1801 servers = self.servers.list()
1802
1803 self.mock_is_feature_available.return_value = True
1804 self.mock_flavor_list.return_value = self.flavors.list()
1805 self.mock_image_list_detailed.return_value = (self.images.list(),
1806 False, False)
1807 self.mock_server_list_paged.return_value = [servers, False, False]
1808 self.mock_servers_update_addresses.return_value = None
1809 self.mock_tenant_absolute_limits.return_value = self.limits['absolute']
1810 self.mock_floating_ip_supported.return_value = True
1811 self.mock_floating_ip_simple_associate_supported.return_value = True
1812
1813 url = reverse('horizon:project:instances:index')
1814 res = self.client.get(url)
1815 for server in servers:
1816 _action_id = ''.join(["instances__row_",
1817 server.id,
1818 "__action_decryptpassword"])
1819 if settings.OPENSTACK_ENABLE_PASSWORD_RETRIEVE and \
1820 server.status == "ACTIVE" and \
1821 server.key_name is not None:
1822 self.assertContains(res, _action_id)
1823 else:
1824 self.assertNotContains(res, _action_id)
1825
1826 self.assert_mock_multiple_calls_with_same_arguments(
1827 self.mock_is_feature_available, 10,
1828 mock.call(helpers.IsHttpRequest(), 'locked_attribute'))
1829 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
1830 self._assert_mock_image_list_detailed_calls()
1831
1832 search_opts = {'marker': None, 'paginate': True}
1833 self.mock_server_list_paged.assert_called_once_with(
1834 helpers.IsHttpRequest(),
1835 sort_dir='desc',
1836 search_opts=search_opts)
1837 self.mock_servers_update_addresses.assert_called_once_with(
1838 helpers.IsHttpRequest(), servers)
1839 self.assert_mock_multiple_calls_with_same_arguments(
1840 self.mock_tenant_absolute_limits, 2,
1841 mock.call(helpers.IsHttpRequest(), reserved=True))
1842 self.assert_mock_multiple_calls_with_same_arguments(
1843 self.mock_floating_ip_supported, 10,
1844 mock.call(helpers.IsHttpRequest()))
1845 self.assert_mock_multiple_calls_with_same_arguments(
1846 self.mock_floating_ip_simple_associate_supported, 5,
1847 mock.call(helpers.IsHttpRequest()))
1848
1849 @helpers.create_mocks({api.nova: ('get_password',)})
1850 def test_decrypt_instance_password(self):
1851 server = self.servers.first()
1852 enc_password = "azerty"
1853 self.mock_get_password.return_value = enc_password
1854 url = reverse('horizon:project:instances:decryptpassword',
1855 args=[server.id,
1856 server.key_name])
1857 res = self.client.get(url)
1858 self.assertTemplateUsed(res, 'project/instances/decryptpassword.html')
1859 self.mock_get_password.assert_called_once_with(
1860 helpers.IsHttpRequest(), server.id)
1861
1862 @helpers.create_mocks({api.nova: ('get_password',)})
1863 def test_decrypt_instance_get_exception(self):
1864 server = self.servers.first()
1865 keypair = self.keypairs.first()
1866 self.mock_get_password.side_effect = self.exceptions.nova
1867 url = reverse('horizon:project:instances:decryptpassword',
1868 args=[server.id,
1869 keypair])
1870 res = self.client.get(url)
1871 self.assertRedirectsNoFollow(res, INDEX_URL)
1872 self.mock_get_password.assert_called_once_with(
1873 helpers.IsHttpRequest(), server.id)
1874
1875 instance_update_get_stubs = {
1876 api.nova: ('server_get', 'is_feature_available'),
1877 api.neutron: ('security_group_list',
1878 'server_security_groups',)}
1879
1880 @helpers.create_mocks(instance_update_get_stubs)
1881 def test_instance_update_get(self):
1882 server = self.servers.first()
1883
1884 self.mock_server_get.return_value = server
1885 self.mock_security_group_list.return_value = []
1886 self.mock_server_security_groups.return_value = []
1887 self.mock_is_feature_available.return_value = False
1888
1889 url = reverse('horizon:project:instances:update', args=[server.id])
1890 res = self.client.get(url)
1891
1892 self.assertTemplateUsed(res, views.WorkflowView.template_name)
1893
1894 self.mock_server_get.assert_called_once_with(
1895 helpers.IsHttpRequest(), server.id)
1896 self.mock_security_group_list(helpers.IsHttpRequest(), tenant_id=None)
1897 self.mock_server_security_groups(helpers.IsHttpRequest(), server.id)
1898 self.mock_is_feature_available.assert_called_once_with(
1899 helpers.IsHttpRequest(), "instance_description"
1900 )
1901
1902 @helpers.create_mocks(instance_update_get_stubs)
1903 def test_instance_update_get_server_get_exception(self):
1904 server = self.servers.first()
1905
1906 self.mock_server_get.side_effect = self.exceptions.nova
1907
1908 url = reverse('horizon:project:instances:update',
1909 args=[server.id])
1910 res = self.client.get(url)
1911
1912 self.assertRedirectsNoFollow(res, INDEX_URL)
1913 self.mock_server_get.assert_called_once_with(
1914 helpers.IsHttpRequest(), server.id)
1915
1916 def _instance_update_post(self, server_id, server_name, secgroups):
1917 default_role_field_name = 'default_' + \
1918 workflows.update_instance.INSTANCE_SEC_GROUP_SLUG + '_role'
1919 formData = {'name': server_name,
1920 default_role_field_name: 'member',
1921 SEC_GROUP_ROLE_PREFIX + 'member': secgroups}
1922 url = reverse('horizon:project:instances:update',
1923 args=[server_id])
1924 return self.client.post(url, formData)
1925
1926 instance_update_post_stubs = {
1927 api.nova: ('server_get', 'server_update', 'is_feature_available'),
1928 api.neutron: ('security_group_list',
1929 'server_security_groups',
1930 'server_update_security_groups')}
1931
1932 @helpers.create_mocks(instance_update_post_stubs)
1933 def test_instance_update_post(self):
1934 server = self.servers.first()
1935 secgroups = self.security_groups.list()[:3]
1936
1937 server_groups = [secgroups[0], secgroups[1]]
1938 wanted_groups = [secgroups[1].id, secgroups[2].id]
1939
1940 self.mock_server_get.return_value = server
1941 self.mock_is_feature_available.return_value = False
1942 self.mock_security_group_list.return_value = secgroups
1943 self.mock_server_security_groups.return_value = server_groups
1944 self.mock_server_update.return_value = server
1945 self.mock_server_update_security_groups.return_value = None
1946
1947 res = self._instance_update_post(server.id, server.name, wanted_groups)
1948 self.assertNoFormErrors(res)
1949 self.assertRedirectsNoFollow(res, INDEX_URL)
1950
1951 self.mock_server_get.assert_called_once_with(
1952 helpers.IsHttpRequest(), server.id)
1953 self.mock_security_group_list.assert_called_once_with(
1954 helpers.IsHttpRequest(), tenant_id=self.tenant.id)
1955 self.mock_server_security_groups.assert_called_once_with(
1956 helpers.IsHttpRequest(), server.id)
1957 self.mock_server_update.assert_called_once_with(
1958 helpers.IsHttpRequest(), server.id, server.name, description=None)
1959 self.mock_server_update_security_groups.assert_called_once_with(
1960 helpers.IsHttpRequest(), server.id, wanted_groups)
1961 self.mock_is_feature_available.assert_called_once_with(
1962 helpers.IsHttpRequest(), "instance_description"
1963 )
1964
1965 @helpers.create_mocks(instance_update_post_stubs)
1966 def test_instance_update_post_with_desc(self):
1967 server = self.servers.first()
1968 secgroups = self.security_groups.list()[:3]
1969
1970 server_groups = [secgroups[0], secgroups[1]]
1971 test_description = 'test description'
1972
1973 self.mock_server_get.return_value = server
1974 self.mock_is_feature_available.return_value = True
1975 self.mock_security_group_list.return_value = secgroups
1976 self.mock_server_security_groups.return_value = server_groups
1977 self.mock_server_update.return_value = server
1978
1979 formData = {'name': server.name,
1980 'description': test_description}
1981 url = reverse('horizon:project:instances:update',
1982 args=[server.id])
1983 res = self.client.post(url, formData)
1984 self.assertNoFormErrors(res)
1985 self.assertRedirectsNoFollow(res, INDEX_URL)
1986
1987 self.mock_server_get.assert_called_once_with(
1988 helpers.IsHttpRequest(), server.id)
1989 self.mock_security_group_list.assert_called_once_with(
1990 helpers.IsHttpRequest(), tenant_id=self.tenant.id)
1991 self.mock_server_security_groups.assert_called_once_with(
1992 helpers.IsHttpRequest(), server.id)
1993 self.mock_server_update.assert_called_once_with(
1994 helpers.IsHttpRequest(), server.id, server.name,
1995 description=test_description)
1996 self.mock_is_feature_available.assert_called_once_with(
1997 helpers.IsHttpRequest(), "instance_description"
1998 )
1999
2000 @helpers.create_mocks(instance_update_post_stubs)
2001 def test_instance_update_post_api_exception(self):
2002 server = self.servers.first()
2003
2004 self.mock_server_get.return_value = server
2005 self.mock_is_feature_available.return_value = False
2006 self.mock_security_group_list.return_value = []
2007 self.mock_server_security_groups.return_value = []
2008 self.mock_server_update.side_effect = self.exceptions.nova
2009 self.mock_server_update_security_groups.return_value = None
2010
2011 res = self._instance_update_post(server.id, server.name, [])
2012 self.assertRedirectsNoFollow(res, INDEX_URL)
2013
2014 self.mock_server_get.assert_called_once_with(
2015 helpers.IsHttpRequest(), server.id)
2016 self.mock_security_group_list.assert_called_once_with(
2017 helpers.IsHttpRequest(), tenant_id=self.tenant.id)
2018 self.mock_server_security_groups.assert_called_once_with(
2019 helpers.IsHttpRequest(), server.id)
2020 self.mock_server_update.assert_called_once_with(
2021 helpers.IsHttpRequest(), server.id, server.name, description=None)
2022 self.mock_server_update_security_groups.assert_called_once_with(
2023 helpers.IsHttpRequest(), server.id, [])
2024 self.mock_is_feature_available.assert_called_once_with(
2025 helpers.IsHttpRequest(), "instance_description"
2026 )
2027
2028 @helpers.create_mocks(instance_update_post_stubs)
2029 def test_instance_update_post_secgroup_api_exception(self):
2030 server = self.servers.first()
2031
2032 self.mock_server_get.return_value = server
2033 self.mock_is_feature_available.return_value = False
2034 self.mock_security_group_list.return_value = []
2035 self.mock_server_security_groups.return_value = []
2036 self.mock_server_update.return_value = server
2037 self.mock_server_update_security_groups.side_effect = \
2038 self.exceptions.nova
2039
2040 res = self._instance_update_post(server.id, server.name, [])
2041 self.assertRedirectsNoFollow(res, INDEX_URL)
2042
2043 self.mock_server_get.assert_called_once_with(
2044 helpers.IsHttpRequest(), server.id)
2045 self.mock_security_group_list.assert_called_once_with(
2046 helpers.IsHttpRequest(), tenant_id=self.tenant.id)
2047 self.mock_server_security_groups.assert_called_once_with(
2048 helpers.IsHttpRequest(), server.id)
2049 self.mock_server_update.assert_called_once_with(
2050 helpers.IsHttpRequest(), server.id, server.name, description=None)
2051 self.mock_server_update_security_groups.assert_called_once_with(
2052 helpers.IsHttpRequest(), server.id, [])
2053 self.mock_is_feature_available.assert_called_once_with(
2054 helpers.IsHttpRequest(), "instance_description"
2055 )
2056
2057
2058 class InstanceLaunchInstanceTests(InstanceTestBase,
2059 InstanceTableTestMixin):
2060
2061 @helpers.create_mocks({api.nova: ('is_feature_available',
2062 'flavor_list',
2063 'keypair_list',
2064 'server_group_list',
2065 'availability_zone_list',),
2066 cinder: ('volume_snapshot_list',
2067 'volume_list',),
2068 api.neutron: ('network_list',
2069 'port_list_with_trunk_types',
2070 'security_group_list',),
2071 api.glance: ('image_list_detailed',),
2072 quotas: ('tenant_quota_usages',)})
2073 def test_launch_instance_get(self,
2074 expect_password_fields=True,
2075 custom_flavor_sort=None,
2076 only_one_network=False,
2077 config_drive_default=False):
2078 image = self.versioned_images.first()
2079
2080 self.mock_volume_list.return_value = []
2081 self.mock_volume_snapshot_list.return_value = []
2082 self._mock_glance_image_list_detailed(self.versioned_images.list())
2083 self.mock_network_list.side_effect = [
2084 self.networks.list()[:1],
2085 [] if only_one_network else self.networks.list()[1:],
2086 self.networks.list()[:1],
2087 self.networks.list()[1:],
2088 ]
2089 self.mock_port_list_with_trunk_types.return_value = self.ports.list()
2090 self.mock_server_group_list.return_value = self.server_groups.list()
2091 self.mock_tenant_quota_usages.return_value = self.quota_usages.first()
2092 self._mock_nova_lists()
2093
2094 url = reverse('horizon:project:instances:launch')
2095 params = urlencode({"source_type": "image_id",
2096 "source_id": image.id})
2097 res = self.client.get("%s?%s" % (url, params))
2098
2099 workflow = res.context['workflow']
2100 self.assertTemplateUsed(res, views.WorkflowView.template_name)
2101 self.assertEqual(res.context['workflow'].name,
2102 workflows.LaunchInstance.name)
2103 step = workflow.get_step("setinstancedetailsaction")
2104 self.assertEqual(step.action.initial['image_id'], image.id)
2105 self.assertQuerysetEqual(
2106 workflow.steps,
2107 ['<SetInstanceDetails: setinstancedetailsaction>',
2108 '<SetAccessControls: setaccesscontrolsaction>',
2109 '<SetNetwork: setnetworkaction>',
2110 '<SetNetworkPorts: setnetworkportsaction>',
2111 '<PostCreationStep: customizeaction>',
2112 '<SetAdvanced: setadvancedaction>'])
2113
2114 if custom_flavor_sort == 'id':
2115 # Reverse sorted by id
2116 sorted_flavors = (
2117 ('eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee', 'm1.metadata'),
2118 ('dddddddd-dddd-dddd-dddd-dddddddddddd', 'm1.secret'),
2119 ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'm1.massive'),
2120 ('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'm1.tiny'),
2121 )
2122 elif custom_flavor_sort == 'name':
2123 sorted_flavors = (
2124 ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'm1.massive'),
2125 ('eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee', 'm1.metadata'),
2126 ('dddddddd-dddd-dddd-dddd-dddddddddddd', 'm1.secret'),
2127 ('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'm1.tiny'),
2128 )
2129 elif custom_flavor_sort == helpers.my_custom_sort:
2130 sorted_flavors = (
2131 ('dddddddd-dddd-dddd-dddd-dddddddddddd', 'm1.secret'),
2132 ('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'm1.tiny'),
2133 ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'm1.massive'),
2134 ('eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee', 'm1.metadata'),
2135 )
2136 else:
2137 # Default - sorted by RAM
2138 sorted_flavors = (
2139 ('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'm1.tiny'),
2140 ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'm1.massive'),
2141 ('dddddddd-dddd-dddd-dddd-dddddddddddd', 'm1.secret'),
2142 ('eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee', 'm1.metadata'),
2143 )
2144
2145 select_options = ''.join([
2146 '<option value="%s">%s</option>' % (f[0], f[1])
2147 for f in sorted_flavors
2148 ])
2149 self.assertContains(res, select_options)
2150
2151 password_field_label = 'Admin Pass'
2152 if expect_password_fields:
2153 self.assertContains(res, password_field_label)
2154 else:
2155 self.assertNotContains(res, password_field_label)
2156
2157 boot_from_image_field_label = 'Boot from image (creates a new volume)'
2158 self.assertContains(res, boot_from_image_field_label)
2159
2160 # NOTE(adriant): Django 1.11 changes the checked syntax to use html5
2161 # "checked" rather than XHTML's "checked='checked'".
2162 checked_box = (
2163 '<input type="checkbox" name="network" '
2164 'value="82288d84-e0a5-42ac-95be-e6af08727e42" '
2165 'id="id_network_0" checked />'
2166 )
2167 if only_one_network:
2168 self.assertContains(res, checked_box, html=True)
2169 else:
2170 self.assertNotContains(res, checked_box, html=True)
2171
2172 self.assertContains(res, 'Disk Partition')
2173 self.assertContains(res, 'Configuration Drive')
2174
2175 step = workflow.get_step("setadvancedaction")
2176 self.assertEqual(step.action.initial['config_drive'],
2177 config_drive_default)
2178
2179 self.mock_volume_list.assert_has_calls([
2180 mock.call(helpers.IsHttpRequest(),
2181 search_opts=VOLUME_SEARCH_OPTS),
2182 mock.call(helpers.IsHttpRequest(),
2183 search_opts=VOLUME_BOOTABLE_SEARCH_OPTS),
2184 ])
2185 self.mock_volume_snapshot_list.assert_called_once_with(
2186 helpers.IsHttpRequest(),
2187 search_opts=SNAPSHOT_SEARCH_OPTS)
2188
2189 self._check_glance_image_list_detailed(count=8)
2190
2191 self.mock_network_list.assert_has_calls([
2192 mock.call(helpers.IsHttpRequest(),
2193 tenant_id=self.tenant.id, shared=False),
2194 mock.call(helpers.IsHttpRequest(), shared=True),
2195 mock.call(helpers.IsHttpRequest(),
2196 tenant_id=self.tenant.id, shared=False),
2197 mock.call(helpers.IsHttpRequest(), shared=True),
2198 ])
2199 self.assertEqual(4, self.mock_network_list.call_count)
2200 self.mock_port_list_with_trunk_types.assert_has_calls(
2201 [mock.call(helpers.IsHttpRequest(),
2202 network_id=net.id, tenant_id=self.tenant.id)
2203 for net in self.networks.list()])
2204 self.assertEqual(len(self.networks.list()),
2205 self.mock_port_list_with_trunk_types.call_count)
2206 self.mock_server_group_list.assert_called_once_with(
2207 helpers.IsHttpRequest())
2208 self.mock_tenant_quota_usages.assert_called_once_with(
2209 helpers.IsHttpRequest(),
2210 targets=('instances', 'cores', 'ram', 'volumes', 'gigabytes'))
2211 self._check_nova_lists(flavor_count=2)
2212
2213 @helpers.update_settings(
2214 OPENSTACK_HYPERVISOR_FEATURES={'can_set_password': False})
2215 def test_launch_instance_get_without_password(self):
2216 self.test_launch_instance_get(expect_password_fields=False)
2217
2218 @helpers.update_settings(
2219 OPENSTACK_HYPERVISOR_FEATURES={'requires_keypair': True})
2220 def test_launch_instance_required_key(self):
2221 flavor = self.flavors.first()
2222 image = self.images.first()
2223 image.min_ram = flavor.ram
2224 image.min_disk = flavor.disk
2225 res = self._launch_form_instance(image, flavor, keypair=None)
2226 msg = "This field is required"
2227 self.assertContains(res, msg)
2228
2229 @django.test.utils.override_settings(
2230 LAUNCH_INSTANCE_DEFAULTS={'config_drive': True})
2231 def test_launch_instance_get_with_config_drive_default(self):
2232 self.test_launch_instance_get(config_drive_default=True)
2233
2234 @django.test.utils.override_settings(
2235 CREATE_INSTANCE_FLAVOR_SORT={
2236 'key': 'id',
2237 'reverse': True,
2238 })
2239 def test_launch_instance_get_custom_flavor_sort_by_id(self):
2240 self.test_launch_instance_get(custom_flavor_sort='id')
2241
2242 @django.test.utils.override_settings(
2243 CREATE_INSTANCE_FLAVOR_SORT={
2244 'key': 'name',
2245 'reverse': False,
2246 })
2247 def test_launch_instance_get_custom_flavor_sort_by_name(self):
2248 self.test_launch_instance_get(custom_flavor_sort='name')
2249
2250 @django.test.utils.override_settings(
2251 CREATE_INSTANCE_FLAVOR_SORT={
2252 'key': helpers.my_custom_sort,
2253 'reverse': False,
2254 })
2255 def test_launch_instance_get_custom_flavor_sort_by_callable(self):
2256 self.test_launch_instance_get(
2257 custom_flavor_sort=helpers.my_custom_sort)
2258
2259 @django.test.utils.override_settings(
2260 CREATE_INSTANCE_FLAVOR_SORT={
2261 'key': 'no_such_column',
2262 'reverse': False,
2263 })
2264 def test_launch_instance_get_custom_flavor_sort_by_missing_column(self):
2265 self.test_launch_instance_get(custom_flavor_sort='no_such_column')
2266
2267 def test_launch_instance_get_with_only_one_network(self):
2268 self.test_launch_instance_get(only_one_network=True)
2269
2270 @helpers.create_mocks({api.nova: ('is_feature_available',
2271 'flavor_list',
2272 'keypair_list',
2273 'server_group_list',
2274 'availability_zone_list',),
2275 cinder: ('volume_snapshot_list',
2276 'volume_list',),
2277 api.neutron: ('network_list',
2278 'port_list_with_trunk_types',
2279 'security_group_list',),
2280 api.glance: ('image_list_detailed',),
2281 quotas: ('tenant_quota_usages',)})
2282 def test_launch_instance_get_images_snapshots(self,
2283 block_device_mapping_v2=True,
2284 only_one_network=False,
2285 disk_config=True,
2286 config_drive=True):
2287 self.mock_volume_list.return_value = []
2288 self.mock_volume_snapshot_list.return_value = []
2289 self._mock_glance_image_list_detailed(self.versioned_images.list() +
2290 self.versioned_snapshots.list())
2291 self.mock_network_list.side_effect = [
2292 self.networks.list()[:1],
2293 [] if only_one_network else self.networks.list()[1:],
2294 self.networks.list()[:1],
2295 self.networks.list()[1:],
2296 ]
2297 self.mock_port_list_with_trunk_types.return_value = self.ports.list()
2298 self.mock_server_group_list.return_value = self.server_groups.list()
2299 self.mock_tenant_quota_usages.return_value = self.limits['absolute']
2300 self._mock_nova_lists()
2301
2302 url = reverse('horizon:project:instances:launch')
2303 res = self.client.get(url)
2304
2305 image_sources = (res.context_data['workflow'].steps[0].
2306 action.fields['image_id'].choices)
2307
2308 snapshot_sources = (res.context_data['workflow'].steps[0].
2309 action.fields['instance_snapshot_id'].choices)
2310
2311 images = [image.id for image in self.versioned_images.list()]
2312 snapshots = [s.id for s in self.versioned_snapshots.list()]
2313
2314 image_sources_ids = []
2315 snapshot_sources_ids = []
2316 for image in image_sources:
2317 self.assertTrue(image[0] in images or image[0] == '')
2318 if image[0] != '':
2319 image_sources_ids.append(image[0])
2320
2321 for image in images:
2322 self.assertIn(image, image_sources_ids)
2323
2324 for snapshot in snapshot_sources:
2325 self.assertTrue(snapshot[0] in snapshots or snapshot[0] == '')
2326 if snapshot[0] != '':
2327 snapshot_sources_ids.append(snapshot[0])
2328
2329 for snapshot in snapshots:
2330 self.assertIn(snapshot, snapshot_sources_ids)
2331
2332 self.mock_volume_list.assert_has_calls([
2333 mock.call(helpers.IsHttpRequest(),
2334 search_opts=VOLUME_SEARCH_OPTS),
2335 mock.call(helpers.IsHttpRequest(),
2336 search_opts=VOLUME_BOOTABLE_SEARCH_OPTS),
2337 ])
2338 self.mock_volume_snapshot_list.assert_called_once_with(
2339 helpers.IsHttpRequest(),
2340 search_opts=SNAPSHOT_SEARCH_OPTS)
2341 self._check_glance_image_list_detailed(count=8)
2342 self.mock_network_list.assert_has_calls([
2343 mock.call(helpers.IsHttpRequest(),
2344 tenant_id=self.tenant.id, shared=False),
2345 mock.call(helpers.IsHttpRequest(), shared=True),
2346 mock.call(helpers.IsHttpRequest(),
2347 tenant_id=self.tenant.id, shared=False),
2348 mock.call(helpers.IsHttpRequest(), shared=True),
2349 ])
2350 self.assertEqual(4, self.mock_network_list.call_count)
2351 self.mock_port_list_with_trunk_types.assert_has_calls(
2352 [mock.call(helpers.IsHttpRequest(),
2353 network_id=net.id, tenant_id=self.tenant.id)
2354 for net in self.networks.list()])
2355 self.mock_server_group_list.assert_called_once_with(
2356 helpers.IsHttpRequest())
2357 self.mock_tenant_quota_usages.assert_called_once_with(
2358 helpers.IsHttpRequest(),
2359 targets=('instances', 'cores', 'ram', 'volumes', 'gigabytes'))
2360 self._check_nova_lists(flavor_count=2)
2361
2362 @helpers.create_mocks({api.nova: ('is_feature_available',
2363 'flavor_list',
2364 'keypair_list',
2365 'server_group_list',
2366 'availability_zone_list',),
2367 cinder: ('volume_snapshot_list',
2368 'volume_list',),
2369 api.neutron: ('network_list',
2370 'port_list_with_trunk_types',
2371 'security_group_list',),
2372 api.glance: ('image_list_detailed',),
2373 quotas: ('tenant_quota_usages',)})
2374 def test_launch_instance_get_bootable_volumes(self,
2375 block_device_mapping_v2=True,
2376 only_one_network=False,
2377 disk_config=True,
2378 config_drive=True):
2379 volumes = [v for v in self.cinder_volumes.list()
2380 if (v.status == AVAILABLE and v.bootable == 'true')]
2381 self.mock_volume_list.return_value = volumes
2382 self.mock_volume_snapshot_list.return_value = []
2383 self._mock_glance_image_list_detailed(self.versioned_images.list())
2384 self.mock_network_list.side_effect = [
2385 self.networks.list()[:1],
2386 [] if only_one_network else self.networks.list()[1:],
2387 self.networks.list()[:1],
2388 self.networks.list()[1:],
2389 ]
2390 self.mock_port_list_with_trunk_types.return_value = self.ports.list()
2391 self.mock_server_group_list.return_value = self.server_groups.list()
2392 self.mock_tenant_quota_usages.return_value = self.quota_usages.first()
2393 self._mock_nova_lists()
2394
2395 url = reverse('horizon:project:instances:launch')
2396 res = self.client.get(url)
2397
2398 bootable_volumes = [v.id for v in self.cinder_volumes.list()
2399 if (v.bootable == 'true' and
2400 v.status == 'available')]
2401
2402 volume_sources = (res.context_data['workflow'].steps[0].
2403 action.fields['volume_id'].choices)
2404
2405 volume_sources_ids = []
2406 for volume in volume_sources:
2407 self.assertTrue(volume[0].split(":vol")[0] in bootable_volumes or
2408 volume[0] == '')
2409 if volume[0] != '':
2410 volume_sources_ids.append(volume[0].split(":vol")[0])
2411
2412 for volume in bootable_volumes:
2413 self.assertIn(volume, volume_sources_ids)
2414
2415 self.mock_volume_list.assert_has_calls([
2416 mock.call(helpers.IsHttpRequest(),
2417 search_opts=VOLUME_SEARCH_OPTS),
2418 mock.call(helpers.IsHttpRequest(),
2419 search_opts=VOLUME_BOOTABLE_SEARCH_OPTS),
2420 ])
2421 self.mock_volume_snapshot_list.assert_called_once_with(
2422 helpers.IsHttpRequest(),
2423 search_opts=SNAPSHOT_SEARCH_OPTS)
2424 self._check_glance_image_list_detailed(count=8)
2425 self.mock_network_list.assert_has_calls([
2426 mock.call(helpers.IsHttpRequest(),
2427 tenant_id=self.tenant.id, shared=False),
2428 mock.call(helpers.IsHttpRequest(), shared=True),
2429 mock.call(helpers.IsHttpRequest(),
2430 tenant_id=self.tenant.id, shared=False),
2431 mock.call(helpers.IsHttpRequest(), shared=True),
2432 ])
2433 self.assertEqual(4, self.mock_network_list.call_count)
2434 self.mock_port_list_with_trunk_types.assert_has_calls(
2435 [mock.call(helpers.IsHttpRequest(),
2436 network_id=net.id, tenant_id=self.tenant.id)
2437 for net in self.networks.list()])
2438 self.mock_server_group_list.assert_called_once_with(
2439 helpers.IsHttpRequest())
2440 self.mock_tenant_quota_usages.assert_called_once_with(
2441 helpers.IsHttpRequest(),
2442 targets=('instances', 'cores', 'ram', 'volumes', 'gigabytes'))
2443 self._check_nova_lists(flavor_count=2)
2444
2445 @helpers.create_mocks({api.glance: ('image_list_detailed',),
2446 api.neutron: ('network_list',
2447 'port_create',
2448 'port_list_with_trunk_types',
2449 'security_group_list',),
2450 api.nova: ('is_feature_available',
2451 'flavor_list',
2452 'keypair_list',
2453 'availability_zone_list',
2454 'server_group_list',
2455 'server_create',),
2456 cinder: ('volume_list',
2457 'volume_snapshot_list',),
2458 quotas: ('tenant_quota_usages',)})
2459 def test_launch_instance_post(self):
2460 flavor = self.flavors.first()
2461 image = self.versioned_images.first()
2462 keypair = self.keypairs.first()
2463 server = self.servers.first()
2464 sec_group = self.security_groups.first()
2465 avail_zone = self.availability_zones.first()
2466 customization_script = 'user data'
2467 nics = [{"net-id": self.networks.first().id, "v4-fixed-ip": ''}]
2468 quota_usages = self.quota_usages.first()
2469 scheduler_hints = {"group": self.server_groups.first().id}
2470
2471 self._mock_nova_glance_neutron_lists()
2472
2473 self.mock_server_group_list.return_value = self.server_groups.list()
2474 self.mock_volume_list.return_value = []
2475 self.mock_volume_snapshot_list.return_value = []
2476 self.mock_server_create.return_value = None
2477 self.mock_tenant_quota_usages.return_value = quota_usages
2478 self.mock_flavor_list.return_value = self.flavors.list()
2479
2480 form_data = {'flavor': flavor.id,
2481 'source_type': 'image_id',
2482 'image_id': image.id,
2483 'keypair': keypair.name,
2484 'name': server.name,
2485 'script_source': 'raw',
2486 'script_data': customization_script,
2487 'project_id': self.tenants.first().id,
2488 'user_id': self.user.id,
2489 'groups': str(sec_group.id),
2490 'availability_zone': avail_zone.zoneName,
2491 'volume_type': '',
2492 'network': self.networks.first().id,
2493 'count': 1,
2494 'server_group': self.server_groups.first().id,
2495 'disk_config': 'AUTO',
2496 'config_drive': True,
2497 }
2498 url = reverse('horizon:project:instances:launch')
2499 res = self.client.post(url, form_data)
2500
2501 self.assertNoFormErrors(res)
2502 self.assertRedirectsNoFollow(res, INDEX_URL)
2503
2504 self._check_nova_glance_neutron_lists(flavor_count=2, image_count=8)
2505 self.mock_server_group_list.assert_called_once_with(
2506 helpers.IsHttpRequest())
2507 self.mock_volume_list.assert_has_calls([
2508 mock.call(helpers.IsHttpRequest(),
2509 search_opts=VOLUME_SEARCH_OPTS),
2510 mock.call(helpers.IsHttpRequest(),
2511 search_opts=VOLUME_BOOTABLE_SEARCH_OPTS),
2512 ])
2513 self.mock_volume_snapshot_list.assert_called_once_with(
2514 helpers.IsHttpRequest(),
2515 search_opts=SNAPSHOT_SEARCH_OPTS)
2516 self.mock_server_create.assert_called_once_with(
2517 helpers.IsHttpRequest(),
2518 server.name,
2519 image.id,
2520 flavor.id,
2521 keypair.name,
2522 customization_script,
2523 [str(sec_group.id)],
2524 block_device_mapping=None,
2525 block_device_mapping_v2=None,
2526 nics=nics,
2527 availability_zone=avail_zone.zoneName,
2528 instance_count=helpers.IsA(int),
2529 admin_pass='',
2530 disk_config='AUTO',
2531 config_drive=True,
2532 scheduler_hints=scheduler_hints)
2533 self.mock_tenant_quota_usages.assert_called_once_with(
2534 helpers.IsHttpRequest(),
2535 targets=('instances', 'cores', 'ram', 'volumes', ))
2536 self.assert_mock_multiple_calls_with_same_arguments(
2537 self.mock_flavor_list, 2,
2538 mock.call(helpers.IsHttpRequest()))
2539
2540 @helpers.create_mocks({api.glance: ('image_list_detailed',),
2541 api.neutron: ('network_list',
2542 'port_create',
2543 'port_list_with_trunk_types',
2544 'security_group_list',),
2545 api.nova: ('is_feature_available',
2546 'flavor_list',
2547 'keypair_list',
2548 'availability_zone_list',
2549 'server_group_list',
2550 'server_create',),
2551 cinder: ('volume_list',
2552 'volume_snapshot_list',),
2553 quotas: ('tenant_quota_usages',)})
2554 def test_launch_instance_post_boot_from_volume(self):
2555 flavor = self.flavors.first()
2556 keypair = self.keypairs.first()
2557 server = self.servers.first()
2558 volume = self.cinder_volumes.first()
2559 sec_group = self.security_groups.first()
2560 avail_zone = self.availability_zones.first()
2561 customization_script = 'user data'
2562 device_name = 'vda'
2563 volume_choice = "%s:vol" % volume.id
2564
2565 volume_source_id = volume.id.split(':')[0]
2566 block_device_mapping = None
2567 block_device_mapping_2 = [
2568 {'device_name': 'vda',
2569 'source_type': 'volume',
2570 'destination_type': 'volume',
2571 'delete_on_termination': False,
2572 'uuid': volume_source_id,
2573 'boot_index': '0',
2574 'volume_size': 1
2575 }
2576 ]
2577
2578 nics = [{"net-id": self.networks.first().id, "v4-fixed-ip": ''}]
2579 quota_usages = self.quota_usages.first()
2580
2581 self._mock_nova_glance_neutron_lists()
2582
2583 volumes = [v for v in self.cinder_volumes.list()
2584 if (v.status == AVAILABLE and v.bootable == 'true')]
2585 self.mock_volume_list.return_value = volumes
2586 self.mock_volume_snapshot_list.return_value = []
2587 self.mock_server_create.return_value = None
2588 self.mock_tenant_quota_usages.return_value = quota_usages
2589
2590 form_data = {'flavor': flavor.id,
2591 'source_type': 'volume_id',
2592 'source_id': volume_choice,
2593 'keypair': keypair.name,
2594 'name': server.name,
2595 'script_source': 'raw',
2596 'script_data': customization_script,
2597 'project_id': self.tenants.first().id,
2598 'user_id': self.user.id,
2599 'groups': str(sec_group.id),
2600 'availability_zone': avail_zone.zoneName,
2601 'volume_size': '1',
2602 'volume_id': volume_choice,
2603 'device_name': device_name,
2604 'network': self.networks.first().id,
2605 'count': 1,
2606 'disk_config': 'AUTO',
2607 'config_drive': True}
2608 url = reverse('horizon:project:instances:launch')
2609 res = self.client.post(url, form_data)
2610
2611 self.assertNoFormErrors(res)
2612 self.assertRedirectsNoFollow(res, INDEX_URL)
2613
2614 self._check_nova_glance_neutron_lists(flavor_count=2, image_count=6)
2615 self.mock_volume_list.assert_has_calls([
2616 mock.call(helpers.IsHttpRequest(),
2617 search_opts=VOLUME_SEARCH_OPTS),
2618 mock.call(helpers.IsHttpRequest(),
2619 search_opts=VOLUME_BOOTABLE_SEARCH_OPTS),
2620 ])
2621 self.mock_volume_snapshot_list.assert_called_once_with(
2622 helpers.IsHttpRequest(),
2623 search_opts=SNAPSHOT_SEARCH_OPTS)
2624 self.mock_server_group_list.assert_called_once_with(
2625 helpers.IsHttpRequest())
2626
2627 self.mock_server_create.assert_called_once_with(
2628 helpers.IsHttpRequest(),
2629 server.name,
2630 '',
2631 flavor.id,
2632 keypair.name,
2633 customization_script,
2634 [str(sec_group.id)],
2635 block_device_mapping=block_device_mapping,
2636 block_device_mapping_v2=block_device_mapping_2,
2637 nics=nics,
2638 availability_zone=avail_zone.zoneName,
2639 instance_count=helpers.IsA(int),
2640 admin_pass='',
2641 disk_config='AUTO',
2642 config_drive=True,
2643 scheduler_hints={})
2644 self.mock_tenant_quota_usages.assert_called_once_with(
2645 helpers.IsHttpRequest(),
2646 targets=('instances', 'cores', 'ram', 'volumes', ))
2647 self.assert_mock_multiple_calls_with_same_arguments(
2648 self.mock_flavor_list, 2,
2649 mock.call(helpers.IsHttpRequest()))
2650
2651 @helpers.create_mocks({api.glance: ('image_list_detailed',),
2652 api.neutron: ('network_list',
2653 'port_create',
2654 'port_list_with_trunk_types',
2655 'security_group_list',),
2656 api.nova: ('server_create',
2657 'is_feature_available',
2658 'flavor_list',
2659 'keypair_list',
2660 'availability_zone_list',
2661 'server_group_list',),
2662 cinder: ('volume_list',
2663 'volume_snapshot_list',),
2664 quotas: ('tenant_quota_usages',)})
2665 def test_launch_instance_post_no_images_available_boot_from_volume(self):
2666 flavor = self.flavors.first()
2667 keypair = self.keypairs.first()
2668 server = self.servers.first()
2669 volume = self.cinder_volumes.first()
2670 sec_group = self.security_groups.first()
2671 avail_zone = self.availability_zones.first()
2672 customization_script = 'user data'
2673 device_name = 'vda'
2674 volume_choice = "%s:vol" % volume.id
2675 block_device_mapping = [
2676 {'device_name': device_name,
2677 'source_type': 'volume',
2678 'destination_type': 'volume',
2679 'delete_on_termination': False,
2680 'uuid': volume.id,
2681 'boot_index': '0',
2682 'volume_size': None,
2683 }
2684 ]
2685 nics = [{"net-id": self.networks.first().id, "v4-fixed-ip": ''}]
2686 quota_usages = self.quota_usages.first()
2687
2688 self._mock_nova_glance_neutron_lists()
2689
2690 self.mock_flavor_list.return_value = self.flavors.list()
2691 volumes = [v for v in self.cinder_volumes.list()
2692 if (v.status == AVAILABLE and v.bootable == 'true')]
2693 self.mock_volume_list.return_value = volumes
2694 self.mock_volume_snapshot_list.return_value = []
2695 self.mock_tenant_quota_usages.return_value = quota_usages
2696 self.mock_server_create.return_value = None
2697
2698 form_data = {'flavor': flavor.id,
2699 'source_type': 'volume_id',
2700 # 'image_id': '',
2701 'keypair': keypair.name,
2702 'name': server.name,
2703 'script_source': 'raw',
2704 'script_data': customization_script,
2705 'project_id': self.tenants.first().id,
2706 'user_id': self.user.id,
2707 'groups': str(sec_group.id),
2708 'availability_zone': avail_zone.zoneName,
2709 'network': self.networks.first().id,
2710 'volume_type': 'volume_id',
2711 'volume_id': volume_choice,
2712 'device_name': device_name,
2713 'count': 1,
2714 'disk_config': 'MANUAL',
2715 'config_drive': True}
2716 url = reverse('horizon:project:instances:launch')
2717 res = self.client.post(url, form_data)
2718
2719 self.assertNoFormErrors(res)
2720 self.assertRedirectsNoFollow(res, INDEX_URL)
2721
2722 self._check_nova_glance_neutron_lists(flavor_count=2,
2723 image_count=6)
2724 self.assert_mock_multiple_calls_with_same_arguments(
2725 self.mock_flavor_list, 2,
2726 mock.call(helpers.IsHttpRequest()))
2727 self.mock_server_group_list.assert_called_once_with(
2728 helpers.IsHttpRequest())
2729 self.mock_volume_list.assert_has_calls([
2730 mock.call(helpers.IsHttpRequest(),
2731 search_opts=VOLUME_SEARCH_OPTS),
2732 mock.call(helpers.IsHttpRequest(),
2733 search_opts=VOLUME_BOOTABLE_SEARCH_OPTS),
2734 ])
2735 self.mock_volume_snapshot_list.assert_called_once_with(
2736 helpers.IsHttpRequest(),
2737 search_opts=SNAPSHOT_SEARCH_OPTS)
2738 self.mock_tenant_quota_usages.assert_called_once_with(
2739 helpers.IsHttpRequest(),
2740 targets=('instances', 'cores', 'ram', 'volumes', ))
2741
2742 self.mock_server_create.assert_called_once_with(
2743 helpers.IsHttpRequest(),
2744 server.name,
2745 '',
2746 flavor.id,
2747 keypair.name,
2748 customization_script,
2749 [str(sec_group.id)],
2750 block_device_mapping=None,
2751 block_device_mapping_v2=block_device_mapping,
2752 nics=nics,
2753 availability_zone=avail_zone.zoneName,
2754 instance_count=helpers.IsA(int),
2755 admin_pass='',
2756 disk_config='MANUAL',
2757 config_drive=True,
2758 scheduler_hints={})
2759
2760 @helpers.create_mocks({api.glance: ('image_list_detailed',),
2761 api.neutron: ('network_list',
2762 'port_list_with_trunk_types',
2763 'security_group_list',),
2764 api.nova: ('is_feature_available',
2765 'flavor_list',
2766 'keypair_list',
2767 'server_group_list',
2768 'availability_zone_list'),
2769 cinder: ('volume_list',
2770 'volume_snapshot_list',),
2771 quotas: ('tenant_quota_usages',)})
2772 def test_launch_instance_post_no_images_available(self):
2773 flavor = self.flavors.first()
2774 keypair = self.keypairs.first()
2775 server = self.servers.first()
2776 sec_group = self.security_groups.first()
2777 avail_zone = self.availability_zones.first()
2778 customization_script = 'user data'
2779 quota_usages = self.quota_usages.first()
2780
2781 self.mock_tenant_quota_usages.return_value = self.quota_usages.first()
2782 self._mock_glance_image_list_detailed([])
2783 self._mock_neutron_network_and_port_list()
2784 self._mock_nova_lists()
2785 self.mock_volume_list.return_value = []
2786 self.mock_volume_snapshot_list.return_value = []
2787 self.mock_tenant_quota_usages.return_value = quota_usages
2788
2789 form_data = {'flavor': flavor.id,
2790 'source_type': 'image_id',
2791 'image_id': '',
2792 'keypair': keypair.name,
2793 'name': server.name,
2794 'script_source': 'raw',
2795 'script_data': customization_script,
2796 'project_id': self.tenants.first().id,
2797 'user_id': self.user.id,
2798 'groups': str(sec_group.id),
2799 'availability_zone': avail_zone.zoneName,
2800 'volume_type': '',
2801 'count': 1}
2802 url = reverse('horizon:project:instances:launch')
2803 res = self.client.post(url, form_data)
2804
2805 self.assertFormErrors(res, 1, "You must select an image.")
2806 self.assertTemplateUsed(res, views.WorkflowView.template_name)
2807
2808 self.mock_tenant_quota_usages.assert_has_calls([
2809 mock.call(
2810 helpers.IsHttpRequest(),
2811 targets=('instances', 'cores', 'ram', 'volumes', )),
2812 mock.call(
2813 helpers.IsHttpRequest(),
2814 targets=('instances', 'cores', 'ram', 'volumes', 'gigabytes')),
2815 ])
2816 self.assertEqual(2, self.mock_tenant_quota_usages.call_count)
2817 self._check_glance_image_list_detailed(count=8)
2818 self._check_neutron_network_and_port_list()
2819 self._check_nova_lists(flavor_count=3)
2820 self.mock_volume_list.assert_has_calls([
2821 mock.call(helpers.IsHttpRequest(),
2822 search_opts=VOLUME_SEARCH_OPTS),
2823 mock.call(helpers.IsHttpRequest(),
2824 search_opts=VOLUME_BOOTABLE_SEARCH_OPTS),
2825 ])
2826 self.mock_volume_snapshot_list.assert_called_once_with(
2827 helpers.IsHttpRequest(),
2828 search_opts=SNAPSHOT_SEARCH_OPTS)
2829 self.mock_tenant_quota_usages.assert_has_calls([
2830 mock.call(
2831 helpers.IsHttpRequest(),
2832 targets=('instances', 'cores', 'ram', 'volumes', )),
2833 mock.call(
2834 helpers.IsHttpRequest(),
2835 targets=('instances', 'cores', 'ram', 'volumes', 'gigabytes')),
2836 ])
2837 self.assertEqual(2, self.mock_tenant_quota_usages.call_count)
2838
2839 @helpers.create_mocks({
2840 api.glance: ('image_list_detailed',),
2841 api.neutron: ('network_list',
2842 'port_create',
2843 'port_list_with_trunk_types',
2844 'security_group_list',),
2845 api.nova: ('is_feature_available',
2846 'flavor_list',
2847 'keypair_list',
2848 'availability_zone_list',
2849 'server_group_list',
2850 'server_create',),
2851 cinder: ('volume_list',
2852 'volume_snapshot_list',),
2853 quotas: ('tenant_quota_usages',)})
2854 def test_launch_instance_post_boot_from_snapshot(self):
2855 flavor = self.flavors.first()
2856 keypair = self.keypairs.first()
2857 server = self.servers.first()
2858 snapshot = self.cinder_volume_snapshots.first()
2859 sec_group = self.security_groups.first()
2860 avail_zone = self.availability_zones.first()
2861 customization_script = 'user data'
2862 device_name = 'vda'
2863 snapshot_choice = "%s:snap" % snapshot.id
2864
2865 snapshot_source_id = snapshot.id.split(':')[0]
2866 block_device_mapping = None
2867 block_device_mapping_2 = [
2868 {'device_name': 'vda',
2869 'source_type': 'snapshot',
2870 'destination_type': 'volume',
2871 'delete_on_termination': 0,
2872 'uuid': snapshot_source_id,
2873 'boot_index': '0',
2874 'volume_size': 1
2875 }
2876 ]
2877
2878 nics = [{"net-id": self.networks.first().id, "v4-fixed-ip": ''}]
2879 quota_usages = self.quota_usages.first()
2880
2881 self._mock_nova_glance_neutron_lists()
2882
2883 volumes = [v for v in self.cinder_volumes.list()
2884 if (getattr(v, 'bootable', 'false') == 'true')]
2885 snapshots = [v for v in self.cinder_volume_snapshots.list()
2886 if (v.status == AVAILABLE)]
2887 self.mock_volume_list.return_value = volumes
2888 self.mock_volume_snapshot_list.return_value = snapshots
2889 self.mock_server_create.return_value = None
2890 self.mock_tenant_quota_usages.return_value = quota_usages
2891
2892 form_data = {'flavor': flavor.id,
2893 'source_type': 'volume_snapshot_id',
2894 'source_id': snapshot_choice,
2895 'keypair': keypair.name,
2896 'name': server.name,
2897 'script_source': 'raw',
2898 'script_data': customization_script,
2899 'project_id': self.tenants.first().id,
2900 'user_id': self.user.id,
2901 'groups': str(sec_group.id),
2902 'availability_zone': avail_zone.zoneName,
2903 'volume_size': '1',
2904 'volume_snapshot_id': snapshot_choice,
2905 'device_name': device_name,
2906 'network': self.networks.first().id,
2907 'count': 1,
2908 'disk_config': 'AUTO',
2909 'config_drive': True}
2910 url = reverse('horizon:project:instances:launch')
2911 res = self.client.post(url, form_data)
2912
2913 self.assertNoFormErrors(res)
2914 self.assertRedirectsNoFollow(res, INDEX_URL)
2915
2916 self.assert_mock_multiple_calls_with_same_arguments(
2917 self.mock_flavor_list, 2,
2918 mock.call(helpers.IsHttpRequest()))
2919 self.mock_volume_list.assert_has_calls([
2920 mock.call(helpers.IsHttpRequest(),
2921 search_opts=VOLUME_SEARCH_OPTS),
2922 mock.call(helpers.IsHttpRequest(),
2923 search_opts=VOLUME_BOOTABLE_SEARCH_OPTS),
2924 ])
2925 self.mock_volume_snapshot_list.assert_called_once_with(
2926 helpers.IsHttpRequest(),
2927 search_opts=SNAPSHOT_SEARCH_OPTS)
2928
2929 self.mock_server_create.assert_called_once_with(
2930 helpers.IsHttpRequest(),
2931 server.name,
2932 '',
2933 flavor.id,
2934 keypair.name,
2935 customization_script,
2936 [str(sec_group.id)],
2937 block_device_mapping=block_device_mapping,
2938 block_device_mapping_v2=block_device_mapping_2,
2939 nics=nics,
2940 availability_zone=avail_zone.zoneName,
2941 instance_count=helpers.IsA(int),
2942 admin_pass='',
2943 disk_config='AUTO',
2944 config_drive=True,
2945 scheduler_hints={})
2946 self.mock_tenant_quota_usages.assert_called_once_with(
2947 helpers.IsHttpRequest(),
2948 targets=('instances', 'cores', 'ram', 'volumes', ))
2949
2950 @helpers.create_mocks({
2951 api.glance: ('image_list_detailed',),
2952 api.neutron: ('network_list',
2953 'port_create',
2954 'port_list_with_trunk_types',
2955 'security_group_list',),
2956 api.nova: ('is_feature_available',
2957 'flavor_list',
2958 'keypair_list',
2959 'availability_zone_list',
2960 'server_group_list',
2961 'server_create'),
2962 cinder: ('volume_list',
2963 'volume_snapshot_list'),
2964 quotas: ('tenant_quota_usages',)})
2965 def test_launch_instance_post_boot_from_snapshot_error(self):
2966 flavor = self.flavors.first()
2967 keypair = self.keypairs.first()
2968 server = self.servers.first()
2969 avail_zone = self.availability_zones.first()
2970 quota_usages = self.quota_usages.first()
2971
2972 self.mock_image_list_detailed.return_value = [[], False, False]
2973 self.mock_tenant_quota_usages.return_value = quota_usages
2974 self._mock_neutron_network_and_port_list()
2975
2976 bad_snapshot_id = 'a-bogus-id'
2977
2978 form_data = {'flavor': flavor.id,
2979 'source_type': 'instance_snapshot_id',
2980 'instance_snapshot_id': bad_snapshot_id,
2981 'keypair': keypair.name,
2982 'name': server.name,
2983 'script_source': 'raw',
2984 'availability_zone': avail_zone.zoneName,
2985 'network': self.networks.first().id,
2986 'volume_id': '',
2987 'volume_snapshot_id': '',
2988 'image_id': '',
2989 'device_name': 'vda',
2990 'count': 1,
2991 'customization_script': ''}
2992
2993 url = reverse('horizon:project:instances:launch')
2994 res = self.client.post(url, form_data)
2995
2996 self.assertFormErrors(res, 3, "You must select a snapshot.")
2997
2998 self.assertEqual(4, self.mock_image_list_detailed.call_count)
2999 self.mock_image_list_detailed.assert_has_calls([
3000 mock.call(helpers.IsHttpRequest(),
3001 filters={'is_public': True,
3002 'status': 'active'}),
3003 mock.call(helpers.IsHttpRequest(),
3004 filters={'property-owner_id': self.tenant.id,
3005 'status': 'active'}),
3006 mock.call(helpers.IsHttpRequest(),
3007 filters={'status': 'active', 'visibility': 'community'}),
3008 mock.call(helpers.IsHttpRequest(),
3009 filters={'status': 'active', 'visibility': 'shared'}),
3010 ])
3011
3012 self.mock_tenant_quota_usages.assert_has_calls([
3013 mock.call(
3014 helpers.IsHttpRequest(),
3015 targets=('instances', 'cores', 'ram', 'volumes', )),
3016 mock.call(
3017 helpers.IsHttpRequest(),
3018 targets=('instances', 'cores', 'ram', 'volumes', 'gigabytes')),
3019 ])
3020 self.assertEqual(2, self.mock_tenant_quota_usages.call_count)
3021
3022 self._check_neutron_network_and_port_list()
3023
3024 @helpers.create_mocks({api.glance: ('image_list_detailed',),
3025 api.neutron: ('network_list',
3026 'port_list_with_trunk_types',
3027 'security_group_list',),
3028 cinder: ('volume_list',
3029 'volume_snapshot_list',),
3030 api.nova: ('is_feature_available',
3031 'flavor_list',
3032 'keypair_list',
3033 'availability_zone_list',
3034 'server_group_list',),
3035 quotas: ('tenant_quota_usages',)})
3036 def test_launch_flavorlist_error(self):
3037 self.mock_volume_list.return_value = []
3038 self.mock_volume_snapshot_list.return_value = []
3039 self._mock_glance_image_list_detailed(self.versioned_images.list())
3040 self._mock_neutron_network_and_port_list()
3041 self.mock_tenant_quota_usages.return_value = self.quota_usages.first()
3042 self.mock_flavor_list.side_effect = self.exceptions.nova
3043 self.mock_keypair_list.return_value = self.keypairs.list()
3044 self.mock_security_group_list.return_value = \
3045 self.security_groups.list()
3046 self.mock_availability_zone_list.return_value = \
3047 self.availability_zones.list()
3048 self.mock_server_group_list.return_value = self.server_groups.list()
3049
3050 url = reverse('horizon:project:instances:launch')
3051 res = self.client.get(url)
3052
3053 self.assertTemplateUsed(res, views.WorkflowView.template_name)
3054
3055 self.mock_volume_list.assert_has_calls([
3056 mock.call(helpers.IsHttpRequest(),
3057 search_opts=VOLUME_SEARCH_OPTS),
3058 mock.call(helpers.IsHttpRequest(),
3059 search_opts=VOLUME_BOOTABLE_SEARCH_OPTS),
3060 ])
3061 self.mock_volume_snapshot_list.assert_called_once_with(
3062 helpers.IsHttpRequest(),
3063 search_opts=SNAPSHOT_SEARCH_OPTS)
3064
3065 self._check_glance_image_list_detailed(count=8)
3066 self._check_neutron_network_and_port_list()
3067
3068 self.mock_tenant_quota_usages.assert_called_once_with(
3069 helpers.IsHttpRequest(),
3070 targets=('instances', 'cores', 'ram', 'volumes', 'gigabytes'))
3071 self.assert_mock_multiple_calls_with_same_arguments(
3072 self.mock_flavor_list, 2,
3073 mock.call(helpers.IsHttpRequest()))
3074 self.mock_keypair_list.assert_called_once_with(helpers.IsHttpRequest())
3075 self.mock_security_group_list.assert_called_once_with(
3076 helpers.IsHttpRequest())
3077 self.mock_availability_zone_list.assert_called_once_with(
3078 helpers.IsHttpRequest())
3079 self.mock_server_group_list.assert_called_once_with(
3080 helpers.IsHttpRequest())
3081
3082 @helpers.create_mocks({api.glance: ('image_list_detailed',),
3083 api.neutron: ('network_list',
3084 'port_create',
3085 'port_delete',
3086 'port_list_with_trunk_types',
3087 'security_group_list',),
3088 api.nova: ('is_feature_available',
3089 'flavor_list',
3090 'keypair_list',
3091 'availability_zone_list',
3092 'server_group_list',
3093 'server_create',),
3094 cinder: ('volume_list',
3095 'volume_snapshot_list',),
3096 quotas: ('tenant_quota_usages',)})
3097 def test_launch_form_keystone_exception(self):
3098 flavor = self.flavors.first()
3099 image = self.versioned_images.first()
3100 keypair = self.keypairs.first()
3101 server = self.servers.first()
3102 sec_group = self.security_groups.first()
3103 avail_zone = self.availability_zones.first()
3104 customization_script = 'user data'
3105 nics = [{"net-id": self.networks.first().id, "v4-fixed-ip": ''}]
3106 quota_usages = self.quota_usages.first()
3107
3108 volumes = [v for v in self.cinder_volumes.list()
3109 if (v.status == AVAILABLE and v.bootable == 'true')]
3110 self.mock_volume_list.return_value = volumes
3111 volumes = [v for v in self.cinder_volumes.list()
3112 if (v.status == AVAILABLE)]
3113 self.mock_volume_snapshot_list.return_value = volumes
3114 self.mock_flavor_list.return_value = self.flavors.list()
3115 self.mock_keypair_list.return_value = self.keypairs.list()
3116 self.mock_security_group_list.return_value = \
3117 self.security_groups.list()
3118 self.mock_availability_zone_list.return_value = \
3119 self.availability_zones.list()
3120 self._mock_glance_image_list_detailed(self.versioned_images.list())
3121 self._mock_neutron_network_and_port_list()
3122 self.mock_server_create.side_effect = self.exceptions.keystone
3123 self.mock_tenant_quota_usages.return_value = quota_usages
3124 self.mock_flavor_list.return_value = self.flavors.list()
3125
3126 form_data = {'flavor': flavor.id,
3127 'source_type': 'image_id',
3128 'source_id': image.id,
3129 'volume_size': '1',
3130 'image_id': image.id,
3131 'availability_zone': avail_zone.zoneName,
3132 'keypair': keypair.name,
3133 'name': server.name,
3134 'script_source': 'raw',
3135 'script_data': customization_script,
3136 'project_id': self.tenants.first().id,
3137 'user_id': self.user.id,
3138 'groups': str(sec_group.id),
3139 'volume_type': '',
3140 'network': self.networks.first().id,
3141 'count': 1,
3142 'admin_pass': 'password',
3143 'confirm_admin_pass': 'password',
3144 'disk_config': 'AUTO',
3145 'config_drive': False}
3146 url = reverse('horizon:project:instances:launch')
3147 res = self.client.post(url, form_data)
3148
3149 self.assertRedirectsNoFollow(res, INDEX_URL)
3150
3151 self.mock_volume_list.assert_has_calls([
3152 mock.call(helpers.IsHttpRequest(),
3153 search_opts=VOLUME_SEARCH_OPTS),
3154 mock.call(helpers.IsHttpRequest(),
3155 search_opts=VOLUME_BOOTABLE_SEARCH_OPTS),
3156 ])
3157 self.mock_volume_snapshot_list.assert_called_once_with(
3158 helpers.IsHttpRequest(),
3159 search_opts=SNAPSHOT_SEARCH_OPTS)
3160 self.assert_mock_multiple_calls_with_same_arguments(
3161 self.mock_flavor_list, 2,
3162 mock.call(helpers.IsHttpRequest()))
3163 self.mock_keypair_list.assert_called_once_with(helpers.IsHttpRequest())
3164 self.mock_security_group_list.assert_called_once_with(
3165 helpers.IsHttpRequest())
3166 self.mock_availability_zone_list.assert_called_once_with(
3167 helpers.IsHttpRequest())
3168
3169 self._check_glance_image_list_detailed(count=8)
3170 self._check_neutron_network_and_port_list()
3171
3172 self.mock_server_create.assert_called_once_with(
3173 helpers.IsHttpRequest(),
3174 server.name,
3175 image.id,
3176 flavor.id,
3177 keypair.name,
3178 customization_script,
3179 [str(sec_group.id)],
3180 block_device_mapping=None,
3181 block_device_mapping_v2=None,
3182 nics=nics,
3183 availability_zone=avail_zone.zoneName,
3184 instance_count=helpers.IsA(int),
3185 admin_pass='password',
3186 disk_config='AUTO',
3187 config_drive=False,
3188 scheduler_hints={})
3189 self.mock_tenant_quota_usages.assert_called_once_with(
3190 helpers.IsHttpRequest(),
3191 targets=('instances', 'cores', 'ram', 'volumes', ))
3192
3193 @helpers.create_mocks({api.glance: ('image_list_detailed',),
3194 api.neutron: ('network_list',
3195 'port_list_with_trunk_types',
3196 'security_group_list',),
3197 api.nova: ('is_feature_available',
3198 'flavor_list',
3199 'keypair_list',
3200 'availability_zone_list',
3201 'server_group_list'),
3202 cinder: ('volume_list',
3203 'volume_snapshot_list',),
3204 quotas: ('tenant_quota_usages',)})
3205 def test_launch_form_instance_count_error(self):
3206 flavor = self.flavors.first()
3207 image = self.versioned_images.first()
3208 keypair = self.keypairs.first()
3209 server = self.servers.first()
3210 volume = self.cinder_volumes.first()
3211 sec_group = self.security_groups.first()
3212 avail_zone = self.availability_zones.first()
3213 customization_script = 'user data'
3214 device_name = 'vda'
3215 volume_choice = "%s:vol" % volume.id
3216 quota_usages = self.quota_usages.first()
3217
3218 self._mock_nova_glance_neutron_lists()
3219
3220 volumes = [v for v in self.cinder_volumes.list()
3221 if (v.status == AVAILABLE and v.bootable == 'true')]
3222 self.mock_volume_list.return_value = volumes
3223 self.mock_volume_snapshot_list.return_value = []
3224 self.mock_flavor_list.return_value = self.flavors.list()
3225 self.mock_tenant_quota_usages.return_value = quota_usages
3226
3227 form_data = {'flavor': flavor.id,
3228 'source_type': 'image_id',
3229 'image_id': image.id,
3230 'availability_zone': avail_zone.zoneName,
3231 'keypair': keypair.name,
3232 'name': server.name,
3233 'script_source': 'raw',
3234 'script_data': customization_script,
3235 'project_id': self.tenants.first().id,
3236 'user_id': self.user.id,
3237 'groups': str(sec_group.id),
3238 'volume_type': 'volume_id',
3239 'volume_id': volume_choice,
3240 'device_name': device_name,
3241 'count': 0}
3242 url = reverse('horizon:project:instances:launch')
3243 res = self.client.post(url, form_data)
3244
3245 self.assertContains(res, "greater than or equal to 1")
3246
3247 self._check_nova_glance_neutron_lists(flavor_count=3,
3248 image_count=10)
3249 self.mock_volume_list.assert_has_calls([
3250 mock.call(helpers.IsHttpRequest(),
3251 search_opts=VOLUME_SEARCH_OPTS),
3252 mock.call(helpers.IsHttpRequest(),
3253 search_opts=VOLUME_BOOTABLE_SEARCH_OPTS),
3254 ])
3255 self.mock_volume_snapshot_list.assert_called_once_with(
3256 helpers.IsHttpRequest(),
3257 search_opts=SNAPSHOT_SEARCH_OPTS)
3258
3259 self.assert_mock_multiple_calls_with_same_arguments(
3260 self.mock_flavor_list, 3,
3261 mock.call(helpers.IsHttpRequest()))
3262 self.mock_tenant_quota_usages.assert_has_calls([
3263 mock.call(
3264 helpers.IsHttpRequest(),
3265 targets=('instances', 'cores', 'ram', 'volumes', )),
3266 mock.call(
3267 helpers.IsHttpRequest(),
3268 targets=('instances', 'cores', 'ram', 'volumes', 'gigabytes')),
3269 ])
3270 self.assertEqual(2, self.mock_tenant_quota_usages.call_count)
3271
3272 @helpers.create_mocks({api.glance: ('image_list_detailed',),
3273 api.neutron: ('network_list',
3274 'port_list_with_trunk_types',
3275 'security_group_list',),
3276 api.nova: ('is_feature_available',
3277 'flavor_list',
3278 'keypair_list',
3279 'server_group_list',
3280 'availability_zone_list',),
3281 cinder: ('volume_list',
3282 'volume_snapshot_list',),
3283 quotas: ('tenant_quota_usages',)})
3284 def _test_launch_form_count_error(self, resource, avail):
3285 flavor = self.flavors.first()
3286 image = self.versioned_images.first()
3287 keypair = self.keypairs.first()
3288 server = self.servers.first()
3289 volume = self.cinder_volumes.first()
3290 sec_group = self.security_groups.first()
3291 avail_zone = self.availability_zones.first()
3292 customization_script = 'user data'
3293 device_name = 'vda'
3294 volume_choice = "%s:vol" % volume.id
3295 quota_usages = self.quota_usages.first()
3296 if resource == 'both':
3297 quota_usages['cores']['available'] = avail
3298 quota_usages['ram']['available'] = 512
3299 else:
3300 quota_usages[resource]['available'] = avail
3301
3302 self._mock_nova_glance_neutron_lists()
3303 volumes = [v for v in self.cinder_volumes.list()
3304 if (v.status == AVAILABLE and v.bootable == 'true')]
3305 self.mock_volume_list.return_value = volumes
3306 self.mock_volume_snapshot_list.return_value = []
3307 self.mock_flavor_list.return_value = self.flavors.list()
3308 self.mock_tenant_quota_usages.return_value = quota_usages
3309
3310 form_data = {'flavor': flavor.id,
3311 'source_type': 'image_id',
3312 'image_id': image.id,
3313 'availability_zone': avail_zone.zoneName,
3314 'keypair': keypair.name,
3315 'name': server.name,
3316 'script_source': 'raw',
3317 'script_data': customization_script,
3318 'project_id': self.tenants.first().id,
3319 'user_id': self.user.id,
3320 'groups': str(sec_group.id),
3321 'volume_type': 'volume_id',
3322 'volume_id': volume_choice,
3323 'device_name': device_name,
3324 'count': 2}
3325 url = reverse('horizon:project:instances:launch')
3326 res = self.client.post(url, form_data)
3327
3328 if resource == 'ram':
3329 msg = ("The following requested resource(s) exceed quota(s): "
3330 "RAM(Available: %s" % avail)
3331 if resource == 'cores':
3332 msg = ("The following requested resource(s) exceed quota(s): "
3333 "Cores(Available: %s" % avail)
3334 if resource == 'both':
3335 msg = ("The following requested resource(s) exceed quota(s): "
3336 "Cores(Available: %(avail)s, Requested: 2), RAM(Available: "
3337 "512, Requested: 1024)" % {'avail': avail})
3338 self.assertContains(res, msg)
3339
3340 self._check_nova_glance_neutron_lists(flavor_count=3,
3341 image_count=10)
3342 self.mock_volume_list.assert_has_calls([
3343 mock.call(helpers.IsHttpRequest(),
3344 search_opts=VOLUME_SEARCH_OPTS),
3345 mock.call(helpers.IsHttpRequest(),
3346 search_opts=VOLUME_BOOTABLE_SEARCH_OPTS),
3347 ])
3348 self.mock_volume_snapshot_list.assert_called_once_with(
3349 helpers.IsHttpRequest(),
3350 search_opts=SNAPSHOT_SEARCH_OPTS)
3351
3352 self.assert_mock_multiple_calls_with_same_arguments(
3353 self.mock_flavor_list, 3,
3354 mock.call(helpers.IsHttpRequest()))
3355 self.mock_tenant_quota_usages.assert_has_calls([
3356 mock.call(
3357 helpers.IsHttpRequest(),
3358 targets=('instances', 'cores', 'ram', 'volumes', )),
3359 mock.call(
3360 helpers.IsHttpRequest(),
3361 targets=('instances', 'cores', 'ram', 'volumes', 'gigabytes')),
3362 ])
3363 self.assertEqual(2, self.mock_tenant_quota_usages.call_count)
3364
3365 def test_launch_form_cores_count_error_glance_v2(self):
3366 self._test_launch_form_count_error('cores', 1)
3367
3368 def test_launch_form_ram_count_error(self):
3369 self._test_launch_form_count_error('ram', 512)
3370
3371 def test_launch_form_ram_cores_count_error(self):
3372 self._test_launch_form_count_error('both', 1)
3373
3374 @helpers.create_mocks({api.glance: ('image_list_detailed',),
3375 api.neutron: ('network_list',
3376 'port_list_with_trunk_types',
3377 'security_group_list',),
3378 api.nova: ('is_feature_available',
3379 'flavor_list',
3380 'keypair_list',
3381 'server_group_list',
3382 'availability_zone_list',),
3383 cinder: ('volume_list',
3384 'volume_snapshot_list',),
3385 quotas: ('tenant_quota_usages',)})
3386 def _launch_form_instance(self, image, flavor, keypair=None):
3387 server = self.servers.first()
3388 volume = self.cinder_volumes.first()
3389 sec_group = self.security_groups.first()
3390 avail_zone = self.availability_zones.first()
3391 customization_script = 'user data'
3392 device_name = 'vda'
3393 volume_choice = "%s:vol" % volume.id
3394 quota_usages = self.quota_usages.first()
3395
3396 self._mock_nova_glance_neutron_lists()
3397 volumes = [v for v in self.cinder_volumes.list()
3398 if (v.status == AVAILABLE and v.bootable == 'true')]
3399 self.mock_volume_list.return_value = volumes
3400 self.mock_volume_snapshot_list.return_value = []
3401 self.mock_flavor_list.return_value = self.flavors.list()
3402 self.mock_tenant_quota_usages.return_value = quota_usages
3403
3404 form_data = {'flavor': flavor.id,
3405 'source_type': 'image_id',
3406 'image_id': image.id,
3407 'availability_zone': avail_zone.zoneName,
3408 'name': server.name,
3409 'script_source': 'raw',
3410 'script_data': customization_script,
3411 'project_id': self.tenants.first().id,
3412 'user_id': self.user.id,
3413 'groups': str(sec_group.id),
3414 'volume_type': 'volume_id',
3415 'volume_id': volume_choice,
3416 'device_name': device_name,
3417 'count': 1}
3418 if keypair:
3419 form_data['keypair'] = keypair.name
3420
3421 url = reverse('horizon:project:instances:launch')
3422 res = self.client.post(url, form_data)
3423
3424 self._check_nova_glance_neutron_lists(flavor_count=3,
3425 image_count=10)
3426 self.mock_volume_list.assert_has_calls([
3427 mock.call(helpers.IsHttpRequest(),
3428 search_opts=VOLUME_SEARCH_OPTS),
3429 mock.call(helpers.IsHttpRequest(),
3430 search_opts=VOLUME_BOOTABLE_SEARCH_OPTS),
3431 ])
3432 self.mock_volume_snapshot_list.assert_called_once_with(
3433 helpers.IsHttpRequest(),
3434 search_opts=SNAPSHOT_SEARCH_OPTS)
3435 self.assert_mock_multiple_calls_with_same_arguments(
3436 self.mock_flavor_list, 3,
3437 mock.call(helpers.IsHttpRequest()))
3438 self.mock_tenant_quota_usages.assert_has_calls([
3439 mock.call(
3440 helpers.IsHttpRequest(),
3441 targets=('instances', 'cores', 'ram', 'volumes', )),
3442 mock.call(
3443 helpers.IsHttpRequest(),
3444 targets=('instances', 'cores', 'ram', 'volumes', 'gigabytes')),
3445 ])
3446 self.assertEqual(2, self.mock_tenant_quota_usages.call_count)
3447
3448 return res
3449
3450 def test_launch_form_instance_requirement_error_disk(self):
3451 flavor = self.flavors.get(id="bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb")
3452 image = self.versioned_images.first()
3453 image.min_ram = flavor.ram
3454 image.min_disk = flavor.disk + 1
3455 keypair = self.keypairs.first()
3456 res = self._launch_form_instance(image, flavor, keypair)
3457 msg = (f"The flavor '{flavor.name}' is too small for requested "
3458 f"image. Minimum requirements: {image.min_ram} MB of RAM and "
3459 f"{image.min_disk} GB of Root Disk.")
3460 self.assertContains(res, msg, html=True)
3461
3462 def test_launch_form_instance_requirement_error_ram(self):
3463 flavor = self.flavors.first()
3464 image = self.versioned_images.first()
3465 image.min_ram = flavor.ram + 1
3466 image.min_disk = flavor.disk
3467 keypair = self.keypairs.first()
3468 res = self._launch_form_instance(image, flavor, keypair)
3469 msg = (f"The flavor '{flavor.name}' is too small for requested "
3470 f"image. Minimum requirements: {image.min_ram} MB of RAM and "
3471 f"{image.min_disk} GB of Root Disk.")
3472 self.assertContains(res, msg, html=True)
3473
3474 def test_launch_form_instance_zero_value_flavor_with_min_req(self):
3475 flavor = self.flavors.first()
3476 image = self.versioned_images.first()
3477 image.min_ram = flavor.ram
3478 image.min_disk = flavor.disk + 1
3479 keypair = self.keypairs.first()
3480 res = self._launch_form_instance(image, flavor, keypair)
3481 msg = (f"The flavor &39;{flavor.name}&39; is too small for requested "
3482 f"image. Minimum requirements: {image.min_ram} MB of RAM and "
3483 f"{image.min_disk} GB of Root Disk.")
3484 self.assertNotContains(res, msg, html=True)
3485
3486 @helpers.create_mocks({api.glance: ('image_list_detailed',),
3487 api.neutron: ('network_list',
3488 'port_list_with_trunk_types',
3489 'security_group_list',),
3490 api.nova: ('is_feature_available',
3491 'flavor_list',
3492 'keypair_list',
3493 'server_group_list',
3494 'availability_zone_list',),
3495 cinder: ('volume_list',
3496 'volume_snapshot_list',),
3497 quotas: ('tenant_quota_usages',)})
3498 def _test_launch_form_instance_show_device_name(self, device_name,
3499 widget_class,
3500 widget_attrs):
3501 flavor = self.flavors.first()
3502 image = self.versioned_images.first()
3503 keypair = self.keypairs.first()
3504 server = self.servers.first()
3505 volume = self.cinder_volumes.first()
3506 sec_group = self.security_groups.first()
3507 avail_zone = self.availability_zones.first()
3508 customization_script = 'user data'
3509 volume_choice = "%s:vol" % volume.id
3510 quota_usages = self.quota_usages.first()
3511
3512 self.mock_flavor_list.return_value = self.flavors.list()
3513 self.mock_keypair_list.return_value = self.keypairs.list()
3514 self.mock_security_group_list.return_value = \
3515 self.security_groups.list()
3516 self.mock_availability_zone_list.return_value = \
3517 self.availability_zones.list()
3518 self.mock_server_group_list.return_value = self.server_groups.list()
3519 self.mock_image_list_detailed.side_effect = [
3520 [self.versioned_images.list(), False, False],
3521 [[], False, False],
3522 ]
3523 self.mock_network_list.side_effect = [
3524 self.networks.list()[:1],
3525 self.networks.list()[1:],
3526 self.networks.list()[:1],
3527 self.networks.list()[1:],
3528 ]
3529 self.mock_port_list_with_trunk_types.return_value = self.ports.list()
3530 volumes = [v for v in self.cinder_volumes.list()
3531 if (v.status == AVAILABLE and v.bootable == 'true')]
3532 self.mock_volume_list.return_value = volumes
3533 self.mock_volume_snapshot_list.return_value = []
3534 self.mock_flavor_list.return_value = self.flavors.list()
3535 self.mock_tenant_quota_usages.return_value = quota_usages
3536
3537 form_data = {'flavor': flavor.id,
3538 'source_type': 'volume_image_id',
3539 'image_id': image.id,
3540 'availability_zone': avail_zone.zoneName,
3541 'keypair': keypair.name,
3542 'name': server.name,
3543 'customization_script': customization_script,
3544 'project_id': self.tenants.first().id,
3545 'user_id': self.user.id,
3546 'groups': str(sec_group.id),
3547 'volume_type': 'volume_id',
3548 'volume_id': volume_choice,
3549 'volume_size': max(
3550 image.min_disk, image.size // 1024 ** 3),
3551 'device_name': device_name,
3552 'count': 1}
3553
3554 url = reverse('horizon:project:instances:launch')
3555 res = self.client.post(url, form_data)
3556 self.assertNoFormErrors(res)
3557 widget_content = widget_class().render(**widget_attrs)
3558 # In django 1.4, the widget's html attributes are not always rendered
3559 # in the same order and checking the fully rendered widget fails.
3560 for widget_part in widget_content.split():
3561 self.assertContains(res, widget_part)
3562
3563 self.assert_mock_multiple_calls_with_same_arguments(
3564 self.mock_flavor_list, 3,
3565 mock.call(helpers.IsHttpRequest()))
3566 self.mock_keypair_list.assert_called_once_with(helpers.IsHttpRequest())
3567 self.mock_security_group_list.assert_called_once_with(
3568 helpers.IsHttpRequest())
3569 self.mock_availability_zone_list.assert_called_once_with(
3570 helpers.IsHttpRequest())
3571 self.mock_server_group_list.assert_called_once_with(
3572 helpers.IsHttpRequest())
3573 self.assertEqual(10, self.mock_image_list_detailed.call_count)
3574 self.mock_image_list_detailed.assert_has_calls(
3575 [
3576 mock.call(helpers.IsHttpRequest(),
3577 filters={'is_public': True,
3578 'status': 'active'}),
3579 mock.call(helpers.IsHttpRequest(),
3580 filters={'property-owner_id': self.tenant.id,
3581 'status': 'active'})
3582 ] +
3583 [
3584 mock.call(helpers.IsHttpRequest(),
3585 filters={'status': 'active',
3586 'visibility': 'community'}),
3587 mock.call(helpers.IsHttpRequest(),
3588 filters={'status': 'active',
3589 'visibility': 'shared'})
3590 ] * 3
3591 )
3592 self.assertEqual(4, self.mock_network_list.call_count)
3593 self.mock_network_list.assert_has_calls([
3594 mock.call(
3595 helpers.IsHttpRequest(),
3596 tenant_id=self.tenant.id,
3597 shared=False),
3598 mock.call(
3599 helpers.IsHttpRequest(),
3600 shared=True),
3601 mock.call(
3602 helpers.IsHttpRequest(),
3603 tenant_id=self.tenant.id,
3604 shared=False),
3605 mock.call(
3606 helpers.IsHttpRequest(),
3607 shared=True),
3608 ])
3609 self.assertEqual(len(self.networks.list()),
3610 self.mock_port_list_with_trunk_types.call_count)
3611 self.mock_port_list_with_trunk_types.assert_has_calls(
3612 [mock.call(helpers.IsHttpRequest(),
3613 network_id=net.id,
3614 tenant_id=self.tenant.id)
3615 for net in self.networks.list()])
3616 self.mock_volume_list.assert_has_calls([
3617 mock.call(helpers.IsHttpRequest(),
3618 search_opts=VOLUME_SEARCH_OPTS),
3619 mock.call(helpers.IsHttpRequest(),
3620 search_opts=VOLUME_BOOTABLE_SEARCH_OPTS),
3621 ])
3622 self.mock_volume_snapshot_list.assert_called_once_with(
3623 helpers.IsHttpRequest(),
3624 search_opts=SNAPSHOT_SEARCH_OPTS)
3625 self.assert_mock_multiple_calls_with_same_arguments(
3626 self.mock_flavor_list, 3,
3627 mock.call(helpers.IsHttpRequest()))
3628 self.mock_tenant_quota_usages.assert_has_calls([
3629 mock.call(
3630 helpers.IsHttpRequest(),
3631 targets=('instances', 'cores', 'ram', 'volumes', )),
3632 mock.call(
3633 helpers.IsHttpRequest(),
3634 targets=('instances', 'cores', 'ram', 'volumes', 'gigabytes')),
3635 ])
3636 self.assertEqual(2, self.mock_tenant_quota_usages.call_count)
3637
3638 @helpers.update_settings(
3639 OPENSTACK_HYPERVISOR_FEATURES={'can_set_mount_point': True},)
3640 def test_launch_form_instance_device_name_showed(self):
3641 self._test_launch_form_instance_show_device_name(
3642 'vda', widgets.TextInput, {
3643 'name': 'device_name', 'value': 'vda',
3644 'attrs': {'id': 'id_device_name'}}
3645 )
3646
3647 @helpers.update_settings(
3648 OPENSTACK_HYPERVISOR_FEATURES={'can_set_mount_point': False})
3649 def test_launch_form_instance_device_name_hidden(self):
3650 self._test_launch_form_instance_show_device_name(
3651 '', widgets.HiddenInput, {
3652 'name': 'device_name', 'value': '',
3653 'attrs': {'id': 'id_device_name'}}
3654 )
3655
3656 @helpers.create_mocks({api.glance: ('image_list_detailed',),
3657 api.neutron: ('network_list',
3658 'port_list_with_trunk_types',
3659 'security_group_list',),
3660 api.nova: ('is_feature_available',
3661 'flavor_list',
3662 'keypair_list',
3663 'server_group_list',
3664 'availability_zone_list',),
3665 cinder: ('volume_list',
3666 'volume_snapshot_list',),
3667 quotas: ('tenant_quota_usages',)})
3668 def _test_launch_form_instance_volume_size(self, image, volume_size, msg,
3669 avail_volumes=None):
3670 flavor = self.flavors.get(name='m1.massive')
3671 keypair = self.keypairs.first()
3672 server = self.servers.first()
3673 sec_group = self.security_groups.first()
3674 avail_zone = self.availability_zones.first()
3675 customization_script = 'user data'
3676 device_name = 'vda'
3677 quota_usages = self.quota_usages.first()
3678 quota_usages['cores']['available'] = 2000
3679 if avail_volumes is not None:
3680 quota_usages['volumes']['available'] = avail_volumes
3681
3682 self.mock_flavor_list.return_value = self.flavors.list()
3683 self.mock_keypair_list.return_value = self.keypairs.list()
3684 self.mock_security_group_list.return_value = \
3685 self.security_groups.list()
3686 self.mock_availability_zone_list.return_value = \
3687 self.availability_zones.list()
3688 self._mock_glance_image_list_detailed(self.versioned_images.list())
3689 self._mock_neutron_network_and_port_list()
3690 self.mock_server_group_list.return_value = self.server_groups.list()
3691 volumes = [v for v in self.cinder_volumes.list()
3692 if (v.status == AVAILABLE and v.bootable == 'true')]
3693 self.mock_volume_list.return_value = volumes
3694 self.mock_volume_snapshot_list.return_value = []
3695 self.mock_tenant_quota_usages.return_value = quota_usages
3696
3697 form_data = {
3698 'flavor': flavor.id,
3699 'source_type': 'volume_image_id',
3700 'image_id': image.id,
3701 'availability_zone': avail_zone.zoneName,
3702 'keypair': keypair.name,
3703 'name': server.name,
3704 'script_source': 'raw',
3705 'script_data': customization_script,
3706 'project_id': self.tenants.first().id,
3707 'user_id': self.user.id,
3708 'groups': str(sec_group.id),
3709 'volume_size': volume_size,
3710 'device_name': device_name,
3711 'count': 1
3712 }
3713 url = reverse('horizon:project:instances:launch')
3714
3715 res = self.client.post(url, form_data)
3716 self.assertContains(res, msg, html=True)
3717
3718 self.mock_keypair_list.assert_called_once_with(helpers.IsHttpRequest())
3719 self.mock_security_group_list.assert_called_once_with(
3720 helpers.IsHttpRequest())
3721 self.mock_availability_zone_list.assert_called_once_with(
3722 helpers.IsHttpRequest())
3723 if avail_volumes is None:
3724 image_list_count = 10
3725 else:
3726 image_list_count = 8
3727 self._check_glance_image_list_detailed(count=image_list_count)
3728 self._check_neutron_network_and_port_list()
3729 self.mock_server_group_list.assert_called_once_with(
3730 helpers.IsHttpRequest())
3731 self.mock_volume_list.assert_has_calls([
3732 mock.call(helpers.IsHttpRequest(),
3733 search_opts=VOLUME_SEARCH_OPTS),
3734 mock.call(helpers.IsHttpRequest(),
3735 search_opts=VOLUME_BOOTABLE_SEARCH_OPTS),
3736 ])
3737 self.mock_volume_snapshot_list.assert_called_once_with(
3738 helpers.IsHttpRequest(),
3739 search_opts=SNAPSHOT_SEARCH_OPTS)
3740 if avail_volumes is None:
3741 flavor_list_count = 3
3742 else:
3743 flavor_list_count = 2
3744 self.assert_mock_multiple_calls_with_same_arguments(
3745 self.mock_flavor_list, flavor_list_count,
3746 mock.call(helpers.IsHttpRequest()))
3747 self.mock_tenant_quota_usages.assert_has_calls([
3748 mock.call(
3749 helpers.IsHttpRequest(),
3750 targets=('instances', 'cores', 'ram', 'volumes', )),
3751 mock.call(
3752 helpers.IsHttpRequest(),
3753 targets=('instances', 'cores', 'ram', 'volumes', 'gigabytes')),
3754 ])
3755 self.assertEqual(2, self.mock_tenant_quota_usages.call_count)
3756
3757 def test_launch_form_instance_volume_size_error(self):
3758 image = self.versioned_images.get(name='protected_images')
3759 volume_size = image.min_disk // 2
3760 msg = ("The Volume size is too small for the '%s' image "
3761 "and has to be greater than or equal to '%s' GB." %
3762 (image.name, image.min_disk))
3763 self._test_launch_form_instance_volume_size(image, volume_size, msg)
3764
3765 def test_launch_form_instance_non_int_volume_size(self):
3766 image = self.versioned_images.get(name='protected_images')
3767 msg = "Enter a whole number."
3768 self._test_launch_form_instance_volume_size(image, 1.5, msg)
3769
3770 def test_launch_form_instance_volume_exceed_quota(self):
3771 image = self.versioned_images.get(name='protected_images')
3772 msg = ("The requested instance cannot be launched. "
3773 "Requested volume exceeds quota: Available: 0, Requested: 1.")
3774 self._test_launch_form_instance_volume_size(image, image.min_disk,
3775 msg, 0)
3776
3777 @helpers.create_mocks({
3778 api.nova: ('flavor_list',
3779 'server_list_paged',
3780 'tenant_absolute_limits',
3781 'is_feature_available',),
3782 api.glance: ('image_list_detailed',),
3783 api.neutron: ('floating_ip_simple_associate_supported',
3784 'floating_ip_supported',),
3785 api.network: ('servers_update_addresses',),
3786 api.cinder: ('volume_list',),
3787 })
3788 def test_launch_button_attributes(self):
3789 servers = self.servers.list()
3790 limits = self.limits['absolute']
3791
3792 self.mock_is_feature_available.return_value = True
3793 self.mock_flavor_list.return_value = self.flavors.list()
3794 self.mock_image_list_detailed.return_value = (self.images.list(),
3795 False, False)
3796 self.mock_server_list_paged.return_value = [servers, False, False]
3797 self.mock_servers_update_addresses.return_value = None
3798 self.mock_tenant_absolute_limits.return_value = limits
3799 self.mock_floating_ip_supported.return_value = True
3800 self.mock_floating_ip_simple_associate_supported.return_value = True
3801
3802 tables.LaunchLink()
3803 res = self.client.get(INDEX_URL)
3804
3805 launch_action = self.getAndAssertTableAction(res, 'instances',
3806 'launch-ng')
3807
3808 self.assertEqual(set(['btn-launch']),
3809 set(launch_action.classes))
3810 self.assertEqual('Launch Instance', launch_action.verbose_name)
3811 self.assertEqual((('compute', 'os_compute_api:servers:create'),),
3812 launch_action.policy_rules)
3813
3814 self.assert_mock_multiple_calls_with_same_arguments(
3815 self.mock_is_feature_available, 10,
3816 mock.call(helpers.IsHttpRequest(), 'locked_attribute'))
3817 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
3818 self._assert_mock_image_list_detailed_calls()
3819
3820 search_opts = {'marker': None, 'paginate': True}
3821 self.mock_server_list_paged.assert_called_once_with(
3822 helpers.IsHttpRequest(),
3823 sort_dir='desc',
3824 search_opts=search_opts)
3825 self.mock_servers_update_addresses.assert_called_once_with(
3826 helpers.IsHttpRequest(), servers)
3827 self.assert_mock_multiple_calls_with_same_arguments(
3828 self.mock_tenant_absolute_limits, 3,
3829 mock.call(helpers.IsHttpRequest(), reserved=True))
3830 self.assert_mock_multiple_calls_with_same_arguments(
3831 self.mock_floating_ip_supported, 10,
3832 mock.call(helpers.IsHttpRequest()))
3833 self.assert_mock_multiple_calls_with_same_arguments(
3834 self.mock_floating_ip_simple_associate_supported, 5,
3835 mock.call(helpers.IsHttpRequest()))
3836
3837 @helpers.create_mocks({
3838 api.nova: ('flavor_list',
3839 'server_list_paged',
3840 'tenant_absolute_limits',
3841 'is_feature_available',),
3842 api.glance: ('image_list_detailed',),
3843 api.neutron: ('floating_ip_simple_associate_supported',
3844 'floating_ip_supported',),
3845 api.network: ('servers_update_addresses',),
3846 api.cinder: ('volume_list',),
3847 })
3848 def test_launch_button_disabled_when_quota_exceeded(self):
3849 servers = self.servers.list()
3850 limits = self.limits['absolute']
3851 limits['totalInstancesUsed'] = limits['maxTotalInstances']
3852
3853 self.mock_is_feature_available.return_value = True
3854 self.mock_flavor_list.return_value = self.flavors.list()
3855 self.mock_image_list_detailed.return_value = (self.images.list(),
3856 False, False)
3857 self.mock_server_list_paged.return_value = [servers, False, False]
3858 self.mock_servers_update_addresses.return_value = None
3859 self.mock_tenant_absolute_limits.return_value = limits
3860 self.mock_floating_ip_supported.return_value = True
3861 self.mock_floating_ip_simple_associate_supported.return_value = True
3862
3863 tables.LaunchLink()
3864 res = self.client.get(INDEX_URL)
3865
3866 launch_action = self.getAndAssertTableAction(
3867 res, 'instances', 'launch-ng')
3868
3869 self.assertIn('disabled', launch_action.classes,
3870 'The launch button should be disabled')
3871 self.assertEqual('Launch Instance (Quota exceeded)',
3872 launch_action.verbose_name)
3873
3874 self.assert_mock_multiple_calls_with_same_arguments(
3875 self.mock_is_feature_available, 10,
3876 mock.call(helpers.IsHttpRequest(), 'locked_attribute'))
3877 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
3878 self._assert_mock_image_list_detailed_calls()
3879
3880 search_opts = {'marker': None, 'paginate': True}
3881 self.mock_server_list_paged.assert_called_once_with(
3882 helpers.IsHttpRequest(),
3883 sort_dir='desc',
3884 search_opts=search_opts)
3885 self.mock_servers_update_addresses.assert_called_once_with(
3886 helpers.IsHttpRequest(), servers)
3887 self.assert_mock_multiple_calls_with_same_arguments(
3888 self.mock_tenant_absolute_limits, 3,
3889 mock.call(helpers.IsHttpRequest(), reserved=True))
3890 self.assert_mock_multiple_calls_with_same_arguments(
3891 self.mock_floating_ip_supported, 10,
3892 mock.call(helpers.IsHttpRequest()))
3893 self.assert_mock_multiple_calls_with_same_arguments(
3894 self.mock_floating_ip_simple_associate_supported, 5,
3895 mock.call(helpers.IsHttpRequest()))
3896
3897 @helpers.create_mocks({api.glance: ('image_list_detailed',),
3898 api.neutron: ('network_list',
3899 'port_list_with_trunk_types',
3900 'security_group_list',),
3901 api.nova: ('is_feature_available',
3902 'flavor_list',
3903 'keypair_list',
3904 'availability_zone_list',
3905 'server_group_list',
3906 'tenant_absolute_limits',
3907 'server_create',),
3908 cinder: ('volume_list',
3909 'volume_snapshot_list',),
3910 quotas: ('tenant_quota_usages',)})
3911 def test_launch_with_empty_device_name_allowed(self):
3912 flavor = self.flavors.get(name='m1.massive')
3913 image = self.versioned_images.first()
3914 keypair = self.keypairs.first()
3915 server = self.servers.first()
3916 sec_group = self.security_groups.first()
3917 avail_zone = self.availability_zones.first()
3918 customization_script = 'user data'
3919 nics = [{'net-id': self.networks.first().id, 'v4-fixed-ip': ''}]
3920 device_name = ''
3921 quota_usages = self.quota_usages.first()
3922 quota_usages['cores']['available'] = 2000
3923 device_mapping_v2 = [{'device_name': None, # device_name must be None
3924 'source_type': 'image',
3925 'destination_type': 'volume',
3926 'delete_on_termination': False,
3927 'uuid': image.id,
3928 'boot_index': '0',
3929 'volume_size': image.size}]
3930
3931 self._mock_nova_glance_neutron_lists()
3932 volumes = [v for v in self.cinder_volumes.list()
3933 if (v.status == AVAILABLE and v.bootable == 'true')]
3934 self.mock_volume_list.return_value = volumes
3935 self.mock_volume_snapshot_list.return_value = []
3936 self.mock_flavor_list.return_value = self.flavors.list()
3937 self.mock_tenant_quota_usages.return_value = quota_usages
3938 self.mock_server_create.return_value = None
3939
3940 form_data = {
3941 'flavor': flavor.id,
3942 'source_type': 'volume_image_id',
3943 'image_id': image.id,
3944 'availability_zone': avail_zone.zoneName,
3945 'keypair': keypair.name,
3946 'name': server.name,
3947 'script_source': 'raw',
3948 'script_data': customization_script,
3949 'project_id': self.tenants.first().id,
3950 'user_id': self.user.id,
3951 'groups': str(sec_group.id),
3952 'volume_size': image.size,
3953 'device_name': device_name,
3954 'network': self.networks.first().id,
3955 'count': 1
3956 }
3957 url = reverse('horizon:project:instances:launch')
3958
3959 res = self.client.post(url, form_data)
3960 self.assertNoFormErrors(res)
3961
3962 self._check_nova_glance_neutron_lists(flavor_count=2,
3963 image_count=8)
3964
3965 self.mock_volume_list.assert_has_calls([
3966 mock.call(helpers.IsHttpRequest(),
3967 search_opts=VOLUME_SEARCH_OPTS),
3968 mock.call(helpers.IsHttpRequest(),
3969 search_opts=VOLUME_BOOTABLE_SEARCH_OPTS),
3970 ])
3971 self.mock_volume_snapshot_list.assert_called_once_with(
3972 helpers.IsHttpRequest(),
3973 search_opts=SNAPSHOT_SEARCH_OPTS)
3974
3975 self.assert_mock_multiple_calls_with_same_arguments(
3976 self.mock_flavor_list, 2,
3977 mock.call(helpers.IsHttpRequest()))
3978 self.mock_tenant_quota_usages.assert_called_once_with(
3979 helpers.IsHttpRequest(),
3980 targets=('instances', 'cores', 'ram', 'volumes', ))
3981 self.mock_server_create.assert_called_once_with(
3982 helpers.IsHttpRequest(),
3983 server.name,
3984 '',
3985 flavor.id,
3986 keypair.name,
3987 customization_script,
3988 [str(sec_group.id)],
3989 block_device_mapping=None,
3990 block_device_mapping_v2=device_mapping_v2,
3991 nics=nics,
3992 availability_zone=avail_zone.zoneName,
3993 instance_count=helpers.IsA(int),
3994 admin_pass='',
3995 config_drive=False,
3996 disk_config='',
3997 scheduler_hints={})
3998
3999
4000 class InstanceTests2(InstanceTestBase, InstanceTableTestMixin):
4001
4002 @helpers.create_mocks({
4003 api.nova: ('flavor_list',
4004 'server_list_paged',
4005 'tenant_absolute_limits',
4006 'is_feature_available',),
4007 api.glance: ('image_list_detailed',),
4008 api.neutron: ('floating_ip_simple_associate_supported',
4009 'floating_ip_supported',),
4010 api.network: ('servers_update_addresses',),
4011 api.cinder: ('volume_list',),
4012 })
4013 def test_index_options_after_migrate(self):
4014 servers = self.servers.list()
4015 server = self.servers.first()
4016 server.status = "VERIFY_RESIZE"
4017
4018 self.mock_is_feature_available.return_value = True
4019 self.mock_flavor_list.return_value = self.flavors.list()
4020 self.mock_image_list_detailed.return_value = (self.images.list(),
4021 False, False)
4022 self.mock_server_list_paged.return_value = [servers, False, False]
4023 self.mock_servers_update_addresses.return_value = None
4024 self.mock_tenant_absolute_limits.return_value = self.limits['absolute']
4025 self.mock_floating_ip_supported.return_value = True
4026 self.mock_floating_ip_simple_associate_supported.return_value = True
4027
4028 res = self.client.get(INDEX_URL)
4029 self.assertContains(res, "instances__confirm")
4030 self.assertContains(res, "instances__revert")
4031
4032 self.assert_mock_multiple_calls_with_same_arguments(
4033 self.mock_is_feature_available, 10,
4034 mock.call(helpers.IsHttpRequest(), 'locked_attribute'))
4035 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
4036 self._assert_mock_image_list_detailed_calls()
4037
4038 search_opts = {'marker': None, 'paginate': True}
4039 self.mock_server_list_paged.assert_called_once_with(
4040 helpers.IsHttpRequest(),
4041 sort_dir='desc',
4042 search_opts=search_opts)
4043 self.mock_servers_update_addresses.assert_called_once_with(
4044 helpers.IsHttpRequest(), servers)
4045 self.assert_mock_multiple_calls_with_same_arguments(
4046 self.mock_tenant_absolute_limits, 2,
4047 mock.call(helpers.IsHttpRequest(), reserved=True))
4048 self.assert_mock_multiple_calls_with_same_arguments(
4049 self.mock_floating_ip_supported, 10,
4050 mock.call(helpers.IsHttpRequest()))
4051 self.assert_mock_multiple_calls_with_same_arguments(
4052 self.mock_floating_ip_simple_associate_supported, 5,
4053 mock.call(helpers.IsHttpRequest()))
4054
4055 @helpers.create_mocks({api.nova: ('is_feature_available',
4056 'flavor_list',
4057 'keypair_list',
4058 'server_group_list',
4059 'availability_zone_list'),
4060 cinder: ('volume_snapshot_list',
4061 'volume_list',),
4062 api.neutron: ('network_list',
4063 'port_list_with_trunk_types',
4064 'security_group_list',),
4065 api.glance: ('image_list_detailed',),
4066 quotas: ('tenant_quota_usages',)})
4067 def test_select_default_keypair_if_only_one(self):
4068 keypair = self.keypairs.first()
4069
4070 self.mock_volume_list.return_value = []
4071 self.mock_volume_snapshot_list.return_value = []
4072 self._mock_glance_image_list_detailed(self.versioned_images.list())
4073 self._mock_neutron_network_and_port_list()
4074 self.mock_tenant_quota_usages.return_value = self.quota_usages.first()
4075 self._mock_nova_lists()
4076
4077 url = reverse('horizon:project:instances:launch')
4078 res = self.client.get(url)
4079 self.assertContains(
4080 res, "<option selected='selected' value='%(key)s'>"
4081 "%(key)s</option>" % {'key': keypair.name},
4082 html=True,
4083 msg_prefix="The default key pair was not selected.")
4084
4085 self.mock_volume_list.assert_has_calls([
4086 mock.call(helpers.IsHttpRequest(),
4087 search_opts=VOLUME_SEARCH_OPTS),
4088 mock.call(helpers.IsHttpRequest(),
4089 search_opts=VOLUME_BOOTABLE_SEARCH_OPTS),
4090 ])
4091 self.mock_volume_snapshot_list.assert_called_once_with(
4092 helpers.IsHttpRequest(), search_opts=SNAPSHOT_SEARCH_OPTS)
4093 self._check_glance_image_list_detailed(count=8)
4094 self._check_neutron_network_and_port_list()
4095 self.mock_tenant_quota_usages.assert_called_once_with(
4096 helpers.IsHttpRequest(),
4097 targets=('instances', 'cores', 'ram', 'volumes', 'gigabytes'))
4098 self._check_nova_lists(flavor_count=2)
4099
4100 @helpers.create_mocks({
4101 api.neutron: ('floating_ip_target_list_by_instance',
4102 'tenant_floating_ip_list',
4103 'floating_ip_disassociate',
4104 'tenant_floating_ip_release'),
4105 })
4106 def _test_disassociate_floating_ip(self, is_release):
4107 servers = self.servers.list()
4108 server = servers[0]
4109 port = [p for p in self.ports.list() if p.device_id == server.id][0]
4110 fip_target = api.neutron.FloatingIpTarget(
4111 port, port['fixed_ips'][0]['ip_address'], server.name)
4112 fip = self.floating_ips.first()
4113 fip.port_id = port.id
4114
4115 self.mock_floating_ip_target_list_by_instance.return_value = \
4116 [fip_target]
4117 self.mock_tenant_floating_ip_list.return_value = [fip]
4118 self.mock_floating_ip_disassociate.return_value = None
4119 self.mock_tenant_floating_ip_release.return_value = None
4120
4121 url = reverse('horizon:project:instances:disassociate',
4122 args=[server.id])
4123 form_data = {'fip': fip.id,
4124 'is_release': is_release}
4125 res = self.client.post(url, form_data)
4126
4127 self.assertRedirectsNoFollow(res, INDEX_URL)
4128
4129 self.mock_floating_ip_target_list_by_instance.assert_called_once_with(
4130 helpers.IsHttpRequest(), server.id)
4131 self.mock_tenant_floating_ip_list.assert_called_once_with(
4132 helpers.IsHttpRequest())
4133 if is_release:
4134 self.mock_floating_ip_disassociate.assert_not_called()
4135 self.mock_tenant_floating_ip_release.assert_called_once_with(
4136 helpers.IsHttpRequest(), fip.id)
4137 else:
4138 self.mock_floating_ip_disassociate.assert_called_once_with(
4139 helpers.IsHttpRequest(), fip.id)
4140 self.mock_tenant_floating_ip_release.assert_not_called()
4141
4142 @helpers.create_mocks({api.neutron: ('floating_ip_disassociate',)})
4143 def test_disassociate_floating_ip(self):
4144 self._test_disassociate_floating_ip(is_release=False)
4145
4146 @helpers.create_mocks({api.neutron: ('tenant_floating_ip_release',)})
4147 def test_disassociate_floating_ip_with_release(self):
4148 self._test_disassociate_floating_ip(is_release=True)
4149
4150 def _populate_server_flavor_nova_api_ge_2_47(self, server):
4151 flavor_id = server.flavor['id']
4152 flavor = self.flavors.get(id=flavor_id)
4153 server.flavor = {
4154 'original_name': flavor.name,
4155 'vcpus': flavor.vcpus,
4156 'ram': flavor.ram,
4157 'swap': flavor.swap,
4158 'disk': flavor.disk,
4159 'ephemeral': flavor.ephemeral,
4160 'extra_specs': flavor.extra_specs,
4161 }
4162 return server
4163
4164 @helpers.create_mocks({api.nova: ('server_get',
4165 'flavor_list',
4166 'server_group_list',
4167 'tenant_absolute_limits',
4168 'is_feature_available')})
4169 def _test_instance_resize_get(self, server, nova_api_lt_2_47=False):
4170 self.mock_server_get.return_value = server
4171 self.mock_flavor_list.return_value = self.flavors.list()
4172 self.mock_server_group_list.return_value = self.server_groups.list()
4173 self.mock_tenant_absolute_limits.return_value = self.limits['absolute']
4174
4175 url = reverse('horizon:project:instances:resize', args=[server.id])
4176 res = self.client.get(url)
4177
4178 workflow = res.context['workflow']
4179 self.assertTemplateUsed(res, views.WorkflowView.template_name)
4180 self.assertEqual(res.context['workflow'].name,
4181 workflows.ResizeInstance.name)
4182 self.assertContains(res, 'Disk Partition')
4183
4184 config_drive_field_label = 'Configuration Drive'
4185 self.assertNotContains(res, config_drive_field_label)
4186
4187 step = workflow.get_step("flavor_choice")
4188 self.assertEqual(step.action.initial['old_flavor_name'],
4189 self.flavors.first().name)
4190
4191 step = workflow.get_step("setadvancedaction")
4192 self.assertEqual(step.action.fields['disk_config'].label,
4193 'Disk Partition')
4194 self.assertQuerysetEqual(workflow.steps,
4195 ['<SetFlavorChoice: flavor_choice>',
4196 '<SetAdvanced: setadvancedaction>'])
4197 option = '<option value="%s">%s</option>'
4198
4199 def is_original_flavor(server, flavor, nova_api_lt_2_47):
4200 if nova_api_lt_2_47:
4201 return flavor.id == server.flavor['id']
4202 else:
4203 return flavor.name == server.flavor['original_name']
4204
4205 for flavor in self.flavors.list():
4206 if is_original_flavor(server, flavor, nova_api_lt_2_47):
4207 self.assertNotContains(res, option % (flavor.id, flavor.name))
4208 else:
4209 self.assertContains(res, option % (flavor.id, flavor.name))
4210
4211 self.mock_server_get.assert_called_once_with(helpers.IsHttpRequest(),
4212 server.id)
4213 self.assert_mock_multiple_calls_with_same_arguments(
4214 self.mock_flavor_list, 2,
4215 mock.call(helpers.IsHttpRequest()))
4216 self.mock_server_group_list.assert_called_once_with(
4217 helpers.IsHttpRequest())
4218 self.mock_tenant_absolute_limits.assert_called_once_with(
4219 helpers.IsHttpRequest(), reserved=True)
4220
4221 def test_instance_resize_get_nova_api_lt_2_47(self):
4222 server = self.servers.first()
4223 self._test_instance_resize_get(server, nova_api_lt_2_47=True)
4224
4225 def test_instance_resize_get_nova_api_ge_2_47(self):
4226 server = self.servers.first()
4227 self._populate_server_flavor_nova_api_ge_2_47(server)
4228 self._test_instance_resize_get(server)
4229
4230 @helpers.create_mocks({api.nova: ('server_get',)})
4231 def test_instance_resize_get_server_get_exception(self):
4232 server = self.servers.first()
4233
4234 self.mock_server_get.side_effect = self.exceptions.nova
4235
4236 url = reverse('horizon:project:instances:resize',
4237 args=[server.id])
4238 res = self.client.get(url)
4239
4240 self.assertRedirectsNoFollow(res, INDEX_URL)
4241 self.mock_server_get.assert_called_once_with(
4242 helpers.IsHttpRequest(), server.id)
4243
4244 @helpers.create_mocks({api.nova: ('server_get',
4245 'flavor_list',)})
4246 def test_instance_resize_get_flavor_list_exception(self):
4247 server = self.servers.first()
4248 self.mock_server_get.return_value = server
4249 self.mock_flavor_list.side_effect = self.exceptions.nova
4250
4251 url = reverse('horizon:project:instances:resize',
4252 args=[server.id])
4253 res = self.client.get(url)
4254
4255 self.assertRedirectsNoFollow(res, INDEX_URL)
4256
4257 self.mock_server_get.assert_called_once_with(helpers.IsHttpRequest(),
4258 server.id)
4259 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
4260
4261 # TODO(amotoki): This is requred only when nova API <=2.46 is used.
4262 # Once server_get() uses nova API >=2.47 only, this test can be droppped.
4263 @helpers.create_mocks({api.nova: ('server_get',
4264 'flavor_list',
4265 'flavor_get',
4266 'server_group_list',
4267 'tenant_absolute_limits',
4268 'is_feature_available')})
4269 def test_instance_resize_get_current_flavor_not_found(self):
4270 server = self.servers.first()
4271 self.mock_server_get.return_value = server
4272 self.mock_flavor_list.return_value = []
4273 self.mock_flavor_get.side_effect = self.exceptions.nova
4274 self.mock_server_group_list.return_value = self.server_groups.list()
4275 self.mock_tenant_absolute_limits.return_value = self.limits['absolute']
4276
4277 url = reverse('horizon:project:instances:resize', args=[server.id])
4278 res = self.client.get(url)
4279
4280 self.assertTemplateUsed(res, views.WorkflowView.template_name)
4281
4282 self.mock_server_get.assert_called_once_with(helpers.IsHttpRequest(),
4283 server.id)
4284 self.assert_mock_multiple_calls_with_same_arguments(
4285 self.mock_flavor_list, 2,
4286 mock.call(helpers.IsHttpRequest()))
4287 self.mock_flavor_get.assert_called_once_with(
4288 helpers.IsHttpRequest(), server.flavor['id'])
4289 self.mock_server_group_list.assert_called_once_with(
4290 helpers.IsHttpRequest())
4291 self.mock_tenant_absolute_limits.assert_called_once_with(
4292 helpers.IsHttpRequest(), reserved=True)
4293
4294 def _instance_resize_post(self, server_id, flavor_id, disk_config):
4295 formData = {'flavor': flavor_id,
4296 'default_role': 'member',
4297 'disk_config': disk_config}
4298 url = reverse('horizon:project:instances:resize',
4299 args=[server_id])
4300 return self.client.post(url, formData)
4301
4302 instance_resize_post_stubs = {
4303 api.nova: ('server_get', 'server_resize',
4304 'flavor_list', 'flavor_get', 'server_group_list',
4305 'is_feature_available')}
4306
4307 @helpers.create_mocks(instance_resize_post_stubs)
4308 def test_instance_resize_post(self):
4309 server = self.servers.first()
4310 flavors = [flavor for flavor in self.flavors.list()
4311 if flavor.id != server.flavor['id']]
4312 flavor = flavors[0]
4313
4314 self.mock_server_get.return_value = server
4315 self.mock_flavor_list.return_value = self.flavors.list()
4316 self.mock_server_group_list.return_value = self.server_groups.list()
4317 self.mock_server_resize.return_value = []
4318
4319 res = self._instance_resize_post(server.id, flavor.id, 'AUTO')
4320 self.assertNoFormErrors(res)
4321 self.assertRedirectsNoFollow(res, INDEX_URL)
4322
4323 self.mock_server_get.assert_called_once_with(helpers.IsHttpRequest(),
4324 server.id)
4325 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
4326 self.mock_server_group_list.assert_called_once_with(
4327 helpers.IsHttpRequest())
4328 self.mock_server_resize.assert_called_once_with(
4329 helpers.IsHttpRequest(), server.id, flavor.id, 'AUTO')
4330
4331 @helpers.create_mocks(instance_resize_post_stubs)
4332 def test_instance_resize_post_api_exception(self):
4333 server = self.servers.first()
4334 flavors = [flavor for flavor in self.flavors.list()
4335 if flavor.id != server.flavor['id']]
4336 flavor = flavors[0]
4337
4338 self.mock_server_get.return_value = server
4339 self.mock_flavor_list.return_value = self.flavors.list()
4340 self.mock_server_group_list.return_value = self.server_groups.list()
4341 self.mock_server_resize.side_effect = self.exceptions.nova
4342
4343 res = self._instance_resize_post(server.id, flavor.id, 'AUTO')
4344 self.assertRedirectsNoFollow(res, INDEX_URL)
4345
4346 self.mock_server_get.assert_called_once_with(helpers.IsHttpRequest(),
4347 server.id)
4348 self.mock_flavor_list.assert_called_once_with(helpers.IsHttpRequest())
4349 self.mock_server_group_list.assert_called_once_with(
4350 helpers.IsHttpRequest())
4351 self.mock_server_resize.assert_called_once_with(
4352 helpers.IsHttpRequest(), server.id, flavor.id, 'AUTO')
4353
4354 @helpers.create_mocks({api.glance: ('image_list_detailed',),
4355 api.nova: ('server_get',
4356 'is_feature_available',)})
4357 def test_rebuild_instance_get(self, expect_password_fields=True):
4358 server = self.servers.first()
4359 self._mock_glance_image_list_detailed(self.images.list())
4360 self.mock_is_feature_available.return_value = False
4361 self.mock_server_get.return_value = server
4362
4363 url = reverse('horizon:project:instances:rebuild', args=[server.id])
4364 res = self.client.get(url)
4365
4366 self.assertTemplateUsed(res, 'project/instances/rebuild.html')
4367
4368 password_field_label = 'Rebuild Password'
4369 if expect_password_fields:
4370 self.assertContains(res, password_field_label)
4371 else:
4372 self.assertNotContains(res, password_field_label)
4373
4374 self.mock_server_get.assert_called_once_with(
4375 helpers.IsHttpRequest(), server.id)
4376 self._check_glance_image_list_detailed(count=4)
4377 self.mock_is_feature_available.assert_called_once_with(
4378 helpers.IsHttpRequest(), "instance_description"
4379 )
4380
4381 @django.test.utils.override_settings(
4382 OPENSTACK_HYPERVISOR_FEATURES={'can_set_password': False})
4383 def test_rebuild_instance_get_without_set_password(self):
4384 self.test_rebuild_instance_get(expect_password_fields=False)
4385
4386 def _instance_rebuild_post(self, server_id, image_id,
4387 password=None, confirm_password=None,
4388 disk_config=None):
4389 form_data = {'instance_id': server_id,
4390 'image': image_id,
4391 'disk_config': disk_config}
4392 if password is not None:
4393 form_data.update(password=password)
4394 if confirm_password is not None:
4395 form_data.update(confirm_password=confirm_password)
4396 url = reverse('horizon:project:instances:rebuild',
4397 args=[server_id])
4398 return self.client.post(url, form_data)
4399
4400 instance_rebuild_post_stubs = {
4401 api.nova: ('server_get',
4402 'server_rebuild',
4403 'is_feature_available',),
4404 api.glance: ('image_list_detailed',)}
4405
4406 @helpers.create_mocks(instance_rebuild_post_stubs)
4407 def test_rebuild_instance_post_with_password(self):
4408 server = self.servers.first()
4409 image = self.images.first()
4410 password = 'testpass'
4411
4412 self.mock_server_get.return_value = server
4413 self._mock_glance_image_list_detailed(self.images.list())
4414 self.mock_server_rebuild.return_value = []
4415 self.mock_is_feature_available.return_value = False
4416
4417 res = self._instance_rebuild_post(server.id, image.id,
4418 password=password,
4419 confirm_password=password,
4420 disk_config='AUTO')
4421 self.assertNoFormErrors(res)
4422 self.assertRedirectsNoFollow(res, INDEX_URL)
4423
4424 self.mock_server_get.assert_called_once_with(
4425 helpers.IsHttpRequest(), server.id)
4426 self._check_glance_image_list_detailed(count=4)
4427 self.mock_server_rebuild.assert_called_once_with(
4428 helpers.IsHttpRequest(), server.id, image.id, password, 'AUTO',
4429 description=None)
4430 self.mock_is_feature_available.assert_called_once_with(
4431 helpers.IsHttpRequest(), "instance_description"
4432 )
4433
4434 @helpers.create_mocks(instance_rebuild_post_stubs)
4435 def test_rebuild_instance_post_with_password_equals_none(self):
4436 server = self.servers.first()
4437 image = self.images.first()
4438
4439 self.mock_server_get.return_value = server
4440 self._mock_glance_image_list_detailed(self.images.list())
4441 self.mock_server_rebuild.side_effect = self.exceptions.nova
4442 self.mock_is_feature_available.return_value = False
4443
4444 res = self._instance_rebuild_post(server.id, image.id,
4445 password=None,
4446 confirm_password=None,
4447 disk_config='AUTO')
4448 self.assertRedirectsNoFollow(res, INDEX_URL)
4449
4450 self.mock_server_get.assert_called_once_with(
4451 helpers.IsHttpRequest(), server.id)
4452 self._check_glance_image_list_detailed(count=4)
4453 self.mock_server_rebuild.assert_called_once_with(
4454 helpers.IsHttpRequest(), server.id, image.id, None, 'AUTO',
4455 description=None)
4456 self.mock_is_feature_available.assert_called_once_with(
4457 helpers.IsHttpRequest(), "instance_description"
4458 )
4459
4460 @helpers.create_mocks(instance_rebuild_post_stubs)
4461 def test_rebuild_instance_post_password_do_not_match(self):
4462 server = self.servers.first()
4463 image = self.images.first()
4464 pass1 = 'somepass'
4465 pass2 = 'notsomepass'
4466
4467 self.mock_server_get.return_value = server
4468 self._mock_glance_image_list_detailed(self.images.list())
4469 self.mock_is_feature_available.return_value = False
4470
4471 res = self._instance_rebuild_post(server.id, image.id,
4472 password=pass1,
4473 confirm_password=pass2,
4474 disk_config='MANUAL')
4475
4476 self.