Closing keystone sessions after done with them.
[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, keystone_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, self.os_session)
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, self.os_session)
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, self.os_session)
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         super(self.__class__, self).__clean__()
127
128     def test_create_keypair(self):
129         """
130         Tests the creation of an OpenStack keypair that does not exist.
131         """
132         self.keypair = nova_utils.upload_keypair(self.nova, self.keypair_name,
133                                                  self.public_key)
134         result = nova_utils.keypair_exists(self.nova, self.keypair)
135         self.assertEqual(self.keypair, result)
136         keypair = nova_utils.get_keypair_by_name(self.nova, self.keypair_name)
137         self.assertEqual(self.keypair, keypair)
138
139     def test_create_delete_keypair(self):
140         """
141         Tests the creation of an OpenStack keypair that does not exist.
142         """
143         self.keypair = nova_utils.upload_keypair(self.nova, self.keypair_name,
144                                                  self.public_key)
145         result = nova_utils.keypair_exists(self.nova, self.keypair)
146         self.assertEqual(self.keypair, result)
147         nova_utils.delete_keypair(self.nova, self.keypair)
148         result2 = nova_utils.keypair_exists(self.nova, self.keypair)
149         self.assertIsNone(result2)
150
151     def test_create_key_from_file(self):
152         """
153         Tests that the generated RSA keys are properly saved to files
154         :return:
155         """
156         file_utils.save_keys_to_files(self.keys, self.pub_key_file_path,
157                                       self.priv_key_file_path)
158         self.keypair = nova_utils.upload_keypair_file(self.nova,
159                                                       self.keypair_name,
160                                                       self.pub_key_file_path)
161         pub_key_file = open(os.path.expanduser(self.pub_key_file_path))
162         pub_key = pub_key_file.read()
163         pub_key_file.close()
164         self.assertEqual(self.keypair.public_key, pub_key)
165
166
167 class NovaUtilsFlavorTests(OSComponentTestCase):
168     """
169     Test basic nova flavor functionality
170     """
171
172     def setUp(self):
173         """
174         Instantiates the CreateImage object that is responsible for downloading
175         and creating an OS image file within OpenStack
176         """
177         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
178         self.flavor_settings = FlavorConfig(
179             name=guid + '-name', flavor_id=guid + '-id', ram=1, disk=1,
180             vcpus=1, ephemeral=1, swap=2, rxtx_factor=3.0, is_public=False)
181         self.nova = nova_utils.nova_client(self.os_creds, self.os_session)
182         self.flavor = None
183
184     def tearDown(self):
185         """
186         Cleans the image and downloaded image file
187         """
188         if self.flavor:
189             try:
190                 nova_utils.delete_flavor(self.nova, self.flavor)
191             except:
192                 pass
193
194         super(self.__class__, self).__clean__()
195
196     def test_create_flavor(self):
197         """
198         Tests the creation of an OpenStack keypair that does not exist.
199         """
200         self.flavor = nova_utils.create_flavor(self.nova, self.flavor_settings)
201         self.validate_flavor()
202
203     def test_create_delete_flavor(self):
204         """
205         Tests the creation of an OpenStack keypair that does not exist.
206         """
207         self.flavor = nova_utils.create_flavor(self.nova, self.flavor_settings)
208         self.validate_flavor()
209         nova_utils.delete_flavor(self.nova, self.flavor)
210         flavor = nova_utils.get_flavor_by_name(self.nova,
211                                                self.flavor_settings.name)
212         self.assertIsNone(flavor)
213
214     def validate_flavor(self):
215         """
216         Validates the flavor_settings against the OpenStack flavor object
217         """
218         self.assertIsNotNone(self.flavor)
219         self.assertEqual(self.flavor_settings.name, self.flavor.name)
220         self.assertEqual(self.flavor_settings.flavor_id, self.flavor.id)
221         self.assertEqual(self.flavor_settings.ram, self.flavor.ram)
222         self.assertEqual(self.flavor_settings.disk, self.flavor.disk)
223         self.assertEqual(self.flavor_settings.vcpus, self.flavor.vcpus)
224         self.assertEqual(self.flavor_settings.ephemeral, self.flavor.ephemeral)
225
226         if self.flavor_settings.swap == 0:
227             self.assertEqual('', self.flavor.swap)
228         else:
229             self.assertEqual(self.flavor_settings.swap, self.flavor.swap)
230
231         self.assertEqual(self.flavor_settings.rxtx_factor,
232                          self.flavor.rxtx_factor)
233         self.assertEqual(self.flavor_settings.is_public, self.flavor.is_public)
234
235
236 class NovaUtilsInstanceTests(OSComponentTestCase):
237     """
238     Tests the creation of VM instances via nova_utils.py
239     """
240
241     def setUp(self):
242         """
243         Setup objects required by VM instances
244         :return:
245         """
246
247         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
248
249         self.nova = nova_utils.nova_client(
250             self.os_creds, self.os_session)
251         self.keystone = keystone_utils.keystone_client(
252             self.os_creds, self.os_session)
253         self.neutron = neutron_utils.neutron_client(
254             self.os_creds, self.os_session)
255         self.glance = glance_utils.glance_client(
256             self.os_creds, self.os_session)
257
258         self.image_creator = None
259         self.network_creator = None
260         self.flavor_creator = None
261         self.port = None
262         self.vm_inst = None
263
264         try:
265             image_settings = openstack_tests.cirros_image_settings(
266                 name=guid + '-image', image_metadata=self.image_metadata)
267             self.image_creator = OpenStackImage(
268                 self.os_creds, image_settings=image_settings)
269             self.image_creator.create()
270
271             network_settings = openstack_tests.get_priv_net_config(
272                 guid + '-net', guid + '-subnet').network_settings
273             self.network_creator = OpenStackNetwork(
274                 self.os_creds, network_settings)
275             self.network_creator.create()
276
277             self.flavor_creator = OpenStackFlavor(
278                 self.os_creds,
279                 FlavorConfig(
280                     name=guid + '-flavor-name', ram=256, disk=10, vcpus=1))
281             self.flavor_creator.create()
282
283             port_settings = PortConfig(
284                 name=guid + '-port', network_name=network_settings.name)
285             self.port = neutron_utils.create_port(
286                 self.neutron, self.os_creds, port_settings)
287
288             self.instance_settings = VmInstanceConfig(
289                 name=guid + '-vm_inst',
290                 flavor=self.flavor_creator.flavor_settings.name,
291                 port_settings=[port_settings])
292         except:
293             self.tearDown()
294             raise
295
296     def tearDown(self):
297         """
298         Cleanup deployed resources
299         :return:
300         """
301         if self.vm_inst:
302             try:
303                 nova_utils.delete_vm_instance(self.nova, self.vm_inst)
304             except:
305                 pass
306         if self.port:
307             try:
308                 neutron_utils.delete_port(self.neutron, self.port)
309             except:
310                 pass
311         if self.flavor_creator:
312             try:
313                 self.flavor_creator.clean()
314             except:
315                 pass
316         if self.network_creator:
317             try:
318                 self.network_creator.clean()
319             except:
320                 pass
321         if self.image_creator:
322             try:
323                 self.image_creator.clean()
324             except:
325                 pass
326
327         super(self.__class__, self).__clean__()
328
329     def test_create_instance(self):
330         """
331         Tests the nova_utils.create_server() method
332         :return:
333         """
334
335         self.vm_inst = nova_utils.create_server(
336             self.nova, self.keystone, self.neutron, self.glance,
337             self.instance_settings, self.image_creator.image_settings,
338             self.os_creds.project_name)
339
340         self.assertIsNotNone(self.vm_inst)
341
342         # Wait until instance is ACTIVE
343         iters = 0
344         active = False
345         while iters < 60:
346             if create_instance.STATUS_ACTIVE == nova_utils.get_server_status(
347                     self.nova, self.vm_inst):
348                 active = True
349                 break
350
351             time.sleep(3)
352             iters += 1
353
354         self.assertTrue(active)
355         vm_inst = nova_utils.get_latest_server_object(
356             self.nova, self.neutron, self.keystone, self.vm_inst,
357             self.os_creds.project_name)
358
359         self.assertEqual(self.vm_inst.name, vm_inst.name)
360         self.assertEqual(self.vm_inst.id, vm_inst.id)
361
362
363 class NovaUtilsInstanceVolumeTests(OSComponentTestCase):
364     """
365     Tests the creation of VM instances via nova_utils.py
366     """
367
368     def setUp(self):
369         """
370         Setup objects required by VM instances
371         :return:
372         """
373
374         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
375
376         self.nova = nova_utils.nova_client(self.os_creds, self.os_session)
377         self.cinder = cinder_utils.cinder_client(
378             self.os_creds, self.os_session)
379
380         self.image_creator = None
381         self.network_creator = None
382         self.flavor_creator = None
383         self.volume_creator = None
384         self.instance_creator = None
385
386         try:
387             image_settings = openstack_tests.cirros_image_settings(
388                 name=guid + '-image', image_metadata=self.image_metadata)
389             self.image_creator = OpenStackImage(
390                 self.os_creds, image_settings=image_settings)
391             self.image_creator.create()
392
393             network_settings = openstack_tests.get_priv_net_config(
394                 guid + '-net', guid + '-subnet').network_settings
395             self.network_creator = OpenStackNetwork(
396                 self.os_creds, network_settings)
397             self.network_creator.create()
398
399             self.flavor_creator = OpenStackFlavor(
400                 self.os_creds,
401                 FlavorConfig(
402                     name=guid + '-flavor-name', ram=256, disk=10, vcpus=1))
403             self.flavor_creator.create()
404
405             # Create Volume
406             volume_settings = VolumeConfig(
407                 name=self.__class__.__name__ + '-' + str(guid))
408             self.volume_creator = OpenStackVolume(
409                 self.os_creds, volume_settings)
410             self.volume_creator.create(block=True)
411
412             port_settings = PortConfig(
413                 name=guid + '-port', network_name=network_settings.name)
414             instance_settings = VmInstanceConfig(
415                 name=guid + '-vm_inst',
416                 flavor=self.flavor_creator.flavor_settings.name,
417                 port_settings=[port_settings])
418             self.instance_creator = OpenStackVmInstance(
419                 self.os_creds, instance_settings, image_settings)
420             self.instance_creator.create(block=True)
421         except:
422             self.tearDown()
423             raise
424
425     def tearDown(self):
426         """
427         Cleanup deployed resources
428         :return:
429         """
430         if self.instance_creator:
431             try:
432                 self.instance_creator.clean()
433             except:
434                 pass
435         if self.volume_creator:
436             try:
437                 self.volume_creator.clean()
438             except:
439                 pass
440         if self.flavor_creator:
441             try:
442                 self.flavor_creator.clean()
443             except:
444                 pass
445         if self.network_creator:
446             try:
447                 self.network_creator.clean()
448             except:
449                 pass
450         if self.image_creator:
451             try:
452                 self.image_creator.clean()
453             except:
454                 pass
455
456         super(self.__class__, self).__clean__()
457
458     def test_add_remove_volume(self):
459         """
460         Tests the nova_utils.attach_volume() and detach_volume functions with
461         a timeout value
462         :return:
463         """
464
465         self.assertIsNotNone(self.volume_creator.get_volume())
466         self.assertEqual(0, len(self.volume_creator.get_volume().attachments))
467
468         # Attach volume to VM
469         neutron = neutron_utils.neutron_client(
470             self.os_creds, self.os_session)
471         keystone = keystone_utils.keystone_client(
472             self.os_creds, self.os_session)
473         self.assertIsNotNone(nova_utils.attach_volume(
474             self.nova, neutron, keystone, self.instance_creator.get_vm_inst(),
475             self.volume_creator.get_volume(), self.os_creds.project_name))
476
477         vol_attach = None
478         vol_detach = None
479         attached = False
480         start_time = time.time()
481         while time.time() < start_time + 120:
482             vol_attach = cinder_utils.get_volume_by_id(
483                 self.cinder, self.volume_creator.get_volume().id)
484
485             if len(vol_attach.attachments) > 0:
486                 attached = True
487                 break
488
489             time.sleep(3)
490
491         self.assertTrue(attached)
492         self.assertIsNotNone(vol_attach)
493
494         keystone = keystone_utils.keystone_client(
495             self.os_creds, self.os_session)
496         vm_attach = nova_utils.get_server_object_by_id(
497             self.nova, neutron, keystone,
498             self.instance_creator.get_vm_inst().id, self.os_creds.project_name)
499
500         # Validate Attachment
501         self.assertIsNotNone(vol_attach)
502         self.assertEqual(self.volume_creator.get_volume().id, vol_attach.id)
503         self.assertEqual(1, len(vol_attach.attachments))
504         self.assertEqual(vm_attach.volume_ids[0]['id'],
505                          vol_attach.attachments[0]['volume_id'])
506
507         # Detach volume to VM
508         self.assertIsNotNone(nova_utils.detach_volume(
509             self.nova, neutron, keystone, self.instance_creator.get_vm_inst(),
510             self.volume_creator.get_volume(), self.os_creds.project_name))
511
512         start_time = time.time()
513         while time.time() < start_time + 120:
514             vol_detach = cinder_utils.get_volume_by_id(
515                 self.cinder, self.volume_creator.get_volume().id)
516             if len(vol_detach.attachments) == 0:
517                 attached = False
518                 break
519
520             time.sleep(3)
521
522         self.assertFalse(attached)
523         self.assertIsNotNone(vol_detach)
524
525         vm_detach = nova_utils.get_server_object_by_id(
526             self.nova, neutron, keystone,
527             self.instance_creator.get_vm_inst().id, self.os_creds.project_name)
528
529         # Validate Detachment
530         self.assertIsNotNone(vol_detach)
531         self.assertEqual(self.volume_creator.get_volume().id, vol_detach.id)
532
533         self.assertEqual(0, len(vol_detach.attachments))
534         self.assertEqual(0, len(vm_detach.volume_ids))
535
536     def test_attach_volume_nowait(self):
537         """
538         Tests the nova_utils.attach_volume() with a timeout value that is too
539         small to have the volume attachment data to be included on the VmInst
540         object that was supposed to be returned
541         """
542
543         self.assertIsNotNone(self.volume_creator.get_volume())
544         self.assertEqual(0, len(self.volume_creator.get_volume().attachments))
545
546         # Attach volume to VM
547         neutron = neutron_utils.neutron_client(self.os_creds, self.os_session)
548         keystone = keystone_utils.keystone_client(
549             self.os_creds, self.os_session)
550         with self.assertRaises(NovaException):
551             nova_utils.attach_volume(
552                 self.nova, neutron, keystone,
553                 self.instance_creator.get_vm_inst(),
554                 self.volume_creator.get_volume(), self.os_creds.project_name,
555                 0)
556
557     def test_detach_volume_nowait(self):
558         """
559         Tests the nova_utils.detach_volume() with a timeout value that is too
560         small to have the volume attachment data to be included on the VmInst
561         object that was supposed to be returned
562         """
563
564         self.assertIsNotNone(self.volume_creator.get_volume())
565         self.assertEqual(0, len(self.volume_creator.get_volume().attachments))
566
567         # Attach volume to VM
568         neutron = neutron_utils.neutron_client(self.os_creds, self.os_session)
569         keystone = keystone_utils.keystone_client(
570             self.os_creds, self.os_session)
571         nova_utils.attach_volume(
572             self.nova, neutron, keystone, self.instance_creator.get_vm_inst(),
573             self.volume_creator.get_volume(), self.os_creds.project_name)
574
575         # Check VmInst for attachment
576         keystone = keystone_utils.keystone_client(
577             self.os_creds, self.os_session)
578         latest_vm = nova_utils.get_server_object_by_id(
579             self.nova, neutron, keystone,
580             self.instance_creator.get_vm_inst().id, self.os_creds.project_name)
581         self.assertEqual(1, len(latest_vm.volume_ids))
582
583         # Check Volume for attachment
584         vol_attach = None
585         attached = False
586         start_time = time.time()
587         while time.time() < start_time + 120:
588             vol_attach = cinder_utils.get_volume_by_id(
589                 self.cinder, self.volume_creator.get_volume().id)
590
591             if len(vol_attach.attachments) > 0:
592                 attached = True
593                 break
594
595             time.sleep(3)
596
597         self.assertTrue(attached)
598         self.assertIsNotNone(vol_attach)
599
600         # Detach volume
601         with self.assertRaises(NovaException):
602             nova_utils.detach_volume(
603                 self.nova, neutron, keystone,
604                 self.instance_creator.get_vm_inst(),
605                 self.volume_creator.get_volume(), self.os_creds.project_name,
606                 0)