Extra changes on Flavor Config
[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                 self.os_creds.project_name, guid + '-net',
273                 guid + '-subnet').network_settings
274             self.network_creator = OpenStackNetwork(
275                 self.os_creds, network_settings)
276             self.network_creator.create()
277
278             self.flavor_creator = OpenStackFlavor(
279                 self.os_creds,
280                 FlavorConfig(
281                     name=guid + '-flavor-name', ram=256, disk=10, vcpus=1))
282             self.flavor_creator.create()
283
284             port_settings = PortConfig(
285                 name=guid + '-port', network_name=network_settings.name)
286             self.port = neutron_utils.create_port(
287                 self.neutron, self.os_creds, port_settings)
288
289             self.instance_settings = VmInstanceConfig(
290                 name=guid + '-vm_inst',
291                 flavor=self.flavor_creator.flavor_settings.name,
292                 port_settings=[port_settings])
293         except:
294             self.tearDown()
295             raise
296
297     def tearDown(self):
298         """
299         Cleanup deployed resources
300         :return:
301         """
302         if self.vm_inst:
303             try:
304                 nova_utils.delete_vm_instance(self.nova, self.vm_inst)
305             except:
306                 pass
307         if self.port:
308             try:
309                 neutron_utils.delete_port(self.neutron, self.port)
310             except:
311                 pass
312         if self.flavor_creator:
313             try:
314                 self.flavor_creator.clean()
315             except:
316                 pass
317         if self.network_creator:
318             try:
319                 self.network_creator.clean()
320             except:
321                 pass
322         if self.image_creator:
323             try:
324                 self.image_creator.clean()
325             except:
326                 pass
327
328         super(self.__class__, self).__clean__()
329
330     def test_create_instance(self):
331         """
332         Tests the nova_utils.create_server() method
333         :return:
334         """
335
336         self.vm_inst = nova_utils.create_server(
337             self.nova, self.keystone, self.neutron, self.glance,
338             self.instance_settings, self.image_creator.image_settings,
339             self.os_creds.project_name)
340
341         self.assertIsNotNone(self.vm_inst)
342
343         # Wait until instance is ACTIVE
344         iters = 0
345         active = False
346         status = None
347         while iters < 60:
348             status = nova_utils.get_server_status(self.nova, self.vm_inst)
349             if create_instance.STATUS_ACTIVE == status:
350                 active = True
351                 break
352
353             time.sleep(3)
354             iters += 1
355
356         self.assertTrue(active, msg='VM {} status {} is not {}'.format(
357             self.vm_inst.name, status, create_instance.STATUS_ACTIVE))
358         vm_inst = nova_utils.get_latest_server_object(
359             self.nova, self.neutron, self.keystone, self.vm_inst,
360             self.os_creds.project_name)
361
362         self.assertEqual(self.vm_inst.name, vm_inst.name)
363         self.assertEqual(self.vm_inst.id, vm_inst.id)
364
365
366 class NovaUtilsInstanceVolumeTests(OSComponentTestCase):
367     """
368     Tests the creation of VM instances via nova_utils.py
369     """
370
371     def setUp(self):
372         """
373         Setup objects required by VM instances
374         :return:
375         """
376
377         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
378
379         self.nova = nova_utils.nova_client(self.os_creds, self.os_session)
380         self.cinder = cinder_utils.cinder_client(
381             self.os_creds, self.os_session)
382
383         self.image_creator = None
384         self.network_creator = None
385         self.flavor_creator = None
386         self.volume_creator = None
387         self.instance_creator = None
388
389         try:
390             image_settings = openstack_tests.cirros_image_settings(
391                 name=guid + '-image', image_metadata=self.image_metadata)
392             self.image_creator = OpenStackImage(
393                 self.os_creds, image_settings=image_settings)
394             self.image_creator.create()
395
396             network_settings = openstack_tests.get_priv_net_config(
397                 self.os_creds.project_name, guid + '-net',
398                 guid + '-subnet').network_settings
399             self.network_creator = OpenStackNetwork(
400                 self.os_creds, network_settings)
401             self.network_creator.create()
402
403             flavor_settings = openstack_tests.get_flavor_config(
404                 name=guid + '-flavor', ram=256, disk=10, vcpus=1,
405                 metadata=self.flavor_metadata)
406             self.flavor_creator = OpenStackFlavor(
407                 self.os_creds, flavor_settings)
408             self.flavor_creator.create()
409
410             # Create Volume
411             volume_settings = VolumeConfig(
412                 name=self.__class__.__name__ + '-' + str(guid))
413             self.volume_creator = OpenStackVolume(
414                 self.os_creds, volume_settings)
415             self.volume_creator.create(block=True)
416
417             port_settings = PortConfig(
418                 name=guid + '-port', network_name=network_settings.name)
419             instance_settings = VmInstanceConfig(
420                 name=guid + '-vm_inst',
421                 flavor=self.flavor_creator.flavor_settings.name,
422                 port_settings=[port_settings])
423             self.instance_creator = OpenStackVmInstance(
424                 self.os_creds, instance_settings, image_settings)
425             self.instance_creator.create(block=True)
426         except:
427             self.tearDown()
428             raise
429
430     def tearDown(self):
431         """
432         Cleanup deployed resources
433         :return:
434         """
435         if self.instance_creator:
436             try:
437                 self.instance_creator.clean()
438             except:
439                 pass
440         if self.volume_creator:
441             try:
442                 self.volume_creator.clean()
443             except:
444                 pass
445         if self.flavor_creator:
446             try:
447                 self.flavor_creator.clean()
448             except:
449                 pass
450         if self.network_creator:
451             try:
452                 self.network_creator.clean()
453             except:
454                 pass
455         if self.image_creator:
456             try:
457                 self.image_creator.clean()
458             except:
459                 pass
460
461         super(self.__class__, self).__clean__()
462
463     def test_add_remove_volume(self):
464         """
465         Tests the nova_utils.attach_volume() and detach_volume functions with
466         a timeout value
467         :return:
468         """
469
470         self.assertIsNotNone(self.volume_creator.get_volume())
471         self.assertEqual(0, len(self.volume_creator.get_volume().attachments))
472
473         # Attach volume to VM
474         neutron = neutron_utils.neutron_client(
475             self.os_creds, self.os_session)
476         keystone = keystone_utils.keystone_client(
477             self.os_creds, self.os_session)
478         self.assertIsNotNone(nova_utils.attach_volume(
479             self.nova, neutron, keystone, self.instance_creator.get_vm_inst(),
480             self.volume_creator.get_volume(), self.os_creds.project_name))
481
482         vol_attach = None
483         vol_detach = None
484         attached = False
485         start_time = time.time()
486         while time.time() < start_time + 120:
487             vol_attach = cinder_utils.get_volume_by_id(
488                 self.cinder, self.volume_creator.get_volume().id)
489
490             if len(vol_attach.attachments) > 0:
491                 attached = True
492                 break
493
494             time.sleep(3)
495
496         self.assertTrue(attached)
497         self.assertIsNotNone(vol_attach)
498
499         keystone = keystone_utils.keystone_client(
500             self.os_creds, self.os_session)
501         vm_attach = nova_utils.get_server_object_by_id(
502             self.nova, neutron, keystone,
503             self.instance_creator.get_vm_inst().id, self.os_creds.project_name)
504
505         # Validate Attachment
506         self.assertIsNotNone(vol_attach)
507         self.assertEqual(self.volume_creator.get_volume().id, vol_attach.id)
508         self.assertEqual(1, len(vol_attach.attachments))
509         self.assertEqual(vm_attach.volume_ids[0]['id'],
510                          vol_attach.attachments[0]['volume_id'])
511
512         # Detach volume to VM
513         self.assertIsNotNone(nova_utils.detach_volume(
514             self.nova, neutron, keystone, self.instance_creator.get_vm_inst(),
515             self.volume_creator.get_volume(), self.os_creds.project_name))
516
517         start_time = time.time()
518         while time.time() < start_time + 120:
519             vol_detach = cinder_utils.get_volume_by_id(
520                 self.cinder, self.volume_creator.get_volume().id)
521             if len(vol_detach.attachments) == 0:
522                 attached = False
523                 break
524
525             time.sleep(3)
526
527         self.assertFalse(attached)
528         self.assertIsNotNone(vol_detach)
529
530         vm_detach = nova_utils.get_server_object_by_id(
531             self.nova, neutron, keystone,
532             self.instance_creator.get_vm_inst().id, self.os_creds.project_name)
533
534         # Validate Detachment
535         self.assertIsNotNone(vol_detach)
536         self.assertEqual(self.volume_creator.get_volume().id, vol_detach.id)
537
538         self.assertEqual(0, len(vol_detach.attachments))
539         self.assertEqual(0, len(vm_detach.volume_ids))
540
541     def test_attach_volume_nowait(self):
542         """
543         Tests the nova_utils.attach_volume() with a timeout value that is too
544         small to have the volume attachment data to be included on the VmInst
545         object that was supposed to be returned
546         """
547
548         self.assertIsNotNone(self.volume_creator.get_volume())
549         self.assertEqual(0, len(self.volume_creator.get_volume().attachments))
550
551         # Attach volume to VM
552         neutron = neutron_utils.neutron_client(self.os_creds, self.os_session)
553         keystone = keystone_utils.keystone_client(
554             self.os_creds, self.os_session)
555         with self.assertRaises(NovaException):
556             nova_utils.attach_volume(
557                 self.nova, neutron, keystone,
558                 self.instance_creator.get_vm_inst(),
559                 self.volume_creator.get_volume(), self.os_creds.project_name,
560                 0)
561
562     def test_detach_volume_nowait(self):
563         """
564         Tests the nova_utils.detach_volume() with a timeout value that is too
565         small to have the volume attachment data to be included on the VmInst
566         object that was supposed to be returned
567         """
568
569         self.assertIsNotNone(self.volume_creator.get_volume())
570         self.assertEqual(0, len(self.volume_creator.get_volume().attachments))
571
572         # Attach volume to VM
573         neutron = neutron_utils.neutron_client(self.os_creds, self.os_session)
574         keystone = keystone_utils.keystone_client(
575             self.os_creds, self.os_session)
576         nova_utils.attach_volume(
577             self.nova, neutron, keystone, self.instance_creator.get_vm_inst(),
578             self.volume_creator.get_volume(), self.os_creds.project_name)
579
580         # Check VmInst for attachment
581         keystone = keystone_utils.keystone_client(
582             self.os_creds, self.os_session)
583         latest_vm = nova_utils.get_server_object_by_id(
584             self.nova, neutron, keystone,
585             self.instance_creator.get_vm_inst().id, self.os_creds.project_name)
586         self.assertEqual(1, len(latest_vm.volume_ids))
587
588         # Check Volume for attachment
589         vol_attach = None
590         attached = False
591         start_time = time.time()
592         while time.time() < start_time + 120:
593             vol_attach = cinder_utils.get_volume_by_id(
594                 self.cinder, self.volume_creator.get_volume().id)
595
596             if len(vol_attach.attachments) > 0:
597                 attached = True
598                 break
599
600             time.sleep(3)
601
602         self.assertTrue(attached)
603         self.assertIsNotNone(vol_attach)
604
605         # Detach volume
606         with self.assertRaises(NovaException):
607             nova_utils.detach_volume(
608                 self.nova, neutron, keystone,
609                 self.instance_creator.get_vm_inst(),
610                 self.volume_creator.get_volume(), self.os_creds.project_name,
611                 0)