openstack: nova_utils_tests: Increase timeout for API calls
[snaps.git] / snaps / openstack / utils / tests / nova_utils_tests.py
1 # Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
2 #                    and others.  All rights reserved.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at:
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 import logging
16 import time
17 import uuid
18
19 import os
20
21 from snaps import file_utils
22 from snaps.config.flavor import FlavorConfig
23 from snaps.config.network import PortConfig
24 from snaps.config.vm_inst import VmInstanceConfig
25 from snaps.config.volume import VolumeConfig
26 from snaps.openstack import create_instance
27 from snaps.openstack.create_flavor import OpenStackFlavor
28 from snaps.openstack.create_image import OpenStackImage
29 from snaps.openstack.create_instance import OpenStackVmInstance
30 from snaps.openstack.create_network import OpenStackNetwork
31 from snaps.openstack.create_volume import OpenStackVolume
32 from snaps.openstack.tests import openstack_tests
33 from snaps.openstack.tests.os_source_file_test import OSComponentTestCase
34 from snaps.openstack.utils import (
35     nova_utils, neutron_utils, glance_utils, cinder_utils)
36 from snaps.openstack.utils.nova_utils import NovaException
37
38 __author__ = 'spisarski'
39
40 logger = logging.getLogger('nova_utils_tests')
41
42
43 class NovaSmokeTests(OSComponentTestCase):
44     """
45     Tests to ensure that the nova client can communicate with the cloud
46     """
47
48     def test_nova_connect_success(self):
49         """
50         Tests to ensure that the proper credentials can connect.
51         """
52         nova = nova_utils.nova_client(self.os_creds)
53
54         # This should not throw an exception
55         nova.flavors.list()
56
57     def test_nova_get_hypervisor_hosts(self):
58         """
59         Tests to ensure that get_hypervisors() function works.
60         """
61         nova = nova_utils.nova_client(self.os_creds)
62
63         hosts = nova_utils.get_hypervisor_hosts(nova)
64         # This should not throw an exception
65         self.assertGreaterEqual(len(hosts), 1)
66
67     def test_nova_connect_fail(self):
68         """
69         Tests to ensure that the improper credentials cannot connect.
70         """
71         from snaps.openstack.os_credentials import OSCreds
72
73         nova = nova_utils.nova_client(
74             OSCreds(username='user', password='pass',
75                     auth_url=self.os_creds.auth_url,
76                     project_name=self.os_creds.project_name,
77                     proxy_settings=self.os_creds.proxy_settings))
78
79         # This should throw an exception
80         with self.assertRaises(Exception):
81             nova.flavors.list()
82
83
84 class NovaUtilsKeypairTests(OSComponentTestCase):
85     """
86     Test basic nova keypair functionality
87     """
88
89     def setUp(self):
90         """
91         Instantiates the CreateImage object that is responsible for downloading
92         and creating an OS image file within OpenStack
93         """
94         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
95         self.priv_key_file_path = 'tmp/' + guid
96         self.pub_key_file_path = self.priv_key_file_path + '.pub'
97
98         self.nova = nova_utils.nova_client(self.os_creds)
99         self.keys = nova_utils.create_keys()
100         self.public_key = nova_utils.public_key_openssh(self.keys)
101         self.keypair_name = guid
102         self.keypair = None
103
104     def tearDown(self):
105         """
106         Cleans the image and downloaded image file
107         """
108         if self.keypair:
109             try:
110                 nova_utils.delete_keypair(self.nova, self.keypair)
111             except:
112                 pass
113
114         try:
115             os.chmod(self.priv_key_file_path, 0o777)
116             os.remove(self.priv_key_file_path)
117         except:
118             pass
119
120         try:
121             os.chmod(self.pub_key_file_path, 0o777)
122             os.remove(self.pub_key_file_path)
123         except:
124             pass
125
126     def test_create_keypair(self):
127         """
128         Tests the creation of an OpenStack keypair that does not exist.
129         """
130         self.keypair = nova_utils.upload_keypair(self.nova, self.keypair_name,
131                                                  self.public_key)
132         result = nova_utils.keypair_exists(self.nova, self.keypair)
133         self.assertEqual(self.keypair, result)
134         keypair = nova_utils.get_keypair_by_name(self.nova, self.keypair_name)
135         self.assertEqual(self.keypair, keypair)
136
137     def test_create_delete_keypair(self):
138         """
139         Tests the creation of an OpenStack keypair that does not exist.
140         """
141         self.keypair = nova_utils.upload_keypair(self.nova, self.keypair_name,
142                                                  self.public_key)
143         result = nova_utils.keypair_exists(self.nova, self.keypair)
144         self.assertEqual(self.keypair, result)
145         nova_utils.delete_keypair(self.nova, self.keypair)
146         result2 = nova_utils.keypair_exists(self.nova, self.keypair)
147         self.assertIsNone(result2)
148
149     def test_create_key_from_file(self):
150         """
151         Tests that the generated RSA keys are properly saved to files
152         :return:
153         """
154         file_utils.save_keys_to_files(self.keys, self.pub_key_file_path,
155                                       self.priv_key_file_path)
156         self.keypair = nova_utils.upload_keypair_file(self.nova,
157                                                       self.keypair_name,
158                                                       self.pub_key_file_path)
159         pub_key_file = open(os.path.expanduser(self.pub_key_file_path))
160         pub_key = pub_key_file.read()
161         pub_key_file.close()
162         self.assertEqual(self.keypair.public_key, pub_key)
163
164
165 class NovaUtilsFlavorTests(OSComponentTestCase):
166     """
167     Test basic nova flavor functionality
168     """
169
170     def setUp(self):
171         """
172         Instantiates the CreateImage object that is responsible for downloading
173         and creating an OS image file within OpenStack
174         """
175         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
176         self.flavor_settings = FlavorConfig(
177             name=guid + '-name', flavor_id=guid + '-id', ram=1, disk=1,
178             vcpus=1, ephemeral=1, swap=2, rxtx_factor=3.0, is_public=False)
179         self.nova = nova_utils.nova_client(self.os_creds)
180         self.flavor = None
181
182     def tearDown(self):
183         """
184         Cleans the image and downloaded image file
185         """
186         if self.flavor:
187             try:
188                 nova_utils.delete_flavor(self.nova, self.flavor)
189             except:
190                 pass
191
192     def test_create_flavor(self):
193         """
194         Tests the creation of an OpenStack keypair that does not exist.
195         """
196         self.flavor = nova_utils.create_flavor(self.nova, self.flavor_settings)
197         self.validate_flavor()
198
199     def test_create_delete_flavor(self):
200         """
201         Tests the creation of an OpenStack keypair that does not exist.
202         """
203         self.flavor = nova_utils.create_flavor(self.nova, self.flavor_settings)
204         self.validate_flavor()
205         nova_utils.delete_flavor(self.nova, self.flavor)
206         flavor = nova_utils.get_flavor_by_name(self.nova,
207                                                self.flavor_settings.name)
208         self.assertIsNone(flavor)
209
210     def validate_flavor(self):
211         """
212         Validates the flavor_settings against the OpenStack flavor object
213         """
214         self.assertIsNotNone(self.flavor)
215         self.assertEqual(self.flavor_settings.name, self.flavor.name)
216         self.assertEqual(self.flavor_settings.flavor_id, self.flavor.id)
217         self.assertEqual(self.flavor_settings.ram, self.flavor.ram)
218         self.assertEqual(self.flavor_settings.disk, self.flavor.disk)
219         self.assertEqual(self.flavor_settings.vcpus, self.flavor.vcpus)
220         self.assertEqual(self.flavor_settings.ephemeral, self.flavor.ephemeral)
221
222         if self.flavor_settings.swap == 0:
223             self.assertEqual('', self.flavor.swap)
224         else:
225             self.assertEqual(self.flavor_settings.swap, self.flavor.swap)
226
227         self.assertEqual(self.flavor_settings.rxtx_factor,
228                          self.flavor.rxtx_factor)
229         self.assertEqual(self.flavor_settings.is_public, self.flavor.is_public)
230
231
232 class NovaUtilsInstanceTests(OSComponentTestCase):
233     """
234     Tests the creation of VM instances via nova_utils.py
235     """
236
237     def setUp(self):
238         """
239         Setup objects required by VM instances
240         :return:
241         """
242
243         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
244
245         self.nova = nova_utils.nova_client(self.os_creds)
246         self.neutron = neutron_utils.neutron_client(self.os_creds)
247         self.glance = glance_utils.glance_client(self.os_creds)
248
249         self.image_creator = None
250         self.network_creator = None
251         self.flavor_creator = None
252         self.port = None
253         self.vm_inst = None
254
255         try:
256             image_settings = openstack_tests.cirros_image_settings(
257                 name=guid + '-image', image_metadata=self.image_metadata)
258             self.image_creator = OpenStackImage(
259                 self.os_creds, image_settings=image_settings)
260             self.image_creator.create()
261
262             network_settings = openstack_tests.get_priv_net_config(
263                 guid + '-net', guid + '-subnet').network_settings
264             self.network_creator = OpenStackNetwork(
265                 self.os_creds, network_settings)
266             self.network_creator.create()
267
268             self.flavor_creator = OpenStackFlavor(
269                 self.os_creds,
270                 FlavorConfig(
271                     name=guid + '-flavor-name', ram=256, disk=10, vcpus=1))
272             self.flavor_creator.create()
273
274             port_settings = PortConfig(
275                 name=guid + '-port', network_name=network_settings.name)
276             self.port = neutron_utils.create_port(
277                 self.neutron, self.os_creds, port_settings)
278
279             self.instance_settings = VmInstanceConfig(
280                 name=guid + '-vm_inst',
281                 flavor=self.flavor_creator.flavor_settings.name,
282                 port_settings=[port_settings])
283         except:
284             self.tearDown()
285             raise
286
287     def tearDown(self):
288         """
289         Cleanup deployed resources
290         :return:
291         """
292         if self.vm_inst:
293             try:
294                 nova_utils.delete_vm_instance(self.nova, self.vm_inst)
295             except:
296                 pass
297         if self.port:
298             try:
299                 neutron_utils.delete_port(self.neutron, self.port)
300             except:
301                 pass
302         if self.flavor_creator:
303             try:
304                 self.flavor_creator.clean()
305             except:
306                 pass
307         if self.network_creator:
308             try:
309                 self.network_creator.clean()
310             except:
311                 pass
312         if self.image_creator:
313             try:
314                 self.image_creator.clean()
315             except:
316                 pass
317
318     def test_create_instance(self):
319         """
320         Tests the nova_utils.create_server() method
321         :return:
322         """
323
324         self.vm_inst = nova_utils.create_server(
325             self.nova, self.neutron, self.glance, self.instance_settings,
326             self.image_creator.image_settings, self.project_id)
327
328         self.assertIsNotNone(self.vm_inst)
329
330         # Wait until instance is ACTIVE
331         iters = 0
332         active = False
333         while iters < 60:
334             if create_instance.STATUS_ACTIVE == nova_utils.get_server_status(
335                     self.nova, self.vm_inst):
336                 active = True
337                 break
338
339             time.sleep(3)
340             iters += 1
341
342         self.assertTrue(active)
343         vm_inst = nova_utils.get_latest_server_object(
344             self.nova, self.neutron, self.vm_inst, self.project_id)
345
346         self.assertEqual(self.vm_inst.name, vm_inst.name)
347         self.assertEqual(self.vm_inst.id, vm_inst.id)
348
349
350 class NovaUtilsInstanceVolumeTests(OSComponentTestCase):
351     """
352     Tests the creation of VM instances via nova_utils.py
353     """
354
355     def setUp(self):
356         """
357         Setup objects required by VM instances
358         :return:
359         """
360
361         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
362
363         self.nova = nova_utils.nova_client(self.os_creds)
364         self.cinder = cinder_utils.cinder_client(self.os_creds)
365
366         self.image_creator = None
367         self.network_creator = None
368         self.flavor_creator = None
369         self.volume_creator = None
370         self.instance_creator = None
371
372         try:
373             image_settings = openstack_tests.cirros_image_settings(
374                 name=guid + '-image', image_metadata=self.image_metadata)
375             self.image_creator = OpenStackImage(
376                 self.os_creds, image_settings=image_settings)
377             self.image_creator.create()
378
379             network_settings = openstack_tests.get_priv_net_config(
380                 guid + '-net', guid + '-subnet').network_settings
381             self.network_creator = OpenStackNetwork(
382                 self.os_creds, network_settings)
383             self.network_creator.create()
384
385             self.flavor_creator = OpenStackFlavor(
386                 self.os_creds,
387                 FlavorConfig(
388                     name=guid + '-flavor-name', ram=256, disk=10, vcpus=1))
389             self.flavor_creator.create()
390
391             # Create Volume
392             volume_settings = VolumeConfig(
393                 name=self.__class__.__name__ + '-' + str(guid))
394             self.volume_creator = OpenStackVolume(
395                 self.os_creds, volume_settings)
396             self.volume_creator.create(block=True)
397
398             port_settings = PortConfig(
399                 name=guid + '-port', network_name=network_settings.name)
400             instance_settings = VmInstanceConfig(
401                 name=guid + '-vm_inst',
402                 flavor=self.flavor_creator.flavor_settings.name,
403                 port_settings=[port_settings])
404             self.instance_creator = OpenStackVmInstance(
405                 self.os_creds, instance_settings, image_settings)
406             self.instance_creator.create(block=True)
407         except:
408             self.tearDown()
409             raise
410
411     def tearDown(self):
412         """
413         Cleanup deployed resources
414         :return:
415         """
416         if self.instance_creator:
417             try:
418                 self.instance_creator.clean()
419             except:
420                 pass
421         if self.volume_creator:
422             try:
423                 self.volume_creator.clean()
424             except:
425                 pass
426         if self.flavor_creator:
427             try:
428                 self.flavor_creator.clean()
429             except:
430                 pass
431         if self.network_creator:
432             try:
433                 self.network_creator.clean()
434             except:
435                 pass
436         if self.image_creator:
437             try:
438                 self.image_creator.clean()
439             except:
440                 pass
441
442     def test_add_remove_volume(self):
443         """
444         Tests the nova_utils.attach_volume() and detach_volume functions with
445         a timeout value
446         :return:
447         """
448
449         self.assertIsNotNone(self.volume_creator.get_volume())
450         self.assertEqual(0, len(self.volume_creator.get_volume().attachments))
451
452         # Attach volume to VM
453         neutron = neutron_utils.neutron_client(self.os_creds)
454         self.assertIsNotNone(nova_utils.attach_volume(
455             self.nova, neutron, self.instance_creator.get_vm_inst(),
456             self.volume_creator.get_volume(), self.project_id))
457
458         vol_attach = None
459         attached = False
460         start_time = time.time()
461         while time.time() < start_time + 120:
462             vol_attach = cinder_utils.get_volume_by_id(
463                 self.cinder, self.volume_creator.get_volume().id)
464
465             if len(vol_attach.attachments) > 0:
466                 attached = True
467                 break
468
469             time.sleep(3)
470
471         self.assertTrue(attached)
472         self.assertIsNotNone(vol_attach)
473
474         vm_attach = nova_utils.get_server_object_by_id(
475             self.nova, neutron, self.instance_creator.get_vm_inst().id,
476             self.project_id)
477
478         # Validate Attachment
479         self.assertIsNotNone(vol_attach)
480         self.assertEqual(self.volume_creator.get_volume().id, vol_attach.id)
481         self.assertEqual(1, len(vol_attach.attachments))
482         self.assertEqual(vm_attach.volume_ids[0]['id'],
483                          vol_attach.attachments[0]['volume_id'])
484
485         # Detach volume to VM
486         self.assertIsNotNone(nova_utils.detach_volume(
487             self.nova, neutron, self.instance_creator.get_vm_inst(),
488             self.volume_creator.get_volume(), self.project_id))
489
490         vol_detach = cinder_utils.get_volume_by_id(
491             self.cinder, self.volume_creator.get_volume().id)
492         vm_detach = nova_utils.get_server_object_by_id(
493             self.nova, neutron, self.instance_creator.get_vm_inst().id,
494             self.project_id)
495
496         # Validate Detachment
497         self.assertIsNotNone(vol_detach)
498         self.assertEqual(self.volume_creator.get_volume().id, vol_detach.id)
499
500         if len(vol_detach.attachments) > 0:
501             vol_detach = cinder_utils.get_volume_by_id(
502                 self.cinder, self.volume_creator.get_volume().id)
503
504         self.assertEqual(0, len(vol_detach.attachments))
505         self.assertEqual(0, len(vm_detach.volume_ids))
506
507     def test_attach_volume_nowait(self):
508         """
509         Tests the nova_utils.attach_volume() with a timeout value that is too
510         small to have the volume attachment data to be included on the VmInst
511         object that was supposed to be returned
512         """
513
514         self.assertIsNotNone(self.volume_creator.get_volume())
515         self.assertEqual(0, len(self.volume_creator.get_volume().attachments))
516
517         # Attach volume to VM
518         neutron = neutron_utils.neutron_client(self.os_creds)
519         with self.assertRaises(NovaException):
520             nova_utils.attach_volume(
521                 self.nova, neutron, self.instance_creator.get_vm_inst(),
522                 self.volume_creator.get_volume(), self.project_id, 0)
523
524     def test_detach_volume_nowait(self):
525         """
526         Tests the nova_utils.detach_volume() with a timeout value that is too
527         small to have the volume attachment data to be included on the VmInst
528         object that was supposed to be returned
529         """
530
531         self.assertIsNotNone(self.volume_creator.get_volume())
532         self.assertEqual(0, len(self.volume_creator.get_volume().attachments))
533
534         # Attach volume to VM
535         neutron = neutron_utils.neutron_client(self.os_creds)
536         nova_utils.attach_volume(
537             self.nova, neutron, self.instance_creator.get_vm_inst(),
538             self.volume_creator.get_volume(), self.project_id)
539
540         # Check VmInst for attachment
541         latest_vm = nova_utils.get_server_object_by_id(
542             self.nova, neutron, self.instance_creator.get_vm_inst().id,
543             self.project_id)
544         self.assertEqual(1, len(latest_vm.volume_ids))
545
546         # Check Volume for attachment
547         vol_attach = None
548         attached = False
549         start_time = time.time()
550         while time.time() < start_time + 120:
551             vol_attach = cinder_utils.get_volume_by_id(
552                 self.cinder, self.volume_creator.get_volume().id)
553
554             if len(vol_attach.attachments) > 0:
555                 attached = True
556                 break
557
558             time.sleep(3)
559
560         self.assertTrue(attached)
561         self.assertIsNotNone(vol_attach)
562
563         # Detach volume
564         with self.assertRaises(NovaException):
565             nova_utils.detach_volume(
566                 self.nova, neutron, self.instance_creator.get_vm_inst(),
567                 self.volume_creator.get_volume(), self.project_id, 0)