summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/demo.py56
-rw-r--r--snaps/openstack/create_project.py4
-rw-r--r--snaps/openstack/create_user.py33
-rw-r--r--snaps/openstack/tests/create_project_tests.py21
-rw-r--r--snaps/openstack/tests/create_user_tests.py27
-rw-r--r--snaps/openstack/tests/os_source_file_test.py97
-rw-r--r--snaps/openstack/utils/keystone_utils.py61
-rw-r--r--snaps/openstack/utils/tests/keystone_utils_tests.py38
8 files changed, 264 insertions, 73 deletions
diff --git a/examples/demo.py b/examples/demo.py
new file mode 100644
index 0000000..b2231f8
--- /dev/null
+++ b/examples/demo.py
@@ -0,0 +1,56 @@
+import logging
+logging.basicConfig(level=logging.INFO)
+
+# Credentials
+from snaps.openstack.os_credentials import OSCreds, ProxySettings
+
+
+proxy_settings = ProxySettings(host='10.197.123.27', port='3128',
+ ssh_proxy_cmd='/usr/local/bin/corkscrew 10.197.123.27 3128 %h %p')
+
+os_creds = OSCreds(username='admin', password='cable123', auth_url='http://192.168.67.10:5000/v2.0/',
+ project_name='admin', proxy_settings=proxy_settings)
+
+
+# Images
+from snaps.openstack.create_image import ImageSettings, OpenStackImage
+
+image_settings = ImageSettings(name='cirros-test', image_user='cirros', img_format='qcow2',
+ url='http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-x86_64-disk.img')
+
+image = OpenStackImage(os_creds, image_settings)
+image.create()
+# See in Horizon
+
+
+# Network
+from snaps.openstack.create_network import NetworkSettings, SubnetSettings, OpenStackNetwork
+
+subnet_settings = SubnetSettings(name='test-subnet', cidr='10.0.0.1/24')
+network_settings = NetworkSettings(name='test-net', subnet_settings=[subnet_settings])
+network = OpenStackNetwork(os_creds, network_settings)
+network.create()
+
+
+# Flavors
+from snaps.openstack.create_flavor import FlavorSettings, OpenStackFlavor
+
+flavor_settings = FlavorSettings(name='test-flavor', ram=128, disk=10, vcpus=2)
+flavor = OpenStackFlavor(os_creds, flavor_settings)
+flavor.create()
+
+# Instances
+from snaps.openstack.create_network import PortSettings
+from snaps.openstack.create_instance import VmInstanceSettings, OpenStackVmInstance
+
+port_settings = PortSettings(name='test-port', network_name=network_settings.name)
+instance_settings = VmInstanceSettings(name='test-inst', flavor=flavor_settings.name, port_settings=[port_settings])
+
+vm_inst = OpenStackVmInstance(os_creds, instance_settings, image_settings)
+vm_inst.create(block=True)
+
+# Cleanup
+vm_inst.clean()
+flavor.clean()
+network.clean()
+image.clean()
diff --git a/snaps/openstack/create_project.py b/snaps/openstack/create_project.py
index c865f15..0384ccc 100644
--- a/snaps/openstack/create_project.py
+++ b/snaps/openstack/create_project.py
@@ -96,8 +96,8 @@ class OpenStackProject:
self.__role = keystone_utils.create_role(
self.__keystone, self.project_settings.name + '-role')
- keystone_utils.assoc_user_to_project(self.__keystone, self.__role,
- user, self.__project)
+ keystone_utils.grant_user_role_to_project(self.__keystone, self.__role,
+ user, self.__project)
class ProjectSettings:
diff --git a/snaps/openstack/create_user.py b/snaps/openstack/create_user.py
index 4c96121..18de215 100644
--- a/snaps/openstack/create_user.py
+++ b/snaps/openstack/create_user.py
@@ -49,13 +49,9 @@ class OpenStackUser:
self.__keystone = keystone_utils.keystone_client(self.__os_creds)
self.__user = keystone_utils.get_user(self.__keystone,
self.user_settings.name)
- if self.__user:
- logger.info('Found user with name - ' + self.user_settings.name)
- elif not cleanup:
+ if not self.__user and not cleanup:
self.__user = keystone_utils.create_user(self.__keystone,
self.user_settings)
- else:
- logger.info('Did not create user due to cleanup mode')
return self.__user
@@ -111,27 +107,30 @@ class UserSettings:
:param email: the user's email address (optional)
:param enabled: denotes whether or not the user is enabled
(default True)
+ :param roles: dict where key is the role name and value is a list of
+ project names
"""
self.name = kwargs.get('name')
self.password = kwargs.get('password')
self.project_name = kwargs.get('project_name')
self.email = kwargs.get('email')
-
- if kwargs.get('domain_name'):
- self.domain_name = kwargs['domain_name']
- else:
- self.domain_name = 'default'
-
- if kwargs.get('enabled') is not None:
- self.enabled = kwargs['enabled']
- else:
- self.enabled = True
+ self.domain_name = kwargs.get('domain_name', 'default')
+ self.enabled = kwargs.get('enabled', True)
+ self.roles = kwargs.get('roles', dict())
if not self.name or not self.password:
- raise Exception(
+ raise UserSettingsException(
'The attributes name and password are required for '
'UserSettings')
if not isinstance(self.enabled, bool):
- raise Exception('The attribute enabled must be of type boolean')
+ raise UserSettingsException('The attribute enabled must be of type'
+ ' boolean')
+
+
+class UserSettingsException(Exception):
+ """
+ Raised when there is a problem with the values set in the UserSettings
+ class
+ """
diff --git a/snaps/openstack/tests/create_project_tests.py b/snaps/openstack/tests/create_project_tests.py
index da93b13..3c6b2d1 100644
--- a/snaps/openstack/tests/create_project_tests.py
+++ b/snaps/openstack/tests/create_project_tests.py
@@ -194,9 +194,10 @@ class CreateProjectUserTests(OSComponentTestCase):
created_project = self.project_creator.create()
self.assertIsNotNone(created_project)
- user_creator = OpenStackUser(self.os_creds,
- UserSettings(name=self.guid + '-user',
- password=self.guid))
+ user_creator = OpenStackUser(
+ self.os_creds, UserSettings(
+ name=self.guid + '-user',
+ password=self.guid, roles={'admin': 'admin'}))
self.project_creator.assoc_user(user_creator.create())
self.user_creators.append(user_creator)
@@ -222,15 +223,17 @@ class CreateProjectUserTests(OSComponentTestCase):
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))
+ user_creator_1 = OpenStackUser(
+ self.os_creds, UserSettings(
+ name=self.guid + '-user1', password=self.guid,
+ roles={'admin': 'admin'}))
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))
+ user_creator_2 = OpenStackUser(
+ self.os_creds, UserSettings(
+ name=self.guid + '-user2', password=self.guid,
+ roles={'admin': 'admin'}))
self.project_creator.assoc_user(user_creator_2.create())
self.user_creators.append(user_creator_2)
diff --git a/snaps/openstack/tests/create_user_tests.py b/snaps/openstack/tests/create_user_tests.py
index fdc3644..a3cc8ce 100644
--- a/snaps/openstack/tests/create_user_tests.py
+++ b/snaps/openstack/tests/create_user_tests.py
@@ -103,7 +103,8 @@ class CreateUserSuccessTests(OSComponentTestCase):
guid = str(uuid.uuid4())[:-19]
guid = self.__class__.__name__ + '-' + guid
self.user_settings = UserSettings(name=guid + '-name',
- password=guid + '-password')
+ password=guid + '-password',
+ roles={'admin': 'admin'})
self.keystone = keystone_utils.keystone_client(self.os_creds)
@@ -162,3 +163,27 @@ class CreateUserSuccessTests(OSComponentTestCase):
# Delete user
self.user_creator.clean()
self.assertIsNone(self.user_creator.get_user())
+
+ def test_create_admin_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.assertEqual(created_user, retrieved_user)
+
+ role = keystone_utils.get_os_role_by_name(self.keystone, 'admin')
+ self.assertIsNotNone(role)
+
+ os_proj = keystone_utils.get_project(
+ keystone=self.keystone, project_name=self.os_creds.project_name)
+ user_roles = keystone_utils.get_os_roles_by_user(
+ self.keystone, retrieved_user, os_proj)
+ self.assertIsNotNone(user_roles)
+ self.assertEqual(1, len(user_roles))
+ self.assertEqual(role.id, user_roles[0].id)
diff --git a/snaps/openstack/tests/os_source_file_test.py b/snaps/openstack/tests/os_source_file_test.py
index 4b421e8..01aa88a 100644
--- a/snaps/openstack/tests/os_source_file_test.py
+++ b/snaps/openstack/tests/os_source_file_test.py
@@ -24,23 +24,23 @@ from snaps.openstack.tests import openstack_tests
from snaps.openstack.utils import deploy_utils, keystone_utils
-dev_os_env_file = pkg_resources.resource_filename('snaps.openstack.tests.conf', 'os_env.yaml')
-
-# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-# To run these tests from an IDE, the CWD must be set to the snaps directory of this project
-# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+dev_os_env_file = pkg_resources.resource_filename(
+ 'snaps.openstack.tests.conf', 'os_env.yaml')
class OSComponentTestCase(unittest.TestCase):
- def __init__(self, method_name='runTest', os_creds=None, ext_net_name=None, image_metadata=None,
- log_level=logging.DEBUG):
+ def __init__(self, method_name='runTest', os_creds=None, ext_net_name=None,
+ image_metadata=None, log_level=logging.DEBUG):
"""
Super for test classes requiring a connection to OpenStack
:param method_name: default 'runTest'
- :param os_creds: the OSCreds object, when null it searches for the file {cwd}/openstack/tests/conf/os_env.yaml
- :param ext_net_name: the name of the external network that is used for creating routers for floating IPs
- :param image_metadata: ability to override images being used in the tests (see examples/image-metadata)
+ :param os_creds: the OSCreds object, when null it searches for the file
+ in the package snaps.openstack.tests.conf.os_env.yaml
+ :param ext_net_name: the name of the external network that is used for
+ creating routers for floating IPs
+ :param image_metadata: ability to override images being used in the
+ tests (see examples/image-metadata)
:param log_level: the logging level of your test run (default DEBUG)
"""
super(OSComponentTestCase, self).__init__(method_name)
@@ -50,7 +50,8 @@ class OSComponentTestCase(unittest.TestCase):
if os_creds:
self.os_creds = os_creds
else:
- self.os_creds = openstack_tests.get_credentials(dev_os_env_file=dev_os_env_file)
+ self.os_creds = openstack_tests.get_credentials(
+ dev_os_env_file=dev_os_env_file)
self.ext_net_name = ext_net_name
@@ -61,7 +62,8 @@ class OSComponentTestCase(unittest.TestCase):
self.image_metadata = image_metadata
@staticmethod
- def parameterize(testcase_klass, os_creds, ext_net_name, image_metadata=None, log_level=logging.DEBUG):
+ def parameterize(testcase_klass, os_creds, ext_net_name,
+ image_metadata=None, log_level=logging.DEBUG):
""" Create a suite containing all tests taken from the given
subclass, passing them the parameter 'param'.
"""
@@ -69,37 +71,46 @@ class OSComponentTestCase(unittest.TestCase):
test_names = test_loader.getTestCaseNames(testcase_klass)
suite = unittest.TestSuite()
for name in test_names:
- suite.addTest(testcase_klass(name, os_creds, ext_net_name, image_metadata, log_level))
+ suite.addTest(testcase_klass(name, os_creds, ext_net_name,
+ image_metadata, log_level))
return suite
class OSIntegrationTestCase(OSComponentTestCase):
- def __init__(self, method_name='runTest', os_creds=None, ext_net_name=None, use_keystone=False,
- flavor_metadata=None, image_metadata=None, log_level=logging.DEBUG):
+ def __init__(self, method_name='runTest', os_creds=None, ext_net_name=None,
+ use_keystone=False, flavor_metadata=None, image_metadata=None,
+ log_level=logging.DEBUG):
"""
Super for integration tests requiring a connection to OpenStack
:param method_name: default 'runTest'
- :param os_creds: the OSCreds object, when null it searches for the file {cwd}/openstack/tests/conf/os_env.yaml
- :param ext_net_name: the name of the external network that is used for creating routers for floating IPs
- :param use_keystone: when true, these tests will create a new user/project under which to run the test
- :param image_metadata: dict() containing the URLs for the disk, kernel, and ramdisk images when multi-part
- images are required.
- image_metadata={'disk_url': 'http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-x86_64-disk.img',
- 'kernel_url': 'http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-x86_64-kernel',
- 'ramdisk_url': 'http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-x86_64-initramfs'})
- :param flavor_metadata: dict() to be sent directly into the Nova client generally used for page sizes
+ :param os_creds: the OSCreds object, when null it searches for the file
+ in the package snaps.openstack.tests.conf.os_env.yaml
+ :param ext_net_name: the name of the external network that is used for
+ creating routers for floating IPs
+ :param use_keystone: when true, these tests will create a new
+ user/project under which to run the test
+ :param image_metadata: dict() containing the URLs for the disk, kernel,
+ and ramdisk images when multi-part images are
+ required. See below for a simple example
+ image_metadata={'disk_url': '{URI}/cirros-0.3.4-x86_64-disk.img',
+ 'kernel_url': '{URI}/cirros-0.3.4-x86_64-kernel',
+ 'ramdisk_url': '{URI}/cirros-0.3.4-x86_64-initramfs'})
+ :param flavor_metadata: dict() to be sent directly into the Nova client
+ generally used for page sizes
:param log_level: the logging level of your test run (default DEBUG)
"""
- super(OSIntegrationTestCase, self).__init__(method_name=method_name, os_creds=os_creds,
- ext_net_name=ext_net_name, image_metadata=image_metadata,
- log_level=log_level)
+ super(OSIntegrationTestCase, self).__init__(
+ method_name=method_name, os_creds=os_creds,
+ ext_net_name=ext_net_name, image_metadata=image_metadata,
+ log_level=log_level)
self.use_keystone = use_keystone
self.keystone = None
self.flavor_metadata = flavor_metadata
@staticmethod
- def parameterize(testcase_klass, os_creds, ext_net_name, use_keystone=False, flavor_metadata=None,
+ def parameterize(testcase_klass, os_creds, ext_net_name,
+ use_keystone=False, flavor_metadata=None,
image_metadata=None, log_level=logging.DEBUG):
"""
Create a suite containing all tests taken from the given
@@ -109,17 +120,20 @@ class OSIntegrationTestCase(OSComponentTestCase):
test_names = test_loader.getTestCaseNames(testcase_klass)
suite = unittest.TestSuite()
for name in test_names:
- suite.addTest(testcase_klass(name, os_creds, ext_net_name, use_keystone, flavor_metadata, image_metadata,
- log_level))
+ suite.addTest(testcase_klass(name, os_creds, ext_net_name,
+ use_keystone, flavor_metadata,
+ image_metadata, 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
+ 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
+ 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
@@ -130,11 +144,17 @@ class OSIntegrationTestCase(OSComponentTestCase):
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.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)
+ self.admin_os_creds, UserSettings(
+ name=guid + '-user', password=guid,
+ project_name=project_name, roles={
+ 'admin': self.project_creator.project_settings.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())
@@ -142,8 +162,9 @@ class OSIntegrationTestCase(OSComponentTestCase):
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
+ 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)
diff --git a/snaps/openstack/utils/keystone_utils.py b/snaps/openstack/utils/keystone_utils.py
index 3823914..0a850d3 100644
--- a/snaps/openstack/utils/keystone_utils.py
+++ b/snaps/openstack/utils/keystone_utils.py
@@ -207,12 +207,28 @@ def create_user(keystone, user_settings):
email=user_settings.email, tenant_id=project_id,
enabled=user_settings.enabled)
else:
- # TODO - need to support groups
os_user = keystone.users.create(
name=user_settings.name, password=user_settings.password,
email=user_settings.email, project=project,
domain=user_settings.domain_name, enabled=user_settings.enabled)
+ for role_name, role_project in user_settings.roles.items():
+ os_role = get_os_role_by_name(keystone, role_name)
+ os_project = get_project(keystone=keystone, project_name=role_project)
+
+ if os_role and os_project:
+ existing_roles = get_os_roles_by_user(keystone, os_user,
+ os_project)
+ found = False
+ for role in existing_roles:
+ if role.id == os_role.id:
+ found = True
+
+ if not found:
+ grant_user_role_to_project(
+ keystone=keystone, user=os_user, role=os_role,
+ project=os_project)
+
if os_user:
return User(name=os_user.name, user_id=os_user.id)
@@ -226,6 +242,45 @@ def delete_user(keystone, user):
keystone.users.delete(user.id)
+def get_os_role_by_name(keystone, name):
+ """
+ Returns an OpenStack role object of a given name or None if not exists
+ :param keystone: the keystone client
+ :param name: the role name
+ :return: the OpenStack role object
+ """
+ roles = keystone.roles.list()
+ for role in roles:
+ if role.name == name:
+ return role
+
+
+def get_os_roles_by_user(keystone, user, project):
+ """
+ Returns a list of OpenStack role object associated with a user
+ :param keystone: the keystone client
+ :param user: the OpenStack user object
+ :param project: the OpenStack project object (only required for v2)
+ :return: a list of OpenStack role objects
+ """
+ if keystone.version == V2_VERSION:
+ os_user = get_os_user(keystone, user)
+ roles = keystone.roles.roles_for_user(os_user, project)
+ return roles
+ else:
+ return keystone.roles.list(user=user, project=project)
+
+
+def get_os_role_by_id(keystone, role_id):
+ """
+ Returns an OpenStack role object of a given name or None if not exists
+ :param keystone: the keystone client
+ :param role_id: the role ID
+ :return: the OpenStack role object
+ """
+ return keystone.roles.get(role_id)
+
+
def create_role(keystone, name):
"""
Creates an OpenStack role
@@ -246,9 +301,9 @@ def delete_role(keystone, role):
keystone.roles.delete(role)
-def assoc_user_to_project(keystone, role, user, project):
+def grant_user_role_to_project(keystone, role, user, project):
"""
- Adds a user to a project
+ Grants user and role to a project
:param keystone: the Keystone client
:param role: the role used to join a project/user
:param user: the user to add to the project (SNAPS-OO User Domain object
diff --git a/snaps/openstack/utils/tests/keystone_utils_tests.py b/snaps/openstack/utils/tests/keystone_utils_tests.py
index 1fc9d38..89b2b2c 100644
--- a/snaps/openstack/utils/tests/keystone_utils_tests.py
+++ b/snaps/openstack/utils/tests/keystone_utils_tests.py
@@ -59,12 +59,13 @@ class KeystoneUtilsTests(OSComponentTestCase):
Instantiates the CreateImage object that is responsible for downloading
and creating an OS image file within OpenStack
"""
- guid = uuid.uuid4()
- self.username = self.__class__.__name__ + '-' + str(guid)
+ self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+ self.username = self.guid + '-username'
self.user = None
- self.project_name = self.__class__.__name__ + '-' + str(guid)
+ self.project_name = self.guid + '-projName'
self.project = None
+ self.role = None
self.keystone = keystone_utils.keystone_client(self.os_creds)
def tearDown(self):
@@ -77,6 +78,9 @@ class KeystoneUtilsTests(OSComponentTestCase):
if self.user:
keystone_utils.delete_user(self.keystone, self.user)
+ if self.role:
+ keystone_utils.delete_role(self.keystone, self.role)
+
def test_create_user_minimal(self):
"""
Tests the keystone_utils.create_user() function
@@ -151,3 +155,31 @@ class KeystoneUtilsTests(OSComponentTestCase):
self.assertNotEqual(endpoint_public, endpoint_internal)
self.assertNotEqual(endpoint_public, endpoint_admin)
self.assertEqual(endpoint_admin, endpoint_internal)
+
+ def test_grant_user_role_to_project(self):
+ """
+ Tests the keystone_utils function grant_user_role_to_project()
+ :return:
+ """
+ user_settings = UserSettings(name=self.username,
+ password=str(uuid.uuid4()))
+ self.user = keystone_utils.create_user(self.keystone, user_settings)
+ self.assertEqual(self.username, self.user.name)
+
+ project_settings = ProjectSettings(name=self.project_name)
+ self.project = keystone_utils.create_project(self.keystone,
+ project_settings)
+ self.assertEqual(self.project_name, self.project.name)
+
+ role_name = self.guid + '-role'
+ self.role = keystone_utils.create_role(self.keystone, role_name)
+ self.assertEqual(role_name, self.role.name)
+
+ keystone_utils.grant_user_role_to_project(
+ self.keystone, self.role, self.user, self.project)
+
+ user_roles = keystone_utils.get_os_roles_by_user(
+ self.keystone, self.user, self.project)
+ self.assertIsNotNone(user_roles)
+ self.assertEqual(1, len(user_roles))
+ self.assertEqual(self.role.id, user_roles[0].id)