diff options
author | spisarski <s.pisarski@cablelabs.com> | 2017-02-15 09:13:54 -0700 |
---|---|---|
committer | spisarski <s.pisarski@cablelabs.com> | 2017-02-15 09:15:34 -0700 |
commit | 57777f3df521553a06cd01a3861b415d2905ceca (patch) | |
tree | f3b3be457baec7b5231309989aa3ffa9658cd25d /snaps/openstack/tests | |
parent | 73ef791a1cde68e0d8d69cddf63534fbb90f3e2d (diff) |
Initial patch with all code from CableLabs repository.
Change-Id: I70a2778718c5e7f21fd14e4ad28c9269d3761cc7
Signed-off-by: spisarski <s.pisarski@cablelabs.com>
Diffstat (limited to 'snaps/openstack/tests')
-rw-r--r-- | snaps/openstack/tests/__init__.py | 15 | ||||
-rw-r--r-- | snaps/openstack/tests/conf/os_env.yaml.template | 39 | ||||
-rw-r--r-- | snaps/openstack/tests/conf/overcloudrc_test | 9 | ||||
-rw-r--r-- | snaps/openstack/tests/create_flavor_tests.py | 311 | ||||
-rw-r--r-- | snaps/openstack/tests/create_image_tests.py | 362 | ||||
-rw-r--r-- | snaps/openstack/tests/create_instance_tests.py | 1474 | ||||
-rw-r--r-- | snaps/openstack/tests/create_keypairs_tests.py | 203 | ||||
-rw-r--r-- | snaps/openstack/tests/create_network_tests.py | 533 | ||||
-rw-r--r-- | snaps/openstack/tests/create_project_tests.py | 228 | ||||
-rw-r--r-- | snaps/openstack/tests/create_router_tests.py | 264 | ||||
-rw-r--r-- | snaps/openstack/tests/create_security_group_tests.py | 355 | ||||
-rw-r--r-- | snaps/openstack/tests/create_user_tests.py | 155 | ||||
-rw-r--r-- | snaps/openstack/tests/openstack_tests.py | 144 | ||||
-rw-r--r-- | snaps/openstack/tests/os_source_file_test.py | 131 | ||||
-rw-r--r-- | snaps/openstack/tests/validation_utils.py | 69 |
15 files changed, 4292 insertions, 0 deletions
diff --git a/snaps/openstack/tests/__init__.py b/snaps/openstack/tests/__init__.py new file mode 100644 index 0000000..e3e876e --- /dev/null +++ b/snaps/openstack/tests/__init__.py @@ -0,0 +1,15 @@ +# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs") +# and others. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +__author__ = 'spisarski' diff --git a/snaps/openstack/tests/conf/os_env.yaml.template b/snaps/openstack/tests/conf/os_env.yaml.template new file mode 100644 index 0000000..da6b942 --- /dev/null +++ b/snaps/openstack/tests/conf/os_env.yaml.template @@ -0,0 +1,39 @@ +# Keystone v2.0 +#username: admin +#password: admin +#os_auth_url: http://<host>:<port>/v2.0/ +#project_name: admin +#ext_net: <external network name> +#http_proxy: <host>:<port> +#ssh_proxy_cmd: '/usr/local/bin/corkscrew <host> <port> %h %p' +#ssh_proxy_cmd: 'ssh <host> nc %h %p' + +# Keystone v2.0 +#username: admin +#password: admin +#os_auth_url: http://<host>:<port>/v3 +#project_name: admin +#identity_api_version: 3 +#ext_net: <external network name> + + + +#username: admin +#password: cable123 +#os_auth_url: http://192.168.67.10:5000/v2.0/ +#project_name: admin +#ext_net: external +#http_proxy: 10.197.123.27:3128 +#ssh_proxy_cmd: '/usr/local/bin/corkscrew 10.197.123.27 3128 %h %p' + + + + +username: admin +password: admin +os_auth_url: http://192.168.0.2:5000/v3 +project_name: admin +identity_api_version: 3 +ext_net: admin_floating_net +http_proxy: 10.197.123.27:3128 +ssh_proxy_cmd: '/usr/local/bin/corkscrew localhost 3128 %h %p'
\ No newline at end of file diff --git a/snaps/openstack/tests/conf/overcloudrc_test b/snaps/openstack/tests/conf/overcloudrc_test new file mode 100644 index 0000000..87746d8 --- /dev/null +++ b/snaps/openstack/tests/conf/overcloudrc_test @@ -0,0 +1,9 @@ +export NOVA_VERSION=1.1 +export OS_PASSWORD=test_pw +export OS_AUTH_URL=http://foo:5000/v2.0/ +export OS_USERNAME=admin +export OS_TENANT_NAME=admin +export COMPUTE_API_VERSION=1.1 +export OS_NO_CACHE=True +export OS_CLOUDNAME=undercloud +export OS_IMAGE_API_VERSION=1
\ No newline at end of file diff --git a/snaps/openstack/tests/create_flavor_tests.py b/snaps/openstack/tests/create_flavor_tests.py new file mode 100644 index 0000000..c75bdf6 --- /dev/null +++ b/snaps/openstack/tests/create_flavor_tests.py @@ -0,0 +1,311 @@ +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import uuid +import unittest + +from snaps.openstack.create_flavor import FlavorSettings, OpenStackFlavor +from snaps.openstack.tests.os_source_file_test import OSComponentTestCase +from snaps.openstack.utils import nova_utils + +__author__ = 'spisarski' + + +class FlavorSettingsUnitTests(unittest.TestCase): + """ + Tests the construction of the FlavorSettings class + """ + + def test_no_params(self): + with self.assertRaises(Exception): + FlavorSettings() + + def test_empty_config(self): + with self.assertRaises(Exception): + FlavorSettings(config=dict()) + + def test_name_only(self): + with self.assertRaises(Exception): + FlavorSettings(name='foo') + + def test_config_with_name_only(self): + with self.assertRaises(Exception): + FlavorSettings(config={'name': 'foo'}) + + def test_name_ram_only(self): + with self.assertRaises(Exception): + FlavorSettings(name='foo', ram=1) + + def test_config_with_name_ram_only(self): + with self.assertRaises(Exception): + FlavorSettings(config={'name': 'foo', 'ram': 1}) + + def test_name_ram_disk_only(self): + with self.assertRaises(Exception): + FlavorSettings(name='foo', ram=1, disk=1) + + def test_config_with_name_ram_disk_only(self): + with self.assertRaises(Exception): + FlavorSettings(config={'name': 'foo', 'ram': 1, 'disk': 1}) + + def test_ram_string(self): + with self.assertRaises(Exception): + FlavorSettings(name='foo', ram='bar', disk=2, vcpus=3, ephemeral=4, swap=5, rxtx_factor=6.0, + is_public=False) + + def test_config_ram_string(self): + with self.assertRaises(Exception): + FlavorSettings(config={'name': 'foo', 'ram': 'bar', 'disk': 2, 'vcpus': 3, 'ephemeral': 4, 'swap': 5, + 'rxtx_factor': 6.0, 'is_public': False}) + + def test_ram_float(self): + with self.assertRaises(Exception): + FlavorSettings(name='foo', ram=1.5, disk=2, vcpus=3, ephemeral=4, swap=5, rxtx_factor=6.0, is_public=False) + + def test_config_ram_float(self): + with self.assertRaises(Exception): + FlavorSettings(config={'name': 'foo', 'ram': 1.5, 'disk': 2, 'vcpus': 3, 'ephemeral': 4, 'swap': 5, + 'rxtx_factor': 6.0, 'is_public': False}) + + def test_disk_string(self): + with self.assertRaises(Exception): + FlavorSettings(name='foo', ram=1, disk='bar', vcpus=3, ephemeral=4, swap=5, rxtx_factor=6.0, + is_public=False) + + def test_config_disk_string(self): + with self.assertRaises(Exception): + FlavorSettings(config={'name': 'foo', 'ram': 1, 'disk': 'bar', 'vcpus': 3, 'ephemeral': 4, 'swap': 5, + 'rxtx_factor': 6.0, 'is_public': False}) + + def test_disk_float(self): + with self.assertRaises(Exception): + FlavorSettings(name='foo', ram=1, disk=2.5, vcpus=3, ephemeral=4, swap=5, rxtx_factor=6.0, is_public=False) + + def test_config_disk_float(self): + with self.assertRaises(Exception): + FlavorSettings(config={'name': 'foo', 'ram': 1, 'disk': 2.5, 'vcpus': 3, 'ephemeral': 4, 'swap': 5, + 'rxtx_factor': 6.0, 'is_public': False}) + + def test_vcpus_string(self): + with self.assertRaises(Exception): + FlavorSettings(name='foo', ram=1, disk=2, vcpus='bar', ephemeral=4, swap=5, rxtx_factor=6.0, + is_public=False) + + def test_config_vcpus_string(self): + with self.assertRaises(Exception): + FlavorSettings(config={'name': 'foo', 'ram': 1, 'disk': 2, 'vcpus': 'bar', 'ephemeral': 4, 'swap': 5, + 'rxtx_factor': 6.0, 'is_public': False}) + + def test_ephemeral_string(self): + with self.assertRaises(Exception): + FlavorSettings(name='foo', ram=1, disk=2, vcpus=3, ephemeral='bar', swap=5, rxtx_factor=6.0, + is_public=False) + + def test_config_ephemeral_string(self): + with self.assertRaises(Exception): + FlavorSettings(config={'name': 'foo', 'ram': 1, 'disk': 2, 'vcpus': 3, 'ephemeral': 'bar', 'swap': 5, + 'rxtx_factor': 6.0, 'is_public': False}) + + def test_ephemeral_float(self): + with self.assertRaises(Exception): + FlavorSettings(name='foo', ram=1, disk=2, vcpus=3, ephemeral=4.5, swap=5, rxtx_factor=6.0, is_public=False) + + def test_config_ephemeral_float(self): + with self.assertRaises(Exception): + FlavorSettings(config={'name': 'foo', 'ram': 1, 'disk': 2, 'vcpus': 3, 'ephemeral': 4.5, 'swap': 5, + 'rxtx_factor': 6.0, 'is_public': False}) + + def test_swap_string(self): + with self.assertRaises(Exception): + FlavorSettings(name='foo', ram=1, disk=2, vcpus=3, ephemeral=4, swap='bar', rxtx_factor=6.0, + is_public=False) + + def test_config_swap_string(self): + with self.assertRaises(Exception): + FlavorSettings(config={'name': 'foo', 'ram': 1, 'disk': 2, 'vcpus': 3, 'ephemeral': 4, 'swap': 'bar', + 'rxtx_factor': 6.0, 'is_public': False}) + + def test_swap_float(self): + with self.assertRaises(Exception): + FlavorSettings(name='foo', ram=1, disk=2, vcpus=3, ephemeral=4, swap=5.5, rxtx_factor=6.0, is_public=False) + + def test_config_swap_float(self): + with self.assertRaises(Exception): + FlavorSettings(config={'name': 'foo', 'ram': 1, 'disk': 2, 'vcpus': 3, 'ephemeral': 4, 'swap': 5.5, + 'rxtx_factor': 6.0, 'is_public': False}) + + def test_rxtx_string(self): + with self.assertRaises(Exception): + FlavorSettings(name='foo', ram=1, disk=2, vcpus=3, ephemeral=4, swap=5, rxtx_factor='bar', is_public=False) + + def test_config_rxtx_string(self): + with self.assertRaises(Exception): + FlavorSettings(config={'name': 'foo', 'ram': 1, 'disk': 2, 'vcpus': 3, 'ephemeral': 4, 'swap': 5, + 'rxtx_factor': 'bar', 'is_public': False}) + + def test_is_pub_string(self): + with self.assertRaises(Exception): + FlavorSettings(name='foo', ram=1, disk=2, vcpus=3, ephemeral=4, swap=5, rxtx_factor=6.0, is_public='bar') + + def test_config_is_pub_string(self): + with self.assertRaises(Exception): + FlavorSettings(config={'name': 'foo', 'ram': 1, 'disk': 2, 'vcpus': 3, 'ephemeral': 4, 'swap': 5, + 'rxtx_factor': 6.0, 'is_public': 'bar'}) + + def test_name_ram_disk_vcpus_only(self): + settings = FlavorSettings(name='foo', ram=1, disk=2, vcpus=3) + self.assertEquals('foo', settings.name) + self.assertEquals('auto', settings.flavor_id) + self.assertEquals(1, settings.ram) + self.assertEquals(2, settings.disk) + self.assertEquals(3, settings.vcpus) + self.assertEquals(0, settings.ephemeral) + self.assertEquals(0, settings.swap) + self.assertEquals(1.0, settings.rxtx_factor) + self.assertEquals(True, settings.is_public) + + def test_config_with_name_ram_disk_vcpus_only(self): + settings = FlavorSettings(config={'name': 'foo', 'ram': 1, 'disk': 2, 'vcpus': 3}) + self.assertEquals('foo', settings.name) + self.assertEquals('auto', settings.flavor_id) + self.assertEquals(1, settings.ram) + self.assertEquals(2, settings.disk) + self.assertEquals(3, settings.vcpus) + self.assertEquals(0, settings.ephemeral) + self.assertEquals(0, settings.swap) + self.assertEquals(1.0, settings.rxtx_factor) + self.assertEquals(True, settings.is_public) + + def test_all(self): + settings = FlavorSettings(name='foo', flavor_id='bar', ram=1, disk=2, vcpus=3, ephemeral=4, swap=5, rxtx_factor=6.0, + is_public=False) + self.assertEquals('foo', settings.name) + self.assertEquals('bar', settings.flavor_id) + self.assertEquals(1, settings.ram) + self.assertEquals(2, settings.disk) + self.assertEquals(3, settings.vcpus) + self.assertEquals(4, settings.ephemeral) + self.assertEquals(5, settings.swap) + self.assertEquals(6.0, settings.rxtx_factor) + self.assertEquals(False, settings.is_public) + + def test_config_all(self): + settings = FlavorSettings(config={'name': 'foo', 'flavor_id': 'bar', 'ram': 1, 'disk': 2, 'vcpus': 3, 'ephemeral': 4, + 'swap': 5, 'rxtx_factor': 6.0, 'is_public': False}) + self.assertEquals('foo', settings.name) + self.assertEquals('bar', settings.flavor_id) + self.assertEquals(1, settings.ram) + self.assertEquals(2, settings.disk) + self.assertEquals(3, settings.vcpus) + self.assertEquals(4, settings.ephemeral) + self.assertEquals(5, settings.swap) + self.assertEquals(6.0, settings.rxtx_factor) + self.assertEquals(False, settings.is_public) + + +class CreateFlavorTests(OSComponentTestCase): + """ + Test for the CreateSecurityGroup class defined in create_security_group.py + """ + + def setUp(self): + """ + Instantiates the CreateSecurityGroup object that is responsible for downloading and creating an OS image file + within OpenStack + """ + guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) + self.flavor_name = guid + 'name' + + self.nova = nova_utils.nova_client(self.os_creds) + + # Initialize for cleanup + self.flavor_creator = None + + def tearDown(self): + """ + Cleans the image and downloaded image file + """ + if self.flavor_creator: + self.flavor_creator.clean() + + def test_create_flavor(self): + """ + Tests the creation of an OpenStack flavor. + """ + # Create Flavor + flavor_settings = FlavorSettings(name=self.flavor_name, ram=1, disk=1, vcpus=1) + self.flavor_creator = OpenStackFlavor(self.os_creds, flavor_settings) + flavor = self.flavor_creator.create() + self.assertTrue(validate_flavor(flavor_settings, flavor)) + + def test_create_flavor_existing(self): + """ + Tests the creation of an OpenStack flavor then starts another creator to ensure it has not been done twice. + """ + # Create Flavor + flavor_settings = FlavorSettings(name=self.flavor_name, ram=1, disk=1, vcpus=1) + self.flavor_creator = OpenStackFlavor(self.os_creds, flavor_settings) + flavor = self.flavor_creator.create() + self.assertTrue(validate_flavor(flavor_settings, flavor)) + + flavor_creator_2 = OpenStackFlavor(self.os_creds, flavor_settings) + flavor2 = flavor_creator_2.create() + + self.assertEquals(flavor.id, flavor2.id) + + def test_create_clean_flavor(self): + """ + Tests the creation and cleanup of an OpenStack flavor. + """ + # Create Flavor + flavor_settings = FlavorSettings(name=self.flavor_name, ram=1, disk=1, vcpus=1) + self.flavor_creator = OpenStackFlavor(self.os_creds, flavor_settings) + flavor = self.flavor_creator.create() + self.assertTrue(validate_flavor(flavor_settings, flavor)) + + # Clean Flavor + self.flavor_creator.clean() + + self.assertIsNone(self.flavor_creator.get_flavor()) + self.assertIsNone(nova_utils.get_flavor_by_name(self.nova, flavor_settings.name)) + + def test_create_delete_flavor(self): + """ + Tests the creation of an OpenStack Security Group, the deletion, then cleanup to ensure clean() does not + raise any exceptions. + """ + # Create Flavor + flavor_settings = FlavorSettings(name=self.flavor_name, ram=1, disk=1, vcpus=1) + self.flavor_creator = OpenStackFlavor(self.os_creds, flavor_settings) + flavor = self.flavor_creator.create() + self.assertTrue(validate_flavor(flavor_settings, flavor)) + + # Delete Flavor + nova_utils.delete_flavor(self.nova, flavor) + self.assertIsNone(nova_utils.get_flavor_by_name(self.nova, flavor_settings.name)) + + # Attempt to cleanup + self.flavor_creator.clean() + + self.assertIsNone(self.flavor_creator.get_flavor()) + + # TODO - Add more tests to exercise all configuration options + + +def validate_flavor(flavor_settings, flavor): + """ + Validates the flavor_settings against the OpenStack flavor object + :param flavor_settings: the settings used to create the flavor + :param flavor: the OpenStack flavor object + """ + return flavor is not None \ + and flavor_settings.name == flavor.name \ + and flavor_settings.ram == flavor.ram \ + and flavor_settings.disk == flavor.disk \ + and flavor_settings.vcpus == flavor.vcpus diff --git a/snaps/openstack/tests/create_image_tests.py b/snaps/openstack/tests/create_image_tests.py new file mode 100644 index 0000000..24bf0f2 --- /dev/null +++ b/snaps/openstack/tests/create_image_tests.py @@ -0,0 +1,362 @@ +# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs") +# and others. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os +import shutil +import uuid +import unittest + +from snaps import file_utils +from snaps.openstack.create_image import ImageSettings + +import openstack_tests +from snaps.openstack.utils import glance_utils, nova_utils +from snaps.openstack import create_image +from snaps.openstack import os_credentials +from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase + +__author__ = 'spisarski' + + +class ImageSettingsUnitTests(unittest.TestCase): + """ + Tests the construction of the ImageSettings class + """ + + def test_no_params(self): + with self.assertRaises(Exception): + ImageSettings() + + def test_empty_config(self): + with self.assertRaises(Exception): + ImageSettings(config=dict()) + + def test_name_only(self): + with self.assertRaises(Exception): + ImageSettings(name='foo') + + def test_config_with_name_only(self): + with self.assertRaises(Exception): + ImageSettings(config={'name': 'foo'}) + + def test_name_user_only(self): + with self.assertRaises(Exception): + ImageSettings(name='foo', image_user='bar') + + def test_config_with_name_user_only(self): + with self.assertRaises(Exception): + ImageSettings(config={'name': 'foo', 'image_user': 'bar'}) + + def test_name_user_format_only(self): + with self.assertRaises(Exception): + ImageSettings(name='foo', image_user='bar', img_format='qcow2') + + def test_config_with_name_user_format_only(self): + with self.assertRaises(Exception): + ImageSettings(config={'name': 'foo', 'image_user': 'bar', 'format': 'qcow2'}) + + def test_name_user_format_url_file_only(self): + with self.assertRaises(Exception): + ImageSettings(name='foo', image_user='bar', img_format='qcow2', url='http://foo.com', + image_file='/foo/bar.qcow') + + def test_config_with_name_user_format_url_file_only(self): + with self.assertRaises(Exception): + ImageSettings(config={'name': 'foo', 'image_user': 'bar', 'format': 'qcow2', + 'download_url': 'http://foo.com', 'image_file': '/foo/bar.qcow'}) + + def test_name_user_format_url_only(self): + settings = ImageSettings(name='foo', image_user='bar', img_format='qcow2', url='http://foo.com') + self.assertEquals('foo', settings.name) + self.assertEquals('bar', settings.image_user) + self.assertEquals('qcow2', settings.format) + self.assertEquals('http://foo.com', settings.url) + self.assertIsNone(settings.image_file) + self.assertIsNone(settings.nic_config_pb_loc) + + def test_config_with_name_user_format_url_only(self): + settings = ImageSettings(config={'name': 'foo', 'image_user': 'bar', 'format': 'qcow2', + 'download_url': 'http://foo.com'}) + self.assertEquals('foo', settings.name) + self.assertEquals('bar', settings.image_user) + self.assertEquals('qcow2', settings.format) + self.assertEquals('http://foo.com', settings.url) + self.assertIsNone(settings.image_file) + self.assertIsNone(settings.nic_config_pb_loc) + + def test_name_user_format_file_only(self): + settings = ImageSettings(name='foo', image_user='bar', img_format='qcow2', image_file='/foo/bar.qcow') + self.assertEquals('foo', settings.name) + self.assertEquals('bar', settings.image_user) + self.assertEquals('qcow2', settings.format) + self.assertIsNone(settings.url) + self.assertEquals('/foo/bar.qcow', settings.image_file) + self.assertIsNone(settings.nic_config_pb_loc) + + def test_config_with_name_user_format_file_only(self): + settings = ImageSettings(config={'name': 'foo', 'image_user': 'bar', 'format': 'qcow2', + 'image_file': '/foo/bar.qcow'}) + self.assertEquals('foo', settings.name) + self.assertEquals('bar', settings.image_user) + self.assertEquals('qcow2', settings.format) + self.assertIsNone(settings.url) + self.assertEquals('/foo/bar.qcow', settings.image_file) + self.assertIsNone(settings.nic_config_pb_loc) + + def test_all_url(self): + settings = ImageSettings(name='foo', image_user='bar', img_format='qcow2', url='http://foo.com', + nic_config_pb_loc='/foo/bar') + self.assertEquals('foo', settings.name) + self.assertEquals('bar', settings.image_user) + self.assertEquals('qcow2', settings.format) + self.assertEquals('http://foo.com', settings.url) + self.assertIsNone(settings.image_file) + self.assertEquals('/foo/bar', settings.nic_config_pb_loc) + + def test_config_all_url(self): + settings = ImageSettings(config={'name': 'foo', 'image_user': 'bar', 'format': 'qcow2', + 'download_url': 'http://foo.com', 'nic_config_pb_loc': '/foo/bar'}) + self.assertEquals('foo', settings.name) + self.assertEquals('bar', settings.image_user) + self.assertEquals('qcow2', settings.format) + self.assertEquals('http://foo.com', settings.url) + self.assertIsNone(settings.image_file) + self.assertEquals('/foo/bar', settings.nic_config_pb_loc) + + def test_all_file(self): + settings = ImageSettings(name='foo', image_user='bar', img_format='qcow2', image_file='/foo/bar.qcow', + nic_config_pb_loc='/foo/bar') + self.assertEquals('foo', settings.name) + self.assertEquals('bar', settings.image_user) + self.assertEquals('qcow2', settings.format) + self.assertIsNone(settings.url) + self.assertEquals('/foo/bar.qcow', settings.image_file) + self.assertEquals('/foo/bar', settings.nic_config_pb_loc) + + def test_config_all_file(self): + settings = ImageSettings(config={'name': 'foo', 'image_user': 'bar', 'format': 'qcow2', + 'image_file': '/foo/bar.qcow', 'nic_config_pb_loc': '/foo/bar'}) + self.assertEquals('foo', settings.name) + self.assertEquals('bar', settings.image_user) + self.assertEquals('qcow2', settings.format) + self.assertIsNone(settings.url) + self.assertEquals('/foo/bar.qcow', settings.image_file) + self.assertEquals('/foo/bar', settings.nic_config_pb_loc) + + +class CreateImageSuccessTests(OSIntegrationTestCase): + """ + Test for the CreateImage class defined in create_image.py + """ + + def setUp(self): + """ + Instantiates the CreateImage object that is responsible for downloading and creating an OS image file + within OpenStack + """ + super(self.__class__, self).__start__() + + guid = uuid.uuid4() + self.image_name = self.__class__.__name__ + '-' + str(guid) + + self.nova = nova_utils.nova_client(self.os_creds) + self.glance = glance_utils.glance_client(self.os_creds) + + self.tmp_dir = 'tmp/' + str(guid) + if not os.path.exists(self.tmp_dir): + os.makedirs(self.tmp_dir) + + def tearDown(self): + """ + Cleans the image and downloaded image file + """ + if self.image_creator: + self.image_creator.clean() + + if os.path.exists(self.tmp_dir) and os.path.isdir(self.tmp_dir): + shutil.rmtree(self.tmp_dir) + + super(self.__class__, self).__clean__() + + def test_create_image_clean_url(self): + """ + Tests the creation of an OpenStack image from a URL. + """ + # Create Image + os_image_settings = openstack_tests.cirros_url_image(name=self.image_name) + self.image_creator = create_image.OpenStackImage(self.os_creds, os_image_settings) + + created_image = self.image_creator.create() + self.assertIsNotNone(created_image) + + retrieved_image = glance_utils.get_image(self.nova, self.glance, os_image_settings.name) + self.assertIsNotNone(retrieved_image) + + self.assertEquals(created_image.name, retrieved_image.name) + self.assertEquals(created_image.id, retrieved_image.id) + + def test_create_image_clean_file(self): + """ + Tests the creation of an OpenStack image from a file. + """ + url_image_settings = openstack_tests.cirros_url_image('foo') + image_file = file_utils.download(url_image_settings.url, self.tmp_dir) + file_image_settings = openstack_tests.file_image_test_settings(name=self.image_name, file_path=image_file.name) + self.image_creator = create_image.OpenStackImage(self.os_creds, file_image_settings) + + self.image = self.image_creator.create() + self.assertIsNotNone(self.image) + self.assertEqual(self.image_name, self.image.name) + + created_image = self.image_creator.create() + self.assertIsNotNone(created_image) + + retrieved_image = glance_utils.get_image(self.nova, self.glance, file_image_settings.name) + self.assertIsNotNone(retrieved_image) + + self.assertEquals(created_image.name, retrieved_image.name) + self.assertEquals(created_image.id, retrieved_image.id) + + def test_create_delete_image(self): + """ + Tests the creation then deletion of an OpenStack image to ensure clean() does not raise an Exception. + """ + # Create Image + os_image_settings = openstack_tests.cirros_url_image(name=self.image_name) + self.image_creator = create_image.OpenStackImage(self.os_creds, os_image_settings) + created_image = self.image_creator.create() + self.assertIsNotNone(created_image) + + # Delete Image manually + glance_utils.delete_image(self.glance, created_image) + + self.assertIsNone(glance_utils.get_image(self.nova, self.glance, self.image_creator.image_settings.name)) + + # Must not throw an exception when attempting to cleanup non-existent image + self.image_creator.clean() + self.assertIsNone(self.image_creator.get_image()) + + def test_create_same_image(self): + """ + Tests the creation of an OpenStack image when the image already exists. + """ + # Create Image + os_image_settings = openstack_tests.cirros_url_image(name=self.image_name) + self.image_creator = create_image.OpenStackImage(self.os_creds, os_image_settings) + image1 = self.image_creator.create() + # Should be retrieving the instance data + os_image_2 = create_image.OpenStackImage(self.os_creds, os_image_settings) + image2 = os_image_2.create() + self.assertEquals(image1.id, image2.id) + + +class CreateImageNegativeTests(OSIntegrationTestCase): + """ + Negative test cases for the CreateImage class + """ + + def setUp(self): + super(self.__class__, self).__start__() + + self.image_name = self.__class__.__name__ + '-' + str(uuid.uuid4()) + self.image_creator = None + + def tearDown(self): + if self.image_creator: + self.image_creator.clean() + + super(self.__class__, self).__clean__() + + def test_none_image_name(self): + """ + Expect an exception when the image name is None + """ + os_image_settings = openstack_tests.cirros_url_image(name=self.image_name) + with self.assertRaises(Exception): + self.image_creator = create_image.OpenStackImage( + self.os_creds, create_image.ImageSettings( + name=None, image_user=os_image_settings.image_user, img_format=os_image_settings.format, + url=os_image_settings.url)) + + self.fail('Exception should have been thrown prior to this line') + + def test_bad_image_url(self): + """ + Expect an exception when the image download url is bad + """ + os_image_settings = openstack_tests.cirros_url_image(name=self.image_name) + self.image_creator = create_image.OpenStackImage(self.os_creds, create_image.ImageSettings( + name=os_image_settings.name, image_user=os_image_settings.image_user, + img_format=os_image_settings.format, url="http://foo.bar")) + with self.assertRaises(Exception): + self.image_creator.create() + + def test_bad_image_file(self): + """ + Expect an exception when the image file does not exist + """ + os_image_settings = openstack_tests.cirros_url_image(name=self.image_name) + self.image_creator = create_image.OpenStackImage( + self.os_creds, + create_image.ImageSettings(name=os_image_settings.name, image_user=os_image_settings.image_user, + img_format=os_image_settings.format, image_file="/foo/bar.qcow")) + with self.assertRaises(Exception): + self.image_creator.create() + + def test_none_proj_name(self): + """ + Expect an exception when the project name is None + """ + os_image_settings = openstack_tests.cirros_url_image(name=self.image_name) + with self.assertRaises(Exception): + self.image_creator = create_image.OpenStackImage( + os_credentials.OSCreds(self.os_creds.username, self.os_creds.password, self.os_creds.auth_url, None, + proxy_settings=self.os_creds.proxy_settings), + os_image_settings) + self.image_creator.create() + + def test_none_auth_url(self): + """ + Expect an exception when the project name is None + """ + os_image_settings = openstack_tests.cirros_url_image(name=self.image_name) + with self.assertRaises(Exception): + self.image_creator = create_image.OpenStackImage( + os_credentials.OSCreds(self.os_creds.username, self.os_creds.password, None, + self.os_creds.project_name, proxy_settings=self.os_creds.proxy_settings), + os_image_settings) + self.image_creator.create() + + def test_none_password(self): + """ + Expect an exception when the project name is None + """ + os_image_settings = openstack_tests.cirros_url_image(name=self.image_name) + with self.assertRaises(Exception): + self.image_creator = create_image.OpenStackImage( + os_credentials.OSCreds(self.os_creds.username, None, self.os_creds.os_auth_url, + self.os_creds.project_name, proxy_settings=self.os_creds.proxy_settings), + os_image_settings) + + def test_none_user(self): + """ + Expect an exception when the project name is None + """ + os_image_settings = openstack_tests.cirros_url_image(name=self.image_name) + with self.assertRaises(Exception): + self.image_creator = create_image.OpenStackImage( + os_credentials.OSCreds(None, self.os_creds.password, self.os_creds.os_auth_url, + self.os_creds.project_name, + proxy_settings=self.os_creds.proxy_settings), + os_image_settings) diff --git a/snaps/openstack/tests/create_instance_tests.py b/snaps/openstack/tests/create_instance_tests.py new file mode 100644 index 0000000..756b45f --- /dev/null +++ b/snaps/openstack/tests/create_instance_tests.py @@ -0,0 +1,1474 @@ +# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs") +# and others. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import logging +import os +import time +import unittest +import uuid + +from snaps.openstack.create_instance import VmInstanceSettings, OpenStackVmInstance, FloatingIpSettings +from snaps.openstack.create_flavor import OpenStackFlavor, FlavorSettings +from snaps.openstack.create_keypairs import OpenStackKeypair, KeypairSettings +from snaps.openstack.create_network import OpenStackNetwork, PortSettings +from snaps.openstack.create_router import OpenStackRouter +from snaps.openstack.create_image import OpenStackImage +from snaps.openstack.create_security_group import SecurityGroupSettings, OpenStackSecurityGroup +from snaps.openstack.tests import openstack_tests, validation_utils +from snaps.openstack.utils import nova_utils +from snaps.openstack.tests.os_source_file_test import OSComponentTestCase, OSIntegrationTestCase + +__author__ = 'spisarski' + +VM_BOOT_TIMEOUT = 600 + +logger = logging.getLogger('create_instance_tests') + + +class VmInstanceSettingsUnitTests(unittest.TestCase): + """ + Tests the construction of the VmInstanceSettings class + """ + + def test_no_params(self): + with self.assertRaises(Exception): + VmInstanceSettings() + + def test_empty_config(self): + with self.assertRaises(Exception): + VmInstanceSettings(config=dict()) + + def test_name_only(self): + with self.assertRaises(Exception): + VmInstanceSettings(name='foo') + + def test_config_with_name_only(self): + with self.assertRaises(Exception): + VmInstanceSettings(config={'name': 'foo'}) + + def test_name_flavor_only(self): + settings = VmInstanceSettings(name='foo', flavor='bar') + self.assertEquals('foo', settings.name) + self.assertEquals('bar', settings.flavor) + self.assertEquals(0, len(settings.port_settings)) + self.assertEquals(0, len(settings.security_group_names)) + self.assertEquals(0, len(settings.floating_ip_settings)) + self.assertIsNone(settings.sudo_user) + self.assertEquals(900, settings.vm_boot_timeout) + self.assertEquals(300, settings.vm_delete_timeout) + self.assertEquals(180, settings.ssh_connect_timeout) + self.assertIsNone(settings.availability_zone) + + def test_config_with_name_flavor_only(self): + settings = VmInstanceSettings(config={'name': 'foo', 'flavor': 'bar'}) + self.assertEquals('foo', settings.name) + self.assertEquals('bar', settings.flavor) + self.assertEquals(0, len(settings.port_settings)) + self.assertEquals(0, len(settings.security_group_names)) + self.assertEquals(0, len(settings.floating_ip_settings)) + self.assertIsNone(settings.sudo_user) + self.assertEquals(900, settings.vm_boot_timeout) + self.assertEquals(300, settings.vm_delete_timeout) + self.assertEquals(180, settings.ssh_connect_timeout) + self.assertIsNone(settings.availability_zone) + + def test_all(self): + port_settings = PortSettings(name='foo-port', network_name='bar-net') + fip_settings = FloatingIpSettings(name='foo-fip', port_name='bar-port', router_name='foo-bar-router') + + settings = VmInstanceSettings(name='foo', flavor='bar', port_settings=[port_settings], + security_group_names=['sec_grp_1'], floating_ip_settings=[fip_settings], + sudo_user='joe', vm_boot_timeout=999, vm_delete_timeout=333, + ssh_connect_timeout=111, availability_zone='server name') + self.assertEquals('foo', settings.name) + self.assertEquals('bar', settings.flavor) + self.assertEquals(1, len(settings.port_settings)) + self.assertEquals('foo-port', settings.port_settings[0].name) + self.assertEquals('bar-net', settings.port_settings[0].network_name) + self.assertEquals(1, len(settings.security_group_names)) + self.assertEquals('sec_grp_1', settings.security_group_names[0]) + self.assertEquals(1, len(settings.floating_ip_settings)) + self.assertEquals('foo-fip', settings.floating_ip_settings[0].name) + self.assertEquals('bar-port', settings.floating_ip_settings[0].port_name) + self.assertEquals('foo-bar-router', settings.floating_ip_settings[0].router_name) + self.assertEquals('joe', settings.sudo_user) + self.assertEquals(999, settings.vm_boot_timeout) + self.assertEquals(333, settings.vm_delete_timeout) + self.assertEquals(111, settings.ssh_connect_timeout) + self.assertEquals('server name', settings.availability_zone) + + def test_config_all(self): + port_settings = PortSettings(name='foo-port', network_name='bar-net') + fip_settings = FloatingIpSettings(name='foo-fip', port_name='bar-port', router_name='foo-bar-router') + + settings = VmInstanceSettings(config={'name': 'foo', 'flavor': 'bar', 'ports': [port_settings], + 'security_group_names': ['sec_grp_1'], + 'floating_ips': [fip_settings], 'sudo_user': 'joe', + 'vm_boot_timeout': 999, 'vm_delete_timeout': 333, + 'ssh_connect_timeout': 111, 'availability_zone': 'server name'}) + self.assertEquals('foo', settings.name) + self.assertEquals('bar', settings.flavor) + self.assertEquals(1, len(settings.port_settings)) + self.assertEquals('foo-port', settings.port_settings[0].name) + self.assertEquals('bar-net', settings.port_settings[0].network_name) + self.assertEquals(1, len(settings.security_group_names)) + self.assertEquals(1, len(settings.floating_ip_settings)) + self.assertEquals('foo-fip', settings.floating_ip_settings[0].name) + self.assertEquals('bar-port', settings.floating_ip_settings[0].port_name) + self.assertEquals('foo-bar-router', settings.floating_ip_settings[0].router_name) + self.assertEquals('joe', settings.sudo_user) + self.assertEquals(999, settings.vm_boot_timeout) + self.assertEquals(333, settings.vm_delete_timeout) + self.assertEquals(111, settings.ssh_connect_timeout) + self.assertEquals('server name', settings.availability_zone) + + +class FloatingIpSettingsUnitTests(unittest.TestCase): + """ + Tests the construction of the FloatingIpSettings class + """ + + def test_no_params(self): + with self.assertRaises(Exception): + FloatingIpSettings() + + def test_empty_config(self): + with self.assertRaises(Exception): + FloatingIpSettings(config=dict()) + + def test_name_only(self): + with self.assertRaises(Exception): + FloatingIpSettings(name='foo') + + def test_config_with_name_only(self): + with self.assertRaises(Exception): + FloatingIpSettings(config={'name': 'foo'}) + + def test_name_port_only(self): + with self.assertRaises(Exception): + FloatingIpSettings(name='foo', port_name='bar') + + def test_config_with_name_port_only(self): + with self.assertRaises(Exception): + FloatingIpSettings(config={'name': 'foo', 'port_name': 'bar'}) + + def test_name_router_only(self): + with self.assertRaises(Exception): + FloatingIpSettings(name='foo', router_name='bar') + + def test_config_with_name_router_only(self): + with self.assertRaises(Exception): + FloatingIpSettings(config={'name': 'foo', 'router_name': 'bar'}) + + def test_name_port_router_only(self): + settings = FloatingIpSettings(name='foo', port_name='foo-port', router_name='bar-router') + self.assertEquals('foo', settings.name) + self.assertEquals('foo-port', settings.port_name) + self.assertEquals('bar-router', settings.router_name) + self.assertIsNone(settings.subnet_name) + self.assertTrue(settings.provisioning) + + def test_config_with_name_port_router_only(self): + settings = FloatingIpSettings(config={'name': 'foo', 'port_name': 'foo-port', 'router_name': 'bar-router'}) + self.assertEquals('foo', settings.name) + self.assertEquals('foo-port', settings.port_name) + self.assertEquals('bar-router', settings.router_name) + self.assertIsNone(settings.subnet_name) + self.assertTrue(settings.provisioning) + + def test_all(self): + settings = FloatingIpSettings(name='foo', port_name='foo-port', router_name='bar-router', + subnet_name='bar-subnet', provisioning=False) + self.assertEquals('foo', settings.name) + self.assertEquals('foo-port', settings.port_name) + self.assertEquals('bar-router', settings.router_name) + self.assertEquals('bar-subnet', settings.subnet_name) + self.assertFalse(settings.provisioning) + + def test_config_all(self): + settings = FloatingIpSettings(config={'name': 'foo', 'port_name': 'foo-port', 'router_name': 'bar-router', + 'subnet_name': 'bar-subnet', 'provisioning': False}) + self.assertEquals('foo', settings.name) + self.assertEquals('foo-port', settings.port_name) + self.assertEquals('bar-router', settings.router_name) + self.assertEquals('bar-subnet', settings.subnet_name) + self.assertFalse(settings.provisioning) + + +class SimpleHealthCheck(OSIntegrationTestCase): + """ + Test for the CreateInstance class with a single NIC/Port with Floating IPs + """ + + def setUp(self): + """ + Instantiates the CreateImage object that is responsible for downloading and creating an OS image file + within OpenStack + """ + super(self.__class__, self).__start__() + + guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) + self.keypair_priv_filepath = 'tmp/' + guid + self.keypair_pub_filepath = self.keypair_priv_filepath + '.pub' + self.keypair_name = guid + '-kp' + self.vm_inst_name = guid + '-inst' + self.port_1_name = guid + 'port-1' + self.port_2_name = guid + 'port-2' + self.floating_ip_name = guid + 'fip1' + + # Initialize for tearDown() + self.image_creator = None + self.network_creator = None + self.flavor_creator = None + self.inst_creator = None + + self.priv_net_config = openstack_tests.get_priv_net_config( + net_name=guid + '-priv-net', subnet_name=guid + '-priv-subnet') + self.port_settings = PortSettings( + name=self.port_1_name, network_name=self.priv_net_config.network_settings.name) + + self.os_image_settings = openstack_tests.cirros_url_image(name=guid + '-image') + + try: + # Create Image + self.image_creator = OpenStackImage(self.os_creds, self.os_image_settings) + self.image_creator.create() + + # Create Network + self.network_creator = OpenStackNetwork(self.os_creds, self.priv_net_config.network_settings) + self.network_creator.create() + + # Create Flavor + self.flavor_creator = OpenStackFlavor( + self.admin_os_creds, + FlavorSettings(name=guid + '-flavor-name', ram=1024, disk=10, vcpus=1)) + self.flavor_creator.create() + except Exception as e: + self.tearDown() + raise e + + def tearDown(self): + """ + Cleans the created object + """ + if self.inst_creator: + try: + self.inst_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning VM instance with message - ' + e.message) + + if os.path.isfile(self.keypair_pub_filepath): + os.remove(self.keypair_pub_filepath) + + if os.path.isfile(self.keypair_priv_filepath): + os.remove(self.keypair_priv_filepath) + + if self.flavor_creator: + try: + self.flavor_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning flavor with message - ' + e.message) + + if self.network_creator: + try: + self.network_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning network with message - ' + e.message) + + if self.image_creator: + try: + self.image_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning image with message - ' + e.message) + + super(self.__class__, self).__clean__() + + def test_check_vm_ip_dhcp(self): + """ + Tests the creation of an OpenStack instance with a single port and ensures that it's assigned IP address is + the actual. + """ + instance_settings = VmInstanceSettings( + name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=[self.port_settings]) + + self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings) + vm = self.inst_creator.create() + + ip = self.inst_creator.get_port_ip(self.port_settings.name) + self.assertIsNotNone(ip) + + self.assertTrue(self.inst_creator.vm_active(block=True)) + + found = False + timeout = 100 + start_time = time.time() + match_value = 'Lease of ' + ip + ' obtained,' + + while timeout > time.time() - start_time: + output = vm.get_console_output() + if match_value in output: + found = True + break + self.assertTrue(found) + + +class CreateInstanceSimpleTests(OSIntegrationTestCase): + """ + Simple instance creation tests without any other objects + """ + def setUp(self): + """ + Instantiates the CreateImage object that is responsible for downloading and creating an OS image file + within OpenStack + """ + super(self.__class__, self).__start__() + + guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) + self.vm_inst_name = guid + '-inst' + self.nova = nova_utils.nova_client(self.os_creds) + self.os_image_settings = openstack_tests.cirros_url_image(name=guid + '-image') + + # Initialize for tearDown() + self.image_creator = None + self.flavor_creator = None + self.inst_creator = None + + try: + # Create Image + self.image_creator = OpenStackImage(self.os_creds, self.os_image_settings) + self.image_creator.create() + # Create Flavor + self.flavor_creator = OpenStackFlavor( + self.admin_os_creds, + FlavorSettings(name=guid + '-flavor-name', ram=2048, disk=10, vcpus=2)) + self.flavor_creator.create() + except Exception as e: + self.tearDown() + raise e + + def tearDown(self): + """ + Cleans the created object + """ + if self.inst_creator: + try: + self.inst_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning VM instance with message - ' + e.message) + + if self.flavor_creator: + try: + self.flavor_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning flavor with message - ' + e.message) + + if self.image_creator: + try: + self.image_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning image with message - ' + e.message) + + super(self.__class__, self).__clean__() + + def test_create_delete_instance(self): + """ + Tests the creation of an OpenStack instance with a single port with a static IP without a Floating IP. + """ + instance_settings = VmInstanceSettings(name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name) + + self.inst_creator = OpenStackVmInstance( + self.os_creds, instance_settings, self.image_creator.image_settings) + + vm_inst = self.inst_creator.create() + self.assertEquals(1, len(nova_utils.get_servers_by_name(self.nova, instance_settings.name))) + + # Delete instance + nova_utils.delete_vm_instance(self.nova, vm_inst) + + self.assertTrue(self.inst_creator.vm_deleted(block=True)) + self.assertEquals(0, len(nova_utils.get_servers_by_name(self.nova, instance_settings.name))) + + # Exception should not be thrown + self.inst_creator.clean() + + +class CreateInstanceSingleNetworkTests(OSIntegrationTestCase): + """ + Test for the CreateInstance class with a single NIC/Port with Floating IPs + """ + + def setUp(self): + """ + Instantiates the CreateImage object that is responsible for downloading and creating an OS image file + within OpenStack + """ + super(self.__class__, self).__start__() + + guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) + self.keypair_priv_filepath = 'tmp/' + guid + self.keypair_pub_filepath = self.keypair_priv_filepath + '.pub' + self.keypair_name = guid + '-kp' + self.vm_inst_name = guid + '-inst' + self.port_1_name = guid + 'port-1' + self.port_2_name = guid + 'port-2' + self.floating_ip_name = guid + 'fip1' + + # Initialize for tearDown() + self.image_creator = None + self.network_creator = None + self.router_creator = None + self.flavor_creator = None + self.keypair_creator = None + self.inst_creators = list() + + self.pub_net_config = openstack_tests.get_pub_net_config( + net_name=guid + '-pub-net', subnet_name=guid + '-pub-subnet', + router_name=guid + '-pub-router', external_net=self.ext_net_name) + self.os_image_settings = openstack_tests.cirros_url_image(name=guid + '-image') + + try: + # Create Image + self.image_creator = OpenStackImage(self.os_creds, self.os_image_settings) + self.image_creator.create() + + # Create Network + self.network_creator = OpenStackNetwork(self.os_creds, self.pub_net_config.network_settings) + self.network_creator.create() + + # Create Router + self.router_creator = OpenStackRouter(self.os_creds, self.pub_net_config.router_settings) + self.router_creator.create() + + # Create Flavor + self.flavor_creator = OpenStackFlavor( + self.admin_os_creds, + FlavorSettings(name=guid + '-flavor-name', ram=2048, disk=10, vcpus=2)) + self.flavor_creator.create() + + self.keypair_creator = OpenStackKeypair( + self.os_creds, KeypairSettings( + name=self.keypair_name, public_filepath=self.keypair_pub_filepath, + private_filepath=self.keypair_priv_filepath)) + self.keypair_creator.create() + except Exception as e: + self.tearDown() + raise e + + def tearDown(self): + """ + Cleans the created object + """ + for inst_creator in self.inst_creators: + try: + inst_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning VM instance with message - ' + e.message) + + if self.keypair_creator: + try: + self.keypair_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning keypair with message - ' + e.message) + + if os.path.isfile(self.keypair_pub_filepath): + os.remove(self.keypair_pub_filepath) + + if os.path.isfile(self.keypair_priv_filepath): + os.remove(self.keypair_priv_filepath) + + if self.flavor_creator: + try: + self.flavor_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning flavor with message - ' + e.message) + + if self.router_creator: + try: + self.router_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning router with message - ' + e.message) + + if self.network_creator: + try: + self.network_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning network with message - ' + e.message) + + if self.image_creator: + try: + self.image_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning image with message - ' + e.message) + + super(self.__class__, self).__clean__() + + def test_single_port_static(self): + """ + Tests the creation of an OpenStack instance with a single port with a static IP without a Floating IP. + """ + ip_1 = '10.55.1.100' + + port_settings = PortSettings( + name=self.port_1_name, network_name=self.pub_net_config.network_settings.name, + ip_addrs=[{'subnet_name': self.pub_net_config.network_settings.subnet_settings[0].name, 'ip': ip_1}]) + + instance_settings = VmInstanceSettings( + name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings], + floating_ip_settings=[FloatingIpSettings( + name=self.floating_ip_name, port_name=self.port_1_name, + router_name=self.pub_net_config.router_settings.name)]) + + inst_creator = OpenStackVmInstance( + self.os_creds, instance_settings, self.image_creator.image_settings, + keypair_settings=self.keypair_creator.keypair_settings) + self.inst_creators.append(inst_creator) + vm_inst = inst_creator.create() + + self.assertEquals(ip_1, inst_creator.get_port_ip(self.port_1_name)) + self.assertTrue(inst_creator.vm_active(block=True)) + self.assertEquals(vm_inst, inst_creator.get_vm_inst()) + + def test_ssh_client_fip_before_active(self): + """ + Tests the ability to access a VM via SSH and a floating IP when it has been assigned prior to being active. + """ + port_settings = PortSettings( + name=self.port_1_name, network_name=self.pub_net_config.network_settings.name) + + instance_settings = VmInstanceSettings( + name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings], + floating_ip_settings=[FloatingIpSettings( + name=self.floating_ip_name, port_name=self.port_1_name, + router_name=self.pub_net_config.router_settings.name)]) + + inst_creator = OpenStackVmInstance( + self.os_creds, instance_settings, self.image_creator.image_settings, + keypair_settings=self.keypair_creator.keypair_settings) + self.inst_creators.append(inst_creator) + vm_inst = inst_creator.create() + self.assertIsNotNone(vm_inst) + + self.assertTrue(inst_creator.vm_active(block=True)) + self.assertEquals(vm_inst, inst_creator.get_vm_inst()) + + validate_ssh_client(inst_creator) + + def test_ssh_client_fip_after_active(self): + """ + Tests the ability to access a VM via SSH and a floating IP when it has been assigned prior to being active. + """ + port_settings = PortSettings( + name=self.port_1_name, network_name=self.pub_net_config.network_settings.name) + + instance_settings = VmInstanceSettings( + name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings], + floating_ip_settings=[FloatingIpSettings( + name=self.floating_ip_name, port_name=self.port_1_name, + router_name=self.pub_net_config.router_settings.name)]) + + inst_creator = OpenStackVmInstance( + self.os_creds, instance_settings, self.image_creator.image_settings, + keypair_settings=self.keypair_creator.keypair_settings) + self.inst_creators.append(inst_creator) + + # block=True will force the create() method to block until the + vm_inst = inst_creator.create(block=True) + self.assertIsNotNone(vm_inst) + + self.assertTrue(inst_creator.vm_active(block=True)) + self.assertEquals(vm_inst, inst_creator.get_vm_inst()) + + validate_ssh_client(inst_creator) + + # TODO - Determine how allowed_address_pairs is supposed to operate before continuing this test + # see http://docs.openstack.org/developer/dragonflow/specs/allowed_address_pairs.html for a functional description + # def test_allowed_address_port_access(self): + # """ + # Tests to ensure that setting allowed_address_pairs on a port functions as designed + # """ + # port_settings_1 = PortSettings( + # name=self.port_1_name + '-1', network_name=self.pub_net_config.network_settings.name) + # + # instance_settings_1 = VmInstanceSettings( + # name=self.vm_inst_name + '-1', flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings_1], + # floating_ip_settings=[FloatingIpSettings( + # name=self.floating_ip_name + '-1', port_name=port_settings_1.name, + # router_name=self.pub_net_config.router_settings.name)]) + # + # inst_creator_1 = OpenStackVmInstance( + # self.os_creds, instance_settings_1, self.image_creator.image_settings, + # keypair_settings=self.keypair_creator.keypair_settings) + # self.inst_creators.append(inst_creator_1) + # + # # block=True will force the create() method to block until the + # vm_inst_1 = inst_creator_1.create(block=True) + # self.assertIsNotNone(vm_inst_1) + # + # port_settings_1 = PortSettings( + # name=self.port_1_name + '-1', network_name=self.pub_net_config.network_settings.name) + # + # instance_settings_1 = VmInstanceSettings( + # name=self.vm_inst_name + '-1', flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings_1], + # floating_ip_settings=[FloatingIpSettings( + # name=self.floating_ip_name + '-1', port_name=port_settings_1.name, + # router_name=self.pub_net_config.router_settings.name)]) + # + # inst_creator_1 = OpenStackVmInstance( + # self.os_creds, instance_settings_1, self.image_creator.image_settings, + # keypair_settings=self.keypair_creator.keypair_settings) + # self.inst_creators.append(inst_creator_1) + # inst_creator_1.create(block=True) + # + # ip = inst_creator_1.get_port_ip(port_settings_1.name, + # subnet_name=self.pub_net_config.network_settings.subnet_settings[0].name) + # self.assertIsNotNone(ip) + # mac_addr = inst_creator_1.get_port_mac(port_settings_1.name) + # self.assertIsNotNone(mac_addr) + # + # allowed_address_pairs = [{'ip_address': ip, 'mac_address': mac_addr}] + # + # # Create VM that can be accessed by vm_inst_1 + # port_settings_2 = PortSettings( + # name=self.port_1_name + '-2', network_name=self.pub_net_config.network_settings.name, + # allowed_address_pairs=allowed_address_pairs) + # + # instance_settings_2 = VmInstanceSettings( + # name=self.vm_inst_name + '-2', flavor=self.flavor_creator.flavor_settings.name, + # port_settings=[port_settings_2]) + # + # inst_creator_2 = OpenStackVmInstance( + # self.os_creds, instance_settings_2, self.image_creator.image_settings) + # self.inst_creators.append(inst_creator_2) + # inst_creator_2.create(block=True) + # + # # Create VM that cannot be accessed by vm_inst_1 + # ip = '10.55.0.101' + # mac_addr = '0a:1b:2c:3d:4e:5f' + # invalid_address_pairs = [{'ip_address': ip, 'mac_address': mac_addr}] + # + # port_settings_3 = PortSettings( + # name=self.port_1_name + '-3', network_name=self.pub_net_config.network_settings.name, + # allowed_address_pairs=invalid_address_pairs) + # + # instance_settings_3 = VmInstanceSettings( + # name=self.vm_inst_name + '-3', flavor=self.flavor_creator.flavor_settings.name, + # port_settings=[port_settings_3]) + # + # inst_creator_3 = OpenStackVmInstance( + # self.os_creds, instance_settings_3, self.image_creator.image_settings) + # self.inst_creators.append(inst_creator_3) + # inst_creator_3.create(block=True) + # + # print 'foo' + # I expected that this feature would block/allow traffic from specific endpoints (VMs). In this case, I would expect + # inst_1 to be able to access inst_2 but not inst_3; however, they all can access each other. + # TODO - Add validation + + +class CreateInstancePortManipulationTests(OSIntegrationTestCase): + """ + Test for the CreateInstance class with a single NIC/Port where mac and IP values are manually set + """ + + def setUp(self): + """ + Instantiates the CreateImage object that is responsible for downloading and creating an OS image file + within OpenStack + """ + super(self.__class__, self).__start__() + + guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) + self.vm_inst_name = guid + '-inst' + self.port_1_name = guid + 'port-1' + self.port_2_name = guid + 'port-2' + self.floating_ip_name = guid + 'fip1' + + # Initialize for tearDown() + self.image_creator = None + self.network_creator = None + self.flavor_creator = None + self.inst_creator = None + + self.net_config = openstack_tests.get_priv_net_config( + net_name=guid + '-pub-net', subnet_name=guid + '-pub-subnet', + router_name=guid + '-pub-router', external_net=self.ext_net_name) + self.os_image_settings = openstack_tests.cirros_url_image(name=guid + '-image') + + try: + # Create Image + self.image_creator = OpenStackImage(self.os_creds, self.os_image_settings) + self.image_creator.create() + + # Create Network + self.network_creator = OpenStackNetwork(self.os_creds, self.net_config.network_settings) + self.network_creator.create() + + # Create Flavor + self.flavor_creator = OpenStackFlavor( + self.admin_os_creds, + FlavorSettings(name=guid + '-flavor-name', ram=2048, disk=10, vcpus=2)) + self.flavor_creator.create() + except Exception as e: + self.tearDown() + raise e + + def tearDown(self): + """ + Cleans the created object + """ + if self.inst_creator: + try: + self.inst_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning VM instance with message - ' + e.message) + + if self.flavor_creator: + try: + self.flavor_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning flavor with message - ' + e.message) + + if self.network_creator: + try: + self.network_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning network with message - ' + e.message) + + if self.image_creator: + try: + self.image_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning image with message - ' + e.message) + + super(self.__class__, self).__clean__() + + def test_set_custom_valid_ip_one_subnet(self): + """ + Tests the creation of an OpenStack instance with a single port with a static IP on a network with one subnet. + """ + ip = '10.55.0.101' + port_settings = PortSettings( + name=self.port_1_name, network_name=self.net_config.network_settings.name, + ip_addrs=[{'subnet_name': self.net_config.network_settings.subnet_settings[0].name, 'ip': ip}]) + + instance_settings = VmInstanceSettings( + name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings]) + + self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings) + self.inst_creator.create() + + self.assertEquals(ip, self.inst_creator.get_port_ip( + self.port_1_name, subnet_name=self.net_config.network_settings.subnet_settings[0].name)) + + def test_set_custom_invalid_ip_one_subnet(self): + """ + Tests the creation of an OpenStack instance with a single port with a static IP on a network with one subnet. + """ + ip = '10.66.0.101' + port_settings = PortSettings( + name=self.port_1_name, network_name=self.net_config.network_settings.name, + ip_addrs=[{'subnet_name': self.net_config.network_settings.subnet_settings[0].name, 'ip': ip}]) + + instance_settings = VmInstanceSettings( + name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings]) + + self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings) + + with self.assertRaises(Exception): + self.inst_creator.create() + + def test_set_custom_valid_mac(self): + """ + Tests the creation of an OpenStack instance with a single port where the MAC address is assigned. + """ + mac_addr = '0a:1b:2c:3d:4e:5f' + port_settings = PortSettings( + name=self.port_1_name, network_name=self.net_config.network_settings.name, mac_address=mac_addr) + + instance_settings = VmInstanceSettings( + name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings]) + + self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings) + self.inst_creator.create() + + self.assertEquals(mac_addr, self.inst_creator.get_port_mac(self.port_1_name)) + + def test_set_custom_invalid_mac(self): + """ + Tests the creation of an OpenStack instance with a single port where an invalid MAC address value is being + assigned. This should raise an Exception + """ + port_settings = PortSettings( + name=self.port_1_name, network_name=self.net_config.network_settings.name, mac_address='foo') + + instance_settings = VmInstanceSettings( + name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings]) + + self.inst_creator = OpenStackVmInstance( + self.os_creds, instance_settings, self.image_creator.image_settings) + + with self.assertRaises(Exception): + self.inst_creator.create() + + def test_set_custom_mac_and_ip(self): + """ + Tests the creation of an OpenStack instance with a single port where the IP and MAC address is assigned. + """ + ip = '10.55.0.101' + mac_addr = '0a:1b:2c:3d:4e:5f' + port_settings = PortSettings( + name=self.port_1_name, network_name=self.net_config.network_settings.name, mac_address=mac_addr, + ip_addrs=[{'subnet_name': self.net_config.network_settings.subnet_settings[0].name, 'ip': ip}]) + + instance_settings = VmInstanceSettings( + name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings]) + + self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings) + self.inst_creator.create() + + self.assertEquals(ip, self.inst_creator.get_port_ip( + self.port_1_name, subnet_name=self.net_config.network_settings.subnet_settings[0].name)) + self.assertEquals(mac_addr, self.inst_creator.get_port_mac(self.port_1_name)) + + def test_set_allowed_address_pairs(self): + """ + Tests the creation of an OpenStack instance with a single port where max_allowed_address_pair is set. + """ + ip = '10.55.0.101' + mac_addr = '0a:1b:2c:3d:4e:5f' + pair = {'ip_address': ip, 'mac_address': mac_addr} + port_settings = PortSettings( + name=self.port_1_name, network_name=self.net_config.network_settings.name, allowed_address_pairs=[pair]) + + instance_settings = VmInstanceSettings( + name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings]) + + self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings) + self.inst_creator.create() + + port = self.inst_creator.get_port_by_name(port_settings.name) + self.assertIsNotNone(port) + self.assertIsNotNone(port['port'].get('allowed_address_pairs')) + self.assertEquals(1, len(port['port']['allowed_address_pairs'])) + validation_utils.objects_equivalent(pair, port['port']['allowed_address_pairs'][0]) + + def test_set_allowed_address_pairs_bad_mac(self): + """ + Tests the creation of an OpenStack instance with a single port where max_allowed_address_pair is set with an + invalid MAC address. + """ + ip = '10.55.0.101' + mac_addr = 'foo' + pair = {'ip_address': ip, 'mac_address': mac_addr} + pairs = set() + pairs.add((ip, mac_addr)) + port_settings = PortSettings( + name=self.port_1_name, network_name=self.net_config.network_settings.name, allowed_address_pairs=[pair]) + + instance_settings = VmInstanceSettings( + name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings]) + + self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings) + with self.assertRaises(Exception): + self.inst_creator.create() + + def test_set_allowed_address_pairs_bad_ip(self): + """ + Tests the creation of an OpenStack instance with a single port where max_allowed_address_pair is set with an + invalid MAC address. + """ + ip = 'foo' + mac_addr = '0a:1b:2c:3d:4e:5f' + pair = {'ip_address': ip, 'mac_address': mac_addr} + pairs = set() + pairs.add((ip, mac_addr)) + port_settings = PortSettings( + name=self.port_1_name, network_name=self.net_config.network_settings.name, allowed_address_pairs=[pair]) + + instance_settings = VmInstanceSettings( + name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=[port_settings]) + + self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings) + with self.assertRaises(Exception): + self.inst_creator.create() + + +class CreateInstanceOnComputeHost(OSComponentTestCase): + """ + Test for the CreateInstance where one VM is deployed to each compute node + """ + + def setUp(self): + """ + Instantiates the CreateImage object that is responsible for downloading and creating an OS image file + within OpenStack + """ + guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) + self.vm_inst_name = guid + '-inst' + self.port_base_name = guid + 'port' + + # Initialize for tearDown() + self.image_creator = None + self.flavor_creator = None + self.network_creator = None + self.inst_creators = list() + + self.priv_net_config = openstack_tests.get_priv_net_config( + net_name=guid + '-priv-net', subnet_name=guid + '-priv-subnet') + + self.os_image_settings = openstack_tests.cirros_url_image(name=guid + '-image') + + try: + # Create Network + self.network_creator = OpenStackNetwork(self.os_creds, self.priv_net_config.network_settings) + self.network_creator.create() + + # Create Flavor + self.flavor_creator = OpenStackFlavor( + self.os_creds, + FlavorSettings(name=guid + '-flavor-name', ram=512, disk=1, vcpus=1)) + self.flavor_creator.create() + + # Create Image + self.image_creator = OpenStackImage(self.os_creds, self.os_image_settings) + self.image_creator.create() + + except Exception as e: + self.tearDown() + raise e + + def tearDown(self): + """ + Cleans the created object + """ + for inst_creator in self.inst_creators: + try: + inst_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning VM instance with message - ' + e.message) + + if self.flavor_creator: + try: + self.flavor_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning flavor with message - ' + e.message) + + if self.network_creator: + try: + self.network_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning network with message - ' + e.message) + + if self.image_creator: + try: + self.image_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning image with message - ' + e.message) + + def test_deploy_vm_to_each_compute_node(self): + """ + Tests the creation of OpenStack VM instances to each compute node. + """ + from snaps.openstack.utils import nova_utils + nova = nova_utils.nova_client(self.os_creds) + zones = nova_utils.get_nova_availability_zones(nova) + + # Create Instance on each server/zone + ctr = 0 + for zone in zones: + inst_name = self.vm_inst_name + '-' + zone + ctr += 1 + port_settings = PortSettings(name=self.port_base_name + '-' + str(ctr), + network_name=self.priv_net_config.network_settings.name) + + instance_settings = VmInstanceSettings( + name=inst_name, flavor=self.flavor_creator.flavor_settings.name, availability_zone=zone, + port_settings=[port_settings]) + inst_creator = OpenStackVmInstance( + self.os_creds, instance_settings, self.image_creator.image_settings) + self.inst_creators.append(inst_creator) + inst_creator.create() + + # Validate instances to ensure they've been deployed to the correct server + index = 0 + for zone in zones: + creator = self.inst_creators[index] + self.assertTrue(creator.vm_active(block=True)) + vm = creator.get_vm_inst() + deployed_zone = vm._info['OS-EXT-AZ:availability_zone'] + deployed_host = vm._info['OS-EXT-SRV-ATTR:host'] + self.assertEquals(zone, deployed_zone + ':' + deployed_host) + index += 1 + + +class CreateInstancePubPrivNetTests(OSIntegrationTestCase): + """ + Test for the CreateInstance class with two NIC/Ports, eth0 with floating IP and eth1 w/o + These tests require a Centos image + """ + + def setUp(self): + """ + Instantiates the CreateImage object that is responsible for downloading and creating an OS image file + within OpenStack + """ + super(self.__class__, self).__start__() + + # Initialize for tearDown() + self.image_creator = None + self.network_creators = list() + self.router_creators = list() + self.flavor_creator = None + self.keypair_creator = None + self.inst_creator = None + + self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) + self.keypair_priv_filepath = 'tmp/' + self.guid + self.keypair_pub_filepath = self.keypair_priv_filepath + '.pub' + self.keypair_name = self.guid + '-kp' + self.vm_inst_name = self.guid + '-inst' + self.port_1_name = self.guid + '-port-1' + self.port_2_name = self.guid + '-port-2' + self.floating_ip_name = self.guid + 'fip1' + self.priv_net_config = openstack_tests.get_priv_net_config( + net_name=self.guid + '-priv-net', subnet_name=self.guid + '-priv-subnet', + router_name=self.guid + '-priv-router', external_net=self.ext_net_name) + self.pub_net_config = openstack_tests.get_pub_net_config( + net_name=self.guid + '-pub-net', subnet_name=self.guid + '-pub-subnet', + router_name=self.guid + '-pub-router', external_net=self.ext_net_name) + image_name = self.__class__.__name__ + '-' + str(uuid.uuid4()) + self.os_image_settings = openstack_tests.centos_url_image(name=image_name) + + try: + # Create Image + self.image_creator = OpenStackImage(self.os_creds, self.os_image_settings) + self.image_creator.create() + + # First network is public + self.network_creators.append(OpenStackNetwork(self.os_creds, self.pub_net_config.network_settings)) + # Second network is private + self.network_creators.append(OpenStackNetwork(self.os_creds, self.priv_net_config.network_settings)) + for network_creator in self.network_creators: + network_creator.create() + + self.router_creators.append(OpenStackRouter(self.os_creds, self.pub_net_config.router_settings)) + self.router_creators.append(OpenStackRouter(self.os_creds, self.priv_net_config.router_settings)) + + # Create Routers + for router_creator in self.router_creators: + router_creator.create() + + # Create Flavor + self.flavor_creator = OpenStackFlavor( + self.admin_os_creds, + FlavorSettings(name=self.guid + '-flavor-name', ram=2048, disk=10, vcpus=2)) + self.flavor_creator.create() + + # Create Keypair + self.keypair_creator = OpenStackKeypair( + self.os_creds, KeypairSettings( + name=self.keypair_name, public_filepath=self.keypair_pub_filepath, + private_filepath=self.keypair_priv_filepath)) + self.keypair_creator.create() + except Exception as e: + self.tearDown() + raise Exception(e.message) + + def tearDown(self): + """ + Cleans the created objects + """ + if self.inst_creator: + try: + self.inst_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning VM instance with message - ' + e.message) + + if self.keypair_creator: + try: + self.keypair_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning keypair with message - ' + e.message) + + if os.path.isfile(self.keypair_pub_filepath): + os.remove(self.keypair_pub_filepath) + + if os.path.isfile(self.keypair_priv_filepath): + os.remove(self.keypair_priv_filepath) + + if self.flavor_creator: + try: + self.flavor_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning flavor with message - ' + e.message) + + for router_creator in self.router_creators: + try: + router_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning router with message - ' + e.message) + + for network_creator in self.network_creators: + try: + network_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning network with message - ' + e.message) + + if self.image_creator: + try: + self.image_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning image with message - ' + e.message) + + super(self.__class__, self).__clean__() + + def test_dual_ports_dhcp(self): + """ + Tests the creation of an OpenStack instance with a dual ports/NICs with a DHCP assigned IP. + NOTE: This test and any others that call ansible will most likely fail unless you do one of + two things: + 1. Have a ~/.ansible.cfg (or alternate means) to set host_key_checking = False + 2. Set the following environment variable in your executing shell: ANSIBLE_HOST_KEY_CHECKING=False + Should this not be performed, the creation of the host ssh key will cause your ansible calls to fail. + """ + # Create ports/NICs for instance + ports_settings = [] + ctr = 1 + for network_creator in self.network_creators: + ports_settings.append(PortSettings( + name=self.guid + '-port-' + str(ctr), + network_name=network_creator.network_settings.name)) + ctr += 1 + + # Create instance + instance_settings = VmInstanceSettings( + name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, port_settings=ports_settings, + floating_ip_settings=[FloatingIpSettings( + name=self.floating_ip_name, port_name=self.port_1_name, + router_name=self.pub_net_config.router_settings.name)]) + + self.inst_creator = OpenStackVmInstance( + self.os_creds, instance_settings, self.image_creator.image_settings, + keypair_settings=self.keypair_creator.keypair_settings) + + vm_inst = self.inst_creator.create(block=True) + + self.assertEquals(vm_inst, self.inst_creator.get_vm_inst()) + + # Effectively blocks until VM has been properly activated + self.assertTrue(self.inst_creator.vm_active(block=True)) + + # Effectively blocks until VM's ssh port has been opened + self.assertTrue(self.inst_creator.vm_ssh_active(block=True)) + + self.inst_creator.config_nics() + + # TODO - *** ADD VALIDATION HERE *** + # TODO - Add validation that both floating IPs work + # TODO - Add tests where only one NIC has a floating IP + # TODO - Add tests where one attempts to place a floating IP on a network/router without an external gateway + + +class InstanceSecurityGroupTests(OSIntegrationTestCase): + """ + Tests that include, add, and remove security groups from VM instances + """ + def setUp(self): + """ + Instantiates the CreateImage object that is responsible for downloading and creating an OS image file + within OpenStack + """ + super(self.__class__, self).__start__() + + self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) + self.vm_inst_name = self.guid + '-inst' + self.nova = nova_utils.nova_client(self.os_creds) + self.os_image_settings = openstack_tests.cirros_url_image(name=self.guid + '-image') + + self.keypair_priv_filepath = 'tmp/' + self.guid + self.keypair_pub_filepath = self.keypair_priv_filepath + '.pub' + self.keypair_name = self.guid + '-kp' + self.vm_inst_name = self.guid + '-inst' + self.port_1_name = self.guid + 'port-1' + self.port_2_name = self.guid + 'port-2' + self.floating_ip_name = self.guid + 'fip1' + + self.pub_net_config = openstack_tests.get_pub_net_config( + net_name=self.guid + '-pub-net', subnet_name=self.guid + '-pub-subnet', + router_name=self.guid + '-pub-router', external_net=self.ext_net_name) + + # Initialize for tearDown() + self.image_creator = None + self.keypair_creator = None + self.flavor_creator = None + self.network_creator = None + self.router_creator = None + self.inst_creator = None + self.sec_grp_creators = list() + + try: + # Create Image + self.image_creator = OpenStackImage(self.os_creds, self.os_image_settings) + self.image_creator.create() + + # Create Network + self.network_creator = OpenStackNetwork(self.os_creds, self.pub_net_config.network_settings) + self.network_creator.create() + + # Create Router + self.router_creator = OpenStackRouter(self.os_creds, self.pub_net_config.router_settings) + self.router_creator.create() + + # Create Flavor + self.flavor_creator = OpenStackFlavor( + self.admin_os_creds, + FlavorSettings(name=self.guid + '-flavor-name', ram=2048, disk=10, vcpus=2)) + self.flavor_creator.create() + + self.keypair_creator = OpenStackKeypair( + self.os_creds, KeypairSettings( + name=self.keypair_name, public_filepath=self.keypair_pub_filepath, + private_filepath=self.keypair_priv_filepath)) + self.keypair_creator.create() + except Exception as e: + self.tearDown() + raise e + + def tearDown(self): + """ + Cleans the created object + """ + if self.inst_creator: + try: + self.inst_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning VM instance with message - ' + e.message) + + for sec_grp_creator in self.sec_grp_creators: + try: + sec_grp_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning security group with message - ' + e.message) + + if self.keypair_creator: + try: + self.keypair_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning keypair with message - ' + e.message) + + if os.path.isfile(self.keypair_pub_filepath): + os.remove(self.keypair_pub_filepath) + + if os.path.isfile(self.keypair_priv_filepath): + os.remove(self.keypair_priv_filepath) + + if self.flavor_creator: + try: + self.flavor_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning flavor with message - ' + e.message) + + if self.router_creator: + try: + self.router_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning router with message - ' + e.message) + + if self.network_creator: + try: + self.network_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning network with message - ' + e.message) + + if self.image_creator: + try: + self.image_creator.clean() + except Exception as e: + logger.error('Unexpected exception cleaning image with message - ' + e.message) + + super(self.__class__, self).__clean__() + + def test_add_security_group(self): + """ + Tests the addition of a security group created after the instance. + """ + # Create instance + instance_settings = VmInstanceSettings( + name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name) + self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings) + vm_inst = self.inst_creator.create() + self.assertIsNotNone(vm_inst) + + # Create security group object to add to instance + sec_grp_settings = SecurityGroupSettings(name=self.guid + '-name', description='hello group') + sec_grp_creator = OpenStackSecurityGroup(self.os_creds, sec_grp_settings) + sec_grp = sec_grp_creator.create() + self.sec_grp_creators.append(sec_grp_creator) + + # Check that group has not been added + self.assertFalse(inst_has_sec_grp(self.inst_creator.get_vm_inst(), sec_grp_settings.name)) + + # Add security group to instance after activated + self.inst_creator.add_security_group(sec_grp) + + # Validate that security group has been added + self.assertTrue(inst_has_sec_grp(self.inst_creator.get_vm_inst(), sec_grp_settings.name)) + + def test_add_invalid_security_group(self): + """ + Tests the addition of a security group that no longer exists. + """ + # Create instance + instance_settings = VmInstanceSettings( + name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name) + self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings) + vm_inst = self.inst_creator.create() + self.assertIsNotNone(vm_inst) + + # Create security group object to add to instance + sec_grp_settings = SecurityGroupSettings(name=self.guid + '-name', description='hello group') + sec_grp_creator = OpenStackSecurityGroup(self.os_creds, sec_grp_settings) + sec_grp = sec_grp_creator.create() + sec_grp_creator.clean() + self.sec_grp_creators.append(sec_grp_creator) + + # Check that group has not been added + self.assertFalse(inst_has_sec_grp(self.inst_creator.get_vm_inst(), sec_grp_settings.name)) + + # Add security group to instance after activated + self.assertFalse(self.inst_creator.add_security_group(sec_grp)) + + # Validate that security group has been added + self.assertFalse(inst_has_sec_grp(self.inst_creator.get_vm_inst(), sec_grp_settings.name)) + + def test_remove_security_group(self): + """ + Tests the removal of a security group created before and added to the instance. + """ + # Create security group object to add to instance + sec_grp_settings = SecurityGroupSettings(name=self.guid + '-name', description='hello group') + sec_grp_creator = OpenStackSecurityGroup(self.os_creds, sec_grp_settings) + sec_grp = sec_grp_creator.create() + self.sec_grp_creators.append(sec_grp_creator) + + # Create instance + instance_settings = VmInstanceSettings( + name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, + security_group_names=[sec_grp_settings.name]) + self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings) + vm_inst = self.inst_creator.create() + self.assertIsNotNone(vm_inst) + + # Check that group has been added + self.assertTrue(inst_has_sec_grp(vm_inst, sec_grp_settings.name)) + + # Add security group to instance after activated + self.assertTrue(self.inst_creator.remove_security_group(sec_grp)) + + # Validate that security group has been added + self.assertFalse(inst_has_sec_grp(self.inst_creator.get_vm_inst(), sec_grp_settings.name)) + + def test_remove_security_group_never_added(self): + """ + Tests the removal of a security group that was never added in the first place. + """ + # Create security group object to add to instance + sec_grp_settings = SecurityGroupSettings(name=self.guid + '-name', description='hello group') + sec_grp_creator = OpenStackSecurityGroup(self.os_creds, sec_grp_settings) + sec_grp = sec_grp_creator.create() + self.sec_grp_creators.append(sec_grp_creator) + + # Create instance + instance_settings = VmInstanceSettings( + name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name) + self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings) + vm_inst = self.inst_creator.create() + self.assertIsNotNone(vm_inst) + + # Check that group has been added + self.assertFalse(inst_has_sec_grp(vm_inst, sec_grp_settings.name)) + + # Add security group to instance after activated + self.assertFalse(self.inst_creator.remove_security_group(sec_grp)) + + # Validate that security group has been added + self.assertFalse(inst_has_sec_grp(self.inst_creator.get_vm_inst(), sec_grp_settings.name)) + + def test_add_same_security_group(self): + """ + Tests the addition of a security group created before add added to the instance. + """ + # Create security group object to add to instance + sec_grp_settings = SecurityGroupSettings(name=self.guid + '-name', description='hello group') + sec_grp_creator = OpenStackSecurityGroup(self.os_creds, sec_grp_settings) + sec_grp = sec_grp_creator.create() + self.sec_grp_creators.append(sec_grp_creator) + + # Create instance + instance_settings = VmInstanceSettings( + name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, + security_group_names=[sec_grp_settings.name]) + self.inst_creator = OpenStackVmInstance(self.os_creds, instance_settings, self.image_creator.image_settings) + vm_inst = self.inst_creator.create() + self.assertIsNotNone(vm_inst) + + # Check that group has been added + self.assertTrue(inst_has_sec_grp(vm_inst, sec_grp_settings.name)) + + # Add security group to instance after activated + self.assertTrue(self.inst_creator.add_security_group(sec_grp)) + + # Validate that security group has been added + self.assertTrue(inst_has_sec_grp(self.inst_creator.get_vm_inst(), sec_grp_settings.name)) + + +def inst_has_sec_grp(vm_inst, sec_grp_name): + """ + Returns true if instance has a security group of a given name + :return: + """ + if not hasattr(vm_inst, 'security_groups'): + return False + + found = False + for sec_grp_dict in vm_inst.security_groups: + if sec_grp_name in sec_grp_dict['name']: + found = True + break + return found + + +def validate_ssh_client(instance_creator): + """ + Returns True if instance_creator returns an SSH client that is valid + :param instance_creator: the object responsible for creating the VM instance + :return: T/F + """ + ssh_active = instance_creator.vm_ssh_active(block=True) + + if ssh_active: + ssh_client = instance_creator.ssh_client() + if ssh_client: + out = ssh_client.exec_command('pwd')[1] + else: + return False + + channel = out.channel + in_buffer = channel.in_buffer + pwd_out = in_buffer.read(1024) + if not pwd_out or len(pwd_out) < 10: + return False + return True + + return False diff --git a/snaps/openstack/tests/create_keypairs_tests.py b/snaps/openstack/tests/create_keypairs_tests.py new file mode 100644 index 0000000..e4409a9 --- /dev/null +++ b/snaps/openstack/tests/create_keypairs_tests.py @@ -0,0 +1,203 @@ +# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs") +# and others. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os +import uuid +import unittest + +from Crypto.PublicKey import RSA + +from snaps.openstack.create_keypairs import KeypairSettings, OpenStackKeypair +from snaps.openstack.utils import nova_utils +from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase + +__author__ = 'spisarski' + + +class KeypairSettingsUnitTests(unittest.TestCase): + """ + Tests the construction of the KeypairSettings class + """ + + def test_no_params(self): + with self.assertRaises(Exception): + KeypairSettings() + + def test_empty_config(self): + with self.assertRaises(Exception): + KeypairSettings(config=dict()) + + def test_name_only(self): + settings = KeypairSettings(name='foo') + self.assertEquals('foo', settings.name) + self.assertIsNone(settings.public_filepath) + self.assertIsNone(settings.private_filepath) + + def test_config_with_name_only(self): + settings = KeypairSettings(config={'name': 'foo'}) + self.assertEquals('foo', settings.name) + self.assertIsNone(settings.public_filepath) + self.assertIsNone(settings.private_filepath) + + def test_name_pub_only(self): + settings = KeypairSettings(name='foo', public_filepath='/foo/bar.pub') + self.assertEquals('foo', settings.name) + self.assertEquals('/foo/bar.pub', settings.public_filepath) + self.assertIsNone(settings.private_filepath) + + def test_config_with_name_pub_only(self): + settings = KeypairSettings(config={'name': 'foo', 'public_filepath': '/foo/bar.pub'}) + self.assertEquals('foo', settings.name) + self.assertEquals('/foo/bar.pub', settings.public_filepath) + self.assertIsNone(settings.private_filepath) + + def test_name_priv_only(self): + settings = KeypairSettings(name='foo', private_filepath='/foo/bar') + self.assertEquals('foo', settings.name) + self.assertIsNone(settings.public_filepath) + self.assertEquals('/foo/bar', settings.private_filepath) + + def test_config_with_name_priv_only(self): + settings = KeypairSettings(config={'name': 'foo', 'private_filepath': '/foo/bar'}) + self.assertEquals('foo', settings.name) + self.assertIsNone(settings.public_filepath) + self.assertEquals('/foo/bar', settings.private_filepath) + + def test_all(self): + settings = KeypairSettings(name='foo', public_filepath='/foo/bar.pub', private_filepath='/foo/bar') + self.assertEquals('foo', settings.name) + self.assertEquals('/foo/bar.pub', settings.public_filepath) + self.assertEquals('/foo/bar', settings.private_filepath) + + def test_config_all(self): + settings = KeypairSettings(config={'name': 'foo', 'public_filepath': '/foo/bar.pub', + 'private_filepath': '/foo/bar'}) + self.assertEquals('foo', settings.name) + self.assertEquals('/foo/bar.pub', settings.public_filepath) + self.assertEquals('/foo/bar', settings.private_filepath) + + +class CreateKeypairsTests(OSIntegrationTestCase): + """ + Tests for the OpenStackKeypair class + """ + + def setUp(self): + super(self.__class__, self).__start__() + + guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) + self.priv_file_path = 'tmp/' + guid + self.pub_file_path = self.priv_file_path + '.pub' + self.nova = nova_utils.nova_client(self.os_creds) + self.keypair_name = guid + + self.keypair_creator = None + + def tearDown(self): + """ + Cleanup of created keypair + """ + if self.keypair_creator: + self.keypair_creator.clean() + + try: + os.remove(self.pub_file_path) + except: + pass + + try: + os.remove(self.priv_file_path) + except: + pass + + super(self.__class__, self).__clean__() + + def test_create_keypair_only(self): + """ + Tests the creation of a generated keypair without saving to file + :return: + """ + self.keypair_creator = OpenStackKeypair(self.os_creds, KeypairSettings(name=self.keypair_name)) + self.keypair_creator.create() + + keypair = nova_utils.keypair_exists(self.nova, self.keypair_creator.get_keypair()) + self.assertEquals(self.keypair_creator.get_keypair(), keypair) + + def test_create_delete_keypair(self): + """ + Tests the creation then deletion of an OpenStack keypair to ensure clean() does not raise an Exception. + """ + # Create Image + self.keypair_creator = OpenStackKeypair(self.os_creds, KeypairSettings(name=self.keypair_name)) + created_keypair = self.keypair_creator.create() + self.assertIsNotNone(created_keypair) + + # Delete Image manually + nova_utils.delete_keypair(self.nova, created_keypair) + + self.assertIsNone(nova_utils.get_keypair_by_name(self.nova, self.keypair_name)) + + # Must not throw an exception when attempting to cleanup non-existent image + self.keypair_creator.clean() + self.assertIsNone(self.keypair_creator.get_keypair()) + + def test_create_keypair_save_pub_only(self): + """ + Tests the creation of a generated keypair and saves the public key only + :return: + """ + self.keypair_creator = OpenStackKeypair( + self.os_creds, KeypairSettings(name=self.keypair_name, public_filepath=self.pub_file_path)) + self.keypair_creator.create() + + keypair = nova_utils.keypair_exists(self.nova, self.keypair_creator.get_keypair()) + self.assertEquals(self.keypair_creator.get_keypair(), keypair) + + file_key = open(os.path.expanduser(self.pub_file_path)).read() + self.assertEquals(self.keypair_creator.get_keypair().public_key, file_key) + + def test_create_keypair_save_both(self): + """ + Tests the creation of a generated keypair and saves both private and public key files[ + :return: + """ + self.keypair_creator = OpenStackKeypair( + self.os_creds, KeypairSettings(name=self.keypair_name, public_filepath=self.pub_file_path, + private_filepath=self.priv_file_path)) + self.keypair_creator.create() + + keypair = nova_utils.keypair_exists(self.nova, self.keypair_creator.get_keypair()) + self.assertEquals(self.keypair_creator.get_keypair(), keypair) + + file_key = open(os.path.expanduser(self.pub_file_path)).read() + self.assertEquals(self.keypair_creator.get_keypair().public_key, file_key) + + self.assertTrue(os.path.isfile(self.priv_file_path)) + + def test_create_keypair_from_file(self): + """ + Tests the creation of an existing public keypair from a file + :return: + """ + keys = RSA.generate(1024) + nova_utils.save_keys_to_files(keys=keys, pub_file_path=self.pub_file_path) + self.keypair_creator = OpenStackKeypair( + self.os_creds, KeypairSettings(name=self.keypair_name, public_filepath=self.pub_file_path)) + self.keypair_creator.create() + + keypair = nova_utils.keypair_exists(self.nova, self.keypair_creator.get_keypair()) + self.assertEquals(self.keypair_creator.get_keypair(), keypair) + + file_key = open(os.path.expanduser(self.pub_file_path)).read() + self.assertEquals(self.keypair_creator.get_keypair().public_key, file_key) diff --git a/snaps/openstack/tests/create_network_tests.py b/snaps/openstack/tests/create_network_tests.py new file mode 100644 index 0000000..a2b17f8 --- /dev/null +++ b/snaps/openstack/tests/create_network_tests.py @@ -0,0 +1,533 @@ +# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs") +# and others. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import uuid +import unittest + +from snaps.openstack.create_network import OpenStackNetwork, NetworkSettings, SubnetSettings, PortSettings +from snaps.openstack import create_router +from snaps.openstack.tests import openstack_tests +from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase, OSComponentTestCase +from snaps.openstack.utils import neutron_utils +from snaps.openstack.utils.tests import neutron_utils_tests + +__author__ = 'spisarski' + + +class NetworkSettingsUnitTests(unittest.TestCase): + """ + Tests the construction of the NetworkSettings class + """ + + def test_no_params(self): + with self.assertRaises(Exception): + NetworkSettings() + + def test_empty_config(self): + with self.assertRaises(Exception): + NetworkSettings(config=dict()) + + def test_name_only(self): + settings = NetworkSettings(name='foo') + self.assertEquals('foo', settings.name) + self.assertTrue(settings.admin_state_up) + self.assertIsNone(settings.shared) + self.assertIsNone(settings.project_name) + self.assertFalse(settings.external) + self.assertIsNone(settings.network_type) + self.assertEquals(0, len(settings.subnet_settings)) + + def test_config_with_name_only(self): + settings = NetworkSettings(config={'name': 'foo'}) + self.assertEquals('foo', settings.name) + self.assertTrue(settings.admin_state_up) + self.assertIsNone(settings.shared) + self.assertIsNone(settings.project_name) + self.assertFalse(settings.external) + self.assertIsNone(settings.network_type) + self.assertEquals(0, len(settings.subnet_settings)) + + def test_all(self): + sub_settings = SubnetSettings(name='foo-subnet', cidr='10.0.0.0/24') + settings = NetworkSettings(name='foo', admin_state_up=False, shared=True, project_name='bar', external=True, + network_type='flat', physical_network='phy', subnet_settings=[sub_settings]) + self.assertEquals('foo', settings.name) + self.assertFalse(settings.admin_state_up) + self.assertTrue(settings.shared) + self.assertEquals('bar', settings.project_name) + self.assertTrue(settings.external) + self.assertEquals('flat', settings.network_type) + self.assertEquals('phy', settings.physical_network) + self.assertEquals(1, len(settings.subnet_settings)) + self.assertEquals('foo-subnet', settings.subnet_settings[0].name) + + def test_config_all(self): + settings = NetworkSettings(config={'name': 'foo', 'admin_state_up': False, 'shared': True, + 'project_name': 'bar', 'external': True, 'network_type': 'flat', + 'physical_network': 'phy', + 'subnets': + [{'subnet': {'name': 'foo-subnet', 'cidr': '10.0.0.0/24'}}]}) + self.assertEquals('foo', settings.name) + self.assertFalse(settings.admin_state_up) + self.assertTrue(settings.shared) + self.assertEquals('bar', settings.project_name) + self.assertTrue(settings.external) + self.assertEquals('flat', settings.network_type) + self.assertEquals('phy', settings.physical_network) + self.assertEquals(1, len(settings.subnet_settings)) + self.assertEquals('foo-subnet', settings.subnet_settings[0].name) + + +class SubnetSettingsUnitTests(unittest.TestCase): + """ + Tests the construction of the SubnetSettings class + """ + + def test_no_params(self): + with self.assertRaises(Exception): + SubnetSettings() + + def test_empty_config(self): + with self.assertRaises(Exception): + SubnetSettings(config=dict()) + + def test_name_only(self): + with self.assertRaises(Exception): + SubnetSettings(name='foo') + + def test_config_with_name_only(self): + with self.assertRaises(Exception): + SubnetSettings(config={'name': 'foo'}) + + def test_name_cidr_only(self): + settings = SubnetSettings(name='foo', cidr='10.0.0.0/24') + self.assertEquals('foo', settings.name) + self.assertEquals('10.0.0.0/24', settings.cidr) + self.assertEquals(4, settings.ip_version) + self.assertIsNone(settings.project_name) + self.assertIsNone(settings.start) + self.assertIsNone(settings.end) + self.assertIsNone(settings.enable_dhcp) + self.assertEquals(1, len(settings.dns_nameservers)) + self.assertEquals('8.8.8.8', settings.dns_nameservers[0]) + self.assertIsNone(settings.host_routes) + self.assertIsNone(settings.destination) + self.assertIsNone(settings.nexthop) + self.assertIsNone(settings.ipv6_ra_mode) + self.assertIsNone(settings.ipv6_address_mode) + + def test_config_with_name_cidr_only(self): + settings = SubnetSettings(config={'name': 'foo', 'cidr': '10.0.0.0/24'}) + self.assertEquals('foo', settings.name) + self.assertEquals('10.0.0.0/24', settings.cidr) + self.assertEquals(4, settings.ip_version) + self.assertIsNone(settings.project_name) + self.assertIsNone(settings.start) + self.assertIsNone(settings.end) + self.assertIsNone(settings.gateway_ip) + self.assertIsNone(settings.enable_dhcp) + self.assertEquals(1, len(settings.dns_nameservers)) + self.assertEquals('8.8.8.8', settings.dns_nameservers[0]) + self.assertIsNone(settings.host_routes) + self.assertIsNone(settings.destination) + self.assertIsNone(settings.nexthop) + self.assertIsNone(settings.ipv6_ra_mode) + self.assertIsNone(settings.ipv6_address_mode) + + def test_all(self): + host_routes = {'destination': '0.0.0.0/0', 'nexthop': '123.456.78.9'} + settings = SubnetSettings(name='foo', cidr='10.0.0.0/24', ip_version=6, project_name='bar-project', + start='10.0.0.2', end='10.0.0.101', gateway_ip='10.0.0.1', enable_dhcp=False, + dns_nameservers=['8.8.8.8'], host_routes=[host_routes], destination='dest', + nexthop='hop', ipv6_ra_mode='dhcpv6-stateful', ipv6_address_mode='slaac') + self.assertEquals('foo', settings.name) + self.assertEquals('10.0.0.0/24', settings.cidr) + self.assertEquals(6, settings.ip_version) + self.assertEquals('bar-project', settings.project_name) + self.assertEquals('10.0.0.2', settings.start) + self.assertEquals('10.0.0.101', settings.end) + self.assertEquals('10.0.0.1', settings.gateway_ip) + self.assertEquals(False, settings.enable_dhcp) + self.assertEquals(1, len(settings.dns_nameservers)) + self.assertEquals('8.8.8.8', settings.dns_nameservers[0]) + self.assertEquals(1, len(settings.host_routes)) + self.assertEquals(host_routes, settings.host_routes[0]) + self.assertEquals('dest', settings.destination) + self.assertEquals('hop', settings.nexthop) + self.assertEquals('dhcpv6-stateful', settings.ipv6_ra_mode) + self.assertEquals('slaac', settings.ipv6_address_mode) + + def test_config_all(self): + host_routes = {'destination': '0.0.0.0/0', 'nexthop': '123.456.78.9'} + settings = SubnetSettings(config={'name': 'foo', 'cidr': '10.0.0.0/24', 'ip_version': 6, + 'project_name': 'bar-project', 'start': '10.0.0.2', 'end': '10.0.0.101', + 'gateway_ip': '10.0.0.1', 'enable_dhcp': False, + 'dns_nameservers': ['8.8.8.8'], 'host_routes': [host_routes], + 'destination': 'dest', 'nexthop': 'hop', 'ipv6_ra_mode': 'dhcpv6-stateful', + 'ipv6_address_mode': 'slaac'}) + self.assertEquals('foo', settings.name) + self.assertEquals('10.0.0.0/24', settings.cidr) + self.assertEquals(6, settings.ip_version) + self.assertEquals('bar-project', settings.project_name) + self.assertEquals('10.0.0.2', settings.start) + self.assertEquals('10.0.0.101', settings.end) + self.assertEquals('10.0.0.1', settings.gateway_ip) + self.assertEquals(False, settings.enable_dhcp) + self.assertEquals(1, len(settings.dns_nameservers)) + self.assertEquals('8.8.8.8', settings.dns_nameservers[0]) + self.assertEquals(1, len(settings.host_routes)) + self.assertEquals(host_routes, settings.host_routes[0]) + self.assertEquals('dest', settings.destination) + self.assertEquals('hop', settings.nexthop) + self.assertEquals('dhcpv6-stateful', settings.ipv6_ra_mode) + self.assertEquals('slaac', settings.ipv6_address_mode) + + +class PortSettingsUnitTests(unittest.TestCase): + """ + Tests the construction of the PortSettings class + """ + + def test_no_params(self): + with self.assertRaises(Exception): + PortSettings() + + def test_empty_config(self): + with self.assertRaises(Exception): + PortSettings(config=dict()) + + def test_name_only(self): + with self.assertRaises(Exception): + PortSettings(name='foo') + + def test_config_name_only(self): + with self.assertRaises(Exception): + PortSettings(config={'name': 'foo'}) + + def test_name_netname_only(self): + settings = PortSettings(name='foo', network_name='bar') + self.assertEquals('foo', settings.name) + self.assertEquals('bar', settings.network_name) + self.assertTrue(settings.admin_state_up) + self.assertIsNone(settings.project_name) + self.assertIsNone(settings.mac_address) + self.assertIsNone(settings.ip_addrs) + self.assertIsNone(settings.fixed_ips) + self.assertIsNone(settings.security_groups) + self.assertIsNone(settings.allowed_address_pairs) + self.assertIsNone(settings.opt_value) + self.assertIsNone(settings.opt_name) + self.assertIsNone(settings.device_owner) + self.assertIsNone(settings.device_id) + + def test_config_with_name_netname_only(self): + settings = PortSettings(config={'name': 'foo', 'network_name': 'bar'}) + self.assertEquals('foo', settings.name) + self.assertEquals('bar', settings.network_name) + self.assertTrue(settings.admin_state_up) + self.assertIsNone(settings.project_name) + self.assertIsNone(settings.mac_address) + self.assertIsNone(settings.ip_addrs) + self.assertIsNone(settings.fixed_ips) + self.assertIsNone(settings.security_groups) + self.assertIsNone(settings.allowed_address_pairs) + self.assertIsNone(settings.opt_value) + self.assertIsNone(settings.opt_name) + self.assertIsNone(settings.device_owner) + self.assertIsNone(settings.device_id) + + def test_all(self): + ip_addrs = [{'subnet_name', 'foo-sub', 'ip', '10.0.0.10'}] + fixed_ips = {'sub_id', '10.0.0.10'} + allowed_address_pairs = {'10.0.0.101', '1234.5678'} + + settings = PortSettings(name='foo', network_name='bar', admin_state_up=False, project_name='foo-project', + mac_address='1234', ip_addrs=ip_addrs, fixed_ips=fixed_ips, + security_groups=['foo_grp_id'], allowed_address_pairs=allowed_address_pairs, + opt_value='opt value', opt_name='opt name', device_owner='owner', + device_id='device number') + self.assertEquals('foo', settings.name) + self.assertEquals('bar', settings.network_name) + self.assertFalse(settings.admin_state_up) + self.assertEquals('foo-project', settings.project_name) + self.assertEquals('1234', settings.mac_address) + self.assertEquals(ip_addrs, settings.ip_addrs) + self.assertEquals(fixed_ips, settings.fixed_ips) + self.assertEquals(1, len(settings.security_groups)) + self.assertEquals('foo_grp_id', settings.security_groups[0]) + self.assertEquals(allowed_address_pairs, settings.allowed_address_pairs) + self.assertEquals('opt value', settings.opt_value) + self.assertEquals('opt name', settings.opt_name) + self.assertEquals('owner', settings.device_owner) + self.assertEquals('device number', settings.device_id) + + def test_config_all(self): + ip_addrs = [{'subnet_name', 'foo-sub', 'ip', '10.0.0.10'}] + fixed_ips = {'sub_id', '10.0.0.10'} + allowed_address_pairs = {'10.0.0.101', '1234.5678'} + + settings = PortSettings(config={'name': 'foo', 'network_name': 'bar', 'admin_state_up': False, + 'project_name': 'foo-project', 'mac_address': '1234', 'ip_addrs': ip_addrs, + 'fixed_ips': fixed_ips, 'security_groups': ['foo_grp_id'], + 'allowed_address_pairs': allowed_address_pairs, 'opt_value': 'opt value', + 'opt_name': 'opt name', 'device_owner': 'owner', 'device_id': 'device number'}) + self.assertEquals('foo', settings.name) + self.assertEquals('bar', settings.network_name) + self.assertFalse(settings.admin_state_up) + self.assertEquals('foo-project', settings.project_name) + self.assertEquals('1234', settings.mac_address) + self.assertEquals(ip_addrs, settings.ip_addrs) + self.assertEquals(fixed_ips, settings.fixed_ips) + self.assertEquals(1, len(settings.security_groups)) + self.assertEquals('foo_grp_id', settings.security_groups[0]) + self.assertEquals(allowed_address_pairs, settings.allowed_address_pairs) + self.assertEquals('opt value', settings.opt_value) + self.assertEquals('opt name', settings.opt_name) + self.assertEquals('owner', settings.device_owner) + self.assertEquals('device number', settings.device_id) + + +class CreateNetworkSuccessTests(OSIntegrationTestCase): + """ + Test for the CreateNework class defined in create_nework.py + """ + + def setUp(self): + """ + Sets up object for test + """ + super(self.__class__, self).__start__() + + guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) + self.net_config = openstack_tests.get_pub_net_config( + net_name=guid + '-pub-net', subnet_name=guid + '-pub-subnet', + router_name=guid + '-pub-router', external_net=self.ext_net_name) + + self.neutron = neutron_utils.neutron_client(self.os_creds) + + # Initialize for cleanup + self.net_creator = None + self.router_creator = None + self.neutron = neutron_utils.neutron_client(self.os_creds) + + def tearDown(self): + """ + Cleans the network + """ + if self.router_creator: + self.router_creator.clean() + + if self.net_creator: + if len(self.net_creator.get_subnets()) > 0: + # Validate subnet has been deleted + neutron_utils_tests.validate_subnet( + self.neutron, self.net_creator.network_settings.subnet_settings[0].name, + self.net_creator.network_settings.subnet_settings[0].cidr, False) + + if self.net_creator.get_network(): + # Validate network has been deleted + neutron_utils_tests.validate_network(self.neutron, self.net_creator.network_settings.name, + False) + self.net_creator.clean() + + super(self.__class__, self).__clean__() + + def test_create_network_without_router(self): + """ + Tests the creation of an OpenStack network without a router. + """ + # Create Nework + self.net_creator = OpenStackNetwork(self.os_creds, self.net_config.network_settings) + self.net_creator.create() + + # Validate network was created + neutron_utils_tests.validate_network(self.neutron, self.net_creator.network_settings.name, True) + + # Validate subnets + neutron_utils_tests.validate_subnet( + self.neutron, self.net_creator.network_settings.subnet_settings[0].name, + self.net_creator.network_settings.subnet_settings[0].cidr, True) + + def test_create_delete_network(self): + """ + Tests the creation of an OpenStack network, it's deletion, then cleanup. + """ + # Create Nework + self.net_creator = OpenStackNetwork(self.os_creds, self.net_config.network_settings) + self.net_creator.create() + + # Validate network was created + neutron_utils_tests.validate_network(self.neutron, self.net_creator.network_settings.name, True) + + neutron_utils.delete_network(self.neutron, self.net_creator.get_network()) + self.assertIsNone(neutron_utils.get_network(self.neutron, self.net_creator.network_settings.name)) + + # This shall not throw an exception here + self.net_creator.clean() + + def test_create_network_with_router(self): + """ + Tests the creation of an OpenStack network with a router. + """ + # Create Network + self.net_creator = OpenStackNetwork(self.os_creds, self.net_config.network_settings) + self.net_creator.create() + + # Create Router + self.router_creator = create_router.OpenStackRouter(self.os_creds, self.net_config.router_settings) + self.router_creator.create() + + # Validate network was created + neutron_utils_tests.validate_network(self.neutron, self.net_creator.network_settings.name, True) + + # Validate subnets + neutron_utils_tests.validate_subnet( + self.neutron, self.net_creator.network_settings.subnet_settings[0].name, + self.net_creator.network_settings.subnet_settings[0].cidr, True) + + # Validate routers + neutron_utils_tests.validate_router(self.neutron, self.router_creator.router_settings.name, True) + + neutron_utils_tests.validate_interface_router(self.router_creator.get_internal_router_interface(), + self.router_creator.get_router(), + self.net_creator.get_subnets()[0]) + + def test_create_networks_same_name(self): + """ + Tests the creation of an OpenStack network and ensures that the OpenStackNetwork object will not create + a second. + """ + # Create Nework + self.net_creator = OpenStackNetwork(self.os_creds, self.net_config.network_settings) + self.net_creator.create() + + self.net_creator2 = OpenStackNetwork(self.os_creds, self.net_config.network_settings) + self.net_creator2.create() + + self.assertEquals(self.net_creator.get_network()['network']['id'], + self.net_creator2.get_network()['network']['id']) + + +class CreateNetworkTypeTests(OSComponentTestCase): + """ + Test for the CreateNework class defined in create_nework.py for testing creating networks of different types + """ + + def setUp(self): + """ + Sets up object for test + """ + guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) + self.net_config = openstack_tests.get_pub_net_config( + net_name=guid + '-pub-net', subnet_name=guid + '-pub-subnet') + + self.neutron = neutron_utils.neutron_client(self.os_creds) + + # Initialize for cleanup + self.net_creator = None + self.neutron = neutron_utils.neutron_client(self.os_creds) + + def tearDown(self): + """ + Cleans the network + """ + if self.net_creator: + if len(self.net_creator.get_subnets()) > 0: + # Validate subnet has been deleted + neutron_utils_tests.validate_subnet( + self.neutron, self.net_creator.network_settings.subnet_settings[0].name, + self.net_creator.network_settings.subnet_settings[0].cidr, False) + + if self.net_creator.get_network(): + # Validate network has been deleted + neutron_utils_tests.validate_network(self.neutron, self.net_creator.network_settings.name, + False) + self.net_creator.clean() + # TODO - determine why this is not working on Newton + # - Unable to create the network. No tenant network is available for allocation. + # def test_create_network_type_vlan(self): + # """ + # Tests the creation of an OpenStack network of type vlan. + # """ + # # Create Network + # network_type = 'vlan' + # net_settings = NetworkSettings(name=self.net_config.network_settings.name, + # subnet_settings=self.net_config.network_settings.subnet_settings, + # network_type=network_type) + # + # # When setting the network_type, creds must be admin + # self.net_creator = OpenStackNetwork(self.os_creds, net_settings) + # network = self.net_creator.create() + # + # # Validate network was created + # neutron_utils_tests.validate_network(self.neutron, net_settings.name, True) + # + # self.assertEquals(network_type, network['network']['provider:network_type']) + + def test_create_network_type_vxlan(self): + """ + Tests the creation of an OpenStack network of type vxlan. + """ + # Create Network + network_type = 'vxlan' + net_settings = NetworkSettings(name=self.net_config.network_settings.name, + subnet_settings=self.net_config.network_settings.subnet_settings, + network_type=network_type) + + # When setting the network_type, creds must be admin + self.net_creator = OpenStackNetwork(self.os_creds, net_settings) + network = self.net_creator.create() + + # Validate network was created + neutron_utils_tests.validate_network(self.neutron, net_settings.name, True) + + self.assertEquals(network_type, network['network']['provider:network_type']) + + # TODO - determine what value we need to place into physical_network + # - Do not know what vaule to place into the 'physical_network' setting. + # def test_create_network_type_flat(self): + # """ + # Tests the creation of an OpenStack network of type flat. + # """ + # # Create Network + # network_type = 'flat' + # + # # Unable to find documentation on how to find a value that will work here. + # # https://visibilityspots.org/vlan-flat-neutron-provider.html + # # https://community.rackspace.com/products/f/45/t/4225 + # # It appears that this may be due to how OPNFV is configuring OpenStack. + # physical_network = '???' + # net_settings = NetworkSettings(name=self.net_config.network_settings.name, + # subnet_settings=self.net_config.network_settings.subnet_settings, + # network_type=network_type, physical_network=physical_network) + # self.net_creator = OpenStackNetwork(self.os_creds, net_settings) + # network = self.net_creator.create() + # + # # Validate network was created + # neutron_utils_tests.validate_network(self.neutron, net_settings.name, True) + # + # self.assertEquals(network_type, network['network']['provider:network_type']) + + def test_create_network_type_foo(self): + """ + Tests the creation of an OpenStack network of type foo which should raise an exception. + """ + # Create Network + network_type = 'foo' + net_settings = NetworkSettings(name=self.net_config.network_settings.name, + subnet_settings=self.net_config.network_settings.subnet_settings, + network_type=network_type) + self.net_creator = OpenStackNetwork(self.os_creds, net_settings) + with self.assertRaises(Exception): + self.net_creator.create() diff --git a/snaps/openstack/tests/create_project_tests.py b/snaps/openstack/tests/create_project_tests.py new file mode 100644 index 0000000..9d53467 --- /dev/null +++ b/snaps/openstack/tests/create_project_tests.py @@ -0,0 +1,228 @@ +# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs") +# and others. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import uuid +import unittest + +from snaps.openstack.create_project import OpenStackProject, ProjectSettings +from snaps.openstack.create_security_group import OpenStackSecurityGroup +from snaps.openstack.create_security_group import SecurityGroupSettings +from snaps.openstack.create_user import OpenStackUser +from snaps.openstack.create_user import UserSettings +from snaps.openstack.tests.os_source_file_test import OSComponentTestCase +from snaps.openstack.utils import keystone_utils + +__author__ = 'spisarski' + + +class ProjectSettingsUnitTests(unittest.TestCase): + """ + Tests the construction of the ProjectSettings class + """ + + def test_no_params(self): + with self.assertRaises(Exception): + ProjectSettings() + + def test_empty_config(self): + with self.assertRaises(Exception): + ProjectSettings(config=dict()) + + def test_name_only(self): + settings = ProjectSettings(name='foo') + self.assertEquals('foo', settings.name) + self.assertEquals('default', settings.domain) + self.assertIsNone(settings.description) + self.assertTrue(settings.enabled) + + def test_config_with_name_only(self): + settings = ProjectSettings(config={'name': 'foo'}) + self.assertEquals('foo', settings.name) + self.assertEquals('default', settings.domain) + self.assertIsNone(settings.description) + self.assertTrue(settings.enabled) + + def test_all(self): + settings = ProjectSettings(name='foo', domain='bar', description='foobar', enabled=False) + self.assertEquals('foo', settings.name) + self.assertEquals('bar', settings.domain) + self.assertEquals('foobar', settings.description) + self.assertFalse(settings.enabled) + + def test_config_all(self): + settings = ProjectSettings(config={'name': 'foo', 'domain': 'bar', 'description': 'foobar', 'enabled': False}) + self.assertEquals('foo', settings.name) + self.assertEquals('bar', settings.domain) + self.assertEquals('foobar', settings.description) + self.assertFalse(settings.enabled) + + +class CreateProjectSuccessTests(OSComponentTestCase): + """ + Test for the CreateImage class defined in create_image.py + """ + + def setUp(self): + """ + Instantiates the CreateImage object that is responsible for downloading and creating an OS image file + within OpenStack + """ + guid = str(uuid.uuid4())[:-19] + guid = self.__class__.__name__ + '-' + guid + self.project_settings = ProjectSettings(name=guid + '-name') + + self.keystone = keystone_utils.keystone_client(self.os_creds) + + # Initialize for cleanup + self.project_creator = None + + def tearDown(self): + """ + Cleans the image and downloaded image file + """ + if self.project_creator: + self.project_creator.clean() + + def test_create_project(self): + """ + Tests the creation of an OpenStack project. + """ + self.project_creator = OpenStackProject(self.os_creds, self.project_settings) + created_project = self.project_creator.create() + self.assertIsNotNone(created_project) + + retrieved_project = keystone_utils.get_project(keystone=self.keystone, project_name=self.project_settings.name) + self.assertIsNotNone(retrieved_project) + self.assertEquals(created_project, retrieved_project) + + def test_create_project_2x(self): + """ + Tests the creation of an OpenStack project twice to ensure it only creates one. + """ + self.project_creator = OpenStackProject(self.os_creds, self.project_settings) + created_project = self.project_creator.create() + self.assertIsNotNone(created_project) + + retrieved_project = keystone_utils.get_project(keystone=self.keystone, project_name=self.project_settings.name) + self.assertIsNotNone(retrieved_project) + self.assertEquals(created_project, retrieved_project) + + project2 = OpenStackProject(self.os_creds, self.project_settings).create() + self.assertEquals(retrieved_project, project2) + + def test_create_delete_project(self): + """ + Tests the creation of an OpenStack project, it's deletion, then cleanup. + """ + # Create Image + self.project_creator = OpenStackProject(self.os_creds, self.project_settings) + created_project = self.project_creator.create() + self.assertIsNotNone(created_project) + + keystone_utils.delete_project(self.keystone, created_project) + + self.project_creator.clean() + + self.assertIsNone(self.project_creator.get_project()) + + # TODO - Expand tests + + +class CreateProjectUserTests(OSComponentTestCase): + """ + Test for the CreateImage class defined in create_image.py + """ + + def setUp(self): + """ + Instantiates the CreateImage object that is responsible for downloading and creating an OS image file + within OpenStack + """ + guid = str(uuid.uuid4())[:-19] + self.guid = self.__class__.__name__ + '-' + guid + self.project_settings = ProjectSettings(name=self.guid + '-name') + + self.keystone = keystone_utils.keystone_client(self.os_creds) + + # Initialize for cleanup + self.project_creator = None + self.user_creators = list() + + self.sec_grp_creators = list() + + def tearDown(self): + """ + Cleans the image and downloaded image file + """ + for sec_grp_creator in self.sec_grp_creators: + sec_grp_creator.clean() + + for user_creator in self.user_creators: + user_creator.clean() + + if self.project_creator: + self.project_creator.clean() + + def test_create_project_sec_grp_one_user(self): + """ + Tests the creation of an OpenStack object to a project with a new users and to create a security group + """ + self.project_creator = OpenStackProject(self.os_creds, self.project_settings) + created_project = self.project_creator.create() + self.assertIsNotNone(created_project) + + user_creator = OpenStackUser(self.os_creds, UserSettings(name=self.guid + '-user', password=self.guid)) + self.project_creator.assoc_user(user_creator.create()) + self.user_creators.append(user_creator) + + sec_grp_os_creds = user_creator.get_os_creds(self.project_creator.get_project().name) + sec_grp_creator = OpenStackSecurityGroup( + sec_grp_os_creds, SecurityGroupSettings(name=self.guid + '-name', description='hello group')) + sec_grp = sec_grp_creator.create() + self.assertIsNotNone(sec_grp) + self.sec_grp_creators.append(sec_grp_creator) + + if self.keystone.version == keystone_utils.V2_VERSION: + self.assertEquals(self.project_creator.get_project().id, sec_grp['security_group']['tenant_id']) + else: + self.assertEquals(self.project_creator.get_project().id, sec_grp['security_group']['project_id']) + + def test_create_project_sec_grp_two_users(self): + """ + Tests the creation of an OpenStack object to a project with two new users and use each user to create a + security group + """ + self.project_creator = OpenStackProject(self.os_creds, self.project_settings) + created_project = self.project_creator.create() + self.assertIsNotNone(created_project) + + user_creator_1 = OpenStackUser(self.os_creds, UserSettings(name=self.guid + '-user1', password=self.guid)) + self.project_creator.assoc_user(user_creator_1.create()) + self.user_creators.append(user_creator_1) + + user_creator_2 = OpenStackUser(self.os_creds, UserSettings(name=self.guid + '-user2', password=self.guid)) + self.project_creator.assoc_user(user_creator_2.create()) + self.user_creators.append(user_creator_2) + + ctr = 0 + for user_creator in self.user_creators: + ctr += 1 + sec_grp_os_creds = user_creator.get_os_creds(self.project_creator.get_project().name) + + sec_grp_creator = OpenStackSecurityGroup( + sec_grp_os_creds, SecurityGroupSettings(name=self.guid + '-name', description='hello group')) + sec_grp = sec_grp_creator.create() + self.assertIsNotNone(sec_grp) + self.sec_grp_creators.append(sec_grp_creator) + self.assertEquals(self.project_creator.get_project().id, sec_grp['security_group']['tenant_id']) diff --git a/snaps/openstack/tests/create_router_tests.py b/snaps/openstack/tests/create_router_tests.py new file mode 100644 index 0000000..3e22714 --- /dev/null +++ b/snaps/openstack/tests/create_router_tests.py @@ -0,0 +1,264 @@ +# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs") +# and others. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import uuid + +from snaps.openstack import create_network +from snaps.openstack import create_router +from snaps.openstack.create_network import NetworkSettings +from snaps.openstack.create_network import OpenStackNetwork +from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase +from snaps.openstack.create_router import RouterSettings +from snaps.openstack.utils import neutron_utils + +__author__ = 'mmakati' + +cidr1 = '10.200.201.0/24' +cidr2 = '10.200.202.0/24' +static_gateway_ip1 = '10.200.201.1' +static_gateway_ip2 = '10.200.202.1' + + +class CreateRouterSuccessTests(OSIntegrationTestCase): + """ + Class for testing routers with various positive scenarios expected to succeed + """ + + def setUp(self): + """ + Initializes objects used for router testing + """ + super(self.__class__, self).__start__() + + self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) + self.router_creator = None + self.network_creator1 = None + self.network_creator2 = None + self.neutron = neutron_utils.neutron_client(self.os_creds) + + def tearDown(self): + """ + Cleans the remote OpenStack objects used for router testing + """ + if self.router_creator: + self.router_creator.clean() + + if self.network_creator1: + self.network_creator1.clean() + + if self.network_creator2: + self.network_creator2.clean() + + super(self.__class__, self).__clean__() + + def test_create_router_vanilla(self): + """ + Test creation of a most basic router with minimal options. + """ + router_settings = RouterSettings(name=self.guid + '-pub-router', external_gateway=self.ext_net_name) + + self.router_creator = create_router.OpenStackRouter(self.os_creds, router_settings) + self.router_creator.create() + + router = neutron_utils.get_router_by_name(self.neutron, router_settings.name) + self.assertIsNotNone(router) + + self.assertTrue(verify_router_attributes(router, self.router_creator, ext_gateway=self.ext_net_name)) + + def test_create_delete_router(self): + """ + Test that clean() will not raise an exception if the router is deleted by another process. + """ + self.router_settings = RouterSettings(name=self.guid + '-pub-router', external_gateway=self.ext_net_name) + + self.router_creator = create_router.OpenStackRouter(self.os_creds, self.router_settings) + created_router = self.router_creator.create() + self.assertIsNotNone(created_router) + retrieved_router = neutron_utils.get_router_by_name(self.neutron, self.router_settings.name) + self.assertIsNotNone(retrieved_router) + + neutron_utils.delete_router(self.neutron, created_router) + + retrieved_router = neutron_utils.get_router_by_name(self.neutron, self.router_settings.name) + self.assertIsNone(retrieved_router) + + # Should not raise an exception + self.router_creator.clean() + + def test_create_router_admin_state_false(self): + """ + Test creation of a basic router with admin state down. + """ + router_settings = RouterSettings(name=self.guid + '-pub-router', admin_state_up=False) + + self.router_creator = create_router.OpenStackRouter(self.os_creds, router_settings) + self.router_creator.create() + + router = neutron_utils.get_router_by_name(self.neutron, router_settings.name) + self.assertIsNotNone(router) + + self.assertTrue(verify_router_attributes(router, self.router_creator, admin_state=False)) + + def test_create_router_admin_state_True(self): + """ + Test creation of a basic router with admin state Up. + """ + router_settings = RouterSettings(name=self.guid + '-pub-router', admin_state_up=True) + + self.router_creator = create_router.OpenStackRouter(self.os_creds, router_settings) + self.router_creator.create() + + router = neutron_utils.get_router_by_name(self.neutron, router_settings.name) + self.assertIsNotNone(router) + + self.assertTrue(verify_router_attributes(router, self.router_creator, admin_state=True)) + + def test_create_router_private_network(self): + """ + Test creation of a router connected with two private networks and no external gateway + """ + network_settings1 = NetworkSettings(name=self.guid + '-pub-net1', + subnet_settings=[ + create_network.SubnetSettings(cidr=cidr1, + name=self.guid + '-pub-subnet1', + gateway_ip=static_gateway_ip1)]) + network_settings2 = NetworkSettings(name=self.guid + '-pub-net2', + subnet_settings=[ + create_network.SubnetSettings(cidr=cidr2, + name=self.guid + '-pub-subnet2', + gateway_ip=static_gateway_ip2)]) + + self.network_creator1 = OpenStackNetwork(self.os_creds, network_settings1) + self.network_creator2 = OpenStackNetwork(self.os_creds, network_settings2) + + self.network_creator1.create() + self.network_creator2.create() + + port_settings = [create_network.PortSettings(name=self.guid + '-port1', ip_addrs=[ + {'subnet_name': network_settings1.subnet_settings[0].name, 'ip': static_gateway_ip1}], + network_name=network_settings1.name) + , create_network.PortSettings(name=self.guid + '-port2', ip_addrs=[ + {'subnet_name': network_settings2.subnet_settings[0].name, 'ip': static_gateway_ip2}], + network_name=network_settings2.name)] + + router_settings = RouterSettings(name=self.guid + '-pub-router', port_settings=port_settings) + self.router_creator = create_router.OpenStackRouter(self.os_creds, router_settings) + self.router_creator.create() + + router = neutron_utils.get_router_by_name(self.neutron, router_settings.name) + + self.assertTrue(verify_router_attributes(router, self.router_creator)) + + def test_create_router_external_network(self): + """ + Test creation of a router connected to an external network and a private network. + """ + network_settings = NetworkSettings(name=self.guid + '-pub-net1', + subnet_settings=[ + create_network.SubnetSettings(cidr=cidr1, + name=self.guid + '-pub-subnet1', + gateway_ip=static_gateway_ip1)]) + self.network_creator1 = OpenStackNetwork(self.os_creds, network_settings) + self.network_creator1.create() + + port_settings = [create_network.PortSettings(name=self.guid + '-port1', ip_addrs=[ + {'subnet_name': network_settings.subnet_settings[0].name, 'ip': static_gateway_ip1}], + network_name=network_settings.name)] + + router_settings = RouterSettings(name=self.guid + '-pub-router', + external_gateway=self.ext_net_name, port_settings=port_settings) + self.router_creator = create_router.OpenStackRouter(self.os_creds, router_settings) + self.router_creator.create() + + router = neutron_utils.get_router_by_name(self.neutron, router_settings.name) + + self.assertTrue(verify_router_attributes(router, self.router_creator, ext_gateway=self.ext_net_name)) + + +class CreateRouterNegativeTests(OSIntegrationTestCase): + """ + Class for testing routers with various negative scenarios expected to fail. + """ + + def setUp(self): + """ + Initializes objects used for router testing + """ + super(self.__class__, self).__start__() + + self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) + self.router_creator = None + + def tearDown(self): + """ + Cleans the remote OpenStack objects used for router testing + """ + if self.router_creator: + self.router_creator.clean() + + super(self.__class__, self).__clean__() + + def test_create_router_noname(self): + """ + Test creating a router without a name. + """ + with self.assertRaises(Exception): + router_settings = RouterSettings(name=None, external_gateway=self.ext_net_name) + self.router_creator = create_router.OpenStackRouter(self.os_creds, router_settings) + self.router_creator.create() + + def test_create_router_invalid_gateway_name(self): + """ + Test creating a router without a valid network gateway name. + """ + with self.assertRaises(Exception): + router_settings = RouterSettings(name=self.guid + '-pub-router', external_gateway="Invalid_name") + self.router_creator = create_router.OpenStackRouter(self.os_creds, router_settings) + self.router_creator.create() + + +def verify_router_attributes(router_operational, router_creator, admin_state=True, ext_gateway=None): + """ + Helper function to validate the attributes of router created with the one operational + :param router_operational: Operational Router object returned from neutron utils + :param router_creator: router_creator object returned from creating a router in the router test functions + :param admin_state: True if router is expected to be Up, else False + :param snat: True is enable_snat is True, else False + :param ext_gateway: None if router is not connected to external gateway + :return: + """ + + router = router_creator.get_router() + + if not router_operational: + return False + elif not router_creator: + return False + elif not (router_operational['router']['name'] == router_creator.router_settings.name): + return False + elif not (router_operational['router']['id'] == router['router']['id']): + return False + elif not (router_operational['router']['status'] == router['router']['status']): + return False + elif not (router_operational['router']['tenant_id'] == router['router']['tenant_id']): + return False + elif not (admin_state == router_operational['router']['admin_state_up']): + return False + elif (ext_gateway is None) and (router_operational['router']['external_gateway_info'] is not None): + return False + elif ext_gateway is not None: + if router_operational['router']['external_gateway_info'] is None: + return False + return True diff --git a/snaps/openstack/tests/create_security_group_tests.py b/snaps/openstack/tests/create_security_group_tests.py new file mode 100644 index 0000000..079be0c --- /dev/null +++ b/snaps/openstack/tests/create_security_group_tests.py @@ -0,0 +1,355 @@ +# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs") +# and others. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import uuid +import unittest + +from snaps.openstack import create_security_group +from snaps.openstack.create_security_group import SecurityGroupSettings, SecurityGroupRuleSettings, Direction, \ + Ethertype, Protocol +from snaps.openstack.tests import validation_utils +from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase +from snaps.openstack.utils import neutron_utils + +__author__ = 'spisarski' + + +class SecurityGroupRuleSettingsUnitTests(unittest.TestCase): + """ + Tests the construction of the SecurityGroupRuleSettings class + """ + + def test_no_params(self): + with self.assertRaises(Exception): + SecurityGroupRuleSettings() + + def test_empty_config(self): + with self.assertRaises(Exception): + SecurityGroupRuleSettings(config=dict()) + + def test_name_only(self): + with self.assertRaises(Exception): + SecurityGroupRuleSettings(sec_grp_name='foo') + + def test_config_with_name_only(self): + with self.assertRaises(Exception): + SecurityGroupRuleSettings(config={'sec_grp_name': 'foo'}) + + def test_name_and_direction(self): + settings = SecurityGroupRuleSettings(sec_grp_name='foo', direction=Direction.ingress) + self.assertEquals('foo', settings.sec_grp_name) + self.assertEquals(Direction.ingress, settings.direction) + + def test_config_name_and_direction(self): + settings = SecurityGroupRuleSettings(config={'sec_grp_name': 'foo', 'direction': 'ingress'}) + self.assertEquals('foo', settings.sec_grp_name) + self.assertEquals(Direction.ingress, settings.direction) + + def test_all(self): + settings = SecurityGroupRuleSettings( + sec_grp_name='foo', description='fubar', direction=Direction.egress, remote_group_id='rgi', + protocol=Protocol.icmp, ethertype=Ethertype.IPv6, port_range_min=1, port_range_max=2, + remote_ip_prefix='prfx') + self.assertEquals('foo', settings.sec_grp_name) + self.assertEquals('fubar', settings.description) + self.assertEquals(Direction.egress, settings.direction) + self.assertEquals('rgi', settings.remote_group_id) + self.assertEquals(Protocol.icmp, settings.protocol) + self.assertEquals(Ethertype.IPv6, settings.ethertype) + self.assertEquals(1, settings.port_range_min) + self.assertEquals(2, settings.port_range_max) + self.assertEquals('prfx', settings.remote_ip_prefix) + + def test_config_all(self): + settings = SecurityGroupRuleSettings( + config={'sec_grp_name': 'foo', + 'description': 'fubar', + 'direction': 'egress', + 'remote_group_id': 'rgi', + 'protocol': 'tcp', + 'ethertype': 'IPv6', + 'port_range_min': 1, + 'port_range_max': 2, + 'remote_ip_prefix': 'prfx'}) + self.assertEquals('foo', settings.sec_grp_name) + self.assertEquals('fubar', settings.description) + self.assertEquals(Direction.egress, settings.direction) + self.assertEquals('rgi', settings.remote_group_id) + self.assertEquals(Protocol.tcp, settings.protocol) + self.assertEquals(Ethertype.IPv6, settings.ethertype) + self.assertEquals(1, settings.port_range_min) + self.assertEquals(2, settings.port_range_max) + self.assertEquals('prfx', settings.remote_ip_prefix) + + +class SecurityGroupSettingsUnitTests(unittest.TestCase): + """ + Tests the construction of the SecurityGroupSettings class + """ + + def test_no_params(self): + with self.assertRaises(Exception): + SecurityGroupSettings() + + def test_empty_config(self): + with self.assertRaises(Exception): + SecurityGroupSettings(config=dict()) + + def test_name_only(self): + settings = SecurityGroupSettings(name='foo') + self.assertEquals('foo', settings.name) + + def test_config_with_name_only(self): + settings = SecurityGroupSettings(config={'name': 'foo'}) + self.assertEquals('foo', settings.name) + + def test_invalid_rule(self): + rule_setting = SecurityGroupRuleSettings(sec_grp_name='bar', direction=Direction.ingress) + with self.assertRaises(Exception): + SecurityGroupSettings(name='foo', rule_settings=[rule_setting]) + + def test_all(self): + rule_settings = list() + rule_settings.append(SecurityGroupRuleSettings(sec_grp_name='bar', direction=Direction.egress)) + rule_settings.append(SecurityGroupRuleSettings(sec_grp_name='bar', direction=Direction.ingress)) + settings = SecurityGroupSettings( + name='bar', description='fubar', project_name='foo', rule_settings=rule_settings) + + self.assertEquals('bar', settings.name) + self.assertEquals('fubar', settings.description) + self.assertEquals('foo', settings.project_name) + self.assertEquals(rule_settings[0], settings.rule_settings[0]) + self.assertEquals(rule_settings[1], settings.rule_settings[1]) + + def test_config_all(self): + settings = SecurityGroupSettings( + config={'name': 'bar', + 'description': 'fubar', + 'project_name': 'foo', + 'rules': [{'sec_grp_name': 'bar', 'direction': 'ingress'}]}) + + self.assertEquals('bar', settings.name) + self.assertEquals('fubar', settings.description) + self.assertEquals('foo', settings.project_name) + self.assertEquals(1, len(settings.rule_settings)) + self.assertEquals('bar', settings.rule_settings[0].sec_grp_name) + self.assertEquals(Direction.ingress, settings.rule_settings[0].direction) + + +class CreateSecurityGroupTests(OSIntegrationTestCase): + """ + Test for the CreateSecurityGroup class defined in create_security_group.py + """ + + def setUp(self): + """ + Instantiates the CreateSecurityGroup object that is responsible for downloading and creating an OS image file + within OpenStack + """ + super(self.__class__, self).__start__() + + guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) + self.sec_grp_name = guid + 'name' + self.neutron = neutron_utils.neutron_client(self.os_creds) + + # Initialize for cleanup + self.sec_grp_creator = None + + def tearDown(self): + """ + Cleans the image and downloaded image file + """ + if self.sec_grp_creator: + self.sec_grp_creator.clean() + + super(self.__class__, self).__clean__() + + def test_create_group_without_rules(self): + """ + Tests the creation of an OpenStack Security Group without custom rules. + """ + # Create Image + sec_grp_settings = SecurityGroupSettings(name=self.sec_grp_name, description='hello group') + self.sec_grp_creator = create_security_group.OpenStackSecurityGroup(self.os_creds, sec_grp_settings) + self.sec_grp_creator.create() + + sec_grp = neutron_utils.get_security_group(self.neutron, self.sec_grp_name) + self.assertIsNotNone(sec_grp) + + validation_utils.objects_equivalent(self.sec_grp_creator.get_security_group(), sec_grp) + rules = neutron_utils.get_rules_by_security_group(self.neutron, self.sec_grp_creator.get_security_group()) + self.assertEquals(len(self.sec_grp_creator.get_rules()), len(rules)) + validation_utils.objects_equivalent(self.sec_grp_creator.get_rules(), rules) + + def test_create_delete_group(self): + """ + Tests the creation of an OpenStack Security Group without custom rules. + """ + # Create Image + sec_grp_settings = SecurityGroupSettings(name=self.sec_grp_name, description='hello group') + self.sec_grp_creator = create_security_group.OpenStackSecurityGroup(self.os_creds, sec_grp_settings) + created_sec_grp = self.sec_grp_creator.create() + self.assertIsNotNone(created_sec_grp) + + neutron_utils.delete_security_group(self.neutron, created_sec_grp) + self.assertIsNone(neutron_utils.get_security_group(self.neutron, self.sec_grp_creator.sec_grp_settings.name)) + + self.sec_grp_creator.clean() + + def test_create_group_with_one_simple_rule(self): + """ + Tests the creation of an OpenStack Security Group with one simple custom rule. + """ + # Create Image + sec_grp_rule_settings = list() + sec_grp_rule_settings.append(SecurityGroupRuleSettings(sec_grp_name=self.sec_grp_name, + direction=Direction.ingress)) + sec_grp_settings = SecurityGroupSettings(name=self.sec_grp_name, description='hello group', + rule_settings=sec_grp_rule_settings) + self.sec_grp_creator = create_security_group.OpenStackSecurityGroup(self.os_creds, sec_grp_settings) + self.sec_grp_creator.create() + + sec_grp = neutron_utils.get_security_group(self.neutron, self.sec_grp_name) + validation_utils.objects_equivalent(self.sec_grp_creator.get_security_group(), sec_grp) + rules = neutron_utils.get_rules_by_security_group(self.neutron, + self.sec_grp_creator.get_security_group()) + self.assertEquals(len(self.sec_grp_creator.get_rules()), len(rules)) + validation_utils.objects_equivalent(self.sec_grp_creator.get_rules(), rules) + + def test_create_group_with_several_rules(self): + """ + Tests the creation of an OpenStack Security Group with one simple custom rule. + """ + # Create Image + sec_grp_rule_settings = list() + sec_grp_rule_settings.append(SecurityGroupRuleSettings(sec_grp_name=self.sec_grp_name, + direction=Direction.ingress)) + sec_grp_rule_settings.append(SecurityGroupRuleSettings(sec_grp_name=self.sec_grp_name, + direction=Direction.egress, + protocol=Protocol.udp, + ethertype=Ethertype.IPv6)) + sec_grp_rule_settings.append(SecurityGroupRuleSettings(sec_grp_name=self.sec_grp_name, + direction=Direction.egress, + protocol=Protocol.udp, + ethertype=Ethertype.IPv4, + port_range_min=10, + port_range_max=20)) + sec_grp_settings = SecurityGroupSettings(name=self.sec_grp_name, description='hello group', + rule_settings=sec_grp_rule_settings) + self.sec_grp_creator = create_security_group.OpenStackSecurityGroup(self.os_creds, sec_grp_settings) + self.sec_grp_creator.create() + + sec_grp = neutron_utils.get_security_group(self.neutron, self.sec_grp_name) + validation_utils.objects_equivalent(self.sec_grp_creator.get_security_group(), sec_grp) + rules = neutron_utils.get_rules_by_security_group(self.neutron, self.sec_grp_creator.get_security_group()) + self.assertEquals(len(self.sec_grp_creator.get_rules()), len(rules)) + validation_utils.objects_equivalent(self.sec_grp_creator.get_rules(), rules) + + def test_add_rule(self): + """ + Tests the creation of an OpenStack Security Group with one simple custom rule then adds one after creation. + """ + # Create Image + sec_grp_rule_settings = list() + sec_grp_rule_settings.append(SecurityGroupRuleSettings(sec_grp_name=self.sec_grp_name, + direction=Direction.ingress)) + sec_grp_settings = SecurityGroupSettings(name=self.sec_grp_name, description='hello group', + rule_settings=sec_grp_rule_settings) + self.sec_grp_creator = create_security_group.OpenStackSecurityGroup(self.os_creds, sec_grp_settings) + self.sec_grp_creator.create() + + sec_grp = neutron_utils.get_security_group(self.neutron, self.sec_grp_name) + validation_utils.objects_equivalent(self.sec_grp_creator.get_security_group(), sec_grp) + rules = neutron_utils.get_rules_by_security_group(self.neutron, + self.sec_grp_creator.get_security_group()) + self.assertEquals(len(self.sec_grp_creator.get_rules()), len(rules)) + validation_utils.objects_equivalent(self.sec_grp_creator.get_rules(), rules) + + self.sec_grp_creator.add_rule(SecurityGroupRuleSettings(sec_grp_name=self.sec_grp_creator.sec_grp_settings.name, + direction=Direction.egress, protocol=Protocol.icmp)) + rules2 = neutron_utils.get_rules_by_security_group(self.neutron, self.sec_grp_creator.get_security_group()) + self.assertEquals(len(rules) + 1, len(rules2)) + + def test_remove_rule_by_id(self): + """ + Tests the creation of an OpenStack Security Group with two simple custom rules then removes one by the rule ID. + """ + # Create Image + sec_grp_rule_settings = list() + sec_grp_rule_settings.append(SecurityGroupRuleSettings(sec_grp_name=self.sec_grp_name, + direction=Direction.ingress)) + sec_grp_rule_settings.append(SecurityGroupRuleSettings(sec_grp_name=self.sec_grp_name, + direction=Direction.egress, + protocol=Protocol.udp, + ethertype=Ethertype.IPv6)) + sec_grp_rule_settings.append(SecurityGroupRuleSettings(sec_grp_name=self.sec_grp_name, + direction=Direction.egress, + protocol=Protocol.udp, + ethertype=Ethertype.IPv4, + port_range_min=10, + port_range_max=20)) + sec_grp_settings = SecurityGroupSettings(name=self.sec_grp_name, description='hello group', + rule_settings=sec_grp_rule_settings) + self.sec_grp_creator = create_security_group.OpenStackSecurityGroup(self.os_creds, sec_grp_settings) + self.sec_grp_creator.create() + + sec_grp = neutron_utils.get_security_group(self.neutron, self.sec_grp_name) + validation_utils.objects_equivalent(self.sec_grp_creator.get_security_group(), sec_grp) + rules = neutron_utils.get_rules_by_security_group(self.neutron, + self.sec_grp_creator.get_security_group()) + self.assertEquals(len(self.sec_grp_creator.get_rules()), len(rules)) + validation_utils.objects_equivalent(self.sec_grp_creator.get_rules(), rules) + + self.sec_grp_creator.remove_rule(rule_id=rules[0]['security_group_rule']['id']) + rules_after_del = neutron_utils.get_rules_by_security_group(self.neutron, + self.sec_grp_creator.get_security_group()) + self.assertEquals(len(rules) - 1, len(rules_after_del)) + + def test_remove_rule_by_setting(self): + """ + Tests the creation of an OpenStack Security Group with two simple custom rules then removes one by the rule + setting object + """ + # Create Image + sec_grp_rule_settings = list() + sec_grp_rule_settings.append(SecurityGroupRuleSettings(sec_grp_name=self.sec_grp_name, + direction=Direction.ingress)) + sec_grp_rule_settings.append(SecurityGroupRuleSettings(sec_grp_name=self.sec_grp_name, + direction=Direction.egress, + protocol=Protocol.udp, + ethertype=Ethertype.IPv6)) + sec_grp_rule_settings.append(SecurityGroupRuleSettings(sec_grp_name=self.sec_grp_name, + direction=Direction.egress, + protocol=Protocol.udp, + ethertype=Ethertype.IPv4, + port_range_min=10, + port_range_max=20)) + sec_grp_settings = SecurityGroupSettings(name=self.sec_grp_name, description='hello group', + rule_settings=sec_grp_rule_settings) + self.sec_grp_creator = create_security_group.OpenStackSecurityGroup(self.os_creds, sec_grp_settings) + self.sec_grp_creator.create() + + sec_grp = neutron_utils.get_security_group(self.neutron, self.sec_grp_name) + validation_utils.objects_equivalent(self.sec_grp_creator.get_security_group(), sec_grp) + rules = neutron_utils.get_rules_by_security_group(self.neutron, + self.sec_grp_creator.get_security_group()) + self.assertEquals(len(self.sec_grp_creator.get_rules()), len(rules)) + validation_utils.objects_equivalent(self.sec_grp_creator.get_rules(), rules) + + self.sec_grp_creator.remove_rule(rule_setting=sec_grp_rule_settings[0]) + rules_after_del = neutron_utils.get_rules_by_security_group(self.neutron, + self.sec_grp_creator.get_security_group()) + self.assertEquals(len(rules) - 1, len(rules_after_del)) + +# TODO - Add more tests with different rules. Rule creation parameters can be somewhat complex diff --git a/snaps/openstack/tests/create_user_tests.py b/snaps/openstack/tests/create_user_tests.py new file mode 100644 index 0000000..1f7a163 --- /dev/null +++ b/snaps/openstack/tests/create_user_tests.py @@ -0,0 +1,155 @@ +# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs") +# and others. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import uuid +import unittest +from snaps.openstack.create_user import OpenStackUser, UserSettings +from snaps.openstack.tests.os_source_file_test import OSComponentTestCase +from snaps.openstack.utils import keystone_utils + +__author__ = 'spisarski' + + +class UserSettingsUnitTests(unittest.TestCase): + """ + Tests the construction of the UserSettings class + """ + + def test_no_params(self): + with self.assertRaises(Exception): + UserSettings() + + def test_empty_config(self): + with self.assertRaises(Exception): + UserSettings(config=dict()) + + def test_name_only(self): + with self.assertRaises(Exception): + UserSettings(name='foo') + + def test_config_with_name_only(self): + with self.assertRaises(Exception): + UserSettings(config={'name': 'foo'}) + + def test_name_pass_enabled_str(self): + with self.assertRaises(Exception): + UserSettings(name='foo', password='bar', enabled='true') + + def test_config_with_name_pass_enabled_str(self): + with self.assertRaises(Exception): + UserSettings(config={'name': 'foo', 'password': 'bar', 'enabled': 'true'}) + + def test_name_pass_only(self): + settings = UserSettings(name='foo', password='bar') + self.assertEquals('foo', settings.name) + self.assertEquals('bar', settings.password) + self.assertIsNone(settings.project_name) + self.assertIsNone(settings.email) + self.assertTrue(settings.enabled) + + def test_config_with_name_pass_only(self): + settings = UserSettings(config={'name': 'foo', 'password': 'bar'}) + self.assertEquals('foo', settings.name) + self.assertEquals('bar', settings.password) + self.assertIsNone(settings.project_name) + self.assertIsNone(settings.email) + self.assertTrue(settings.enabled) + + def test_all(self): + settings = UserSettings(name='foo', password='bar', project_name='proj-foo', email='foo@bar.com', enabled=False) + self.assertEquals('foo', settings.name) + self.assertEquals('bar', settings.password) + self.assertEquals('proj-foo', settings.project_name) + self.assertEquals('foo@bar.com', settings.email) + self.assertFalse(settings.enabled) + + def test_config_all(self): + settings = UserSettings(config={'name': 'foo', 'password': 'bar', 'project_name': 'proj-foo', + 'email': 'foo@bar.com', 'enabled': False}) + self.assertEquals('foo', settings.name) + self.assertEquals('bar', settings.password) + self.assertEquals('proj-foo', settings.project_name) + self.assertEquals('foo@bar.com', settings.email) + self.assertFalse(settings.enabled) + + +class CreateUserSuccessTests(OSComponentTestCase): + """ + Test for the CreateImage class defined in create_image.py + """ + + def setUp(self): + """ + Instantiates the CreateImage object that is responsible for downloading and creating an OS image file + within OpenStack + """ + guid = str(uuid.uuid4())[:-19] + guid = self.__class__.__name__ + '-' + guid + self.user_settings = UserSettings(name=guid + '-name', password=guid + '-password') + + self.keystone = keystone_utils.keystone_client(self.os_creds) + + # Initialize for cleanup + self.user_creator = None + + def tearDown(self): + """ + Cleans the image and downloaded image file + """ + if self.user_creator: + self.user_creator.clean() + + def test_create_user(self): + """ + Tests the creation of an OpenStack user. + """ + self.user_creator = OpenStackUser(self.os_creds, self.user_settings) + created_user = self.user_creator.create() + self.assertIsNotNone(created_user) + + retrieved_user = keystone_utils.get_user(self.keystone, self.user_settings.name) + self.assertIsNotNone(retrieved_user) + self.assertEquals(created_user, retrieved_user) + + def test_create_user_2x(self): + """ + Tests the creation of an OpenStack user twice to ensure it only creates one. + """ + self.user_creator = OpenStackUser(self.os_creds, self.user_settings) + created_user = self.user_creator.create() + self.assertIsNotNone(created_user) + + retrieved_user = keystone_utils.get_user(self.keystone, self.user_settings.name) + self.assertIsNotNone(retrieved_user) + self.assertEquals(created_user, retrieved_user) + + # Create user for the second time to ensure it is the same + user2 = OpenStackUser(self.os_creds, self.user_settings).create() + self.assertEquals(retrieved_user, user2) + + def test_create_delete_user(self): + """ + Tests the creation of an OpenStack user then delete. + """ + # Create Image + self.user_creator = OpenStackUser(self.os_creds, self.user_settings) + created_user = self.user_creator.create() + self.assertIsNotNone(created_user) + + keystone_utils.delete_user(self.keystone, created_user) + + # Delete user + self.user_creator.clean() + self.assertIsNone(self.user_creator.get_user()) + diff --git a/snaps/openstack/tests/openstack_tests.py b/snaps/openstack/tests/openstack_tests.py new file mode 100644 index 0000000..dab2ea2 --- /dev/null +++ b/snaps/openstack/tests/openstack_tests.py @@ -0,0 +1,144 @@ +# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs") +# and others. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import re + +from snaps import file_utils +from snaps.openstack.create_network import NetworkSettings, SubnetSettings +from snaps.openstack.create_router import RouterSettings +from snaps.openstack.os_credentials import OSCreds, ProxySettings +from snaps.openstack.create_image import ImageSettings +import logging + +__author__ = 'spisarski' + + +logger = logging.getLogger('openstack_tests') + + +def get_credentials(os_env_file=None, proxy_settings_str=None, ssh_proxy_cmd=None, dev_os_env_file=None): + """ + Returns the OpenStack credentials object. It first attempts to retrieve them from a standard OpenStack source file. + If that file is None, it will attempt to retrieve them with a YAML file. + it will retrieve them from a + :param os_env_file: the OpenStack source file + :param proxy_settings_str: proxy settings string <host>:<port> (optional) + :param ssh_proxy_cmd: the SSH proxy command for your environment (optional) + :param dev_os_env_file: the YAML file to retrieve both the OS credentials and proxy settings + :return: the SNAPS credentials object + """ + if os_env_file: + logger.debug('Reading RC file - ' + os_env_file) + config = file_utils.read_os_env_file(os_env_file) + proj_name = config.get('OS_PROJECT_NAME') + if not proj_name: + proj_name = config.get('OS_TENANT_NAME') + + proj_domain_id = 'default' + user_domain_id = 'default' + + if config.get('OS_PROJECT_DOMAIN_ID'): + proj_domain_id = config['OS_PROJECT_DOMAIN_ID'] + if config.get('OS_USER_DOMAIN_ID'): + user_domain_id = config['OS_USER_DOMAIN_ID'] + if config.get('OS_IDENTITY_API_VERSION'): + version = int(config['OS_IDENTITY_API_VERSION']) + else: + version = 2 + + proxy_settings = None + if proxy_settings_str: + tokens = re.split(':', proxy_settings_str) + proxy_settings = ProxySettings(tokens[0], tokens[1], ssh_proxy_cmd) + + os_creds = OSCreds(username=config['OS_USERNAME'], + password=config['OS_PASSWORD'], + auth_url=config['OS_AUTH_URL'], + project_name=proj_name, + identity_api_version=version, + user_domain_id=user_domain_id, + project_domain_id=proj_domain_id, + proxy_settings=proxy_settings) + else: + logger.info('Reading development os_env file - ' + dev_os_env_file) + config = file_utils.read_yaml(dev_os_env_file) + identity_api_version = config.get('identity_api_version') + if not identity_api_version: + identity_api_version = 2 + + proxy_settings = None + proxy_str = config.get('http_proxy') + if proxy_str: + tokens = re.split(':', proxy_str) + proxy_settings = ProxySettings(tokens[0], tokens[1], config.get('ssh_proxy_cmd')) + + os_creds = OSCreds(username=config['username'], password=config['password'], + auth_url=config['os_auth_url'], project_name=config['project_name'], + identity_api_version=identity_api_version, + proxy_settings=proxy_settings) + + logger.info('OS Credentials = ' + str(os_creds)) + return os_creds + + +def cirros_url_image(name): + return ImageSettings(name=name, image_user='cirros', img_format='qcow2', + url='http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-x86_64-disk.img') + + +def file_image_test_settings(name, file_path): + return ImageSettings(name=name, image_user='cirros', img_format='qcow2', + image_file=file_path) + + +def centos_url_image(name): + return ImageSettings(name=name, image_user='centos', img_format='qcow2', + url='http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud.qcow2', + nic_config_pb_loc='./provisioning/ansible/centos-network-setup/playbooks/configure_host.yml') + + +def ubuntu_url_image(name): + return ImageSettings( + name=name, image_user='ubuntu', img_format='qcow2', + url='http://uec-images.ubuntu.com/releases/trusty/14.04/ubuntu-14.04-server-cloudimg-amd64-disk1.img', + nic_config_pb_loc='./provisioning/ansible/ubuntu-network-setup/playbooks/configure_host.yml') + + +def get_priv_net_config(net_name, subnet_name, router_name=None, cidr='10.55.0.0/24', external_net=None): + return OSNetworkConfig(net_name, subnet_name, cidr, router_name, external_gateway=external_net) + + +def get_pub_net_config(net_name, subnet_name=None, router_name=None, cidr='10.55.1.0/24', external_net=None): + return OSNetworkConfig(net_name, subnet_name, cidr, router_name, external_gateway=external_net) + + +class OSNetworkConfig: + """ + Represents the settings required for the creation of a network in OpenStack + """ + + def __init__(self, net_name, subnet_name=None, subnet_cidr=None, router_name=None, external_gateway=None): + + if subnet_name and subnet_cidr: + self.network_settings = NetworkSettings( + name=net_name, subnet_settings=[SubnetSettings(cidr=subnet_cidr, name=subnet_name)]) + else: + self.network_settings = NetworkSettings(name=net_name) + + if router_name: + if subnet_name: + self.router_settings = RouterSettings(name=router_name, external_gateway=external_gateway, + internal_subnets=[subnet_name]) + else: + self.router_settings = RouterSettings(name=router_name, external_gateway=external_gateway) diff --git a/snaps/openstack/tests/os_source_file_test.py b/snaps/openstack/tests/os_source_file_test.py new file mode 100644 index 0000000..fa8d197 --- /dev/null +++ b/snaps/openstack/tests/os_source_file_test.py @@ -0,0 +1,131 @@ +# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs") +# and others. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import unittest +import uuid + +from snaps import file_utils +import openstack_tests +import logging + +# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +# To run these tests from an IDE, the CWD must be set to the python directory of this project +# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +from snaps.openstack.create_project import ProjectSettings +from snaps.openstack.create_user import UserSettings +from snaps.openstack.utils import deploy_utils, keystone_utils + +dev_os_env_file = 'openstack/tests/conf/os_env.yaml' + + +class OSComponentTestCase(unittest.TestCase): + + """ + Super for test classes requiring a connection to OpenStack + """ + def __init__(self, method_name='runTest', os_env_file=None, ext_net_name=None, http_proxy_str=None, + ssh_proxy_cmd=None, log_level=logging.DEBUG): + super(OSComponentTestCase, self).__init__(method_name) + + logging.basicConfig(level=log_level) + + self.os_creds = openstack_tests.get_credentials(os_env_file=os_env_file, proxy_settings_str=http_proxy_str, + ssh_proxy_cmd=ssh_proxy_cmd, dev_os_env_file=dev_os_env_file) + self.ext_net_name = ext_net_name + + if not self.ext_net_name and file_utils.file_exists(dev_os_env_file): + test_conf = file_utils.read_yaml(dev_os_env_file) + self.ext_net_name = test_conf.get('ext_net') + + @staticmethod + def parameterize(testcase_klass, os_env_file, ext_net_name, http_proxy_str=None, ssh_proxy_cmd=None, + log_level=logging.DEBUG): + """ Create a suite containing all tests taken from the given + subclass, passing them the parameter 'param'. + """ + test_loader = unittest.TestLoader() + test_names = test_loader.getTestCaseNames(testcase_klass) + suite = unittest.TestSuite() + for name in test_names: + suite.addTest(testcase_klass(name, os_env_file, ext_net_name, http_proxy_str, ssh_proxy_cmd, log_level)) + return suite + + +class OSIntegrationTestCase(OSComponentTestCase): + + """ + Super for test classes requiring a connection to OpenStack + """ + def __init__(self, method_name='runTest', os_env_file=None, ext_net_name=None, http_proxy_str=None, + ssh_proxy_cmd=None, use_keystone=False, log_level=logging.DEBUG): + super(OSIntegrationTestCase, self).__init__(method_name=method_name, os_env_file=os_env_file, + ext_net_name=ext_net_name, http_proxy_str=http_proxy_str, + ssh_proxy_cmd=ssh_proxy_cmd, log_level=log_level) + self.use_keystone = use_keystone + self.keystone = None + + @staticmethod + def parameterize(testcase_klass, os_env_file, ext_net_name, http_proxy_str=None, ssh_proxy_cmd=None, + use_keystone=False, log_level=logging.DEBUG): + """ Create a suite containing all tests taken from the given + subclass, passing them the parameter 'param'. + """ + test_loader = unittest.TestLoader() + test_names = test_loader.getTestCaseNames(testcase_klass) + suite = unittest.TestSuite() + for name in test_names: + suite.addTest(testcase_klass(name, os_env_file, ext_net_name, http_proxy_str, ssh_proxy_cmd, use_keystone, + log_level)) + return suite + + """ + Super for test classes that should be run within their own project/tenant as they can run for quite some time + """ + def __start__(self): + """ + Creates a project and user to be leveraged by subclass test methods. If implementing class uses this method, + it must call __clean__() else you will be left with unwanted users and tenants + """ + self.project_creator = None + self.user_creator = None + self.admin_os_creds = self.os_creds + self.role = None + + if self.use_keystone: + self.keystone = keystone_utils.keystone_client(self.os_creds) + guid = self.__class__.__name__ + '-' + str(uuid.uuid4())[:-19] + project_name = guid + '-proj' + self.project_creator = deploy_utils.create_project(self.admin_os_creds, ProjectSettings(name=project_name)) + + self.user_creator = deploy_utils.create_user( + self.admin_os_creds, UserSettings(name=guid + '-user', password=guid, project_name=project_name)) + self.os_creds = self.user_creator.get_os_creds(self.project_creator.project_settings.name) + + # add user to project + self.project_creator.assoc_user(self.user_creator.get_user()) + + def __clean__(self): + """ + Cleans up test user and project. + Must be called at the end of child classes tearDown() if __start__() is called during setUp() else these + objects will persist after the test is run + """ + if self.role: + keystone_utils.delete_role(self.keystone, self.role) + + if self.project_creator: + self.project_creator.clean() + + if self.user_creator: + self.user_creator.clean() diff --git a/snaps/openstack/tests/validation_utils.py b/snaps/openstack/tests/validation_utils.py new file mode 100644 index 0000000..7c9bd7f --- /dev/null +++ b/snaps/openstack/tests/validation_utils.py @@ -0,0 +1,69 @@ +# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs") +# and others. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from neutronclient.v2_0.client import _DictWithMeta + +__author__ = 'spisarski' + + +def objects_equivalent(obj1, obj2): + """ + Returns true if both objects are equivalent + :param obj1: + :param obj2: + :return: T/F + """ + if obj1 is None and obj2 is None: + return True + if type(obj1) is dict or type(obj1) is _DictWithMeta: + return dicts_equivalent(obj1, obj2) + elif type(obj1) is list: + return lists_equivalent(obj1, obj2) + else: + return obj1 == obj2 + + +def dicts_equivalent(dict1, dict2): + """ + Returns true when each key/value pair is equal + :param dict1: dict 1 + :param dict2: dict 2 + :return: T/F + """ + if (type(dict1) is dict or type(dict1) is _DictWithMeta) and (type(dict2) is dict or type(dict2) is _DictWithMeta): + for key, value1 in dict1.iteritems(): + if not objects_equivalent(value1, dict2.get(key)): + return False + return True + return False + + +def lists_equivalent(list1, list2): + """ + Returns true when an item in list1 is also contained in list2 + :param list1: list 1 + :param list2: list 2 + :return: T/F + """ + if len(list1) == len(list2) and type(list1) is list and type(list2) is list: + for item1 in list1: + has_equivalent = False + for item2 in list2: + has_equivalent = objects_equivalent(item1, item2) + if has_equivalent: + break + if not has_equivalent: + return False + return True + return False |