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