ccdf45e1c75c194d5c671a5de7569ef5f7f741f5
[snaps.git] / snaps / openstack / tests / create_instance_tests.py
1 # Copyright (c) 2016 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 os
17 import re
18 import time
19 import unittest
20 import uuid
21
22 from snaps.openstack.create_instance import VmInstanceSettings, OpenStackVmInstance, FloatingIpSettings
23 from snaps.openstack.create_flavor import OpenStackFlavor, FlavorSettings
24 from snaps.openstack.create_keypairs import OpenStackKeypair, KeypairSettings
25 from snaps.openstack.create_network import OpenStackNetwork, PortSettings
26 from snaps.openstack.create_router import OpenStackRouter
27 from snaps.openstack.create_image import OpenStackImage
28 from snaps.openstack.create_security_group import SecurityGroupSettings, OpenStackSecurityGroup
29 from snaps.openstack.tests import openstack_tests, validation_utils
30 from snaps.openstack.utils import nova_utils
31 from snaps.openstack.tests.os_source_file_test import OSComponentTestCase, OSIntegrationTestCase
32
33 __author__ = 'spisarski'
34
35 VM_BOOT_TIMEOUT = 600
36
37 logger = logging.getLogger('create_instance_tests')
38
39
40 class VmInstanceSettingsUnitTests(unittest.TestCase):
41     """
42     Tests the construction of the VmInstanceSettings class
43     """
44
45     def test_no_params(self):
46         with self.assertRaises(Exception):
47             VmInstanceSettings()
48
49     def test_empty_config(self):
50         with self.assertRaises(Exception):
51             VmInstanceSettings(config=dict())
52
53     def test_name_only(self):
54         with self.assertRaises(Exception):
55             VmInstanceSettings(name='foo')
56
57     def test_config_with_name_only(self):
58         with self.assertRaises(Exception):
59             VmInstanceSettings(config={'name': 'foo'})
60
61     def test_name_flavor_only(self):
62         settings = VmInstanceSettings(name='foo', flavor='bar')
63         self.assertEquals('foo', settings.name)
64         self.assertEquals('bar', settings.flavor)
65         self.assertEquals(0, len(settings.port_settings))
66         self.assertEquals(0, len(settings.security_group_names))
67         self.assertEquals(0, len(settings.floating_ip_settings))
68         self.assertIsNone(settings.sudo_user)
69         self.assertEquals(900, settings.vm_boot_timeout)
70         self.assertEquals(300, settings.vm_delete_timeout)
71         self.assertEquals(180, settings.ssh_connect_timeout)
72         self.assertIsNone(settings.availability_zone)
73
74     def test_config_with_name_flavor_only(self):
75         settings = VmInstanceSettings(config={'name': 'foo', 'flavor': 'bar'})
76         self.assertEquals('foo', settings.name)
77         self.assertEquals('bar', settings.flavor)
78         self.assertEquals(0, len(settings.port_settings))
79         self.assertEquals(0, len(settings.security_group_names))
80         self.assertEquals(0, len(settings.floating_ip_settings))
81         self.assertIsNone(settings.sudo_user)
82         self.assertEquals(900, settings.vm_boot_timeout)
83         self.assertEquals(300, settings.vm_delete_timeout)
84         self.assertEquals(180, settings.ssh_connect_timeout)
85         self.assertIsNone(settings.availability_zone)
86
87     def test_all(self):
88         port_settings = PortSettings(name='foo-port', network_name='bar-net')
89         fip_settings = FloatingIpSettings(name='foo-fip', port_name='bar-port', router_name='foo-bar-router')
90
91         settings = VmInstanceSettings(name='foo', flavor='bar', port_settings=[port_settings],
92                                       security_group_names=['sec_grp_1'], floating_ip_settings=[fip_settings],
93                                       sudo_user='joe', vm_boot_timeout=999, vm_delete_timeout=333,
94                                       ssh_connect_timeout=111, availability_zone='server name')
95         self.assertEquals('foo', settings.name)
96         self.assertEquals('bar', settings.flavor)
97         self.assertEquals(1, len(settings.port_settings))
98         self.assertEquals('foo-port', settings.port_settings[0].name)
99         self.assertEquals('bar-net', settings.port_settings[0].network_name)
100         self.assertEquals(1, len(settings.security_group_names))
101         self.assertEquals('sec_grp_1', settings.security_group_names[0])
102         self.assertEquals(1, len(settings.floating_ip_settings))
103         self.assertEquals('foo-fip', settings.floating_ip_settings[0].name)
104         self.assertEquals('bar-port', settings.floating_ip_settings[0].port_name)
105         self.assertEquals('foo-bar-router', settings.floating_ip_settings[0].router_name)
106         self.assertEquals('joe', settings.sudo_user)
107         self.assertEquals(999, settings.vm_boot_timeout)
108         self.assertEquals(333, settings.vm_delete_timeout)
109         self.assertEquals(111, settings.ssh_connect_timeout)
110         self.assertEquals('server name', settings.availability_zone)
111
112     def test_config_all(self):
113         port_settings = PortSettings(name='foo-port', network_name='bar-net')
114         fip_settings = FloatingIpSettings(name='foo-fip', port_name='bar-port', router_name='foo-bar-router')
115
116         settings = VmInstanceSettings(config={'name': 'foo', 'flavor': 'bar', 'ports': [port_settings],
117                                               'security_group_names': ['sec_grp_1'],
118                                               'floating_ips': [fip_settings], 'sudo_user': 'joe',
119                                               'vm_boot_timeout': 999, 'vm_delete_timeout': 333,
120                                               'ssh_connect_timeout': 111, 'availability_zone': 'server name'})
121         self.assertEquals('foo', settings.name)
122         self.assertEquals('bar', settings.flavor)
123         self.assertEquals(1, len(settings.port_settings))
124         self.assertEquals('foo-port', settings.port_settings[0].name)
125         self.assertEquals('bar-net', settings.port_settings[0].network_name)
126         self.assertEquals(1, len(settings.security_group_names))
127         self.assertEquals(1, len(settings.floating_ip_settings))
128         self.assertEquals('foo-fip', settings.floating_ip_settings[0].name)
129         self.assertEquals('bar-port', settings.floating_ip_settings[0].port_name)
130         self.assertEquals('foo-bar-router', settings.floating_ip_settings[0].router_name)
131         self.assertEquals('joe', settings.sudo_user)
132         self.assertEquals(999, settings.vm_boot_timeout)
133         self.assertEquals(333, settings.vm_delete_timeout)
134         self.assertEquals(111, settings.ssh_connect_timeout)
135         self.assertEquals('server name', settings.availability_zone)
136
137
138 class FloatingIpSettingsUnitTests(unittest.TestCase):
139     """
140     Tests the construction of the FloatingIpSettings class
141     """
142
143     def test_no_params(self):
144         with self.assertRaises(Exception):
145             FloatingIpSettings()
146
147     def test_empty_config(self):
148         with self.assertRaises(Exception):
149             FloatingIpSettings(config=dict())
150
151     def test_name_only(self):
152         with self.assertRaises(Exception):
153             FloatingIpSettings(name='foo')
154
155     def test_config_with_name_only(self):
156         with self.assertRaises(Exception):
157             FloatingIpSettings(config={'name': 'foo'})
158
159     def test_name_port_only(self):
160         with self.assertRaises(Exception):
161             FloatingIpSettings(name='foo', port_name='bar')
162
163     def test_config_with_name_port_only(self):
164         with self.assertRaises(Exception):
165             FloatingIpSettings(config={'name': 'foo', 'port_name': 'bar'})
166
167     def test_name_router_only(self):
168         with self.assertRaises(Exception):
169             FloatingIpSettings(name='foo', router_name='bar')
170
171     def test_config_with_name_router_only(self):
172         with self.assertRaises(Exception):
173             FloatingIpSettings(config={'name': 'foo', 'router_name': 'bar'})
174
175     def test_name_port_router_only(self):
176         settings = FloatingIpSettings(name='foo', port_name='foo-port', router_name='bar-router')
177         self.assertEquals('foo', settings.name)
178         self.assertEquals('foo-port', settings.port_name)
179         self.assertEquals('bar-router', settings.router_name)
180         self.assertIsNone(settings.subnet_name)
181         self.assertTrue(settings.provisioning)
182
183     def test_config_with_name_port_router_only(self):
184         settings = FloatingIpSettings(config={'name': 'foo', 'port_name': 'foo-port', 'router_name': 'bar-router'})
185         self.assertEquals('foo', settings.name)
186         self.assertEquals('foo-port', settings.port_name)
187         self.assertEquals('bar-router', settings.router_name)
188         self.assertIsNone(settings.subnet_name)
189         self.assertTrue(settings.provisioning)
190
191     def test_all(self):
192         settings = FloatingIpSettings(name='foo', port_name='foo-port', router_name='bar-router',
193                                       subnet_name='bar-subnet', provisioning=False)
194         self.assertEquals('foo', settings.name)
195         self.assertEquals('foo-port', settings.port_name)
196         self.assertEquals('bar-router', settings.router_name)
197         self.assertEquals('bar-subnet', settings.subnet_name)
198         self.assertFalse(settings.provisioning)
199
200     def test_config_all(self):
201         settings = FloatingIpSettings(config={'name': 'foo', 'port_name': 'foo-port', 'router_name': 'bar-router',
202                                               'subnet_name': 'bar-subnet', 'provisioning': False})
203         self.assertEquals('foo', settings.name)
204         self.assertEquals('foo-port', settings.port_name)
205         self.assertEquals('bar-router', settings.router_name)
206         self.assertEquals('bar-subnet', settings.subnet_name)
207         self.assertFalse(settings.provisioning)
208
209
210 class SimpleHealthCheck(OSIntegrationTestCase):
211     """
212     Test for the CreateInstance class with a single NIC/Port with Floating IPs
213     """
214
215     def setUp(self):
216         """
217         Instantiates the CreateImage object that is responsible for downloading and creating an OS image file
218         within OpenStack
219         """
220         super(self.__class__, self).__start__()
221
222         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
223         self.keypair_priv_filepath = 'tmp/' + guid
224         self.keypair_pub_filepath = self.keypair_priv_filepath + '.pub'
225         self.keypair_name = guid + '-kp'
226         self.vm_inst_name = guid + '-inst'
227         self.port_1_name = guid + 'port-1'
228         self.port_2_name = guid + 'port-2'
229         self.floating_ip_name = guid + 'fip1'
230
231         # Initialize for tearDown()
232         self.image_creator = None
233         self.network_creator = None
234         self.flavor_creator = None
235         self.inst_creator = None
236
237         self.priv_net_config = openstack_tests.get_priv_net_config(
238             net_name=guid + '-priv-net', subnet_name=guid + '-priv-subnet')
239         self.port_settings = PortSettings(
240             name=self.port_1_name, network_name=self.priv_net_config.network_settings.name)
241
242         self.os_image_settings = openstack_tests.cirros_url_image(name=guid + '-image')
243
244         try:
245             # Create Image
246             self.image_creator = OpenStackImage(self.os_creds, self.os_image_settings)
247             self.image_creator.create()
248
249             # Create Network
250             self.network_creator = OpenStackNetwork(self.os_creds, self.priv_net_config.network_settings)
251             self.network_creator.create()
252
253             # Create Flavor
254             self.flavor_creator = OpenStackFlavor(
255                 self.admin_os_creds,
256                 FlavorSettings(name=guid + '-flavor-name', ram=1024, disk=10, vcpus=1))
257             self.flavor_creator.create()
258         except Exception as e:
259             self.tearDown()
260             raise e
261
262     def tearDown(self):
263         """
264         Cleans the created object
265         """
266         if self.inst_creator:
267             try:
268                 self.inst_creator.clean()
269             except Exception as e:
270                 logger.error('Unexpected exception cleaning VM instance with message - ' + e.message)
271
272         if os.path.isfile(self.keypair_pub_filepath):
273             os.remove(self.keypair_pub_filepath)
274
275         if os.path.isfile(self.keypair_priv_filepath):
276             os.remove(self.keypair_priv_filepath)
277
278         if self.flavor_creator:
279             try:
280                 self.flavor_creator.clean()
281             except Exception as e:
282                 logger.error('Unexpected exception cleaning flavor with message - ' + e.message)
283
284         if self.network_creator:
285             try:
286                 self.network_creator.clean()
287             except Exception as e:
288                 logger.error('Unexpected exception cleaning network with message - ' + e.message)
289
290         if self.image_creator:
291             try:
292                 self.image_creator.clean()
293             except Exception as e:
294                 logger.error('Unexpected exception cleaning image with message - ' + e.message)
295
296         super(self.__class__, self).__clean__()
297
298     def test_check_vm_ip_dhcp(self):
299         """
300         Tests the creation of an OpenStack instance with a single port and ensures that it's assigned IP address is
301         the actual.
302         """
303         instance_settings = VmInstanceSettings(
304             name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=[self.port_settings])
305
306         self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings)
307         vm = self.inst_creator.create()
308
309         ip = self.inst_creator.get_port_ip(self.port_settings.name)
310         self.assertIsNotNone(ip)
311
312         self.assertTrue(self.inst_creator.vm_active(block=True))
313
314         found = False
315         timeout = 160
316         start_time = time.time()
317         match_value = 'Lease of.*obtained'
318
319         logger.info("Looking for expression %s in the console log" % match_value)
320         while timeout > time.time() - start_time:
321             output = vm.get_console_output()
322             if re.search(match_value, output):
323                 logger.info('DHCP lease obtained logged in console')
324                 if ip in output:
325                     logger.info('With correct IP address')
326                     found = True
327                 else:
328                     logger.error('With incorrect IP address')
329                 break
330
331         self.assertTrue(found)
332
333
334 class CreateInstanceSimpleTests(OSIntegrationTestCase):
335     """
336     Simple instance creation tests without any other objects
337     """
338     def setUp(self):
339         """
340         Instantiates the CreateImage object that is responsible for downloading and creating an OS image file
341         within OpenStack
342         """
343         super(self.__class__, self).__start__()
344
345         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
346         self.vm_inst_name = guid + '-inst'
347         self.nova = nova_utils.nova_client(self.os_creds)
348         self.os_image_settings = openstack_tests.cirros_url_image(name=guid + '-image')
349
350         # Initialize for tearDown()
351         self.image_creator = None
352         self.flavor_creator = None
353         self.inst_creator = None
354
355         try:
356             # Create Image
357             self.image_creator = OpenStackImage(self.os_creds, self.os_image_settings)
358             self.image_creator.create()
359             # Create Flavor
360             self.flavor_creator = OpenStackFlavor(
361                 self.admin_os_creds,
362                 FlavorSettings(name=guid + '-flavor-name', ram=2048, disk=10, vcpus=2))
363             self.flavor_creator.create()
364         except Exception as e:
365             self.tearDown()
366             raise e
367
368     def tearDown(self):
369         """
370         Cleans the created object
371         """
372         if self.inst_creator:
373             try:
374                 self.inst_creator.clean()
375             except Exception as e:
376                 logger.error('Unexpected exception cleaning VM instance with message - ' + e.message)
377
378         if self.flavor_creator:
379             try:
380                 self.flavor_creator.clean()
381             except Exception as e:
382                 logger.error('Unexpected exception cleaning flavor with message - ' + e.message)
383
384         if self.image_creator:
385             try:
386                 self.image_creator.clean()
387             except Exception as e:
388                 logger.error('Unexpected exception cleaning image with message - ' + e.message)
389
390         super(self.__class__, self).__clean__()
391
392     def test_create_delete_instance(self):
393         """
394         Tests the creation of an OpenStack instance with a single port with a static IP without a Floating IP.
395         """
396         instance_settings = VmInstanceSettings(name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name)
397
398         self.inst_creator = OpenStackVmInstance(
399             self.os_creds, instance_settings, self.image_creator.image_settings)
400
401         vm_inst = self.inst_creator.create()
402         self.assertEquals(1, len(nova_utils.get_servers_by_name(self.nova, instance_settings.name)))
403
404         # Delete instance
405         nova_utils.delete_vm_instance(self.nova, vm_inst)
406
407         self.assertTrue(self.inst_creator.vm_deleted(block=True))
408         self.assertEquals(0, len(nova_utils.get_servers_by_name(self.nova, instance_settings.name)))
409
410         # Exception should not be thrown
411         self.inst_creator.clean()
412
413
414 class CreateInstanceSingleNetworkTests(OSIntegrationTestCase):
415     """
416     Test for the CreateInstance class with a single NIC/Port with Floating IPs
417     """
418
419     def setUp(self):
420         """
421         Instantiates the CreateImage object that is responsible for downloading and creating an OS image file
422         within OpenStack
423         """
424         super(self.__class__, self).__start__()
425
426         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
427         self.keypair_priv_filepath = 'tmp/' + guid
428         self.keypair_pub_filepath = self.keypair_priv_filepath + '.pub'
429         self.keypair_name = guid + '-kp'
430         self.vm_inst_name = guid + '-inst'
431         self.port_1_name = guid + 'port-1'
432         self.port_2_name = guid + 'port-2'
433         self.floating_ip_name = guid + 'fip1'
434
435         # Initialize for tearDown()
436         self.image_creator = None
437         self.network_creator = None
438         self.router_creator = None
439         self.flavor_creator = None
440         self.keypair_creator = None
441         self.inst_creators = list()
442
443         self.pub_net_config = openstack_tests.get_pub_net_config(
444             net_name=guid + '-pub-net', subnet_name=guid + '-pub-subnet',
445             router_name=guid + '-pub-router', external_net=self.ext_net_name)
446         self.os_image_settings = openstack_tests.cirros_url_image(name=guid + '-image')
447
448         try:
449             # Create Image
450             self.image_creator = OpenStackImage(self.os_creds, self.os_image_settings)
451             self.image_creator.create()
452
453             # Create Network
454             self.network_creator = OpenStackNetwork(self.os_creds, self.pub_net_config.network_settings)
455             self.network_creator.create()
456
457             # Create Router
458             self.router_creator = OpenStackRouter(self.os_creds, self.pub_net_config.router_settings)
459             self.router_creator.create()
460
461             # Create Flavor
462             self.flavor_creator = OpenStackFlavor(
463                 self.admin_os_creds,
464                 FlavorSettings(name=guid + '-flavor-name', ram=2048, disk=10, vcpus=2))
465             self.flavor_creator.create()
466
467             self.keypair_creator = OpenStackKeypair(
468                 self.os_creds, KeypairSettings(
469                     name=self.keypair_name, public_filepath=self.keypair_pub_filepath,
470                     private_filepath=self.keypair_priv_filepath))
471             self.keypair_creator.create()
472         except Exception as e:
473             self.tearDown()
474             raise e
475
476     def tearDown(self):
477         """
478         Cleans the created object
479         """
480         for inst_creator in self.inst_creators:
481             try:
482                 inst_creator.clean()
483             except Exception as e:
484                 logger.error('Unexpected exception cleaning VM instance with message - ' + e.message)
485
486         if self.keypair_creator:
487             try:
488                 self.keypair_creator.clean()
489             except Exception as e:
490                 logger.error('Unexpected exception cleaning keypair with message - ' + e.message)
491
492         if os.path.isfile(self.keypair_pub_filepath):
493             os.remove(self.keypair_pub_filepath)
494
495         if os.path.isfile(self.keypair_priv_filepath):
496             os.remove(self.keypair_priv_filepath)
497
498         if self.flavor_creator:
499             try:
500                 self.flavor_creator.clean()
501             except Exception as e:
502                 logger.error('Unexpected exception cleaning flavor with message - ' + e.message)
503
504         if self.router_creator:
505             try:
506                 self.router_creator.clean()
507             except Exception as e:
508                 logger.error('Unexpected exception cleaning router with message - ' + e.message)
509
510         if self.network_creator:
511             try:
512                 self.network_creator.clean()
513             except Exception as e:
514                 logger.error('Unexpected exception cleaning network with message - ' + e.message)
515
516         if self.image_creator:
517             try:
518                 self.image_creator.clean()
519             except Exception as e:
520                 logger.error('Unexpected exception cleaning image with message - ' + e.message)
521
522         super(self.__class__, self).__clean__()
523
524     def test_single_port_static(self):
525         """
526         Tests the creation of an OpenStack instance with a single port with a static IP without a Floating IP.
527         """
528         ip_1 = '10.55.1.100'
529
530         port_settings = PortSettings(
531             name=self.port_1_name, network_name=self.pub_net_config.network_settings.name,
532             ip_addrs=[{'subnet_name': self.pub_net_config.network_settings.subnet_settings[0].name, 'ip': ip_1}])
533
534         instance_settings = VmInstanceSettings(
535             name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings],
536             floating_ip_settings=[FloatingIpSettings(
537                 name=self.floating_ip_name, port_name=self.port_1_name,
538                 router_name=self.pub_net_config.router_settings.name)])
539
540         inst_creator = OpenStackVmInstance(
541             self.os_creds, instance_settings, self.image_creator.image_settings,
542             keypair_settings=self.keypair_creator.keypair_settings)
543         self.inst_creators.append(inst_creator)
544         vm_inst = inst_creator.create()
545
546         self.assertEquals(ip_1, inst_creator.get_port_ip(self.port_1_name))
547         self.assertTrue(inst_creator.vm_active(block=True))
548         self.assertEquals(vm_inst, inst_creator.get_vm_inst())
549
550     def test_ssh_client_fip_before_active(self):
551         """
552         Tests the ability to access a VM via SSH and a floating IP when it has been assigned prior to being active.
553         """
554         port_settings = PortSettings(
555             name=self.port_1_name, network_name=self.pub_net_config.network_settings.name)
556
557         instance_settings = VmInstanceSettings(
558             name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings],
559             floating_ip_settings=[FloatingIpSettings(
560                 name=self.floating_ip_name, port_name=self.port_1_name,
561                 router_name=self.pub_net_config.router_settings.name)])
562
563         inst_creator = OpenStackVmInstance(
564             self.os_creds, instance_settings, self.image_creator.image_settings,
565             keypair_settings=self.keypair_creator.keypair_settings)
566         self.inst_creators.append(inst_creator)
567         vm_inst = inst_creator.create()
568         self.assertIsNotNone(vm_inst)
569
570         self.assertTrue(inst_creator.vm_active(block=True))
571         self.assertEquals(vm_inst, inst_creator.get_vm_inst())
572
573         validate_ssh_client(inst_creator)
574
575     def test_ssh_client_fip_after_active(self):
576         """
577         Tests the ability to access a VM via SSH and a floating IP when it has been assigned prior to being active.
578         """
579         port_settings = PortSettings(
580             name=self.port_1_name, network_name=self.pub_net_config.network_settings.name)
581
582         instance_settings = VmInstanceSettings(
583             name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings],
584             floating_ip_settings=[FloatingIpSettings(
585                 name=self.floating_ip_name, port_name=self.port_1_name,
586                 router_name=self.pub_net_config.router_settings.name)])
587
588         inst_creator = OpenStackVmInstance(
589             self.os_creds, instance_settings, self.image_creator.image_settings,
590             keypair_settings=self.keypair_creator.keypair_settings)
591         self.inst_creators.append(inst_creator)
592
593         # block=True will force the create() method to block until the
594         vm_inst = inst_creator.create(block=True)
595         self.assertIsNotNone(vm_inst)
596
597         self.assertTrue(inst_creator.vm_active(block=True))
598         self.assertEquals(vm_inst, inst_creator.get_vm_inst())
599
600         validate_ssh_client(inst_creator)
601
602     # TODO - Determine how allowed_address_pairs is supposed to operate before continuing this test
603     # see http://docs.openstack.org/developer/dragonflow/specs/allowed_address_pairs.html for a functional description
604     # def test_allowed_address_port_access(self):
605     #     """
606     #     Tests to ensure that setting allowed_address_pairs on a port functions as designed
607     #     """
608     #     port_settings_1 = PortSettings(
609     #         name=self.port_1_name + '-1', network_name=self.pub_net_config.network_settings.name)
610     #
611     #     instance_settings_1 = VmInstanceSettings(
612     #         name=self.vm_inst_name + '-1', flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings_1],
613     #         floating_ip_settings=[FloatingIpSettings(
614     #             name=self.floating_ip_name + '-1', port_name=port_settings_1.name,
615     #             router_name=self.pub_net_config.router_settings.name)])
616     #
617     #     inst_creator_1 = OpenStackVmInstance(
618     #         self.os_creds, instance_settings_1, self.image_creator.image_settings,
619     #         keypair_settings=self.keypair_creator.keypair_settings)
620     #     self.inst_creators.append(inst_creator_1)
621     #
622     #     # block=True will force the create() method to block until the
623     #     vm_inst_1 = inst_creator_1.create(block=True)
624     #     self.assertIsNotNone(vm_inst_1)
625     #
626     #     port_settings_1 = PortSettings(
627     #         name=self.port_1_name + '-1', network_name=self.pub_net_config.network_settings.name)
628     #
629     #     instance_settings_1 = VmInstanceSettings(
630     #         name=self.vm_inst_name + '-1', flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings_1],
631     #         floating_ip_settings=[FloatingIpSettings(
632     #             name=self.floating_ip_name + '-1', port_name=port_settings_1.name,
633     #             router_name=self.pub_net_config.router_settings.name)])
634     #
635     #     inst_creator_1 = OpenStackVmInstance(
636     #         self.os_creds, instance_settings_1, self.image_creator.image_settings,
637     #         keypair_settings=self.keypair_creator.keypair_settings)
638     #     self.inst_creators.append(inst_creator_1)
639     #     inst_creator_1.create(block=True)
640     #
641     #     ip = inst_creator_1.get_port_ip(port_settings_1.name,
642     #                                     subnet_name=self.pub_net_config.network_settings.subnet_settings[0].name)
643     #     self.assertIsNotNone(ip)
644     #     mac_addr = inst_creator_1.get_port_mac(port_settings_1.name)
645     #     self.assertIsNotNone(mac_addr)
646     #
647     #     allowed_address_pairs = [{'ip_address': ip, 'mac_address': mac_addr}]
648     #
649     #     # Create VM that can be accessed by vm_inst_1
650     #     port_settings_2 = PortSettings(
651     #         name=self.port_1_name + '-2', network_name=self.pub_net_config.network_settings.name,
652     #         allowed_address_pairs=allowed_address_pairs)
653     #
654     #     instance_settings_2 = VmInstanceSettings(
655     #         name=self.vm_inst_name + '-2', flavor=self.flavor_creator.flavor_settings.name,
656     #         port_settings=[port_settings_2])
657     #
658     #     inst_creator_2 = OpenStackVmInstance(
659     #         self.os_creds, instance_settings_2, self.image_creator.image_settings)
660     #     self.inst_creators.append(inst_creator_2)
661     #     inst_creator_2.create(block=True)
662     #
663     #     # Create VM that cannot be accessed by vm_inst_1
664     #     ip = '10.55.0.101'
665     #     mac_addr = '0a:1b:2c:3d:4e:5f'
666     #     invalid_address_pairs = [{'ip_address': ip, 'mac_address': mac_addr}]
667     #
668     #     port_settings_3 = PortSettings(
669     #         name=self.port_1_name + '-3', network_name=self.pub_net_config.network_settings.name,
670     #         allowed_address_pairs=invalid_address_pairs)
671     #
672     #     instance_settings_3 = VmInstanceSettings(
673     #         name=self.vm_inst_name + '-3', flavor=self.flavor_creator.flavor_settings.name,
674     #         port_settings=[port_settings_3])
675     #
676     #     inst_creator_3 = OpenStackVmInstance(
677     #         self.os_creds, instance_settings_3, self.image_creator.image_settings)
678     #     self.inst_creators.append(inst_creator_3)
679     #     inst_creator_3.create(block=True)
680     #
681     #     print 'foo'
682     # I expected that this feature would block/allow traffic from specific endpoints (VMs). In this case, I would expect
683     # inst_1 to be able to access inst_2 but not inst_3; however, they all can access each other.
684     # TODO - Add validation
685
686
687 class CreateInstancePortManipulationTests(OSIntegrationTestCase):
688     """
689     Test for the CreateInstance class with a single NIC/Port where mac and IP values are manually set
690     """
691
692     def setUp(self):
693         """
694         Instantiates the CreateImage object that is responsible for downloading and creating an OS image file
695         within OpenStack
696         """
697         super(self.__class__, self).__start__()
698
699         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
700         self.vm_inst_name = guid + '-inst'
701         self.port_1_name = guid + 'port-1'
702         self.port_2_name = guid + 'port-2'
703         self.floating_ip_name = guid + 'fip1'
704
705         # Initialize for tearDown()
706         self.image_creator = None
707         self.network_creator = None
708         self.flavor_creator = None
709         self.inst_creator = None
710
711         self.net_config = openstack_tests.get_priv_net_config(
712             net_name=guid + '-pub-net', subnet_name=guid + '-pub-subnet',
713             router_name=guid + '-pub-router', external_net=self.ext_net_name)
714         self.os_image_settings = openstack_tests.cirros_url_image(name=guid + '-image')
715
716         try:
717             # Create Image
718             self.image_creator = OpenStackImage(self.os_creds, self.os_image_settings)
719             self.image_creator.create()
720
721             # Create Network
722             self.network_creator = OpenStackNetwork(self.os_creds, self.net_config.network_settings)
723             self.network_creator.create()
724
725             # Create Flavor
726             self.flavor_creator = OpenStackFlavor(
727                 self.admin_os_creds,
728                 FlavorSettings(name=guid + '-flavor-name', ram=2048, disk=10, vcpus=2))
729             self.flavor_creator.create()
730         except Exception as e:
731             self.tearDown()
732             raise e
733
734     def tearDown(self):
735         """
736         Cleans the created object
737         """
738         if self.inst_creator:
739             try:
740                 self.inst_creator.clean()
741             except Exception as e:
742                 logger.error('Unexpected exception cleaning VM instance with message - ' + e.message)
743
744         if self.flavor_creator:
745             try:
746                 self.flavor_creator.clean()
747             except Exception as e:
748                 logger.error('Unexpected exception cleaning flavor with message - ' + e.message)
749
750         if self.network_creator:
751             try:
752                 self.network_creator.clean()
753             except Exception as e:
754                 logger.error('Unexpected exception cleaning network with message - ' + e.message)
755
756         if self.image_creator:
757             try:
758                 self.image_creator.clean()
759             except Exception as e:
760                 logger.error('Unexpected exception cleaning image with message - ' + e.message)
761
762         super(self.__class__, self).__clean__()
763
764     def test_set_custom_valid_ip_one_subnet(self):
765         """
766         Tests the creation of an OpenStack instance with a single port with a static IP on a network with one subnet.
767         """
768         ip = '10.55.0.101'
769         port_settings = PortSettings(
770             name=self.port_1_name, network_name=self.net_config.network_settings.name,
771             ip_addrs=[{'subnet_name': self.net_config.network_settings.subnet_settings[0].name, 'ip': ip}])
772
773         instance_settings = VmInstanceSettings(
774             name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings])
775
776         self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings)
777         self.inst_creator.create()
778
779         self.assertEquals(ip, self.inst_creator.get_port_ip(
780             self.port_1_name, subnet_name=self.net_config.network_settings.subnet_settings[0].name))
781
782     def test_set_custom_invalid_ip_one_subnet(self):
783         """
784         Tests the creation of an OpenStack instance with a single port with a static IP on a network with one subnet.
785         """
786         ip = '10.66.0.101'
787         port_settings = PortSettings(
788             name=self.port_1_name, network_name=self.net_config.network_settings.name,
789             ip_addrs=[{'subnet_name': self.net_config.network_settings.subnet_settings[0].name, 'ip': ip}])
790
791         instance_settings = VmInstanceSettings(
792             name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings])
793
794         self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings)
795
796         with self.assertRaises(Exception):
797             self.inst_creator.create()
798
799     def test_set_custom_valid_mac(self):
800         """
801         Tests the creation of an OpenStack instance with a single port where the MAC address is assigned.
802         """
803         mac_addr = '0a:1b:2c:3d:4e:5f'
804         port_settings = PortSettings(
805             name=self.port_1_name, network_name=self.net_config.network_settings.name, mac_address=mac_addr)
806
807         instance_settings = VmInstanceSettings(
808             name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings])
809
810         self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings)
811         self.inst_creator.create()
812
813         self.assertEquals(mac_addr, self.inst_creator.get_port_mac(self.port_1_name))
814
815     def test_set_custom_invalid_mac(self):
816         """
817         Tests the creation of an OpenStack instance with a single port where an invalid MAC address value is being
818         assigned. This should raise an Exception
819         """
820         port_settings = PortSettings(
821             name=self.port_1_name, network_name=self.net_config.network_settings.name, mac_address='foo')
822
823         instance_settings = VmInstanceSettings(
824             name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings])
825
826         self.inst_creator = OpenStackVmInstance(
827             self.os_creds, instance_settings, self.image_creator.image_settings)
828
829         with self.assertRaises(Exception):
830             self.inst_creator.create()
831
832     def test_set_custom_mac_and_ip(self):
833         """
834         Tests the creation of an OpenStack instance with a single port where the IP and MAC address is assigned.
835         """
836         ip = '10.55.0.101'
837         mac_addr = '0a:1b:2c:3d:4e:5f'
838         port_settings = PortSettings(
839             name=self.port_1_name, network_name=self.net_config.network_settings.name, mac_address=mac_addr,
840             ip_addrs=[{'subnet_name': self.net_config.network_settings.subnet_settings[0].name, 'ip': ip}])
841
842         instance_settings = VmInstanceSettings(
843             name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings])
844
845         self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings)
846         self.inst_creator.create()
847
848         self.assertEquals(ip, self.inst_creator.get_port_ip(
849             self.port_1_name, subnet_name=self.net_config.network_settings.subnet_settings[0].name))
850         self.assertEquals(mac_addr, self.inst_creator.get_port_mac(self.port_1_name))
851
852     def test_set_allowed_address_pairs(self):
853         """
854         Tests the creation of an OpenStack instance with a single port where max_allowed_address_pair is set.
855         """
856         ip = '10.55.0.101'
857         mac_addr = '0a:1b:2c:3d:4e:5f'
858         pair = {'ip_address': ip, 'mac_address': mac_addr}
859         port_settings = PortSettings(
860             name=self.port_1_name, network_name=self.net_config.network_settings.name, allowed_address_pairs=[pair])
861
862         instance_settings = VmInstanceSettings(
863             name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings])
864
865         self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings)
866         self.inst_creator.create()
867
868         port = self.inst_creator.get_port_by_name(port_settings.name)
869         self.assertIsNotNone(port)
870         self.assertIsNotNone(port['port'].get('allowed_address_pairs'))
871         self.assertEquals(1, len(port['port']['allowed_address_pairs']))
872         validation_utils.objects_equivalent(pair, port['port']['allowed_address_pairs'][0])
873
874     def test_set_allowed_address_pairs_bad_mac(self):
875         """
876         Tests the creation of an OpenStack instance with a single port where max_allowed_address_pair is set with an
877         invalid MAC address.
878         """
879         ip = '10.55.0.101'
880         mac_addr = 'foo'
881         pair = {'ip_address': ip, 'mac_address': mac_addr}
882         pairs = set()
883         pairs.add((ip, mac_addr))
884         port_settings = PortSettings(
885             name=self.port_1_name, network_name=self.net_config.network_settings.name, allowed_address_pairs=[pair])
886
887         instance_settings = VmInstanceSettings(
888             name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings])
889
890         self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings)
891         with self.assertRaises(Exception):
892             self.inst_creator.create()
893
894     def test_set_allowed_address_pairs_bad_ip(self):
895         """
896         Tests the creation of an OpenStack instance with a single port where max_allowed_address_pair is set with an
897         invalid MAC address.
898         """
899         ip = 'foo'
900         mac_addr = '0a:1b:2c:3d:4e:5f'
901         pair = {'ip_address': ip, 'mac_address': mac_addr}
902         pairs = set()
903         pairs.add((ip, mac_addr))
904         port_settings = PortSettings(
905             name=self.port_1_name, network_name=self.net_config.network_settings.name, allowed_address_pairs=[pair])
906
907         instance_settings = VmInstanceSettings(
908             name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings])
909
910         self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings)
911         with self.assertRaises(Exception):
912             self.inst_creator.create()
913
914
915 class CreateInstanceOnComputeHost(OSComponentTestCase):
916     """
917     Test for the CreateInstance where one VM is deployed to each compute node
918     """
919
920     def setUp(self):
921         """
922         Instantiates the CreateImage object that is responsible for downloading and creating an OS image file
923         within OpenStack
924         """
925         guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
926         self.vm_inst_name = guid + '-inst'
927         self.port_base_name = guid + 'port'
928
929         # Initialize for tearDown()
930         self.image_creator = None
931         self.flavor_creator = None
932         self.network_creator = None
933         self.inst_creators = list()
934
935         self.priv_net_config = openstack_tests.get_priv_net_config(
936             net_name=guid + '-priv-net', subnet_name=guid + '-priv-subnet')
937
938         self.os_image_settings = openstack_tests.cirros_url_image(name=guid + '-image')
939
940         try:
941             # Create Network
942             self.network_creator = OpenStackNetwork(self.os_creds, self.priv_net_config.network_settings)
943             self.network_creator.create()
944
945             # Create Flavor
946             self.flavor_creator = OpenStackFlavor(
947                 self.os_creds,
948                 FlavorSettings(name=guid + '-flavor-name', ram=512, disk=1, vcpus=1))
949             self.flavor_creator.create()
950
951             # Create Image
952             self.image_creator = OpenStackImage(self.os_creds, self.os_image_settings)
953             self.image_creator.create()
954
955         except Exception as e:
956             self.tearDown()
957             raise e
958
959     def tearDown(self):
960         """
961         Cleans the created object
962         """
963         for inst_creator in self.inst_creators:
964             try:
965                 inst_creator.clean()
966             except Exception as e:
967                 logger.error('Unexpected exception cleaning VM instance with message - ' + e.message)
968
969         if self.flavor_creator:
970             try:
971                 self.flavor_creator.clean()
972             except Exception as e:
973                 logger.error('Unexpected exception cleaning flavor with message - ' + e.message)
974
975         if self.network_creator:
976             try:
977                 self.network_creator.clean()
978             except Exception as e:
979                 logger.error('Unexpected exception cleaning network with message - ' + e.message)
980
981         if self.image_creator:
982             try:
983                 self.image_creator.clean()
984             except Exception as e:
985                 logger.error('Unexpected exception cleaning image with message - ' + e.message)
986
987     def test_deploy_vm_to_each_compute_node(self):
988         """
989         Tests the creation of OpenStack VM instances to each compute node.
990         """
991         from snaps.openstack.utils import nova_utils
992         nova = nova_utils.nova_client(self.os_creds)
993         zones = nova_utils.get_nova_availability_zones(nova)
994
995         # Create Instance on each server/zone
996         ctr = 0
997         for zone in zones:
998             inst_name = self.vm_inst_name + '-' + zone
999             ctr += 1
1000             port_settings = PortSettings(name=self.port_base_name + '-' + str(ctr),
1001                                          network_name=self.priv_net_config.network_settings.name)
1002
1003             instance_settings = VmInstanceSettings(
1004                 name=inst_name, flavor=self.flavor_creator.flavor_settings.name, availability_zone=zone,
1005                 port_settings=[port_settings])
1006             inst_creator = OpenStackVmInstance(
1007                 self.os_creds, instance_settings, self.image_creator.image_settings)
1008             self.inst_creators.append(inst_creator)
1009             inst_creator.create()
1010
1011         # Validate instances to ensure they've been deployed to the correct server
1012         index = 0
1013         for zone in zones:
1014             creator = self.inst_creators[index]
1015             self.assertTrue(creator.vm_active(block=True))
1016             vm = creator.get_vm_inst()
1017             deployed_zone = vm._info['OS-EXT-AZ:availability_zone']
1018             deployed_host = vm._info['OS-EXT-SRV-ATTR:host']
1019             self.assertEquals(zone, deployed_zone + ':' + deployed_host)
1020             index += 1
1021
1022
1023 class CreateInstancePubPrivNetTests(OSIntegrationTestCase):
1024     """
1025     Test for the CreateInstance class with two NIC/Ports, eth0 with floating IP and eth1 w/o
1026     These tests require a Centos image
1027     """
1028
1029     def setUp(self):
1030         """
1031         Instantiates the CreateImage object that is responsible for downloading and creating an OS image file
1032         within OpenStack
1033         """
1034         super(self.__class__, self).__start__()
1035
1036         # Initialize for tearDown()
1037         self.image_creator = None
1038         self.network_creators = list()
1039         self.router_creators = list()
1040         self.flavor_creator = None
1041         self.keypair_creator = None
1042         self.inst_creator = None
1043
1044         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
1045         self.keypair_priv_filepath = 'tmp/' + self.guid
1046         self.keypair_pub_filepath = self.keypair_priv_filepath + '.pub'
1047         self.keypair_name = self.guid + '-kp'
1048         self.vm_inst_name = self.guid + '-inst'
1049         self.port_1_name = self.guid + '-port-1'
1050         self.port_2_name = self.guid + '-port-2'
1051         self.floating_ip_name = self.guid + 'fip1'
1052         self.priv_net_config = openstack_tests.get_priv_net_config(
1053             net_name=self.guid + '-priv-net', subnet_name=self.guid + '-priv-subnet',
1054             router_name=self.guid + '-priv-router', external_net=self.ext_net_name)
1055         self.pub_net_config = openstack_tests.get_pub_net_config(
1056             net_name=self.guid + '-pub-net', subnet_name=self.guid + '-pub-subnet',
1057             router_name=self.guid + '-pub-router', external_net=self.ext_net_name)
1058         image_name = self.__class__.__name__ + '-' + str(uuid.uuid4())
1059         self.os_image_settings = openstack_tests.centos_url_image(name=image_name)
1060
1061         try:
1062             # Create Image
1063             self.image_creator = OpenStackImage(self.os_creds, self.os_image_settings)
1064             self.image_creator.create()
1065
1066             # First network is public
1067             self.network_creators.append(OpenStackNetwork(self.os_creds, self.pub_net_config.network_settings))
1068             # Second network is private
1069             self.network_creators.append(OpenStackNetwork(self.os_creds, self.priv_net_config.network_settings))
1070             for network_creator in self.network_creators:
1071                 network_creator.create()
1072
1073             self.router_creators.append(OpenStackRouter(self.os_creds, self.pub_net_config.router_settings))
1074             self.router_creators.append(OpenStackRouter(self.os_creds, self.priv_net_config.router_settings))
1075
1076             # Create Routers
1077             for router_creator in self.router_creators:
1078                 router_creator.create()
1079
1080             # Create Flavor
1081             self.flavor_creator = OpenStackFlavor(
1082                 self.admin_os_creds,
1083                 FlavorSettings(name=self.guid + '-flavor-name', ram=2048, disk=10, vcpus=2))
1084             self.flavor_creator.create()
1085
1086             # Create Keypair
1087             self.keypair_creator = OpenStackKeypair(
1088                 self.os_creds, KeypairSettings(
1089                     name=self.keypair_name, public_filepath=self.keypair_pub_filepath,
1090                     private_filepath=self.keypair_priv_filepath))
1091             self.keypair_creator.create()
1092         except Exception as e:
1093             self.tearDown()
1094             raise Exception(e.message)
1095
1096     def tearDown(self):
1097         """
1098         Cleans the created objects
1099         """
1100         if self.inst_creator:
1101             try:
1102                 self.inst_creator.clean()
1103             except Exception as e:
1104                 logger.error('Unexpected exception cleaning VM instance with message - ' + e.message)
1105
1106         if self.keypair_creator:
1107             try:
1108                 self.keypair_creator.clean()
1109             except Exception as e:
1110                 logger.error('Unexpected exception cleaning keypair with message - ' + e.message)
1111
1112         if os.path.isfile(self.keypair_pub_filepath):
1113             os.remove(self.keypair_pub_filepath)
1114
1115         if os.path.isfile(self.keypair_priv_filepath):
1116             os.remove(self.keypair_priv_filepath)
1117
1118         if self.flavor_creator:
1119             try:
1120                 self.flavor_creator.clean()
1121             except Exception as e:
1122                 logger.error('Unexpected exception cleaning flavor with message - ' + e.message)
1123
1124         for router_creator in self.router_creators:
1125             try:
1126                 router_creator.clean()
1127             except Exception as e:
1128                 logger.error('Unexpected exception cleaning router with message - ' + e.message)
1129
1130         for network_creator in self.network_creators:
1131             try:
1132                 network_creator.clean()
1133             except Exception as e:
1134                 logger.error('Unexpected exception cleaning network with message - ' + e.message)
1135
1136         if self.image_creator:
1137             try:
1138                 self.image_creator.clean()
1139             except Exception as e:
1140                 logger.error('Unexpected exception cleaning image with message - ' + e.message)
1141
1142         super(self.__class__, self).__clean__()
1143
1144     def test_dual_ports_dhcp(self):
1145         """
1146         Tests the creation of an OpenStack instance with a dual ports/NICs with a DHCP assigned IP.
1147         NOTE: This test and any others that call ansible will most likely fail unless you do one of
1148         two things:
1149         1. Have a ~/.ansible.cfg (or alternate means) to set host_key_checking = False
1150         2. Set the following environment variable in your executing shell: ANSIBLE_HOST_KEY_CHECKING=False
1151         Should this not be performed, the creation of the host ssh key will cause your ansible calls to fail.
1152         """
1153         # Create ports/NICs for instance
1154         ports_settings = []
1155         ctr = 1
1156         for network_creator in self.network_creators:
1157             ports_settings.append(PortSettings(
1158                 name=self.guid + '-port-' + str(ctr),
1159                 network_name=network_creator.network_settings.name))
1160             ctr += 1
1161
1162         # Create instance
1163         instance_settings = VmInstanceSettings(
1164             name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=ports_settings,
1165             floating_ip_settings=[FloatingIpSettings(
1166                 name=self.floating_ip_name, port_name=self.port_1_name,
1167                 router_name=self.pub_net_config.router_settings.name)])
1168
1169         self.inst_creator = OpenStackVmInstance(
1170             self.os_creds, instance_settings, self.image_creator.image_settings,
1171             keypair_settings=self.keypair_creator.keypair_settings)
1172
1173         vm_inst = self.inst_creator.create(block=True)
1174
1175         self.assertEquals(vm_inst, self.inst_creator.get_vm_inst())
1176
1177         # Effectively blocks until VM has been properly activated
1178         self.assertTrue(self.inst_creator.vm_active(block=True))
1179
1180         # Effectively blocks until VM's ssh port has been opened
1181         self.assertTrue(self.inst_creator.vm_ssh_active(block=True))
1182
1183         self.inst_creator.config_nics()
1184
1185         # TODO - *** ADD VALIDATION HERE ***
1186         # TODO - Add validation that both floating IPs work
1187         # TODO - Add tests where only one NIC has a floating IP
1188         # TODO - Add tests where one attempts to place a floating IP on a network/router without an external gateway
1189
1190
1191 class InstanceSecurityGroupTests(OSIntegrationTestCase):
1192     """
1193     Tests that include, add, and remove security groups from VM instances
1194     """
1195     def setUp(self):
1196         """
1197         Instantiates the CreateImage object that is responsible for downloading and creating an OS image file
1198         within OpenStack
1199         """
1200         super(self.__class__, self).__start__()
1201
1202         self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
1203         self.vm_inst_name = self.guid + '-inst'
1204         self.nova = nova_utils.nova_client(self.os_creds)
1205         self.os_image_settings = openstack_tests.cirros_url_image(name=self.guid + '-image')
1206
1207         self.keypair_priv_filepath = 'tmp/' + self.guid
1208         self.keypair_pub_filepath = self.keypair_priv_filepath + '.pub'
1209         self.keypair_name = self.guid + '-kp'
1210         self.vm_inst_name = self.guid + '-inst'
1211         self.port_1_name = self.guid + 'port-1'
1212         self.port_2_name = self.guid + 'port-2'
1213         self.floating_ip_name = self.guid + 'fip1'
1214
1215         self.pub_net_config = openstack_tests.get_pub_net_config(
1216             net_name=self.guid + '-pub-net', subnet_name=self.guid + '-pub-subnet',
1217             router_name=self.guid + '-pub-router', external_net=self.ext_net_name)
1218
1219         # Initialize for tearDown()
1220         self.image_creator = None
1221         self.keypair_creator = None
1222         self.flavor_creator = None
1223         self.network_creator = None
1224         self.router_creator = None
1225         self.inst_creator = None
1226         self.sec_grp_creators = list()
1227
1228         try:
1229             # Create Image
1230             self.image_creator = OpenStackImage(self.os_creds, self.os_image_settings)
1231             self.image_creator.create()
1232
1233             # Create Network
1234             self.network_creator = OpenStackNetwork(self.os_creds, self.pub_net_config.network_settings)
1235             self.network_creator.create()
1236
1237             # Create Router
1238             self.router_creator = OpenStackRouter(self.os_creds, self.pub_net_config.router_settings)
1239             self.router_creator.create()
1240
1241             # Create Flavor
1242             self.flavor_creator = OpenStackFlavor(
1243                 self.admin_os_creds,
1244                 FlavorSettings(name=self.guid + '-flavor-name', ram=2048, disk=10, vcpus=2))
1245             self.flavor_creator.create()
1246
1247             self.keypair_creator = OpenStackKeypair(
1248                 self.os_creds, KeypairSettings(
1249                     name=self.keypair_name, public_filepath=self.keypair_pub_filepath,
1250                     private_filepath=self.keypair_priv_filepath))
1251             self.keypair_creator.create()
1252         except Exception as e:
1253             self.tearDown()
1254             raise e
1255
1256     def tearDown(self):
1257         """
1258         Cleans the created object
1259         """
1260         if self.inst_creator:
1261             try:
1262                 self.inst_creator.clean()
1263             except Exception as e:
1264                 logger.error('Unexpected exception cleaning VM instance with message - ' + e.message)
1265
1266         for sec_grp_creator in self.sec_grp_creators:
1267             try:
1268                 sec_grp_creator.clean()
1269             except Exception as e:
1270                 logger.error('Unexpected exception cleaning security group with message - ' + e.message)
1271
1272         if self.keypair_creator:
1273             try:
1274                 self.keypair_creator.clean()
1275             except Exception as e:
1276                 logger.error('Unexpected exception cleaning keypair with message - ' + e.message)
1277
1278         if os.path.isfile(self.keypair_pub_filepath):
1279             os.remove(self.keypair_pub_filepath)
1280
1281         if os.path.isfile(self.keypair_priv_filepath):
1282             os.remove(self.keypair_priv_filepath)
1283
1284         if self.flavor_creator:
1285             try:
1286                 self.flavor_creator.clean()
1287             except Exception as e:
1288                 logger.error('Unexpected exception cleaning flavor with message - ' + e.message)
1289
1290         if self.router_creator:
1291             try:
1292                 self.router_creator.clean()
1293             except Exception as e:
1294                 logger.error('Unexpected exception cleaning router with message - ' + e.message)
1295
1296         if self.network_creator:
1297             try:
1298                 self.network_creator.clean()
1299             except Exception as e:
1300                 logger.error('Unexpected exception cleaning network with message - ' + e.message)
1301
1302         if self.image_creator:
1303             try:
1304                 self.image_creator.clean()
1305             except Exception as e:
1306                 logger.error('Unexpected exception cleaning image with message - ' + e.message)
1307
1308         super(self.__class__, self).__clean__()
1309
1310     def test_add_security_group(self):
1311         """
1312         Tests the addition of a security group created after the instance.
1313         """
1314         # Create instance
1315         instance_settings = VmInstanceSettings(
1316             name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name)
1317         self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings)
1318         vm_inst = self.inst_creator.create()
1319         self.assertIsNotNone(vm_inst)
1320
1321         # Create security group object to add to instance
1322         sec_grp_settings = SecurityGroupSettings(name=self.guid + '-name', description='hello group')
1323         sec_grp_creator = OpenStackSecurityGroup(self.os_creds, sec_grp_settings)
1324         sec_grp = sec_grp_creator.create()
1325         self.sec_grp_creators.append(sec_grp_creator)
1326
1327         # Check that group has not been added
1328         self.assertFalse(inst_has_sec_grp(self.inst_creator.get_vm_inst(), sec_grp_settings.name))
1329
1330         # Add security group to instance after activated
1331         self.inst_creator.add_security_group(sec_grp)
1332
1333         # Validate that security group has been added
1334         self.assertTrue(inst_has_sec_grp(self.inst_creator.get_vm_inst(), sec_grp_settings.name))
1335
1336     def test_add_invalid_security_group(self):
1337         """
1338         Tests the addition of a security group that no longer exists.
1339         """
1340         # Create instance
1341         instance_settings = VmInstanceSettings(
1342             name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name)
1343         self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings)
1344         vm_inst = self.inst_creator.create()
1345         self.assertIsNotNone(vm_inst)
1346
1347         # Create security group object to add to instance
1348         sec_grp_settings = SecurityGroupSettings(name=self.guid + '-name', description='hello group')
1349         sec_grp_creator = OpenStackSecurityGroup(self.os_creds, sec_grp_settings)
1350         sec_grp = sec_grp_creator.create()
1351         sec_grp_creator.clean()
1352         self.sec_grp_creators.append(sec_grp_creator)
1353
1354         # Check that group has not been added
1355         self.assertFalse(inst_has_sec_grp(self.inst_creator.get_vm_inst(), sec_grp_settings.name))
1356
1357         # Add security group to instance after activated
1358         self.assertFalse(self.inst_creator.add_security_group(sec_grp))
1359
1360         # Validate that security group has been added
1361         self.assertFalse(inst_has_sec_grp(self.inst_creator.get_vm_inst(), sec_grp_settings.name))
1362
1363     def test_remove_security_group(self):
1364         """
1365         Tests the removal of a security group created before and added to the instance.
1366         """
1367         # Create security group object to add to instance
1368         sec_grp_settings = SecurityGroupSettings(name=self.guid + '-name', description='hello group')
1369         sec_grp_creator = OpenStackSecurityGroup(self.os_creds, sec_grp_settings)
1370         sec_grp = sec_grp_creator.create()
1371         self.sec_grp_creators.append(sec_grp_creator)
1372
1373         # Create instance
1374         instance_settings = VmInstanceSettings(
1375             name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name,
1376             security_group_names=[sec_grp_settings.name])
1377         self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings)
1378         vm_inst = self.inst_creator.create()
1379         self.assertIsNotNone(vm_inst)
1380
1381         # Check that group has been added
1382         self.assertTrue(inst_has_sec_grp(vm_inst, sec_grp_settings.name))
1383
1384         # Add security group to instance after activated
1385         self.assertTrue(self.inst_creator.remove_security_group(sec_grp))
1386
1387         # Validate that security group has been added
1388         self.assertFalse(inst_has_sec_grp(self.inst_creator.get_vm_inst(), sec_grp_settings.name))
1389
1390     def test_remove_security_group_never_added(self):
1391         """
1392         Tests the removal of a security group that was never added in the first place.
1393         """
1394         # Create security group object to add to instance
1395         sec_grp_settings = SecurityGroupSettings(name=self.guid + '-name', description='hello group')
1396         sec_grp_creator = OpenStackSecurityGroup(self.os_creds, sec_grp_settings)
1397         sec_grp = sec_grp_creator.create()
1398         self.sec_grp_creators.append(sec_grp_creator)
1399
1400         # Create instance
1401         instance_settings = VmInstanceSettings(
1402             name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name)
1403         self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings)
1404         vm_inst = self.inst_creator.create()
1405         self.assertIsNotNone(vm_inst)
1406
1407         # Check that group has been added
1408         self.assertFalse(inst_has_sec_grp(vm_inst, sec_grp_settings.name))
1409
1410         # Add security group to instance after activated
1411         self.assertFalse(self.inst_creator.remove_security_group(sec_grp))
1412
1413         # Validate that security group has been added
1414         self.assertFalse(inst_has_sec_grp(self.inst_creator.get_vm_inst(), sec_grp_settings.name))
1415
1416     def test_add_same_security_group(self):
1417         """
1418         Tests the addition of a security group created before add added to the instance.
1419         """
1420         # Create security group object to add to instance
1421         sec_grp_settings = SecurityGroupSettings(name=self.guid + '-name', description='hello group')
1422         sec_grp_creator = OpenStackSecurityGroup(self.os_creds, sec_grp_settings)
1423         sec_grp = sec_grp_creator.create()
1424         self.sec_grp_creators.append(sec_grp_creator)
1425
1426         # Create instance
1427         instance_settings = VmInstanceSettings(
1428             name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name,
1429             security_group_names=[sec_grp_settings.name])
1430         self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings)
1431         vm_inst = self.inst_creator.create()
1432         self.assertIsNotNone(vm_inst)
1433
1434         # Check that group has been added
1435         self.assertTrue(inst_has_sec_grp(vm_inst, sec_grp_settings.name))
1436
1437         # Add security group to instance after activated
1438         self.assertTrue(self.inst_creator.add_security_group(sec_grp))
1439
1440         # Validate that security group has been added
1441         self.assertTrue(inst_has_sec_grp(self.inst_creator.get_vm_inst(), sec_grp_settings.name))
1442
1443
1444 def inst_has_sec_grp(vm_inst, sec_grp_name):
1445     """
1446     Returns true if instance has a security group of a given name
1447     :return:
1448     """
1449     if not hasattr(vm_inst, 'security_groups'):
1450         return False
1451
1452     found = False
1453     for sec_grp_dict in vm_inst.security_groups:
1454         if sec_grp_name in sec_grp_dict['name']:
1455             found = True
1456             break
1457     return found
1458
1459
1460 def validate_ssh_client(instance_creator):
1461     """
1462     Returns True if instance_creator returns an SSH client that is valid
1463     :param instance_creator: the object responsible for creating the VM instance
1464     :return: T/F
1465     """
1466     ssh_active = instance_creator.vm_ssh_active(block=True)
1467
1468     if ssh_active:
1469         ssh_client = instance_creator.ssh_client()
1470         if ssh_client:
1471             out = ssh_client.exec_command('pwd')[1]
1472         else:
1473             return False
1474
1475         channel = out.channel
1476         in_buffer = channel.in_buffer
1477         pwd_out = in_buffer.read(1024)
1478         if not pwd_out or len(pwd_out) < 10:
1479             return False
1480         return True
1481
1482     return False