Refactoring of VolumeSettings to extend VolumeConfig
[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.volume import VolumeConfig
23 from snaps.config.flavor import FlavorConfig
24 from snaps.openstack import create_instance
25 from snaps.openstack.create_flavor import OpenStackFlavor
26 from snaps.openstack.create_image import OpenStackImage
27 from snaps.openstack.create_instance import (
28     VmInstanceSettings, OpenStackVmInstance)
29 from snaps.openstack.create_network import OpenStackNetwork, PortSettings
30 from snaps.openstack.create_volume import OpenStackVolume
31 from snaps.openstack.tests import openstack_tests
32 from snaps.openstack.tests.os_source_file_test import OSComponentTestCase
33 from snaps.openstack.utils import (
34     nova_utils, neutron_utils, glance_utils, cinder_utils)
35
36 __author__ = 'spisarski'
37
38 logger = logging.getLogger('nova_utils_tests')
39
40
41 class NovaSmokeTests(OSComponentTestCase):
42     """
43     Tests to ensure that the nova client can communicate with the cloud
44     """
45
46     def test_nova_connect_success(self):
47         """
48         Tests to ensure that the proper credentials can connect.
49         """
50         nova = nova_utils.nova_client(self.os_creds)
51
52         # This should not throw an exception
53         nova.flavors.list()
54
55     def test_nova_connect_fail(self):
56         """
57         Tests to ensure that the improper credentials cannot connect.
58         """
59         from snaps.openstack.os_credentials import OSCreds
60
61         nova = nova_utils.nova_client(
62             OSCreds(username='user', password='pass',
63                     auth_url=self.os_creds.auth_url,
64                     project_name=self.os_creds.project_name,
65                     proxy_settings=self.os_creds.proxy_settings))
66
67         # This should throw an exception
68         with self.assertRaises(Exception):
69             nova.flavors.list()
70
71
72 class NovaUtilsKeypairTests(OSComponentTestCase):
73     """
74     Test basic nova keypair functionality
75     """
76
77     def setUp(self):
78         """
79         Instantiates the CreateImage object that is responsible for downloading
80         and creating an OS image file within OpenStack
81         """
82         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
83         self.priv_key_file_path = 'tmp/' + guid
84         self.pub_key_file_path = self.priv_key_file_path + '.pub'
85
86         self.nova = nova_utils.nova_client(self.os_creds)
87         self.keys = nova_utils.create_keys()
88         self.public_key = nova_utils.public_key_openssh(self.keys)
89         self.keypair_name = guid
90         self.keypair = None
91
92     def tearDown(self):
93         """
94         Cleans the image and downloaded image file
95         """
96         if self.keypair:
97             try:
98                 nova_utils.delete_keypair(self.nova, self.keypair)
99             except:
100                 pass
101
102         try:
103             os.chmod(self.priv_key_file_path, 0o777)
104             os.remove(self.priv_key_file_path)
105         except:
106             pass
107
108         try:
109             os.chmod(self.pub_key_file_path, 0o777)
110             os.remove(self.pub_key_file_path)
111         except:
112             pass
113
114     def test_create_keypair(self):
115         """
116         Tests the creation of an OpenStack keypair that does not exist.
117         """
118         self.keypair = nova_utils.upload_keypair(self.nova, self.keypair_name,
119                                                  self.public_key)
120         result = nova_utils.keypair_exists(self.nova, self.keypair)
121         self.assertEqual(self.keypair, result)
122         keypair = nova_utils.get_keypair_by_name(self.nova, self.keypair_name)
123         self.assertEqual(self.keypair, keypair)
124
125     def test_create_delete_keypair(self):
126         """
127         Tests the creation of an OpenStack keypair that does not exist.
128         """
129         self.keypair = nova_utils.upload_keypair(self.nova, self.keypair_name,
130                                                  self.public_key)
131         result = nova_utils.keypair_exists(self.nova, self.keypair)
132         self.assertEqual(self.keypair, result)
133         nova_utils.delete_keypair(self.nova, self.keypair)
134         result2 = nova_utils.keypair_exists(self.nova, self.keypair)
135         self.assertIsNone(result2)
136
137     def test_create_key_from_file(self):
138         """
139         Tests that the generated RSA keys are properly saved to files
140         :return:
141         """
142         file_utils.save_keys_to_files(self.keys, self.pub_key_file_path,
143                                       self.priv_key_file_path)
144         self.keypair = nova_utils.upload_keypair_file(self.nova,
145                                                       self.keypair_name,
146                                                       self.pub_key_file_path)
147         pub_key_file = open(os.path.expanduser(self.pub_key_file_path))
148         pub_key = pub_key_file.read()
149         pub_key_file.close()
150         self.assertEqual(self.keypair.public_key, pub_key)
151
152
153 class NovaUtilsFlavorTests(OSComponentTestCase):
154     """
155     Test basic nova flavor functionality
156     """
157
158     def setUp(self):
159         """
160         Instantiates the CreateImage object that is responsible for downloading
161         and creating an OS image file within OpenStack
162         """
163         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
164         self.flavor_settings = FlavorConfig(
165             name=guid + '-name', flavor_id=guid + '-id', ram=1, disk=1,
166             vcpus=1, ephemeral=1, swap=2, rxtx_factor=3.0, is_public=False)
167         self.nova = nova_utils.nova_client(self.os_creds)
168         self.flavor = None
169
170     def tearDown(self):
171         """
172         Cleans the image and downloaded image file
173         """
174         if self.flavor:
175             try:
176                 nova_utils.delete_flavor(self.nova, self.flavor)
177             except:
178                 pass
179
180     def test_create_flavor(self):
181         """
182         Tests the creation of an OpenStack keypair that does not exist.
183         """
184         self.flavor = nova_utils.create_flavor(self.nova, self.flavor_settings)
185         self.validate_flavor()
186
187     def test_create_delete_flavor(self):
188         """
189         Tests the creation of an OpenStack keypair that does not exist.
190         """
191         self.flavor = nova_utils.create_flavor(self.nova, self.flavor_settings)
192         self.validate_flavor()
193         nova_utils.delete_flavor(self.nova, self.flavor)
194         flavor = nova_utils.get_flavor_by_name(self.nova,
195                                                self.flavor_settings.name)
196         self.assertIsNone(flavor)
197
198     def validate_flavor(self):
199         """
200         Validates the flavor_settings against the OpenStack flavor object
201         """
202         self.assertIsNotNone(self.flavor)
203         self.assertEqual(self.flavor_settings.name, self.flavor.name)
204         self.assertEqual(self.flavor_settings.flavor_id, self.flavor.id)
205         self.assertEqual(self.flavor_settings.ram, self.flavor.ram)
206         self.assertEqual(self.flavor_settings.disk, self.flavor.disk)
207         self.assertEqual(self.flavor_settings.vcpus, self.flavor.vcpus)
208         self.assertEqual(self.flavor_settings.ephemeral, self.flavor.ephemeral)
209
210         if self.flavor_settings.swap == 0:
211             self.assertEqual('', self.flavor.swap)
212         else:
213             self.assertEqual(self.flavor_settings.swap, self.flavor.swap)
214
215         self.assertEqual(self.flavor_settings.rxtx_factor,
216                          self.flavor.rxtx_factor)
217         self.assertEqual(self.flavor_settings.is_public, self.flavor.is_public)
218
219
220 class NovaUtilsInstanceTests(OSComponentTestCase):
221     """
222     Tests the creation of VM instances via nova_utils.py
223     """
224
225     def setUp(self):
226         """
227         Setup objects required by VM instances
228         :return:
229         """
230
231         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
232
233         self.nova = nova_utils.nova_client(self.os_creds)
234         self.neutron = neutron_utils.neutron_client(self.os_creds)
235         self.glance = glance_utils.glance_client(self.os_creds)
236
237         self.image_creator = None
238         self.network_creator = None
239         self.flavor_creator = None
240         self.port = None
241         self.vm_inst = None
242
243         try:
244             image_settings = openstack_tests.cirros_image_settings(
245                 name=guid + '-image', image_metadata=self.image_metadata)
246             self.image_creator = OpenStackImage(
247                 self.os_creds, image_settings=image_settings)
248             self.image_creator.create()
249
250             network_settings = openstack_tests.get_priv_net_config(
251                 guid + '-net', guid + '-subnet').network_settings
252             self.network_creator = OpenStackNetwork(
253                 self.os_creds, network_settings)
254             self.network_creator.create()
255
256             self.flavor_creator = OpenStackFlavor(
257                 self.os_creds,
258                 FlavorConfig(
259                     name=guid + '-flavor-name', ram=256, disk=10, vcpus=1))
260             self.flavor_creator.create()
261
262             port_settings = PortSettings(name=guid + '-port',
263                                          network_name=network_settings.name)
264             self.port = neutron_utils.create_port(
265                 self.neutron, self.os_creds, port_settings)
266
267             self.instance_settings = VmInstanceSettings(
268                 name=guid + '-vm_inst',
269                 flavor=self.flavor_creator.flavor_settings.name,
270                 port_settings=[port_settings])
271         except:
272             self.tearDown()
273             raise
274
275     def tearDown(self):
276         """
277         Cleanup deployed resources
278         :return:
279         """
280         if self.vm_inst:
281             try:
282                 nova_utils.delete_vm_instance(self.nova, self.vm_inst)
283             except:
284                 pass
285         if self.port:
286             try:
287                 neutron_utils.delete_port(self.neutron, self.port)
288             except:
289                 pass
290         if self.flavor_creator:
291             try:
292                 self.flavor_creator.clean()
293             except:
294                 pass
295         if self.network_creator:
296             try:
297                 self.network_creator.clean()
298             except:
299                 pass
300         if self.image_creator:
301             try:
302                 self.image_creator.clean()
303             except:
304                 pass
305
306     def test_create_instance(self):
307         """
308         Tests the nova_utils.create_server() method
309         :return:
310         """
311
312         self.vm_inst = nova_utils.create_server(
313             self.nova, self.neutron, self.glance, self.instance_settings,
314             self.image_creator.image_settings)
315
316         self.assertIsNotNone(self.vm_inst)
317
318         # Wait until instance is ACTIVE
319         iters = 0
320         active = False
321         while iters < 60:
322             if create_instance.STATUS_ACTIVE == nova_utils.get_server_status(
323                     self.nova, self.vm_inst):
324                 active = True
325                 break
326
327             time.sleep(3)
328             iters += 1
329
330         self.assertTrue(active)
331         vm_inst = nova_utils.get_latest_server_object(self.nova, self.vm_inst)
332
333         self.assertEqual(self.vm_inst.name, vm_inst.name)
334         self.assertEqual(self.vm_inst.id, vm_inst.id)
335
336
337 class NovaUtilsInstanceVolumeTests(OSComponentTestCase):
338     """
339     Tests the creation of VM instances via nova_utils.py
340     """
341
342     def setUp(self):
343         """
344         Setup objects required by VM instances
345         :return:
346         """
347
348         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
349
350         self.nova = nova_utils.nova_client(self.os_creds)
351         self.cinder = cinder_utils.cinder_client(self.os_creds)
352
353         self.image_creator = None
354         self.network_creator = None
355         self.flavor_creator = None
356         self.volume_creator = None
357         self.instance_creator = None
358
359         try:
360             image_settings = openstack_tests.cirros_image_settings(
361                 name=guid + '-image', image_metadata=self.image_metadata)
362             self.image_creator = OpenStackImage(
363                 self.os_creds, image_settings=image_settings)
364             self.image_creator.create()
365
366             network_settings = openstack_tests.get_priv_net_config(
367                 guid + '-net', guid + '-subnet').network_settings
368             self.network_creator = OpenStackNetwork(
369                 self.os_creds, network_settings)
370             self.network_creator.create()
371
372             self.flavor_creator = OpenStackFlavor(
373                 self.os_creds,
374                 FlavorConfig(
375                     name=guid + '-flavor-name', ram=256, disk=10, vcpus=1))
376             self.flavor_creator.create()
377
378             # Create Volume
379             volume_settings = VolumeConfig(
380                 name=self.__class__.__name__ + '-' + str(guid))
381             self.volume_creator = OpenStackVolume(
382                 self.os_creds, volume_settings)
383             self.volume_creator.create(block=True)
384
385             port_settings = PortSettings(
386                 name=guid + '-port', network_name=network_settings.name)
387             instance_settings = VmInstanceSettings(
388                 name=guid + '-vm_inst',
389                 flavor=self.flavor_creator.flavor_settings.name,
390                 port_settings=[port_settings])
391             self.instance_creator = OpenStackVmInstance(
392                 self.os_creds, instance_settings, image_settings)
393             self.instance_creator.create(block=True)
394         except:
395             self.tearDown()
396             raise
397
398     def tearDown(self):
399         """
400         Cleanup deployed resources
401         :return:
402         """
403         if self.instance_creator:
404             try:
405                 self.instance_creator.clean()
406             except:
407                 pass
408         if self.volume_creator:
409             try:
410                 self.volume_creator.clean()
411             except:
412                 pass
413         if self.flavor_creator:
414             try:
415                 self.flavor_creator.clean()
416             except:
417                 pass
418         if self.network_creator:
419             try:
420                 self.network_creator.clean()
421             except:
422                 pass
423         if self.image_creator:
424             try:
425                 self.image_creator.clean()
426             except:
427                 pass
428
429     def test_add_remove_volume(self):
430         """
431         Tests the nova_utils.create_server() method
432         :return:
433         """
434
435         self.assertIsNotNone(self.volume_creator.get_volume())
436         self.assertEqual(0, len(self.volume_creator.get_volume().attachments))
437
438         # Attach volume to VM
439         nova_utils.attach_volume(
440             self.nova, self.instance_creator.get_vm_inst(),
441             self.volume_creator.get_volume())
442
443         time.sleep(10)
444
445         vol_attach = cinder_utils.get_volume_by_id(
446             self.cinder, self.volume_creator.get_volume().id)
447         vm_attach = nova_utils.get_server_object_by_id(
448             self.nova, self.instance_creator.get_vm_inst().id)
449
450         # Detach volume to VM
451         nova_utils.detach_volume(
452             self.nova, self.instance_creator.get_vm_inst(),
453             self.volume_creator.get_volume())
454
455         time.sleep(10)
456
457         vol_detach = cinder_utils.get_volume_by_id(
458             self.cinder, self.volume_creator.get_volume().id)
459         vm_detach = nova_utils.get_server_object_by_id(
460             self.nova, self.instance_creator.get_vm_inst().id)
461
462         # Validate Attachment
463         self.assertIsNotNone(vol_attach)
464         self.assertEqual(self.volume_creator.get_volume().id, vol_attach.id)
465         self.assertEqual(1, len(vol_attach.attachments))
466         self.assertEqual(vm_attach.volume_ids[0]['id'],
467                          vol_attach.attachments[0]['volume_id'])
468
469         # Validate Detachment
470         self.assertIsNotNone(vol_detach)
471         self.assertEqual(self.volume_creator.get_volume().id, vol_detach.id)
472         self.assertEqual(0, len(vol_detach.attachments))
473         self.assertEqual(0, len(vm_detach.volume_ids))