diff options
103 files changed, 4700 insertions, 1440 deletions
@@ -3,3 +3,5 @@ *.*project build dist +.tox +docs/_build/* diff --git a/INFO.yaml b/INFO.yaml new file mode 100644 index 0000000..b29addf --- /dev/null +++ b/INFO.yaml @@ -0,0 +1,44 @@ +--- +project: 'SNAPS-OO' +project_creation_date: '07.02.2017' +project_category: '' +lifecycle_state: 'Incubation' +project_lead: &opnfv_snaps_ptl + name: 'Steven Pisarski' + email: 's.pisarski@cablelabs.com' + id: 'spisarski' + company: 'cablelabs.com' + timezone: 'Unknown' +primary_contact: *opnfv_snaps_ptl +issue_tracking: + type: 'jira' + url: 'https://jira.opnfv.org/projects/SNAPS' + key: 'SNAPS' +mailing_list: + type: 'mailman2' + url: 'opnfv-tech-discuss@lists.opnfv.org' + tag: '[snaps]' +realtime_discussion: + type: irc + server: 'freenode.net' + channel: '#opnfv-snaps' +meetings: + - type: 'gotomeeting+irc' + agenda: # eg: 'https://wiki.opnfv.org/display/' + url: # eg: 'https://global.gotomeeting.com/join/819733085' + server: 'freenode.net' + channel: '#opnfv-meeting' + repeats: 'weekly' + time: # eg: '16:00 UTC' +repositories: + - 'snaps' +committers: + - <<: *opnfv_snaps_ptl + - name: 'Randy Levensalor' + email: 'r.levensalor@cablelabs.com' + company: 'cablelabs.com' + id: 'RandyL' +tsc: + # yamllint disable rule:line-length + approval: 'http//meetbot.opnfv.org/meetings/opnfv-meeting/2017/opnfv-meeting.2017-02-07-15.00.html' + # yamllint enable rule:line-length diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..3c4453e --- /dev/null +++ b/docs/conf.py @@ -0,0 +1 @@ +from docs_conf.conf import * diff --git a/docs/conf.yaml b/docs/conf.yaml new file mode 100644 index 0000000..0bb596d --- /dev/null +++ b/docs/conf.yaml @@ -0,0 +1,3 @@ +--- +project_cfg: opnfv +project: SNAPS diff --git a/docs/how-to-use/APITests.rst b/docs/how-to-use/APITests.rst index 50cd437..9f46839 100644 --- a/docs/how-to-use/APITests.rst +++ b/docs/how-to-use/APITests.rst @@ -104,6 +104,9 @@ create_project_tests.py - CreateProjectSuccessTests | test_create_project | 2 & 3 | Tests the creation of a project via the OpenStackProject | | | | class | +----------------------------------+---------------+-----------------------------------------------------------+ +| test_create_project_quota | 2 & 3 | Tests the creation of a project via the OpenStackProject | +| _override | | class with overriding the default quota values | ++----------------------------------+---------------+-----------------------------------------------------------+ | test_create_project_2x | 2 & 3 | Tests the creation of a project a second time via the | | | | OpenStackProject class to ensure it is only created once | +----------------------------------+---------------+-----------------------------------------------------------+ @@ -435,7 +438,16 @@ nova_utils_tests.py - NovaUtilsInstanceVolumeTests | Test Name | Nova API | Description | +=======================================+===============+===========================================================+ | test_add_remove_volume | 2 | Ensures that a VM instance can properly attach and detach | -| | | a volume using the nova interface | +| | | a volume using the nova interface while waiting for | +| | | the update to fully occur | ++---------------------------------------+---------------+-----------------------------------------------------------+ +| test_attach_volume_nowait | 2 | Ensures that the call to nova_utils.attach_volume raises | +| | | an exception when the timeout is too short to return an | +| | | properly updated VmInst object | ++---------------------------------------+---------------+-----------------------------------------------------------+ +| test_detach_volume_nowait | 2 | Ensures that the call to nova_utils.detach_volume raises | +| | | an exception when the timeout is too short to return an | +| | | properly updated VmInst object | +---------------------------------------+---------------+-----------------------------------------------------------+ create_flavor_tests.py - CreateFlavorTests diff --git a/docs/how-to-use/IntegrationTests.rst b/docs/how-to-use/IntegrationTests.rst index eb627ad..79ef8ef 100644 --- a/docs/how-to-use/IntegrationTests.rst +++ b/docs/how-to-use/IntegrationTests.rst @@ -46,6 +46,17 @@ create_security_group_tests.py - CreateSecurityGroupTests | | | setting object | +---------------------------------------+---------------+-----------------------------------------------------------+ +create_security_group_tests.py - CreateMultipleSecurityGroupTests +----------------------------------------------------------------- + ++---------------------------------------+---------------+-----------------------------------------------------------+ +| Test Name | API Versions | Description | ++=======================================+===============+===========================================================+ +| test_sec_grp_same_name_diff_proj | Keysone 2 & 3 | Ensures the OpenStackSecurityGroup class does not | +| | Neutron 2 | initialize security groups with the same name from other | +| | | project/tenants | ++---------------------------------------+---------------+-----------------------------------------------------------+ + create_image_tests.py - CreateImageSuccessTests ----------------------------------------------- @@ -144,7 +155,7 @@ create_keypairs_tests.py - CreateKeypairsCleanupTests | test_create_keypair_gen_files_delete_1| 2 | Ensures that new keypair files are deleted by default | | | | by OpenStackKeypair#clean() | +---------------------------------------+---------------+-----------------------------------------------------------+ -| test_create_keypair_gen_files_delete_2| 2 | Ensures that new keypair files are deleted by | +| test_create_keypair_gen_files_delete_2| 2 | Ensures that new keypair files are deleted by | | | | OpenStackKeypair#clean() when the settings delete_on_clean| | | | attribute is set to True | +---------------------------------------+---------------+-----------------------------------------------------------+ @@ -185,6 +196,25 @@ create_network_tests.py - CreateNetworkSuccessTests | | | 'admin' project ID | +---------------------------------------+---------------+-----------------------------------------------------------+ +create_network_tests.py - CreateNetworkGatewayTests +--------------------------------------------------- + ++---------------------------------------+---------------+-----------------------------------------------------------+ +| Test Name | Neutron API | Description | ++=======================================+===============+===========================================================+ +| test_create_subnet_default_gateway_ip | 2 | Ensures that a network can be created with a Subnet that | +| | | has the gateway_ip automatically assigned | ++---------------------------------------+---------------+-----------------------------------------------------------+ +| test_create_subnet_valid_gateway_ip | 2 | Ensures that a network can be created with a Subnet that | +| | | has the gateway_ip statically assigned with a valid IP | ++---------------------------------------+---------------+-----------------------------------------------------------+ +| test_create_subnet_no_gateway | 2 | Ensures that a network can be created where no gateway_ip | +| | | is assigned | ++---------------------------------------+---------------+-----------------------------------------------------------+ +| test_create_subnet_invalid_gateway_ip | 2 | Ensures that a network cannot be created with a Subnet | +| | | has an invalid gateway_ip value such as 'foo' | ++---------------------------------------+---------------+-----------------------------------------------------------+ + create_network_tests.py - CreateNetworkIPv6Tests ------------------------------------------------ @@ -197,6 +227,21 @@ create_network_tests.py - CreateNetworkIPv6Tests | | | IPv6 subnet | +---------------------------------------+---------------+-----------------------------------------------------------+ +create_network_tests.py - CreateMultipleNetworkTests +---------------------------------------------------- + ++---------------------------------------+---------------+-----------------------------------------------------------+ +| Test Name | Neutron API | Description | ++=======================================+===============+===========================================================+ +| test_network_same_name_diff_proj | 2 | Ensures that a network with the same name can be created | +| | | against different projects | ++---------------------------------------+---------------+-----------------------------------------------------------+ +| test_network_create_by_admin_to | 2 | Ensures that a network can be created by the admin user | +| _different_project | | to another project and that a creator with the credentials| +| | | to the other project will not create a new network with | +| | | the same name | ++---------------------------------------+---------------+-----------------------------------------------------------+ + create_router_tests.py - CreateRouterSuccessTests ------------------------------------------------- @@ -215,6 +260,12 @@ create_router_tests.py - CreateRouterSuccessTests | test_create_delete_router | 2 | Ensures that a router can be deleted via the | | | | OpenStackRouter.clean() method | +---------------------------------------+---------------+-----------------------------------------------------------+ +| test_create_with_internal_sub | 2 | Ensures that a router can be joined to a subnet created by| +| | | the same user who created the subnet | ++---------------------------------------+---------------+-----------------------------------------------------------+ +| test_create_with_invalid_internal_sub | 2 | Ensures that a router cannot be created when attempting to| +| | | join a subnet created by the admin user | ++---------------------------------------+---------------+-----------------------------------------------------------+ | test_create_router_admin_state_false | 2 | Ensures that a router can created with | | | | admin_state_up = False | +---------------------------------------+---------------+-----------------------------------------------------------+ @@ -227,6 +278,9 @@ create_router_tests.py - CreateRouterSuccessTests | test_create_router_external_network | 2 | Ensures that a router can be created that is connected to | | | | both external and private internal networks | +---------------------------------------+---------------+-----------------------------------------------------------+ +| test_create_router_with_ext_port | 2 | Ensures that a router can be created by an 'admin' user | +| | | with a port to an external network | ++---------------------------------------+---------------+-----------------------------------------------------------+ create_router_tests.py - CreateRouterNegativeTests -------------------------------------------------- @@ -240,6 +294,51 @@ create_router_tests.py - CreateRouterNegativeTests | test_create_router_invalid_gateway_name| 2 | Ensures that an exception is raised when attempting to | | | | create a router to an external network that does not exist| +----------------------------------------+---------------+-----------------------------------------------------------+ +| test_create_router_admin_ports | 2 | Ensures that an exception is raised when attempting to | +| | | create a router with ports to networks owned by another | +| | | project | ++----------------------------------------+---------------+-----------------------------------------------------------+ + +create_router_tests.py - CreateMultipleRouterTests +-------------------------------------------------- + ++---------------------------------------+---------------+-----------------------------------------------------------+ +| Test Name | Neutron API | Description | ++=======================================+===============+===========================================================+ +| test_router_same_name_diff_proj | 2 | Ensures that a router with the same name can be created | +| | | against different projects | ++---------------------------------------+---------------+-----------------------------------------------------------+ +| test_router_create_by_admin_to | 2 | Ensures that a router can be created by the admin user | +| _different_project | | to another project and that a creator with the credentials| +| | | to the other project will not create a new router with | +| | | the same name | ++---------------------------------------+---------------+-----------------------------------------------------------+ + +create_router_tests.py - CreateRouterSecurityGroupTests +------------------------------------------------------- + ++---------------------------------------+---------------+-----------------------------------------------------------+ +| Test Name | Neutron API | Description | ++=======================================+===============+===========================================================+ +| test_create_router_secure_port | 2 | Ensures that a router's port can have a security group | +| | | applied to it | ++---------------------------------------+---------------+-----------------------------------------------------------+ + +create_router_tests.py - CreateRouterSharedNetworksTests +-------------------------------------------------------- + ++---------------------------------------+---------------+-----------------------------------------------------------+ +| Test Name | Neutron API | Description | ++=======================================+===============+===========================================================+ +| test_create_router_external | 2 | Ensures that a router can be joined to an external network| +| | | that was created by an admin user | ++---------------------------------------+---------------+-----------------------------------------------------------+ +| test_create_router_port_external | 2 | Ensures that a router can have a port created to an | +| | | external network that was created by an admin user | ++---------------------------------------+---------------+-----------------------------------------------------------+ +| test_create_router_port_shared | 2 | Ensures that a router can have a port created to an | +| | | shared network that was created by an admin user | ++---------------------------------------+---------------+-----------------------------------------------------------+ create_qos_tests.py - CreateQoSTests ------------------------------------ @@ -332,9 +431,6 @@ create_volume_tests.py - CreateSimpleVolumeFailureTests | test_create_volume_bad_image | 2 & 3 | Tests to ensure that attempting to create a volume with an| | | | image that does not exist raises a BadRequest exception | +----------------------------------------+---------------+-----------------------------------------------------------+ -| test_create_volume_bad_zone | 2 & 3 | Tests to ensure that attempting to create a volume with an| -| | | invalid availability zone raises a BadRequest exception | -+----------------------------------------+---------------+-----------------------------------------------------------+ create_volume_tests.py - CreateVolumeWithTypeTests -------------------------------------------------- @@ -368,6 +464,25 @@ create_volume_tests.py - CreateVolumeWithImageTests | | | volume when associating with a valid image | +----------------------------------------+---------------+-----------------------------------------------------------+ +create_volume_tests.py - CreateVolMultipleCredsTests +---------------------------------------------------- + ++----------------------------------------+---------------+-----------------------------------------------------------+ +| Test Name | Cinder API | Description | ++========================================+===============+===========================================================+ +| test_create_by_admin_to_other_proj | 2 & 3 | Tests to ensure the creation of a Volume as a user with | +| | | an 'admin' role can create a volume to another project | +| | | and a creator with the credentails to that project will | +| | | not create another with the same name | +| | | Currently inactive due to | +| | | https://bugs.launchpad.net/cinder/+bug/1641982 | ++----------------------------------------+---------------+-----------------------------------------------------------+ +| test_create_two_vol_same_name_diff_proj| 2 & 3 | Tests to ensure the creation of a Volume with the same | +| | | name by two different creators with different credentials | +| | | will create two different volumes with the same name | +| | | that are applied to each project in question | ++----------------------------------------+---------------+-----------------------------------------------------------+ + create_stack_tests.py - CreateStackSuccessTests ----------------------------------------------- @@ -418,6 +533,12 @@ create_stack_tests.py - CreateStackFloatingIpTests | | | VM with a floating IP that can be accessed via | | | | OpenStackVmInstance | +---------------------------------------+---------------+-----------------------------------------------------------+ +| test_connect_via_ssh_heat_vm_derived | 1 | Ensures that an OpenStackHeatStack instance can create a | +| | | VM with a floating IP where a generated initialized | +| | | OpenStackHeatStack can return an initialized | +| | | OpenStackVmInstance object that will be used to access the| +| | | VM via SSH | ++---------------------------------------+---------------+-----------------------------------------------------------+ create_stack_tests.py - CreateStackNestedResourceTests ------------------------------------------------------ @@ -431,6 +552,17 @@ create_stack_tests.py - CreateStackNestedResourceTests | | | initialized OpenStackVmInstance objects | +---------------------------------------+---------------+-----------------------------------------------------------+ +create_stack_tests.py - CreateStackUpdateTests +---------------------------------------------- + ++---------------------------------------+---------------+-----------------------------------------------------------+ +| Test Name | Heat API | Description | ++=======================================+===============+===========================================================+ +| test_update | 1 | Ensures that an OpenStackHeatStack can have the number of | +| | | VMs updated and they are spawned and access can be | +| | | obtained with SSH over floating IPs | ++---------------------------------------+---------------+-----------------------------------------------------------+ + create_stack_tests.py - CreateStackRouterTests ---------------------------------------------- @@ -516,7 +648,21 @@ create_instance_tests.py - CreateInstanceSimpleTests | Test Name | API Versions | Description | +=======================================+===============+===========================================================+ | test_create_delete_instance | Nova 2 | Ensures that the OpenStackVmInstance.clean() method | -| | Neutron 2 | deletes the instance | +| | Neutron 2 | deletes the instance as well as ensuring the VmInst | +| | | availability_zone is populated and compute_host is None | ++---------------------------------------+---------------+-----------------------------------------------------------+ +| test_create_admin_instance | Nova 2 | Ensures that the VmInst object members availability_zone | +| | Neutron 2 | and compute_host return a value | ++---------------------------------------+---------------+-----------------------------------------------------------+ + +create_instance_tests.py - CreateInstanceExternalNetTests +--------------------------------------------------------- + ++---------------------------------------+---------------+-----------------------------------------------------------+ +| Test Name | API Versions | Description | ++=======================================+===============+===========================================================+ +| test_create_instance_public_net | Nova 2 | Ensures that an OpenStackVmInstance initialized as a user | +| | Neutron 2 | of type 'admin' can create a VM against an external net | +---------------------------------------+---------------+-----------------------------------------------------------+ create_instance_tests.py - SimpleHealthCheck @@ -576,6 +722,12 @@ create_instance_tests.py - CreateInstancePortManipulationTests | test_set_custom_valid_ip_one_subnet | Nova 2 | Ensures that an instance's can have a valid static IP is | | | Neutron 2 | properly assigned | +---------------------------------------+---------------+-----------------------------------------------------------+ +| test_set_one_port_two_ip_one_subnet | Nova 2 | Ensures that an instance can have two static IPs on a | +| | Neutron 2 | single port from a single subnet | ++---------------------------------------+---------------+-----------------------------------------------------------+ +| test_set_one_port_two_ip_two_subnets | Nova 2 | Ensures that an instance can have two static IPs on a | +| | Neutron 2 | single port from different subnets on a network | ++---------------------------------------+---------------+-----------------------------------------------------------+ | test_set_custom_invalid_ip_one_subnet | Nova 2 | Ensures that an instance's port with an invalid static IP | | | Neutron 2 | raises an exception | +---------------------------------------+---------------+-----------------------------------------------------------+ diff --git a/docs/how-to-use/LibraryUsage.rst b/docs/how-to-use/LibraryUsage.rst index aa7bf91..3183305 100644 --- a/docs/how-to-use/LibraryUsage.rst +++ b/docs/how-to-use/LibraryUsage.rst @@ -154,6 +154,10 @@ Create Flavor - is\_public - flag that denotes whether or not other projects can access image (default=True) - metadata - freeform dict() for special metadata (optional) + - freeform dict() for values of basic elements + (e.g. ram, vcpu, disk, etc) could be added. + As result the hard coded values of those elements will be + overwritten by the new ones (optional) .. code:: python diff --git a/docs/how-to-use/Testing.rst b/docs/how-to-use/Testing.rst index 92340ab..8e08abb 100644 --- a/docs/how-to-use/Testing.rst +++ b/docs/how-to-use/Testing.rst @@ -26,6 +26,8 @@ Execute the tests | \* -f [optional - When set, will execute tests requiring Floating IPS] | \* -im [optional - File containing image endpoints to override -| \* -fm [optional - JSON string containing a dict() for flavor metadata default='{\"hw:mem_page_size\": \"any\"}'] +| \* -fm [optional - JSON string containing a dict(): - for flavor metadata default='{\"hw:mem_page_size\": \"any\"}' + - for values of basic elements (e.g. ram, vcpu, disk, etc) could be added. + As result the hard coded values of those elements will be overwritten by the new ones] | \* -ci [optional - runs the tests required by SNAPS-OO CI] | \* -r [optional with default value of '1' - The number of test iterations to execute] diff --git a/docs/how-to-use/VirtEnvDeploy.rst b/docs/how-to-use/VirtEnvDeploy.rst index 6c99992..0345f34 100644 --- a/docs/how-to-use/VirtEnvDeploy.rst +++ b/docs/how-to-use/VirtEnvDeploy.rst @@ -121,7 +121,11 @@ Use launcher.py to deploy and clean up example environments. These examples are - rxtx\_factor: the receive/transmit factor to be set on ports if backend supports QoS extension (default 1.0) - is\_public: denotes whether or not the flavor is public (default = True) - - metadata: freeform dict() for special metadata (optional) + - metadata: - freeform dict() for special metadata (optional) + - freeform dict() for values of basic elements + (e.g. ram, vcpu, disk, etc) could be added. + As result the hard coded values of those elements will be + overwritten by the new ones (optional) - qos_specs: the QoS Specs to create diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..aceb2fc --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,16 @@ +.. _snaps: + +.. This work is licensed under a Creative Commons Attribution 4.0 International License. +.. http://creativecommons.org/licenses/by/4.0 +.. SPDX-License-Identifier CC-BY-4.0 +.. (c) Open Platform for NFV Project, Inc. and its contributors + +********************************* +OPNFV Snaps +********************************* + +.. toctree:: + :numbered: + :maxdepth: 3 + + /how-to-use/index diff --git a/docs/release/release-notes.rst b/docs/release/release-notes.rst new file mode 100644 index 0000000..d682dd9 --- /dev/null +++ b/docs/release/release-notes.rst @@ -0,0 +1,25 @@ +================= +SNAPS-OO in OPNFV +================= + +Introduction +============ + +SNAPS-OO is a object-oriented library designed for managing objects deployed +on OpenStack. In addition to its ability to deploy and manage objects, this +project also contains many API and integration tests that can be used to +validate your OpenStack cloud. Also included is a lightweight orchestrator that +can be used for rapid prototyping virtual environments. + +Installation +============ + +To use SNAPS-OO, it must first be installed into your Python 2.7-3.5 runtime. +To install: + +pip install -e <SNAPS-OO directory> + +Using SNAPS-OO +============== + +Please refer to the RST documents in ../how-to-use
\ No newline at end of file diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..9fde2df --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,2 @@ +lfdocs-conf +sphinx_opnfv_theme diff --git a/examples/complex-network/deploy-complex-network.yaml b/examples/complex-network/deploy-complex-network.yaml index 7915c38..be38794 100644 --- a/examples/complex-network/deploy-complex-network.yaml +++ b/examples/complex-network/deploy-complex-network.yaml @@ -70,8 +70,14 @@ openstack: name: mgr-router external_gateway: external internal_subnets: - - mgr-subnet - - mgr-subnet-2 + - subnet: + project_name: admin + network_name: mgr-net + subnet_name: mgr-subnet + - subnet: + project_name: admin + network_name: mgr-net + subnet_name: mgr-subnet-2 interfaces: - port: name: mgr-router-to-site1 @@ -83,12 +89,18 @@ openstack: name: site1-router external_gateway: external internal_subnets: - - site1-subnet + - subnet: + project_name: admin + network_name: site1-net + subnet_name: site1-subnet - router: name: site2-router external_gateway: external internal_subnets: - - site2-subnet + - subnet: + project_name: admin + network_name: site2-net + subnet_name: site2-subnet - router: name: site-to-site-router interfaces: diff --git a/examples/external-network/deploy-ext-net.yaml b/examples/external-network/deploy-ext-net.yaml index ac5e214..b5f00b1 100644 --- a/examples/external-network/deploy-ext-net.yaml +++ b/examples/external-network/deploy-ext-net.yaml @@ -52,7 +52,10 @@ openstack: name: ext-net-router external_gateway: ext-net internal_subnets: - - internal-subnet + - subnet: + project_name: admin + network_name: internal-net + subnet_name: internal-subnet keypairs: - keypair: name: ext-net-kp diff --git a/examples/inst-w-volume/deploy-vm-with-volume.yaml b/examples/inst-w-volume/deploy-vm-with-volume.yaml index 30dbcc0..b15f655 100644 --- a/examples/inst-w-volume/deploy-vm-with-volume.yaml +++ b/examples/inst-w-volume/deploy-vm-with-volume.yaml @@ -95,7 +95,10 @@ openstack: name: {{ router_name }} external_gateway: {{ ext_net }} internal_subnets: - - {{ subnet_name }} + - subnet: + project_name: {{ admin_proj }} + network_name: {{ net_name }} + subnet_name: {{ subnet_name }} keypairs: - keypair: os_user: diff --git a/examples/launch.py b/examples/launch.py index 04bc5d6..cfd7f65 100644 --- a/examples/launch.py +++ b/examples/launch.py @@ -32,6 +32,10 @@ logger = logging.getLogger('snaps_launcher') ARG_NOT_SET = "argument not set" +from warnings import warn +warn('This script will be removed in a subsequent release', + DeprecationWarning) + def main(arguments): """ diff --git a/examples/simple/deploy-simple.yaml b/examples/simple/deploy-simple.yaml index 3124210..fa4c8b2 100644 --- a/examples/simple/deploy-simple.yaml +++ b/examples/simple/deploy-simple.yaml @@ -41,7 +41,10 @@ openstack: name: simple-router external_gateway: external internal_subnets: - - simple-subnet + - subnet: + project_name: admin + network_name: simple-net + subnet_name: simple-subnet keypairs: - keypair: name: simple-kp diff --git a/examples/two-network/deploy-two-net-centos.yaml b/examples/two-network/deploy-two-net-centos.yaml index 4b5ba89..d23fff9 100644 --- a/examples/two-network/deploy-two-net-centos.yaml +++ b/examples/two-network/deploy-two-net-centos.yaml @@ -48,7 +48,10 @@ openstack: name: router-1 external_gateway: external internal_subnets: - - subnet-1 + - subnet: + project_name: admin + network_name: net-1 + subnet_name: subnet-1 keypairs: - keypair: name: two-net diff --git a/examples/two-network/deploy-two-net-ubuntu.yaml b/examples/two-network/deploy-two-net-ubuntu.yaml index 0ad471c..0381bff 100644 --- a/examples/two-network/deploy-two-net-ubuntu.yaml +++ b/examples/two-network/deploy-two-net-ubuntu.yaml @@ -48,7 +48,10 @@ openstack: name: router-1 external_gateway: external internal_subnets: - - subnet-1 + - subnet: + project_name: admin + network_name: net-1 + subnet_name: subnet-1 keypairs: - keypair: name: simple diff --git a/requirements.txt b/requirements.txt index 137159f..6721540 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,14 +1,17 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -python-novaclient>=9.0.0 # Apache-2.0 -python-neutronclient>=6.3.0 # Apache-2.0 +python-novaclient>=9.1.0 # Apache-2.0 +python-neutronclient>=6.7.0 # Apache-2.0 python-keystoneclient>=3.8.0 # Apache-2.0 python-glanceclient>=2.8.0 # Apache-2.0 -python-heatclient>=1.6.1 # Apache-2.0 -python-cinderclient>=3.1.0 # Apache-2.0 -python-magnumclient>=2.0.0 # Apache-2.0 -ansible<2.4,>=2.1.0 +python-heatclient>=1.10.0 # Apache-2.0 +python-cinderclient>=3.3.0 # Apache-2.0 +python-magnumclient>=2.1.0,<2.3 # Apache-2.0 +ansible>=2.4 wrapt>=1.7.0 # BSD License scp -cryptography!=2.0,>=1.6 # BSD/Apache-2.0 +cryptography>=2.1 # BSD/Apache-2.0 +concurrencytest +Jinja2 # BSD License (3 clause) +keystoneauth1 # Apache-2.0 @@ -1,6 +1,6 @@ [metadata] -name = snaps -version = 1.0 +name = snaps-oo +version = 7.0.0 home-page = https://gerrit.opnfv.org/gerrit/gitweb?p=snaps.git;a=summary author = Steve Pisarski author-email = s.pisarski@cablelabs.com diff --git a/snaps/config/network.py b/snaps/config/network.py index 39a4254..b142480 100644 --- a/snaps/config/network.py +++ b/snaps/config/network.py @@ -46,6 +46,7 @@ class NetworkConfig(object): :param segmentation_id: the id of the segmentation (this is required when network_type is 'vlan') :param subnets or subnet_settings: List of SubnetConfig objects. + :param mtu: MTU setting (optional) :return: """ @@ -88,6 +89,8 @@ class NetworkConfig(object): if not self.name or len(self.name) < 1: raise NetworkConfigError('Name required for networks') + self.mtu = kwargs.get('mtu') + def get_project_id(self, os_creds): """ Returns the project ID for a given project_name or None @@ -98,11 +101,15 @@ class NetworkConfig(object): return self.project_id else: if self.project_name: - keystone = keystone_utils.keystone_client(os_creds) - project = keystone_utils.get_project( - keystone=keystone, project_name=self.project_name) - if project: - return project.id + session = keystone_utils.keystone_session(os_creds) + keystone = keystone_utils.keystone_client(os_creds, session) + try: + project = keystone_utils.get_project( + keystone=keystone, project_name=self.project_name) + if project: + return project.id + finally: + keystone_utils.close_session(session) return None @@ -140,6 +147,8 @@ class NetworkConfig(object): out['provider:segmentation_id'] = self.segmentation_id if self.external: out['router:external'] = self.external + if self.mtu: + out['mtu'] = self.mtu return {'network': out} @@ -175,14 +184,17 @@ class SubnetConfig(object): through authorization policies (optional) :param start: The start address for the allocation pools (optional) :param end: The end address for the allocation pools (optional) - :param gateway_ip: The gateway IP address (optional) + :param gateway_ip: The gateway IP address (optional). When not + configured, the IP address will be automatically + assigned; when 'none', no gateway address will be + assigned, else the value must be valid :param enable_dhcp: Set to true if DHCP is enabled and false if DHCP is disabled (optional) :param dns_nameservers: A list of DNS name servers for the subnet. Specify each name server as an IP address and separate multiple entries with a space. For example [8.8.8.7 8.8.8.8] - (default '8.8.8.8') + (default []) :param host_routes: A list of host route dictionaries for the subnet. For example: "host_routes":[ @@ -221,10 +233,7 @@ class SubnetConfig(object): if 'dns_nameservers' in kwargs: self.dns_nameservers = kwargs.get('dns_nameservers') else: - if self.ip_version == 4: - self.dns_nameservers = ['8.8.8.8'] - else: - self.dns_nameservers = list() + self.dns_nameservers = list() self.host_routes = kwargs.get('host_routes') self.destination = kwargs.get('destination') @@ -255,9 +264,13 @@ class SubnetConfig(object): if self.name: out['name'] = self.name if self.project_name: - keystone = keystone_utils.keystone_client(os_creds) - project = keystone_utils.get_project( - keystone=keystone, project_name=self.project_name) + session = keystone_utils.keystone_session(os_creds) + keystone = keystone_utils.keystone_client(os_creds, session) + try: + project = keystone_utils.get_project( + keystone=keystone, project_name=self.project_name) + finally: + keystone_utils.close_session(session) project_id = None if project: project_id = project.id @@ -270,7 +283,10 @@ class SubnetConfig(object): if self.start and self.end: out['allocation_pools'] = [{'start': self.start, 'end': self.end}] if self.gateway_ip: - out['gateway_ip'] = self.gateway_ip + if self.gateway_ip == 'none': + out['gateway_ip'] = None + else: + out['gateway_ip'] = self.gateway_ip if self.enable_dhcp is not None: out['enable_dhcp'] = self.enable_dhcp if self.dns_nameservers and len(self.dns_nameservers) > 0: @@ -401,22 +417,23 @@ class PortConfig(object): raise PortConfigError( 'The attribute network_name is required') - def __get_fixed_ips(self, neutron): + def __get_fixed_ips(self, neutron, network): """ Sets the self.fixed_ips value :param neutron: the Neutron client + :param network: the SNAPS-OO network domain object :return: None """ - fixed_ips = list() if self.ip_addrs: for ip_addr_dict in self.ip_addrs: subnet = neutron_utils.get_subnet( - neutron, subnet_name=ip_addr_dict['subnet_name']) - if subnet and 'ip' in ip_addr_dict: - fixed_ips.append({'ip_address': ip_addr_dict['ip'], - 'subnet_id': subnet.id}) + neutron, network, subnet_name=ip_addr_dict['subnet_name']) + if subnet: + if 'ip' in ip_addr_dict: + fixed_ips.append({'ip_address': ip_addr_dict['ip'], + 'subnet_id': subnet.id}) else: raise PortConfigError( 'Invalid port configuration, subnet does not exist ' @@ -435,22 +452,28 @@ class PortConfig(object): :param os_creds: the OpenStack credentials :return: the dictionary object """ - out = dict() + session = keystone_utils.keystone_session(os_creds) + keystone = keystone_utils.keystone_client(os_creds, session) - project_id = None + project_name = os_creds.project_name if self.project_name: - keystone = keystone_utils.keystone_client(os_creds) - project = keystone_utils.get_project( - keystone=keystone, project_name=self.project_name) - if project: - project_id = project.id + project_name = project_name + try: + network = neutron_utils.get_network( + neutron, keystone, network_name=self.network_name) + if network and not (network.shared or network.external): + network = neutron_utils.get_network( + neutron, keystone, network_name=self.network_name, + project_name=project_name) + finally: + if session: + keystone_utils.close_session(session) - network = neutron_utils.get_network( - neutron, network_name=self.network_name, project_id=project_id) if not network: raise PortConfigError( - 'Cannot locate network with name - ' + self.network_name) + 'Cannot locate network with name - ' + self.network_name + + ' in project - ' + str(project_name)) out['network_id'] = network.id @@ -459,6 +482,11 @@ class PortConfig(object): if self.name: out['name'] = self.name if self.project_name: + project = keystone_utils.get_project( + keystone=keystone, project_name=self.project_name) + project_id = None + if project: + project_id = project.id if project_id: out['tenant_id'] = project_id else: @@ -468,7 +496,7 @@ class PortConfig(object): if self.mac_address: out['mac_address'] = self.mac_address - fixed_ips = self.__get_fixed_ips(neutron) + fixed_ips = self.__get_fixed_ips(neutron, network) if fixed_ips and len(fixed_ips) > 0: out['fixed_ips'] = fixed_ips @@ -476,7 +504,8 @@ class PortConfig(object): sec_grp_ids = list() for sec_grp_name in self.security_groups: sec_grp = neutron_utils.get_security_group( - neutron, sec_grp_name=sec_grp_name) + neutron, keystone, sec_grp_name=sec_grp_name, + project_name=self.project_name) if sec_grp: sec_grp_ids.append(sec_grp.id) out['security_groups'] = sec_grp_ids diff --git a/snaps/config/project.py b/snaps/config/project.py index 6790609..d6d175f 100644 --- a/snaps/config/project.py +++ b/snaps/config/project.py @@ -32,11 +32,12 @@ class ProjectConfig(object): :param users: list of users to associate project to (optional) :param enabled: denotes whether or not the project is enabled (default True) + :param quotas: quota values to override (optional) """ self.name = kwargs.get('name') self.domain_name = kwargs.get( - 'domain', kwargs.get('domain', 'Default')) + 'domain_name', kwargs.get('domain', 'Default')) self.description = kwargs.get('description') if kwargs.get('enabled') is not None: @@ -46,6 +47,8 @@ class ProjectConfig(object): self.users = kwargs.get('users', list()) + self.quotas = kwargs.get('quotas') + if not self.name: raise ProjectConfigError( "The attribute name is required for ProjectConfig") diff --git a/snaps/config/router.py b/snaps/config/router.py index 72164f2..2a0b6a4 100644 --- a/snaps/config/router.py +++ b/snaps/config/router.py @@ -33,7 +33,11 @@ class RouterConfig(object): :param admin_state_up: The administrative status of the router. True = up / False = down (default True) :param internal_subnets: List of subnet names to which to connect this - router for Floating IP purposes + router (this way is deprecated). + *** NEW WAY *** + List of dict where the key is 'subnet' that + contains members with the following keys: + project_name, network_name, and subnet_name :param port_settings: List of PortConfig objects :return: """ @@ -45,6 +49,19 @@ class RouterConfig(object): self.enable_snat = kwargs.get('enable_snat') if kwargs.get('internal_subnets'): self.internal_subnets = kwargs['internal_subnets'] + if isinstance(self.internal_subnets, dict): + if 'subnet' not in self.internal_subnets: + raise RouterConfigError( + 'subnet is a required key to internal_subnets') + if 'project_name' not in self.internal_subnets['subnet']: + raise RouterConfigError( + 'subnet.project is a required key to subnet') + if 'network_name' not in self.internal_subnets['subnet']: + raise RouterConfigError( + 'network_name is a required key to subnet') + if 'subnet_name' not in self.internal_subnets['subnet']: + raise RouterConfigError( + 'subnet_name is a required key to subnet') else: self.internal_subnets = list() @@ -70,39 +87,42 @@ class RouterConfig(object): TODO - expand automated testing to exercise all parameters :param neutron: The neutron client to retrieve external network information if necessary - :param os_creds: The OpenStack credentials + :param os_creds: The OpenStack credentials for retrieving the keystone + client for looking up the project ID when the + self.project_name is not None :return: the dictionary object """ out = dict() ext_gw = dict() - if self.name: - out['name'] = self.name - if self.project_name: - keystone = keystone_utils.keystone_client(os_creds) - project = keystone_utils.get_project( - keystone=keystone, project_name=self.project_name) - project_id = None - if project: - project_id = project.id - if project_id: - out['tenant_id'] = project_id - else: - raise RouterConfigError( - 'Could not find project ID for project named - ' + - self.project_name) - if self.admin_state_up is not None: - out['admin_state_up'] = self.admin_state_up - if self.external_gateway: - ext_net = neutron_utils.get_network( - neutron, network_name=self.external_gateway) - if ext_net: - ext_gw['network_id'] = ext_net.id - out['external_gateway_info'] = ext_gw - else: - raise RouterConfigError( - 'Could not find the external network named - ' + - self.external_gateway) + session = keystone_utils.keystone_session(os_creds) + keystone = keystone_utils.keystone_client(os_creds, session) + try: + if self.name: + out['name'] = self.name + if self.project_name: + project = keystone_utils.get_project( + keystone=keystone, project_name=self.project_name) + if project: + out['tenant_id'] = project.id + else: + raise RouterConfigError( + 'Could not find project ID for project named - ' + + self.project_name) + if self.admin_state_up is not None: + out['admin_state_up'] = self.admin_state_up + if self.external_gateway: + ext_net = neutron_utils.get_network( + neutron, keystone, network_name=self.external_gateway) + if ext_net: + ext_gw['network_id'] = ext_net.id + out['external_gateway_info'] = ext_gw + else: + raise RouterConfigError( + 'Could not find the external network named - ' + + self.external_gateway) + finally: + keystone_utils.close_session(session) return {'router': out} diff --git a/snaps/config/security_group.py b/snaps/config/security_group.py index 32a1e95..9e485c3 100644 --- a/snaps/config/security_group.py +++ b/snaps/config/security_group.py @@ -54,7 +54,7 @@ class SecurityGroupConfig(object): raise SecurityGroupConfigError('The attribute name is required') for rule_setting in self.rule_settings: - if rule_setting.sec_grp_name is not self.name: + if rule_setting.sec_grp_name != self.name: raise SecurityGroupConfigError( 'Rule settings must correspond with the name of this ' 'security group') @@ -204,13 +204,14 @@ class SecurityGroupRuleConfig(object): raise SecurityGroupRuleConfigError( 'direction and sec_grp_name are required') - def dict_for_neutron(self, neutron): + def dict_for_neutron(self, neutron, keystone, project_name): """ Returns a dictionary object representing this object. This is meant to be converted into JSON designed for use by the Neutron API - :param neutron: the neutron client for performing lookups + :param keystone: the keystone client for performing lookups + :param project_name: the name of the project associated with the group :return: the dictionary object """ out = dict() @@ -229,7 +230,8 @@ class SecurityGroupRuleConfig(object): out['protocol'] = self.protocol.value if self.sec_grp_name: sec_grp = neutron_utils.get_security_group( - neutron, sec_grp_name=self.sec_grp_name) + neutron, keystone, sec_grp_name=self.sec_grp_name, + project_name=project_name) if sec_grp: out['security_group_id'] = sec_grp.id else: @@ -312,12 +314,15 @@ def map_direction(direction): :return: the Direction enum object :raise: Exception if value is invalid """ - if not direction: + if not direction or 'None' == str(direction): return None - if isinstance(direction, Direction): - return direction - elif (isinstance(direction, str) or isinstance(direction, unicode) - or isinstance(direction, unicode)): + if isinstance(direction, enum.Enum): + if direction.__class__.__name__ == 'Direction': + return direction + else: + raise SecurityGroupRuleConfigError( + 'Invalid class - ' + direction.__class__.__name__) + elif isinstance(direction, str): dir_str = str(direction) if dir_str == 'egress': return Direction.egress @@ -327,7 +332,7 @@ def map_direction(direction): raise SecurityGroupRuleConfigError( 'Invalid Direction - ' + dir_str) else: - return map_direction(direction.value) + return map_direction(str(direction)) def map_protocol(protocol): @@ -340,19 +345,22 @@ def map_protocol(protocol): """ if not protocol: return None - elif isinstance(protocol, Protocol): - return protocol - elif (isinstance(protocol, str) or isinstance(protocol, unicode) - or isinstance(protocol, int)): + elif isinstance(protocol, enum.Enum): + if protocol.__class__.__name__ == 'Protocol': + return protocol + else: + raise SecurityGroupRuleConfigError( + 'Invalid class - ' + protocol.__class__.__name__) + elif isinstance(protocol, str) or isinstance(protocol, int): for proto_enum in Protocol: if proto_enum.name == protocol or proto_enum.value == protocol: if proto_enum == Protocol.any: return Protocol.null return proto_enum raise SecurityGroupRuleConfigError( - 'Invalid Protocol - ' + protocol) + 'Invalid Protocol - ' + str(protocol)) else: - return map_protocol(protocol.value) + return map_protocol(str(protocol)) def map_ethertype(ethertype): @@ -363,12 +371,15 @@ def map_ethertype(ethertype): :return: the Ethertype enum object :raise: Exception if value is invalid """ - if not ethertype: + if not ethertype or 'None' == str(ethertype): return None - elif isinstance(ethertype, Ethertype): - return ethertype - elif (isinstance(ethertype, str) or isinstance(ethertype, unicode) - or isinstance(ethertype, int)): + elif isinstance(ethertype, enum.Enum): + if ethertype.__class__.__name__ == 'Ethertype': + return ethertype + else: + raise SecurityGroupRuleConfigError( + 'Invalid class - ' + ethertype.__class__.__name__) + elif isinstance(ethertype, str) or isinstance(ethertype, int): eth_str = str(ethertype) if eth_str == 'IPv6' or eth_str == '6': return Ethertype.IPv6 @@ -378,7 +389,7 @@ def map_ethertype(ethertype): raise SecurityGroupRuleConfigError( 'Invalid Ethertype - ' + eth_str) else: - return map_ethertype(ethertype.value) + return map_ethertype(str(ethertype)) class SecurityGroupRuleConfigError(Exception): diff --git a/snaps/config/stack.py b/snaps/config/stack.py index 4d5db29..3fc820e 100644 --- a/snaps/config/stack.py +++ b/snaps/config/stack.py @@ -15,9 +15,12 @@ STACK_DELETE_TIMEOUT = 1200 STACK_COMPLETE_TIMEOUT = 1200 +STACK_UPDATE_TIMEOUT = 1200 POLL_INTERVAL = 3 STATUS_CREATE_FAILED = 'CREATE_FAILED' STATUS_CREATE_COMPLETE = 'CREATE_COMPLETE' +STATUS_UPDATE_FAILED = 'UPDATE_FAILED' +STATUS_UPDATE_COMPLETE = 'UPDATE_COMPLETE' STATUS_DELETE_COMPLETE = 'DELETE_COMPLETE' STATUS_DELETE_FAILED = 'DELETE_FAILED' diff --git a/snaps/config/tests/network_tests.py b/snaps/config/tests/network_tests.py index 43b69c7..cbc71af 100644 --- a/snaps/config/tests/network_tests.py +++ b/snaps/config/tests/network_tests.py @@ -15,7 +15,7 @@ import unittest from snaps.config.network import ( - NetworkConfigError, NetworkConfig, SubnetConfig, SubnetConfigError, + NetworkConfigError, NetworkConfig, SubnetConfig, SubnetConfigError, IPv6Mode, PortConfig, PortConfigError) @@ -42,6 +42,7 @@ class NetworkConfigUnitTests(unittest.TestCase): self.assertIsNone(settings.network_type) self.assertIsNone(settings.segmentation_id) self.assertEqual(0, len(settings.subnet_settings)) + self.assertIsNone(settings.mtu) def test_config_with_name_only(self): settings = NetworkConfig(**{'name': 'foo'}) @@ -53,13 +54,14 @@ class NetworkConfigUnitTests(unittest.TestCase): self.assertIsNone(settings.network_type) self.assertIsNone(settings.segmentation_id) self.assertEqual(0, len(settings.subnet_settings)) + self.assertIsNone(settings.mtu) def test_all(self): sub_settings = SubnetConfig(name='foo-subnet', cidr='10.0.0.0/24') settings = NetworkConfig( name='foo', admin_state_up=False, shared=True, project_name='bar', external=True, network_type='vlan', physical_network='phy', - segmentation_id=2366, subnet_settings=[sub_settings]) + segmentation_id=2366, subnet_settings=[sub_settings], mtu=999) self.assertEqual('foo', settings.name) self.assertFalse(settings.admin_state_up) self.assertTrue(settings.shared) @@ -70,6 +72,7 @@ class NetworkConfigUnitTests(unittest.TestCase): self.assertEqual(2366, settings.segmentation_id) self.assertEqual(1, len(settings.subnet_settings)) self.assertEqual('foo-subnet', settings.subnet_settings[0].name) + self.assertEqual(999, settings.mtu) def test_config_all(self): settings = NetworkConfig( @@ -79,7 +82,8 @@ class NetworkConfigUnitTests(unittest.TestCase): 'segmentation_id': 2366, 'subnets': [{'subnet': {'name': 'foo-subnet', - 'cidr': '10.0.0.0/24'}}]}) + 'cidr': '10.0.0.0/24'}}], + 'mtu': 999}) self.assertEqual('foo', settings.name) self.assertFalse(settings.admin_state_up) self.assertTrue(settings.shared) @@ -90,6 +94,7 @@ class NetworkConfigUnitTests(unittest.TestCase): self.assertEqual(2366, settings.segmentation_id) self.assertEqual(1, len(settings.subnet_settings)) self.assertEqual('foo-subnet', settings.subnet_settings[0].name) + self.assertEqual(999, settings.mtu) class SubnetConfigUnitTests(unittest.TestCase): @@ -122,8 +127,7 @@ class SubnetConfigUnitTests(unittest.TestCase): self.assertIsNone(settings.start) self.assertIsNone(settings.end) self.assertIsNone(settings.enable_dhcp) - self.assertEqual(1, len(settings.dns_nameservers)) - self.assertEqual('8.8.8.8', settings.dns_nameservers[0]) + self.assertEqual(0, len(settings.dns_nameservers)) self.assertIsNone(settings.host_routes) self.assertIsNone(settings.destination) self.assertIsNone(settings.nexthop) @@ -140,8 +144,7 @@ class SubnetConfigUnitTests(unittest.TestCase): self.assertIsNone(settings.end) self.assertIsNone(settings.gateway_ip) self.assertIsNone(settings.enable_dhcp) - self.assertEqual(1, len(settings.dns_nameservers)) - self.assertEqual('8.8.8.8', settings.dns_nameservers[0]) + self.assertEqual(0, len(settings.dns_nameservers)) self.assertIsNone(settings.host_routes) self.assertIsNone(settings.destination) self.assertIsNone(settings.nexthop) diff --git a/snaps/config/tests/project_tests.py b/snaps/config/tests/project_tests.py index 0470d83..99de4a9 100644 --- a/snaps/config/tests/project_tests.py +++ b/snaps/config/tests/project_tests.py @@ -37,6 +37,7 @@ class ProjectConfigUnitTests(unittest.TestCase): self.assertIsNone(settings.description) self.assertTrue(settings.enabled) self.assertEqual(list(), settings.users) + self.assertIsNone(settings.quotas) def test_config_with_name_only(self): settings = ProjectConfig(**{'name': 'foo'}) @@ -45,25 +46,52 @@ class ProjectConfigUnitTests(unittest.TestCase): self.assertIsNone(settings.description) self.assertTrue(settings.enabled) self.assertEqual(list(), settings.users) + self.assertIsNone(settings.quotas) def test_all(self): users = ['test1', 'test2'] + quotas = { + 'cores': 4, 'instances': 5, 'injected_files': 6, + 'injected_file_content_bytes': 60000, 'ram': 70000, 'fixed_ips': 7, + 'key_pairs': 8} settings = ProjectConfig( name='foo', domain='bar', description='foobar', enabled=False, - users=users) + users=users, quotas=quotas) self.assertEqual('foo', settings.name) self.assertEqual('bar', settings.domain_name) self.assertEqual('foobar', settings.description) self.assertFalse(settings.enabled) self.assertEqual(users, settings.users) + self.assertIsNotNone(settings.quotas) + self.assertEquals(4, settings.quotas.get('cores')) + self.assertEquals(5, settings.quotas.get('instances')) + self.assertEquals(6, settings.quotas.get('injected_files')) + self.assertEquals( + 60000, settings.quotas.get('injected_file_content_bytes')) + self.assertEquals(70000, settings.quotas.get('ram')) + self.assertEquals(7, settings.quotas.get('fixed_ips')) + self.assertEquals(8, settings.quotas.get('key_pairs')) def test_config_all(self): users = ['test1', 'test2'] settings = ProjectConfig( **{'name': 'foo', 'domain': 'bar', 'description': 'foobar', - 'enabled': False, 'users': users}) + 'enabled': False, 'users': users, + 'quotas': { + 'cores': 4, 'instances': 5, 'injected_files': 6, + 'injected_file_content_bytes': 60000, 'ram': 70000, + 'fixed_ips': 7, 'key_pairs': 8}}) self.assertEqual('foo', settings.name) self.assertEqual('bar', settings.domain_name) self.assertEqual('foobar', settings.description) self.assertFalse(settings.enabled) self.assertEqual(users, settings.users) + self.assertIsNotNone(settings.quotas) + self.assertEquals(4, settings.quotas.get('cores')) + self.assertEquals(5, settings.quotas.get('instances')) + self.assertEquals(6, settings.quotas.get('injected_files')) + self.assertEquals( + 60000, settings.quotas.get('injected_file_content_bytes')) + self.assertEquals(70000, settings.quotas.get('ram')) + self.assertEquals(7, settings.quotas.get('fixed_ips')) + self.assertEquals(8, settings.quotas.get('key_pairs')) diff --git a/snaps/config/tests/router_tests.py b/snaps/config/tests/router_tests.py index 2c8f91f..1397f23 100644 --- a/snaps/config/tests/router_tests.py +++ b/snaps/config/tests/router_tests.py @@ -31,6 +31,25 @@ class RouterConfigUnitTests(unittest.TestCase): with self.assertRaises(RouterConfigError): RouterConfig(**dict()) + def test_bad_internal_subnets_bad_key(self): + with self.assertRaises(RouterConfigError): + RouterConfig(name='foo', internal_subnets={'foo': 'bar'}) + + def test_bad_internal_subnets_no_project(self): + with self.assertRaises(RouterConfigError): + RouterConfig(name='foo', internal_subnets={ + 'subnet': {'subnet_name': 'bar', 'network_name': 'foo'}}) + + def test_bad_internal_subnets_no_network(self): + with self.assertRaises(RouterConfigError): + RouterConfig(name='foo', internal_subnets={ + 'subnet': {'subnet_name': 'bar', 'project_name': 'foo'}}) + + def test_bad_internal_subnets_no_subnet(self): + with self.assertRaises(RouterConfigError): + RouterConfig(name='foo', internal_subnets={ + 'subnet': {'project_name': 'bar', 'network_name': 'foo'}}) + def test_name_only(self): settings = RouterConfig(name='foo') self.assertEqual('foo', settings.name) @@ -59,7 +78,7 @@ class RouterConfigUnitTests(unittest.TestCase): self.assertTrue(isinstance(settings.port_settings, list)) self.assertEqual(0, len(settings.port_settings)) - def test_all(self): + def test_all_internal_subnets_str(self): port_settings = PortConfig(name='foo', network_name='bar') settings = RouterConfig( name='foo', project_name='bar', external_gateway='foo_gateway', @@ -76,11 +95,36 @@ class RouterConfigUnitTests(unittest.TestCase): self.assertEqual(['10.0.0.1/24'], settings.internal_subnets) self.assertEqual([port_settings], settings.port_settings) - def test_config_all(self): + def test_all_internal_subnets_dict(self): + port_settings = PortConfig(name='foo', network_name='bar') + int_subs = {'subnet': { + 'project_name': 'proj_a', 'network_name': 'net_name', + 'subnet_name': 'sub_name'}} + settings = RouterConfig( + name='foo', project_name='bar', external_gateway='foo_gateway', + admin_state_up=True, enable_snat=False, + internal_subnets=int_subs, + interfaces=[port_settings]) + self.assertEqual('foo', settings.name) + self.assertEqual('bar', settings.project_name) + self.assertEqual('foo_gateway', settings.external_gateway) + self.assertTrue(settings.admin_state_up) + self.assertFalse(settings.enable_snat) + self.assertIsNotNone(settings.internal_subnets) + self.assertTrue(isinstance(settings.internal_subnets, dict)) + self.assertEqual(1, len(settings.internal_subnets)) + self.assertEqual(int_subs, settings.internal_subnets) + self.assertEqual([port_settings], settings.port_settings) + + def test_config_all_internal_subnets_str(self): + int_subs = {'subnet': { + 'project_name': 'proj_a', 'network_name': 'net_name', + 'subnet_name': 'sub_name'}} settings = RouterConfig( **{'name': 'foo', 'project_name': 'bar', 'external_gateway': 'foo_gateway', 'admin_state_up': True, - 'enable_snat': False, 'internal_subnets': ['10.0.0.1/24'], + 'enable_snat': False, + 'internal_subnets': int_subs, 'interfaces': [{'port': {'name': 'foo-port', 'network_name': 'bar-net'}}]}) @@ -90,9 +134,9 @@ class RouterConfigUnitTests(unittest.TestCase): self.assertTrue(settings.admin_state_up) self.assertFalse(settings.enable_snat) self.assertIsNotNone(settings.internal_subnets) - self.assertTrue(isinstance(settings.internal_subnets, list)) + self.assertTrue(isinstance(settings.internal_subnets, dict)) self.assertEqual(1, len(settings.internal_subnets)) - self.assertEqual(['10.0.0.1/24'], settings.internal_subnets) + self.assertEqual(int_subs, settings.internal_subnets) self.assertEqual([PortConfig(**{'name': 'foo-port', 'network_name': 'bar-net'})], settings.port_settings) diff --git a/snaps/config/tests/volume_tests.py b/snaps/config/tests/volume_tests.py index b4b54bd..21adffa 100644 --- a/snaps/config/tests/volume_tests.py +++ b/snaps/config/tests/volume_tests.py @@ -33,6 +33,7 @@ class VolumeConfigUnitTests(unittest.TestCase): def test_name_only(self): settings = VolumeConfig(name='foo') self.assertEqual('foo', settings.name) + self.assertIsNone(settings.project_name) self.assertIsNone(settings.description) self.assertEquals(1, settings.size) self.assertIsNone(settings.image_name) @@ -43,6 +44,7 @@ class VolumeConfigUnitTests(unittest.TestCase): def test_config_with_name_only(self): settings = VolumeConfig(**{'name': 'foo'}) self.assertEqual('foo', settings.name) + self.assertIsNone(settings.project_name) self.assertIsNone(settings.description) self.assertEquals(1, settings.size) self.assertIsNone(settings.image_name) @@ -52,10 +54,12 @@ class VolumeConfigUnitTests(unittest.TestCase): def test_all_strings(self): settings = VolumeConfig( - name='foo', description='desc', size='2', image_name='image', - type_name='type', availability_zone='zone1', multi_attach='true') + name='foo', project_name='proj-foo', description='desc', size='2', + image_name='image', type_name='type', availability_zone='zone1', + multi_attach='true') self.assertEqual('foo', settings.name) + self.assertEqual('proj-foo', settings.project_name) self.assertEqual('desc', settings.description) self.assertEqual(2, settings.size) self.assertEqual('image', settings.image_name) @@ -65,10 +69,12 @@ class VolumeConfigUnitTests(unittest.TestCase): def test_all_correct_type(self): settings = VolumeConfig( - name='foo', description='desc', size=2, image_name='image', - type_name='bar', availability_zone='zone1', multi_attach=True) + name='foo', project_name='proj-foo', description='desc', size=2, + image_name='image', type_name='bar', availability_zone='zone1', + multi_attach=True) self.assertEqual('foo', settings.name) + self.assertEqual('proj-foo', settings.project_name) self.assertEqual('desc', settings.description) self.assertEqual(2, settings.size) self.assertEqual('image', settings.image_name) @@ -78,11 +84,13 @@ class VolumeConfigUnitTests(unittest.TestCase): def test_config_all(self): settings = VolumeConfig( - **{'name': 'foo', 'description': 'desc', 'size': '2', + **{'name': 'foo', 'project_name': 'proj-foo', + 'description': 'desc', 'size': '2', 'image_name': 'foo', 'type_name': 'bar', 'availability_zone': 'zone1', 'multi_attach': 'true'}) self.assertEqual('foo', settings.name) + self.assertEqual('proj-foo', settings.project_name) self.assertEqual('desc', settings.description) self.assertEqual(2, settings.size) self.assertEqual('foo', settings.image_name) diff --git a/snaps/config/volume.py b/snaps/config/volume.py index a31e8f5..0b4b73e 100644 --- a/snaps/config/volume.py +++ b/snaps/config/volume.py @@ -20,6 +20,9 @@ class VolumeConfig(object): """ Constructor :param name: the volume's name (required) + :param project_name: the name of the project to associate (optional) + note: due to a bug in the Cinder API, this functionality will not + work. see https://bugs.launchpad.net/cinder/+bug/1641982 :param description: the volume's name (optional) :param size: the volume's size in GB (default 1) :param image_name: when a glance image is used for the image source @@ -32,6 +35,7 @@ class VolumeConfig(object): """ self.name = kwargs.get('name') + self.project_name = kwargs.get('project_name') self.description = kwargs.get('description') self.size = int(kwargs.get('size', 1)) self.image_name = kwargs.get('image_name') diff --git a/snaps/domain/network.py b/snaps/domain/network.py index 2d02966..3d5e4af 100644 --- a/snaps/domain/network.py +++ b/snaps/domain/network.py @@ -24,6 +24,7 @@ class Network: Constructor :param name: the network's name :param id: the network's ID + :param project_id: the associated project ID :param admin_state_up: T/F - network is up when True :param shared: T/F - network can be shared amongst other project's :param external: T/F - network is deemed to be external @@ -32,19 +33,22 @@ class Network: """ self.name = kwargs.get('name') self.id = kwargs.get('id') + self.project_id = kwargs.get('project_id') self.admin_state_up = kwargs.get('admin_state_up') self.shared = kwargs.get('shared') self.external = kwargs.get('router:external', kwargs.get('external')) self.type = kwargs.get('provider:network_type', kwargs.get('type')) self.subnets = kwargs.get('subnets', list()) + self.mtu = kwargs.get('mtu') def __eq__(self, other): return (self.name == other.name and self.id == other.id and + self.project_id == other.project_id and self.admin_state_up == other.admin_state_up and self.shared == other.shared and self.external == other.external and - self.type == other.type and - self.subnets == other.subnets) + self.subnets == other.subnets and + self.mtu == other.mtu) class Subnet: @@ -57,6 +61,7 @@ class Subnet: Constructor :param name: the network's name :param id: the subnet's ID + :param project_id: the associated project ID :param network_id: the network's ID :param cidr: the CIDR :param ip_version: the IP version @@ -71,6 +76,7 @@ class Subnet: """ self.name = kwargs.get('name') self.id = kwargs.get('id') + self.project_id = kwargs.get('project_id') self.network_id = kwargs.get('network_id') self.cidr = kwargs.get('cidr') self.ip_version = kwargs.get('ip_version') @@ -95,6 +101,7 @@ class Subnet: def __eq__(self, other): return (self.name == other.name and self.id == other.id and + self.project_id == other.project_id and self.network_id == other.network_id and self.cidr == other.cidr and self.ip_version == other.ip_version and @@ -181,8 +188,7 @@ class Router: self.port_subnets = kwargs.get('port_subnets') if (kwargs.get('external_gateway_info') and - isinstance(kwargs.get('external_gateway_info'), dict) and - kwargs.get('external_gateway_info').get('external_fixed_ips')): + isinstance(kwargs.get('external_gateway_info'), dict)): gateway_info = kwargs.get('external_gateway_info') self.external_network_id = gateway_info.get('network_id') diff --git a/snaps/domain/stack.py b/snaps/domain/stack.py index 080ab17..2c593d3 100644 --- a/snaps/domain/stack.py +++ b/snaps/domain/stack.py @@ -19,14 +19,22 @@ class Stack: SNAPS domain object for Heat Stacks. Should contain attributes that are shared amongst cloud providers """ - def __init__(self, name, stack_id): + def __init__(self, name, stack_id, stack_project_id, + status, status_reason): """ Constructor :param name: the stack's name :param stack_id: the stack's stack_id + :param stack_project_id: the project ID that was spawned from this + deployment + :param status: the stack's last known status code + :param status_reason: the stack's last known explanation of the status """ self.name = name self.id = stack_id + self.stack_project_id = stack_project_id + self.status = status + self.status_reason = status_reason def __eq__(self, other): return (self.name == other.name and diff --git a/snaps/domain/test/network_tests.py b/snaps/domain/test/network_tests.py index 3003326..2c4c841 100644 --- a/snaps/domain/test/network_tests.py +++ b/snaps/domain/test/network_tests.py @@ -26,28 +26,34 @@ class NetworkObjectTests(unittest.TestCase): def test_construction_kwargs_1(self): subnet = Subnet( - **{'name': 'foo', 'id': 'bar', 'network_id': 'foo-bar'}) + **{'name': 'foo', 'id': 'bar', 'project_id': 'proj1', + 'network_id': 'foo-bar'}) network = Network( - **{'name': 'foo', 'id': 'bar', 'provider:network_type': 'flat', - 'admin_state_up': False, 'shared': True, - 'router:external': False, 'subnets': [subnet]}) + **{'name': 'foo', 'id': 'bar', 'project_id': 'proj1', + 'provider:network_type': 'flat', 'admin_state_up': False, + 'shared': True, 'router:external': False, 'subnets': [subnet], + 'mtu': 999}) self.assertEqual('foo', network.name) self.assertEqual('bar', network.id) + self.assertEqual('proj1', network.project_id) self.assertEqual('flat', network.type) self.assertFalse(network.admin_state_up) self.assertFalse(network.external) self.assertTrue(network.shared) self.assertEqual([subnet], network.subnets) + self.assertEqual(999, network.mtu) def test_construction_kwargs_2(self): subnet = Subnet( - **{'name': 'foo', 'id': 'bar', 'network_id': 'foo-bar'}) + **{'name': 'foo', 'id': 'bar', 'project_id': 'proj1', + 'network_id': 'foo-bar'}) network = Network( - **{'name': 'foo', 'id': 'bar', 'type': 'flat', - 'admin_state_up': False, 'shared': True, 'external': False, - 'subnets': [subnet]}) + **{'name': 'foo', 'id': 'bar', 'project_id': 'proj1', + 'type': 'flat', 'admin_state_up': False, 'shared': True, + 'external': False, 'subnets': [subnet]}) self.assertEqual('foo', network.name) self.assertEqual('bar', network.id) + self.assertEqual('proj1', network.project_id) self.assertEqual('flat', network.type) self.assertFalse(network.admin_state_up) self.assertFalse(network.external) @@ -56,12 +62,15 @@ class NetworkObjectTests(unittest.TestCase): def test_construction_named(self): subnet = Subnet( - **{'name': 'foo', 'id': 'bar', 'network_id': 'foo-bar'}) + **{'name': 'foo', 'id': 'bar', 'project_id': 'proj1', + 'network_id': 'foo-bar'}) network = Network( - name='foo', id='bar', type='flat', admin_state_up=False, - shared=True, external=False, subnets=[subnet]) + name='foo', id='bar', project_id='proj1', type='flat', + admin_state_up=False, shared=True, external=False, + subnets=[subnet]) self.assertEqual('foo', network.name) self.assertEqual('bar', network.id) + self.assertEqual('proj1', network.project_id) self.assertEqual('flat', network.type) self.assertFalse(network.admin_state_up) self.assertFalse(network.external) @@ -76,12 +85,14 @@ class SubnetObjectTests(unittest.TestCase): def test_construction_kwargs(self): subnet = Subnet( - **{'name': 'foo', 'id': 'bar', 'cidr': '10.0.0.0/24', - 'ip_version': 4, 'gateway_ip': '10.0.0.1', 'enable_dhcp': True, + **{'name': 'foo', 'id': 'bar', 'project_id': 'proj1', + 'cidr': '10.0.0.0/24', 'ip_version': 4, + 'gateway_ip': '10.0.0.1', 'enable_dhcp': True, 'dns_nameservers': ['8.8.8.8'], 'host_routes': list(), 'ipv6_ra_mode': 'hello', 'ipv6_address_mode': 'world'}) self.assertEqual('foo', subnet.name) self.assertEqual('bar', subnet.id) + self.assertEqual('proj1', subnet.project_id) self.assertEqual('10.0.0.0/24', subnet.cidr) self.assertEqual(4, subnet.ip_version) self.assertEqual('10.0.0.1', subnet.gateway_ip) @@ -94,12 +105,13 @@ class SubnetObjectTests(unittest.TestCase): def test_construction_named(self): subnet = Subnet( - name='foo', id='bar', cidr='10.0.0.0/24', + name='foo', id='bar', project_id='proj1', cidr='10.0.0.0/24', ip_version=4, gateway_ip='10.0.0.1', enable_dhcp=True, dns_nameservers=['8.8.8.8'], host_routes=list(), ipv6_ra_mode='hello', ipv6_address_mode='world') self.assertEqual('foo', subnet.name) self.assertEqual('bar', subnet.id) + self.assertEqual('proj1', subnet.project_id) self.assertEqual('10.0.0.0/24', subnet.cidr) self.assertEqual(4, subnet.ip_version) self.assertEqual('10.0.0.1', subnet.gateway_ip) diff --git a/snaps/domain/test/stack_tests.py b/snaps/domain/test/stack_tests.py index 21e31d2..2ad690a 100644 --- a/snaps/domain/test/stack_tests.py +++ b/snaps/domain/test/stack_tests.py @@ -23,14 +23,23 @@ class StackDomainObjectTests(unittest.TestCase): """ def test_construction_positional(self): - stack = Stack('name', 'id') + stack = Stack( + 'name', 'id', 'stack_proj_id', 'fine', 'good') self.assertEqual('name', stack.name) self.assertEqual('id', stack.id) + self.assertEqual('stack_proj_id', stack.stack_project_id) + self.assertEqual('fine', stack.status) + self.assertEqual('good', stack.status_reason) def test_construction_named(self): - stack = Stack(stack_id='id', name='name') + stack = Stack( + stack_id='id', name='name', stack_project_id='stack_proj_id', + status='fine', status_reason='good') self.assertEqual('name', stack.name) self.assertEqual('id', stack.id) + self.assertEqual('stack_proj_id', stack.stack_project_id) + self.assertEqual('fine', stack.status) + self.assertEqual('good', stack.status_reason) class ResourceDomainObjectTests(unittest.TestCase): diff --git a/snaps/domain/test/vm_inst_tests.py b/snaps/domain/test/vm_inst_tests.py index ad7a9ce..c90837d 100644 --- a/snaps/domain/test/vm_inst_tests.py +++ b/snaps/domain/test/vm_inst_tests.py @@ -24,7 +24,7 @@ class VmInstDomainObjectTests(unittest.TestCase): def test_construction_positional(self): vm_inst = VmInst('name', 'id', '456', '123', list(), 'kp-name', - ['foo', 'bar'], ['123', '456']) + ['foo', 'bar'], ['123', '456'], 'host1', 'zone1') self.assertEqual('name', vm_inst.name) self.assertEqual('id', vm_inst.id) self.assertEqual('456', vm_inst.image_id) @@ -33,9 +33,12 @@ class VmInstDomainObjectTests(unittest.TestCase): self.assertEqual('kp-name', vm_inst.keypair_name) self.assertEqual(['foo', 'bar'], vm_inst.sec_grp_names) self.assertEqual(['123', '456'], vm_inst.volume_ids) + self.assertEqual('host1', vm_inst.compute_host) + self.assertEqual('zone1', vm_inst.availability_zone) def test_construction_named(self): vm_inst = VmInst( + availability_zone='zone1', compute_host='host1', volume_ids=['123', '456'], sec_grp_names=['foo', 'bar'], ports=list(), inst_id='id', name='name', flavor_id='123', image_id='456', keypair_name='kp-name') @@ -47,6 +50,8 @@ class VmInstDomainObjectTests(unittest.TestCase): self.assertEqual('kp-name', vm_inst.keypair_name) self.assertEqual(['foo', 'bar'], vm_inst.sec_grp_names) self.assertEqual(['123', '456'], vm_inst.volume_ids) + self.assertEqual('host1', vm_inst.compute_host) + self.assertEqual('zone1', vm_inst.availability_zone) class FloatingIpDomainObjectTests(unittest.TestCase): diff --git a/snaps/domain/test/volume_tests.py b/snaps/domain/test/volume_tests.py index 6feadc9..09401d3 100644 --- a/snaps/domain/test/volume_tests.py +++ b/snaps/domain/test/volume_tests.py @@ -24,10 +24,12 @@ class VolumeDomainObjectTests(unittest.TestCase): """ def test_construction_positional(self): - volume = Volume('name1', 'id1', 'desc_val1', 2, 'type_val1', - 'avail_zone1', False, [{'attached_at': 'foo'}]) + volume = Volume('name1', 'id1', 'proj_id1', 'desc_val1', 2, + 'type_val1', 'avail_zone1', False, + [{'attached_at': 'foo'}]) self.assertEqual('name1', volume.name) self.assertEqual('id1', volume.id) + self.assertEqual('proj_id1', volume.project_id) self.assertEqual('desc_val1', volume.description) self.assertEqual(2, volume.size) self.assertEqual('type_val1', volume.type) @@ -41,9 +43,10 @@ class VolumeDomainObjectTests(unittest.TestCase): volume = Volume(attachments=[{'attached_at': 'foo'}], multi_attach=True, availability_zone='avail_zone2', vol_type='type_val2', size=3, description='desc_val2', - volume_id='id2', name='name2') + volume_id='id2', name='name2', project_id='proj_id1') self.assertEqual('name2', volume.name) self.assertEqual('id2', volume.id) + self.assertEqual('proj_id1', volume.project_id) self.assertEqual('desc_val2', volume.description) self.assertEqual(3, volume.size) self.assertEqual('type_val2', volume.type) diff --git a/snaps/domain/vm_inst.py b/snaps/domain/vm_inst.py index c49b03e..f3b5381 100644 --- a/snaps/domain/vm_inst.py +++ b/snaps/domain/vm_inst.py @@ -20,7 +20,8 @@ class VmInst: are shared amongst cloud providers """ def __init__(self, name, inst_id, image_id, flavor_id, ports, - keypair_name, sec_grp_names, volume_ids): + keypair_name, sec_grp_names, volume_ids, compute_host, + availability_zone): """ Constructor :param name: the image's name @@ -32,6 +33,11 @@ class VmInst: :param keypair_name: the name of the associated keypair :param sec_grp_names: list of security group names :param volume_ids: list of attached volume IDs + :param compute_host: the name of the host on which this VM is running + When the user requesting this query is not part of + the 'admin' role, this value will be None + :param availability_zone: the name of the availability zone to which + this VM has been assigned """ self.name = name self.id = inst_id @@ -41,6 +47,8 @@ class VmInst: self.keypair_name = keypair_name self.sec_grp_names = sec_grp_names self.volume_ids = volume_ids + self.compute_host = compute_host + self.availability_zone = availability_zone def __eq__(self, other): return (self.name == other.name and @@ -49,7 +57,6 @@ class VmInst: self.flavor_id == other.flavor_id and self.ports == other.ports and self.keypair_name == other.keypair_name and - self.sec_grp_names == other.sec_grp_names and self.volume_ids == other.volume_ids) diff --git a/snaps/domain/volume.py b/snaps/domain/volume.py index 0042d71..0ab2a7d 100644 --- a/snaps/domain/volume.py +++ b/snaps/domain/volume.py @@ -19,12 +19,14 @@ class Volume: SNAPS domain object for Volumes. Should contain attributes that are shared amongst cloud providers """ - def __init__(self, name, volume_id, description, size, vol_type, - availability_zone, multi_attach, attachments=list()): + def __init__(self, name, volume_id, project_id, description, size, + vol_type, availability_zone, multi_attach, + attachments=list()): """ Constructor :param name: the volume's name :param volume_id: the volume's id + :param project_id: the volume's associated project id :param description: the volume's description :param size: the volume's size in GB :param vol_type: the volume's type @@ -35,6 +37,7 @@ class Volume: """ self.name = name self.id = volume_id + self.project_id = project_id self.description = description self.size = size self.type = vol_type @@ -43,7 +46,9 @@ class Volume: self.attachments = attachments def __eq__(self, other): - return (self.name == other.name and self.id == other.id + return (self.name == other.name + and self.id == other.id + and self.project_id == other.project_id and self.description == other.description and self.size == other.size and self.type == other.type diff --git a/snaps/file_utils.py b/snaps/file_utils.py index a421dd3..284ae15 100644 --- a/snaps/file_utils.py +++ b/snaps/file_utils.py @@ -12,6 +12,8 @@ # 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 ssl + import os import logging @@ -168,7 +170,8 @@ def __get_url_response(url): proxy_handler = urllib.ProxyHandler({}) opener = urllib.build_opener(proxy_handler) urllib.install_opener(opener) - return urllib.urlopen(url) + context = ssl._create_unverified_context() + return urllib.urlopen(url, context=context) def read_yaml(config_file_path): @@ -180,7 +183,7 @@ def read_yaml(config_file_path): logger.debug('Attempting to load configuration file - ' + config_file_path) config_file = None try: - with open(config_file_path) as config_file: + with open(config_file_path, 'r') as config_file: config = yaml.safe_load(config_file) logger.info('Loaded configuration') return config @@ -190,6 +193,20 @@ def read_yaml(config_file_path): config_file.close() +def persist_dict_to_yaml(the_dict, file_name): + """ + Creates a YAML file from a dict + :param the_dict: the dictionary to store + :param conf_dir: the directory used to store the config file + :return: the file object + """ + logger.info('Persisting %s to [%s]', the_dict, file_name) + file_path = os.path.expanduser(file_name) + yaml_from_dict = yaml.dump( + the_dict, default_flow_style=False, default_style='') + return save_string_to_file(yaml_from_dict, file_path) + + def read_os_env_file(os_env_filename): """ Reads the OS environment source file and returns a map of each key/value diff --git a/snaps/openstack/cluster_template.py b/snaps/openstack/cluster_template.py index c4ba76d..f6c7aa2 100644 --- a/snaps/openstack/cluster_template.py +++ b/snaps/openstack/cluster_template.py @@ -85,6 +85,8 @@ class OpenStackClusterTemplate(OpenStackMagnumObject): self.__cluster_template = None + super(self.__class__, self).clean() + def get_cluster_template(self): """ Returns the domain Volume object as it was populated when create() was diff --git a/snaps/openstack/create_flavor.py b/snaps/openstack/create_flavor.py index 65b9059..48b3e7c 100644 --- a/snaps/openstack/create_flavor.py +++ b/snaps/openstack/create_flavor.py @@ -87,6 +87,8 @@ class OpenStackFlavor(OpenStackComputeObject): self.__flavor = None + super(self.__class__, self).clean() + def get_flavor(self): """ Returns the OpenStack flavor object diff --git a/snaps/openstack/create_image.py b/snaps/openstack/create_image.py index a5520e3..1a8aa12 100644 --- a/snaps/openstack/create_image.py +++ b/snaps/openstack/create_image.py @@ -54,7 +54,10 @@ class OpenStackImage(OpenStackCloudObject): Loads the existing Image :return: The Image domain object or None """ - self.__glance = glance_utils.glance_client(self._os_creds) + super(self.__class__, self).initialize() + + self.__glance = glance_utils.glance_client( + self._os_creds, self._os_session) self.__image = glance_utils.get_image( self.__glance, image_settings=self.image_settings) @@ -145,6 +148,11 @@ class OpenStackImage(OpenStackCloudObject): self.__kernel_image = None self.__ramdisk_image = None + if self.__glance: + self.__glance.http_client.session.session.close() + + super(self.__class__, self).clean() + def get_image(self): """ Returns the domain Image object as it was populated when create() was diff --git a/snaps/openstack/create_instance.py b/snaps/openstack/create_instance.py index d91e360..b158a25 100644 --- a/snaps/openstack/create_instance.py +++ b/snaps/openstack/create_instance.py @@ -15,11 +15,12 @@ import logging import time -from novaclient.exceptions import NotFound, BadRequest +from novaclient.exceptions import NotFound from snaps.config.vm_inst import VmInstanceConfig, FloatingIpConfig from snaps.openstack.openstack_creator import OpenStackComputeObject -from snaps.openstack.utils import glance_utils, cinder_utils, settings_utils +from snaps.openstack.utils import ( + glance_utils, cinder_utils, settings_utils, keystone_utils) from snaps.openstack.utils import neutron_utils from snaps.openstack.utils import nova_utils from snaps.openstack.utils.nova_utils import RebootType @@ -72,7 +73,14 @@ class OpenStackVmInstance(OpenStackComputeObject): """ super(self.__class__, self).initialize() - self.__neutron = neutron_utils.neutron_client(self._os_creds) + self.__neutron = neutron_utils.neutron_client( + self._os_creds, self._os_session) + self.__keystone = keystone_utils.keystone_client( + self._os_creds, self._os_session) + self.__cinder = cinder_utils.cinder_client( + self._os_creds, self._os_session) + self.__glance = glance_utils.glance_client( + self._os_creds, self._os_session) self.__ports = self.__query_ports(self.instance_settings.port_settings) self.__lookup_existing_vm_by_name() @@ -88,7 +96,7 @@ class OpenStackVmInstance(OpenStackComputeObject): """ self.initialize() - if len(self.__ports) == 0: + if len(self.__ports) != len(self.instance_settings.port_settings): self.__ports = self.__create_ports( self.instance_settings.port_settings) if not self.__vm: @@ -103,7 +111,7 @@ class OpenStackVmInstance(OpenStackComputeObject): within the project """ server = nova_utils.get_server( - self._nova, self.__neutron, + self._nova, self.__neutron, self.__keystone, vm_inst_settings=self.instance_settings) if server: if server.name == self.instance_settings.name: @@ -112,8 +120,8 @@ class OpenStackVmInstance(OpenStackComputeObject): 'Found existing machine with name - %s', self.instance_settings.name) - fips = neutron_utils.get_floating_ips(self.__neutron, - self.__ports) + fips = neutron_utils.get_port_floating_ips( + self.__neutron, self.__ports) for port_id, fip in fips: settings = self.instance_settings.floating_ip_settings for fip_setting in settings: @@ -132,10 +140,10 @@ class OpenStackVmInstance(OpenStackComputeObject): active, error, or timeout waiting. Floating IPs will be assigned after active when block=True """ - glance = glance_utils.glance_client(self._os_creds) self.__vm = nova_utils.create_server( - self._nova, self.__neutron, glance, self.instance_settings, - self.image_settings, self.keypair_settings) + self._nova, self.__keystone, self.__neutron, self.__glance, + self.instance_settings, self.image_settings, + self._os_creds.project_name, self.keypair_settings) logger.info('Created instance with name - %s', self.instance_settings.name) @@ -159,20 +167,20 @@ class OpenStackVmInstance(OpenStackComputeObject): if self.instance_settings.volume_names: for volume_name in self.instance_settings.volume_names: - cinder = cinder_utils.cinder_client(self._os_creds) volume = cinder_utils.get_volume( - cinder, volume_name=volume_name) + self.__cinder, self.__keystone, volume_name=volume_name, + project_name=self._os_creds.project_name) if volume and self.vm_active(block=True): - timeout = 30 vm = nova_utils.attach_volume( - self._nova, self.__neutron, self.__vm, volume, timeout) + self._nova, self.__neutron, self.__keystone, self.__vm, + volume, self._os_creds.project_name) if vm: self.__vm = vm else: - logger.warn('Volume [%s] not attached within timeout ' - 'of [%s]', volume.name, timeout) + logger.warn( + 'Volume [%s] attachment timeout ', volume.name) else: logger.warn('Unable to attach volume named [%s]', volume_name) @@ -213,18 +221,15 @@ class OpenStackVmInstance(OpenStackComputeObject): # gateway ext_gateway = self.__ext_gateway_by_router( floating_ip_setting.router_name) - if ext_gateway: - subnet = neutron_utils.get_subnet( - self.__neutron, - subnet_name=floating_ip_setting.subnet_name) + if ext_gateway and self.vm_active(block=True): floating_ip = neutron_utils.create_floating_ip( - self.__neutron, ext_gateway) + self.__neutron, self.__keystone, ext_gateway, port.id) self.__floating_ip_dict[floating_ip_setting.name] = floating_ip logger.info( 'Created floating IP %s via router - %s', floating_ip.ip, floating_ip_setting.router_name) - self.__add_floating_ip(floating_ip, port, subnet) + return floating_ip else: raise VmInstanceCreationError( @@ -239,7 +244,8 @@ class OpenStackVmInstance(OpenStackComputeObject): :return: the external network name or None """ router = neutron_utils.get_router( - self.__neutron, router_name=router_name) + self.__neutron, self.__keystone, router_name=router_name, + project_name=self._os_creds.project_name) if router and router.external_network_id: network = neutron_utils.get_network_by_id( self.__neutron, router.external_network_id) @@ -269,12 +275,12 @@ class OpenStackVmInstance(OpenStackComputeObject): if self.__vm: # Detach Volume for volume_rec in self.__vm.volume_ids: - cinder = cinder_utils.cinder_client(self._os_creds) volume = cinder_utils.get_volume_by_id( - cinder, volume_rec['id']) + self.__cinder, volume_rec['id']) if volume: vm = nova_utils.detach_volume( - self._nova, self.__neutron, self.__vm, volume, 30) + self._nova, self.__neutron, self.__keystone, self.__vm, + volume, self._os_creds.project_name) if vm: self.__vm = vm else: @@ -307,6 +313,8 @@ class OpenStackVmInstance(OpenStackComputeObject): 'VM not deleted within the timeout period of %s ' 'seconds', self.instance_settings.vm_delete_timeout) + super(self.__class__, self).clean() + def __query_ports(self, port_settings): """ Returns the previously configured ports or an empty list if none @@ -319,7 +327,8 @@ class OpenStackVmInstance(OpenStackComputeObject): for port_setting in port_settings: port = neutron_utils.get_port( - self.__neutron, port_settings=port_setting) + self.__neutron, self.__keystone, port_settings=port_setting, + project_name=self._os_creds.project_name) if port: ports.append((port_setting.name, port)) @@ -337,63 +346,16 @@ class OpenStackVmInstance(OpenStackComputeObject): for port_setting in port_settings: port = neutron_utils.get_port( - self.__neutron, port_settings=port_setting) + self.__neutron, self.__keystone, port_settings=port_setting, + project_name=self._os_creds.project_name) if not port: port = neutron_utils.create_port( self.__neutron, self._os_creds, port_setting) - if port: - ports.append((port_setting.name, port)) + if port: + ports.append((port_setting.name, port)) return ports - def __add_floating_ip(self, floating_ip, port, subnet, timeout=30, - poll_interval=POLL_INTERVAL): - """ - Returns True when active else False - TODO - Make timeout and poll_interval configurable... - """ - ip = None - - if subnet: - # Take IP of subnet if there is one configured on which to place - # the floating IP - for fixed_ip in port.ips: - if fixed_ip['subnet_id'] == subnet.id: - ip = fixed_ip['ip_address'] - break - else: - # Simply take the first - ip = port.ips[0]['ip_address'] - - if ip: - count = timeout / poll_interval - while count > 0: - logger.debug('Attempting to add floating IP to instance') - try: - nova_utils.add_floating_ip_to_server( - self._nova, self.__vm, floating_ip, ip) - logger.info( - 'Added floating IP %s to port IP %s on instance %s', - floating_ip.ip, ip, self.instance_settings.name) - return - except BadRequest as bre: - logger.error('Cannot add floating IP [%s]', bre) - raise - except Exception as e: - logger.debug( - 'Retry adding floating IP to instance. Last attempt ' - 'failed with - %s', e) - time.sleep(poll_interval) - count -= 1 - pass - else: - raise VmInstanceCreationError( - 'Unable find IP address on which to place the floating IP') - - logger.error('Timeout attempting to add the floating IP to instance.') - raise VmInstanceCreationError( - 'Timeout while attempting add floating IP to instance') - def get_os_creds(self): """ Returns the OpenStack credentials used to create these objects @@ -407,7 +369,8 @@ class OpenStackVmInstance(OpenStackComputeObject): :return: Server object """ return nova_utils.get_server_object_by_id( - self._nova, self.__neutron, self.__vm.id) + self._nova, self.__neutron, self.__keystone, self.__vm.id, + self._os_creds.project_name) def get_console_output(self): """ @@ -428,8 +391,10 @@ class OpenStackVmInstance(OpenStackComputeObject): port = self.get_port_by_name(port_name) if port: if subnet_name: + network = neutron_utils.get_network_by_id( + self.__neutron, port.network_id) subnet = neutron_utils.get_subnet( - self.__neutron, subnet_name=subnet_name) + self.__neutron, network, subnet_name=subnet_name) if not subnet: logger.warning('Cannot retrieve port IP as subnet could ' 'not be located with name - %s', @@ -474,6 +439,10 @@ class OpenStackVmInstance(OpenStackComputeObject): Returns a dictionary of a VMs info as returned by OpenStack :return: a dict() """ + from warnings import warn + warn('Do not use the returned dict() structure', + DeprecationWarning) + return nova_utils.get_server_info(self._nova, self.__vm) def __get_first_provisioning_floating_ip(self): @@ -505,12 +474,16 @@ class OpenStackVmInstance(OpenStackComputeObject): playbook :param fip_name: the name of the floating IP to use for applying the playbook (default - will take the first) - :return: the return value from ansible """ - return ansible_utils.apply_playbook( + from warnings import warn + warn('This method will be removed in a subsequent release', + DeprecationWarning) + + ansible_utils.apply_playbook( pb_file_loc, [self.get_floating_ip(fip_name=fip_name).ip], - self.get_image_user(), self.keypair_settings.private_filepath, - variables, self._os_creds.proxy_settings) + self.get_image_user(), + ssh_priv_key_file_path=self.keypair_settings.private_filepath, + variables=variables, proxy_setting=self._os_creds.proxy_settings) def get_image_user(self): """ @@ -554,7 +527,8 @@ class OpenStackVmInstance(OpenStackComputeObject): STATUS_ACTIVE, block, self.instance_settings.vm_boot_timeout, poll_interval): self.__vm = nova_utils.get_server_object_by_id( - self._nova, self.__neutron, self.__vm.id) + self._nova, self.__neutron, self.__keystone, self.__vm.id, + self._os_creds.project_name) return True return False @@ -619,18 +593,27 @@ class OpenStackVmInstance(OpenStackComputeObject): status) return status == expected_status_code - def vm_ssh_active(self, block=False, poll_interval=POLL_INTERVAL): + def vm_ssh_active(self, user_override=None, password=None, block=False, + timeout=None, poll_interval=POLL_INTERVAL): """ Returns true when the VM can be accessed via SSH + :param user_override: overrides the user with which to create the + connection + :param password: overrides the use of a password instead of a private + key with which to create the connection :param block: When true, thread will block until active or timeout value in seconds has been exceeded (False) + :param timeout: the number of seconds to retry obtaining the connection + and overrides the ssh_connect_timeout member of the + self.instance_settings object :param poll_interval: The polling interval :return: T/F """ # sleep and wait for VM status change logger.info('Checking if VM is active') - timeout = self.instance_settings.ssh_connect_timeout + if not timeout: + timeout = self.instance_settings.ssh_connect_timeout if self.vm_active(block=True): if block: @@ -639,7 +622,8 @@ class OpenStackVmInstance(OpenStackComputeObject): start = time.time() - timeout while timeout > time.time() - start: - status = self.__ssh_active() + status = self.__ssh_active( + user_override=user_override, password=password) if status: logger.info('SSH is active for VM instance') return True @@ -653,13 +637,14 @@ class OpenStackVmInstance(OpenStackComputeObject): logger.error('Timeout attempting to connect with VM via SSH') return False - def __ssh_active(self): + def __ssh_active(self, user_override=None, password=None): """ Returns True when can create a SSH session else False :return: T/F """ if len(self.__floating_ip_dict) > 0: - ssh = self.ssh_client() + ssh = self.ssh_client( + user_override=user_override, password=password) if ssh: ssh.close() return True @@ -727,19 +712,32 @@ class OpenStackVmInstance(OpenStackComputeObject): else: return self.__get_first_provisioning_floating_ip() - def ssh_client(self, fip_name=None): + def ssh_client(self, fip_name=None, user_override=None, password=None): """ Returns an SSH client using the name or the first known floating IP if exists, else None :param fip_name: the name of the floating IP to return + :param user_override: the username to use instead of the default + :param password: the password to use instead of the private key :return: the SSH client or None """ fip = self.get_floating_ip(fip_name) + + ansible_user = self.get_image_user() + if user_override: + ansible_user = user_override + + if password: + private_key = None + else: + private_key = self.keypair_settings.private_filepath + if fip: return ansible_utils.ssh_client( self.__get_first_provisioning_floating_ip().ip, - self.get_image_user(), - self.keypair_settings.private_filepath, + ansible_user, + private_key_filepath=private_key, + password=password, proxy_settings=self._os_creds.proxy_settings) else: FloatingIPAllocationError( @@ -797,24 +795,32 @@ class OpenStackVmInstance(OpenStackComputeObject): self._nova, self.__vm, reboot_type=reboot_type) -def generate_creator(os_creds, vm_inst, image_config, keypair_config=None): +def generate_creator(os_creds, vm_inst, image_config, project_name, + keypair_config=None): """ Initializes an OpenStackVmInstance object :param os_creds: the OpenStack credentials :param vm_inst: the SNAPS-OO VmInst domain object :param image_config: the associated ImageConfig object + :param project_name: the associated project ID :param keypair_config: the associated KeypairConfig object (optional) :return: an initialized OpenStackVmInstance object """ - nova = nova_utils.nova_client(os_creds) - neutron = neutron_utils.neutron_client(os_creds) - derived_inst_config = settings_utils.create_vm_inst_config( - nova, neutron, vm_inst) - - derived_inst_creator = OpenStackVmInstance( - os_creds, derived_inst_config, image_config, keypair_config) - derived_inst_creator.initialize() - return derived_inst_creator + session = keystone_utils.keystone_session(os_creds) + nova = nova_utils.nova_client(os_creds, session) + keystone = keystone_utils.keystone_client(os_creds, session) + neutron = neutron_utils.neutron_client(os_creds, session) + + try: + derived_inst_config = settings_utils.create_vm_inst_config( + nova, keystone, neutron, vm_inst, project_name) + + derived_inst_creator = OpenStackVmInstance( + os_creds, derived_inst_config, image_config, keypair_config) + derived_inst_creator.initialize() + return derived_inst_creator + finally: + keystone_utils.close_session(session) class VmInstanceSettings(VmInstanceConfig): diff --git a/snaps/openstack/create_keypairs.py b/snaps/openstack/create_keypairs.py index a181a7b..b037c3f 100644 --- a/snaps/openstack/create_keypairs.py +++ b/snaps/openstack/create_keypairs.py @@ -133,6 +133,8 @@ class OpenStackKeypair(OpenStackComputeObject): os.remove(expanded_path) logger.info('Deleted private key file [%s]', expanded_path) + super(self.__class__, self).clean() + def get_keypair(self): """ Returns the OpenStack keypair object diff --git a/snaps/openstack/create_network.py b/snaps/openstack/create_network.py index c9c58e8..13c9667 100644 --- a/snaps/openstack/create_network.py +++ b/snaps/openstack/create_network.py @@ -15,7 +15,7 @@ import logging import enum -from neutronclient.common.exceptions import NetworkNotFoundClient +from neutronclient.common.exceptions import NetworkNotFoundClient, Unauthorized from snaps.config.network import NetworkConfig, SubnetConfig, PortConfig from snaps.openstack.openstack_creator import OpenStackNetworkObject @@ -51,9 +51,14 @@ class OpenStackNetwork(OpenStackNetworkObject): """ super(self.__class__, self).initialize() - self.__network = neutron_utils.get_network( - self._neutron, network_settings=self.network_settings, - project_id=self.network_settings.get_project_id(self._os_creds)) + try: + self.__network = neutron_utils.get_network( + self._neutron, self._keystone, + network_settings=self.network_settings, + project_name=self._os_creds.project_name) + except Unauthorized as e: + logger.warn('Unable to lookup network with name %s - %s', + self.network_settings.name, e) return self.__network @@ -77,12 +82,13 @@ class OpenStackNetwork(OpenStackNetworkObject): """ Removes and deletes all items created in reverse order. """ - if self.__network: - try: - neutron_utils.delete_network(self._neutron, self.__network) - except NetworkNotFoundClient: - pass - self.__network = None + try: + neutron_utils.delete_network(self._neutron, self.__network) + except NetworkNotFoundClient: + pass + self.__network = None + + super(self.__class__, self).clean() def get_network(self): """ diff --git a/snaps/openstack/create_project.py b/snaps/openstack/create_project.py index 0349890..ed7e9cd 100644 --- a/snaps/openstack/create_project.py +++ b/snaps/openstack/create_project.py @@ -74,6 +74,29 @@ class OpenStackProject(OpenStackIdentityObject): logger.warn('Unable to associate user %s due to %s', user.name, e) + if self.project_settings.quotas: + quota_dict = self.project_settings.quotas + nova = nova_utils.nova_client(self._os_creds, self._os_session) + quotas = nova_utils.get_compute_quotas(nova, self.__project.id) + if quotas: + if 'cores' in quota_dict: + quotas.cores = quota_dict['cores'] + if 'instances' in quota_dict: + quotas.instances = quota_dict['instances'] + if 'injected_files' in quota_dict: + quotas.injected_files = quota_dict['injected_files'] + if 'injected_file_content_bytes' in quota_dict: + quotas.injected_file_content_bytes = \ + quota_dict['injected_file_content_bytes'] + if 'ram' in quota_dict: + quotas.ram = quota_dict['ram'] + if 'fixed_ips' in quota_dict: + quotas.fixed_ips = quota_dict['fixed_ips'] + if 'key_pairs' in quota_dict: + quotas.key_pairs = quota_dict['key_pairs'] + + nova_utils.update_quotas(nova, self.__project.id, quotas) + return self.__project def clean(self): @@ -83,16 +106,20 @@ class OpenStackProject(OpenStackIdentityObject): """ if self.__project: # Delete security group 'default' if exists - neutron = neutron_utils.neutron_client(self._os_creds) - default_sec_grp = neutron_utils.get_security_group( - neutron, sec_grp_name='default', - project_id=self.__project.id) - if default_sec_grp: - try: - neutron_utils.delete_security_group( - neutron, default_sec_grp) - except: - pass + neutron = neutron_utils.neutron_client( + self._os_creds, self._os_session) + try: + default_sec_grp = neutron_utils.get_security_group( + neutron, self._keystone, sec_grp_name='default', + project_name=self.__project.name) + if default_sec_grp: + try: + neutron_utils.delete_security_group( + neutron, default_sec_grp) + except: + pass + finally: + neutron.httpclient.session.session.close() # Delete Project try: @@ -114,6 +141,8 @@ class OpenStackProject(OpenStackIdentityObject): if role: keystone_utils.delete_role(self._keystone, role) + super(self.__class__, self).clean() + def get_project(self): """ Returns the OpenStack project object populated on create() @@ -142,32 +171,48 @@ class OpenStackProject(OpenStackIdentityObject): Returns the compute quotas as an instance of the ComputeQuotas class :return: """ - nova = nova_utils.nova_client(self._os_creds) - return nova_utils.get_compute_quotas(nova, self.__project.id) + nova = nova_utils.nova_client(self._os_creds, self._os_session) + + try: + return nova_utils.get_compute_quotas(nova, self.__project.id) + finally: + nova.client.session.session.close() def get_network_quotas(self): """ Returns the network quotas as an instance of the NetworkQuotas class :return: """ - neutron = neutron_utils.neutron_client(self._os_creds) - return neutron_utils.get_network_quotas(neutron, self.__project.id) + neutron = neutron_utils.neutron_client( + self._os_creds, self._os_session) + try: + return neutron_utils.get_network_quotas(neutron, self.__project.id) + finally: + neutron.httpclient.session.session.close() def update_compute_quotas(self, compute_quotas): """ Updates the compute quotas for this project :param compute_quotas: a ComputeQuotas object. """ - nova = nova_utils.nova_client(self._os_creds) - nova_utils.update_quotas(nova, self.__project.id, compute_quotas) + nova = nova_utils.nova_client(self._os_creds, self._os_session) + try: + nova_utils.update_quotas(nova, self.__project.id, compute_quotas) + finally: + nova.client.session.session.close() def update_network_quotas(self, network_quotas): """ Updates the network quotas for this project :param network_quotas: a NetworkQuotas object. """ - neutron = neutron_utils.neutron_client(self._os_creds) - neutron_utils.update_quotas(neutron, self.__project.id, network_quotas) + neutron = neutron_utils.neutron_client( + self._os_creds, self._os_session) + try: + neutron_utils.update_quotas( + neutron, self.__project.id, network_quotas) + finally: + neutron.httpclient.session.session.close() class ProjectSettings(ProjectConfig): @@ -181,4 +226,4 @@ class ProjectSettings(ProjectConfig): from warnings import warn warn('Use snaps.config.project.ProjectConfig instead', DeprecationWarning) - super(self.__class__, self).__init__(**kwargs)
\ No newline at end of file + super(self.__class__, self).__init__(**kwargs) diff --git a/snaps/openstack/create_qos.py b/snaps/openstack/create_qos.py index 44e35a3..7764a57 100644 --- a/snaps/openstack/create_qos.py +++ b/snaps/openstack/create_qos.py @@ -90,6 +90,8 @@ class OpenStackQoS(OpenStackVolumeObject): self.__qos = None + super(self.__class__, self).clean() + def get_qos(self): """ Returns the domain QoS object as it was populated when create() was diff --git a/snaps/openstack/create_router.py b/snaps/openstack/create_router.py index 4f95c3b..3269bbd 100644 --- a/snaps/openstack/create_router.py +++ b/snaps/openstack/create_router.py @@ -14,7 +14,7 @@ # limitations under the License. import logging -from neutronclient.common.exceptions import NotFound +from neutronclient.common.exceptions import NotFound, Unauthorized from snaps.config.router import RouterConfig from snaps.openstack.openstack_creator import OpenStackNetworkObject @@ -61,22 +61,28 @@ class OpenStackRouter(OpenStackNetworkObject): """ super(self.__class__, self).initialize() - self.__router = neutron_utils.get_router( - self._neutron, router_settings=self.router_settings) + try: + self.__router = neutron_utils.get_router( + self._neutron, self._keystone, + router_settings=self.router_settings, + project_name=self._os_creds.project_name) + except Unauthorized as e: + logger.warn('Unable to lookup router with name %s - %s', + self.router_settings.name, e) if self.__router: - for internal_subnet_name in self.router_settings.internal_subnets: - internal_subnet = neutron_utils.get_subnet( - self._neutron, subnet_name=internal_subnet_name) + for sub_config in self.router_settings.internal_subnets: + internal_subnet = self.__get_internal_subnet(sub_config) if internal_subnet: self.__internal_subnets.append(internal_subnet) else: raise RouterCreationError( - 'Subnet not found with name ' + internal_subnet_name) + 'Subnet not found with name ' + internal_subnet.name) for port_setting in self.router_settings.port_settings: port = neutron_utils.get_port( - self._neutron, port_settings=port_setting) + self._neutron, self._keystone, port_settings=port_setting, + project_name=self._os_creds.project_name) if port: self.__ports.append(port) @@ -93,9 +99,8 @@ class OpenStackRouter(OpenStackNetworkObject): self.__router = neutron_utils.create_router( self._neutron, self._os_creds, self.router_settings) - for internal_subnet_name in self.router_settings.internal_subnets: - internal_subnet = neutron_utils.get_subnet( - self._neutron, subnet_name=internal_subnet_name) + for sub_config in self.router_settings.internal_subnets: + internal_subnet = self.__get_internal_subnet(sub_config) if internal_subnet: self.__internal_subnets.append(internal_subnet) if internal_subnet: @@ -106,11 +111,12 @@ class OpenStackRouter(OpenStackNetworkObject): self.__internal_router_interface = router_intf else: raise RouterCreationError( - 'Subnet not found with name ' + internal_subnet_name) + 'Subnet not found with name {}'.format(sub_config)) for port_setting in self.router_settings.port_settings: port = neutron_utils.get_port( - self._neutron, port_settings=port_setting) + self._neutron, self._keystone, port_settings=port_setting, + project_name=self._os_creds.project_name) logger.info( 'Retrieved port %s for router - %s', port_setting.name, self.router_settings.name) @@ -126,9 +132,8 @@ class OpenStackRouter(OpenStackNetworkObject): port_setting.name, self.router_settings.name) self.__ports.append(port) - neutron_utils.add_interface_router(self._neutron, - self.__router, - port=port) + neutron_utils.add_interface_router( + self._neutron, self.__router, port=port) else: raise RouterCreationError( 'Error creating port with name - ' @@ -138,6 +143,28 @@ class OpenStackRouter(OpenStackNetworkObject): self._neutron, self.__router.id) return self.__router + def __get_internal_subnet(self, sub_config): + """ + returns the Subnet domain object from the subnet configurator + :param sub_config: + :return: + """ + if isinstance(sub_config, dict): + sub_dict = sub_config['subnet'] + network = neutron_utils.get_network( + self._neutron, self._keystone, + network_name=sub_dict['network_name'], + project_name=sub_dict['project_name']) + if network: + return neutron_utils.get_subnet( + self._neutron, network, + subnet_name=sub_dict['subnet_name']) + else: + return neutron_utils.get_subnet_by_name( + self._neutron, self._keystone, + subnet_name=sub_config, + project_name=self._os_creds.project_name) + def clean(self): """ Removes and deletes all items created in reverse order. @@ -173,6 +200,8 @@ class OpenStackRouter(OpenStackNetworkObject): pass self.__router = None + super(self.__class__, self).clean() + def get_router(self): """ Returns the OpenStack router object diff --git a/snaps/openstack/create_security_group.py b/snaps/openstack/create_security_group.py index 7a20fe1..490f419 100644 --- a/snaps/openstack/create_security_group.py +++ b/snaps/openstack/create_security_group.py @@ -20,7 +20,6 @@ from neutronclient.common.exceptions import NotFound, Conflict from snaps.config.security_group import ( SecurityGroupConfig, SecurityGroupRuleConfig) from snaps.openstack.openstack_creator import OpenStackNetworkObject -from snaps.openstack.utils import keystone_utils from snaps.openstack.utils import neutron_utils __author__ = 'spisarski' @@ -57,7 +56,9 @@ class OpenStackSecurityGroup(OpenStackNetworkObject): super(self.__class__, self).initialize() self.__security_group = neutron_utils.get_security_group( - self._neutron, sec_grp_settings=self.sec_grp_settings) + self._neutron, self._keystone, + sec_grp_settings=self.sec_grp_settings, + project_name=self._os_creds.project_name) if self.__security_group: # Populate rules existing_rules = neutron_utils.get_rules_by_security_group( @@ -84,10 +85,8 @@ class OpenStackSecurityGroup(OpenStackNetworkObject): logger.info( 'Creating security group %s...' % self.sec_grp_settings.name) - keystone = keystone_utils.keystone_client(self._os_creds) self.__security_group = neutron_utils.create_security_group( - self._neutron, keystone, - self.sec_grp_settings) + self._neutron, self._keystone, self.sec_grp_settings) # Get the rules added for free auto_rules = neutron_utils.get_rules_by_security_group( @@ -103,15 +102,16 @@ class OpenStackSecurityGroup(OpenStackNetworkObject): for sec_grp_rule_setting in self.sec_grp_settings.rule_settings: try: custom_rule = neutron_utils.create_security_group_rule( - self._neutron, sec_grp_rule_setting) + self._neutron, self._keystone, sec_grp_rule_setting, + self._os_creds.project_name) self.__rules[sec_grp_rule_setting] = custom_rule except Conflict as e: logger.warn('Unable to create rule due to conflict - %s', e) # Refresh security group object to reflect the new rules added - self.__security_group = neutron_utils.get_security_group( - self._neutron, sec_grp_settings=self.sec_grp_settings) + self.__security_group = neutron_utils.get_security_group_by_id( + self._neutron, self.__security_group.id) return self.__security_group @@ -159,6 +159,8 @@ class OpenStackSecurityGroup(OpenStackNetworkObject): self.__security_group = None + super(self.__class__, self).clean() + def get_security_group(self): """ Returns the OpenStack security group object @@ -179,8 +181,9 @@ class OpenStackSecurityGroup(OpenStackNetworkObject): :param rule_setting: the rule configuration """ rule_setting.sec_grp_name = self.sec_grp_settings.name - new_rule = neutron_utils.create_security_group_rule(self._neutron, - rule_setting) + new_rule = neutron_utils.create_security_group_rule( + self._neutron, self._keystone, rule_setting, + self._os_creds.project_name) self.__rules[rule_setting] = new_rule self.sec_grp_settings.rule_settings.append(rule_setting) diff --git a/snaps/openstack/create_stack.py b/snaps/openstack/create_stack.py index d4a6b10..71e5d0a 100644 --- a/snaps/openstack/create_stack.py +++ b/snaps/openstack/create_stack.py @@ -23,16 +23,17 @@ from snaps.config.stack import StackConfig from snaps.openstack.create_flavor import OpenStackFlavor from snaps.openstack.create_instance import OpenStackVmInstance from snaps.openstack.create_keypairs import OpenStackKeypair -from snaps.openstack.create_security_group import OpenStackSecurityGroup +from snaps.openstack.create_network import OpenStackNetwork from snaps.openstack.create_router import OpenStackRouter +from snaps.openstack.create_security_group import OpenStackSecurityGroup from snaps.openstack.create_volume import OpenStackVolume from snaps.openstack.create_volume_type import OpenStackVolumeType from snaps.openstack.openstack_creator import OpenStackCloudObject from snaps.openstack.utils import ( nova_utils, settings_utils, glance_utils, cinder_utils) - -from snaps.openstack.create_network import OpenStackNetwork from snaps.openstack.utils import heat_utils, neutron_utils +from snaps.thread_utils import worker_pool + __author__ = 'spisarski' @@ -73,19 +74,35 @@ class OpenStackHeatStack(OpenStackCloudObject, object): self.__stack = None self.__heat_cli = None + self.__neutron = None + self.__nova = None + self.__glance = None + self.__cinder = None + def initialize(self): """ Loads the existing heat stack :return: The Stack domain object or None """ - self.__heat_cli = heat_utils.heat_client(self._os_creds) + super(self.__class__, self).initialize() + + self.__neutron = neutron_utils.neutron_client( + self._os_creds, self._os_session) + self.__nova = nova_utils.nova_client(self._os_creds, self._os_session) + self.__glance = glance_utils.glance_client( + self._os_creds, self._os_session) + self.__cinder = cinder_utils.cinder_client( + self._os_creds, self._os_session) + + self.__heat_cli = heat_utils.heat_client( + self._os_creds, self._os_session) self.__stack = heat_utils.get_stack( self.__heat_cli, stack_settings=self.stack_settings) if self.__stack: logger.info('Found stack with name - ' + self.stack_settings.name) return self.__stack - def create(self): + def create(self, block=False): """ Creates the heat stack in OpenStack if it does not already exist and returns the domain Stack object @@ -101,7 +118,7 @@ class OpenStackHeatStack(OpenStackCloudObject, object): self.stack_settings) logger.info( 'Created stack with name - %s', self.stack_settings.name) - if self.__stack and self.stack_complete(block=True): + if self.__stack and self.stack_complete(block=block): logger.info('Stack is now active with name - %s', self.stack_settings.name) return self.__stack @@ -111,6 +128,28 @@ class OpenStackHeatStack(OpenStackCloudObject, object): logger.error('ERROR: STACK CREATION FAILED: %s', status) raise StackCreationError('Failure while creating stack') + def update(self, env_vals, block=False): + """ + Updates the heat stack in OpenStack + :param: env_vals - the values to update + :return: The Stack domain object or None + """ + if self.__stack: + logger.info('Updating stack - %s', self.__stack.name) + heat_utils.update_stack(self.__heat_cli, self.__stack, env_vals) + if self.stack_updated(block=block): + logger.info('Stack %s is now updated with params: %s', + self.__stack.name, env_vals) + self.stack_settings.env_values = env_vals + self.__stack = heat_utils.get_stack_by_id( + self.__heat_cli, self.__stack.id) + return self.__stack + else: + status = heat_utils.get_stack_status_reason(self.__heat_cli, + self.__stack.id) + logger.error('ERROR: STACK UPDATE FAILED: %s', status) + raise StackUpdateError('Failure while updating stack') + def clean(self): """ Cleanse environment of all artifacts @@ -152,13 +191,21 @@ class OpenStackHeatStack(OpenStackCloudObject, object): self.__stack = None + self.__neutron.httpclient.session.session.close() + self.__nova.client.session.session.close() + self.__glance.http_client.session.session.close() + self.__cinder.client.session.session.close() + + super(self.__class__, self).clean() + def get_stack(self): """ Returns the domain Stack object as it was populated when create() was called :return: the object """ - return self.__stack + if self.__stack: + return heat_utils.get_stack_by_id(self.__heat_cli, self.__stack.id) def get_outputs(self): """ @@ -174,7 +221,9 @@ class OpenStackHeatStack(OpenStackCloudObject, object): object :return: """ - return heat_utils.get_stack_status(self.__heat_cli, self.__stack.id) + stack = self.get_stack() + if stack: + return stack.status def stack_complete(self, block=False, timeout=None, poll_interval=snaps.config.stack.POLL_INTERVAL): @@ -193,6 +242,22 @@ class OpenStackHeatStack(OpenStackCloudObject, object): snaps.config.stack.STATUS_CREATE_COMPLETE, block, timeout, poll_interval, snaps.config.stack.STATUS_CREATE_FAILED) + def stack_updated(self, block=False, + timeout=snaps.config.stack.STACK_UPDATE_TIMEOUT, + poll_interval=snaps.config.stack.POLL_INTERVAL): + """ + Returns true when the stack status returns the value of + expected_status_code + :param block: When true, thread will block until active or timeout + value in seconds has been exceeded (False) + :param timeout: The timeout value + :param poll_interval: The polling interval in seconds + :return: T/F + """ + return self._stack_status_check( + snaps.config.stack.STATUS_UPDATE_COMPLETE, block, timeout, + poll_interval, snaps.config.stack.STATUS_UPDATE_FAILED) + def stack_deleted(self, block=False, timeout=snaps.config.stack.STACK_DELETE_TIMEOUT, poll_interval=snaps.config.stack.POLL_INTERVAL): @@ -216,15 +281,13 @@ class OpenStackHeatStack(OpenStackCloudObject, object): :return: list() of OpenStackNetwork objects """ - neutron = neutron_utils.neutron_client(self._os_creds) - out = list() stack_networks = heat_utils.get_stack_networks( - self.__heat_cli, neutron, self.__stack) + self.__heat_cli, self.__neutron, self.__stack) for stack_network in stack_networks: net_settings = settings_utils.create_network_config( - neutron, stack_network) + self.__neutron, stack_network) net_creator = OpenStackNetwork(self._os_creds, net_settings) out.append(net_creator) net_creator.initialize() @@ -238,15 +301,13 @@ class OpenStackHeatStack(OpenStackCloudObject, object): :return: list() of OpenStackNetwork objects """ - neutron = neutron_utils.neutron_client(self._os_creds) - out = list() stack_security_groups = heat_utils.get_stack_security_groups( - self.__heat_cli, neutron, self.__stack) + self.__heat_cli, self.__neutron, self.__stack) for stack_security_group in stack_security_groups: settings = settings_utils.create_security_group_config( - neutron, stack_security_group) + self.__neutron, stack_security_group) creator = OpenStackSecurityGroup(self._os_creds, settings) out.append(creator) creator.initialize() @@ -260,21 +321,36 @@ class OpenStackHeatStack(OpenStackCloudObject, object): :return: list() of OpenStackRouter objects """ - neutron = neutron_utils.neutron_client(self._os_creds) - out = list() stack_routers = heat_utils.get_stack_routers( - self.__heat_cli, neutron, self.__stack) + self.__heat_cli, self.__neutron, self.__stack) for routers in stack_routers: settings = settings_utils.create_router_config( - neutron, routers) + self.__neutron, routers) creator = OpenStackRouter(self._os_creds, settings) out.append(creator) creator.initialize() return out + def __create_vm_inst(self, heat_keypair_option, stack_server): + + vm_inst_settings = settings_utils.create_vm_inst_config( + self.__nova, self._keystone, self.__neutron, stack_server, + self._os_creds.project_name) + image_settings = settings_utils.determine_image_config( + self.__glance, stack_server, self.image_settings) + keypair_settings = settings_utils.determine_keypair_config( + self.__heat_cli, self.__stack, stack_server, + keypair_settings=self.keypair_settings, + priv_key_key=heat_keypair_option) + vm_inst_creator = OpenStackVmInstance( + self._os_creds, vm_inst_settings, image_settings, + keypair_settings) + vm_inst_creator.initialize() + return vm_inst_creator + def get_vm_inst_creators(self, heat_keypair_option=None): """ Returns a list of VM Instance creator objects as configured by the heat @@ -283,28 +359,21 @@ class OpenStackHeatStack(OpenStackCloudObject, object): """ out = list() - nova = nova_utils.nova_client(self._os_creds) - neutron = neutron_utils.neutron_client(self._os_creds) stack_servers = heat_utils.get_stack_servers( - self.__heat_cli, nova, neutron, self.__stack) - - glance = glance_utils.glance_client(self._os_creds) + self.__heat_cli, self.__nova, self.__neutron, self._keystone, + self.__stack, self._os_creds.project_name) + workers = [] for stack_server in stack_servers: - vm_inst_settings = settings_utils.create_vm_inst_config( - nova, neutron, stack_server) - image_settings = settings_utils.determine_image_config( - glance, stack_server, self.image_settings) - keypair_settings = settings_utils.determine_keypair_config( - self.__heat_cli, self.__stack, stack_server, - keypair_settings=self.keypair_settings, - priv_key_key=heat_keypair_option) - vm_inst_creator = OpenStackVmInstance( - self._os_creds, vm_inst_settings, image_settings, - keypair_settings) - out.append(vm_inst_creator) - vm_inst_creator.initialize() + worker = worker_pool().apply_async( + self.__create_vm_inst, + (heat_keypair_option, + stack_server)) + workers.append(worker) + + for worker in workers: + out.append(worker.get()) return out @@ -316,10 +385,8 @@ class OpenStackHeatStack(OpenStackCloudObject, object): """ out = list() - cinder = cinder_utils.cinder_client(self._os_creds) - volumes = heat_utils.get_stack_volumes( - self.__heat_cli, cinder, self.__stack) + self.__heat_cli, self.__cinder, self.__stack) for volume in volumes: settings = settings_utils.create_volume_config(volume) @@ -342,10 +409,8 @@ class OpenStackHeatStack(OpenStackCloudObject, object): """ out = list() - cinder = cinder_utils.cinder_client(self._os_creds) - vol_types = heat_utils.get_stack_volume_types( - self.__heat_cli, cinder, self.__stack) + self.__heat_cli, self.__cinder, self.__stack) for volume in vol_types: settings = settings_utils.create_volume_type_config(volume) @@ -369,10 +434,9 @@ class OpenStackHeatStack(OpenStackCloudObject, object): """ out = list() - nova = nova_utils.nova_client(self._os_creds) keypairs = heat_utils.get_stack_keypairs( - self.__heat_cli, nova, self.__stack) + self.__heat_cli, self.__nova, self.__stack) for keypair in keypairs: settings = settings_utils.create_keypair_config( @@ -397,10 +461,9 @@ class OpenStackHeatStack(OpenStackCloudObject, object): """ out = list() - nova = nova_utils.nova_client(self._os_creds) flavors = heat_utils.get_stack_flavors( - self.__heat_cli, nova, self.__stack) + self.__heat_cli, self.__nova, self.__stack) for flavor in flavors: settings = settings_utils.create_flavor_config(flavor) @@ -486,6 +549,22 @@ class OpenStackHeatStack(OpenStackCloudObject, object): return status == expected_status_code +def generate_creator(os_creds, stack_inst, image_settings): + """ + Initializes an OpenStackHeatStack object + :param os_creds: the OpenStack credentials + :param stack_inst: the SNAPS-OO VmInst domain object + :param image_settings: list of SNAPS-OO ImageConfig objects + :return: an initialized OpenStackHeatStack object + """ + + heat_config = StackConfig( + name=stack_inst.name, template={'place': 'holder'}) + heat_creator = OpenStackHeatStack(os_creds, heat_config, image_settings) + heat_creator.initialize() + return heat_creator + + class StackSettings(StackConfig): """ Class to hold the configuration settings required for creating OpenStack @@ -505,6 +584,11 @@ class StackCreationError(Exception): Exception to be thrown when an stack cannot be created """ +class StackUpdateError(Exception): + """ + Exception to be thrown when an stack update failed + """ + class StackError(Exception): """ diff --git a/snaps/openstack/create_user.py b/snaps/openstack/create_user.py index 5da3a5e..b187f4e 100644 --- a/snaps/openstack/create_user.py +++ b/snaps/openstack/create_user.py @@ -77,6 +77,8 @@ class OpenStackUser(OpenStackIdentityObject): pass self.__user = None + super(self.__class__, self).clean() + def get_user(self): """ Returns the OpenStack user object populated in create() @@ -91,6 +93,9 @@ class OpenStackUser(OpenStackIdentityObject): credentials :return: """ + if not project_name: + project_name = self._os_creds.project_name + return OSCreds( username=self.user_settings.name, password=self.user_settings.password, @@ -102,6 +107,7 @@ class OpenStackUser(OpenStackIdentityObject): compute_api_version=self._os_creds.compute_api_version, heat_api_version=self._os_creds.heat_api_version, volume_api_version=self._os_creds.volume_api_version, + region_name=self._os_creds.region_name, user_domain_name=self._os_creds.user_domain_name, user_domain_id=self._os_creds.user_domain_id, project_domain_name=self._os_creds.project_domain_name, diff --git a/snaps/openstack/create_volume.py b/snaps/openstack/create_volume.py index c134ca1..b35cd89 100644 --- a/snaps/openstack/create_volume.py +++ b/snaps/openstack/create_volume.py @@ -60,7 +60,9 @@ class OpenStackVolume(OpenStackVolumeObject): super(self.__class__, self).initialize() self.__volume = cinder_utils.get_volume( - self._cinder, volume_settings=self.volume_settings) + self._cinder, self._keystone, + volume_settings=self.volume_settings, + project_name=self._os_creds.project_name) return self.__volume def create(self, block=False): @@ -73,7 +75,7 @@ class OpenStackVolume(OpenStackVolumeObject): if not self.__volume: self.__volume = cinder_utils.create_volume( - self._cinder, self.volume_settings) + self._cinder, self._keystone, self.volume_settings) logger.info( 'Created volume with name - %s', self.volume_settings.name) @@ -124,6 +126,8 @@ class OpenStackVolume(OpenStackVolumeObject): self.__volume = None + super(self.__class__, self).clean() + def get_volume(self): """ Returns the domain Volume object as it was populated when create() was diff --git a/snaps/openstack/create_volume_type.py b/snaps/openstack/create_volume_type.py index a7198d8..9a45561 100644 --- a/snaps/openstack/create_volume_type.py +++ b/snaps/openstack/create_volume_type.py @@ -87,6 +87,8 @@ class OpenStackVolumeType(OpenStackVolumeObject): self.__volume_type = None + super(self.__class__, self).clean() + def get_volume_type(self): """ Returns the domain Volume object as it was populated when create() was diff --git a/snaps/openstack/openstack_creator.py b/snaps/openstack/openstack_creator.py index 0caee9a..6eeac37 100644 --- a/snaps/openstack/openstack_creator.py +++ b/snaps/openstack/openstack_creator.py @@ -29,17 +29,24 @@ class OpenStackCloudObject(CloudObject): Constructor :param os_creds: the OpenStack credentials object """ - # super(self.__class__, self, os_creds) self._os_creds = os_creds + self._os_session = None + self._keystone = None def initialize(self): - raise NotImplementedError('Do not override abstract method') + self._os_session = keystone_utils.keystone_session(self._os_creds) + self._keystone = keystone_utils.keystone_client( + self._os_creds, session=self._os_session) + + def get_os_creds(self): + return self._os_creds def create(self): raise NotImplementedError('Do not override abstract method') def clean(self): - raise NotImplementedError('Do not override abstract method') + if self._os_session: + keystone_utils.close_session(self._os_session) class OpenStackComputeObject(OpenStackCloudObject): @@ -56,14 +63,12 @@ class OpenStackComputeObject(OpenStackCloudObject): self._nova = None def initialize(self): - self._nova = nova_utils.nova_client(self._os_creds) + super(OpenStackComputeObject, self).initialize() + self._nova = nova_utils.nova_client(self._os_creds, self._os_session) def create(self): raise NotImplementedError('Do not override abstract method') - def clean(self): - raise NotImplementedError('Do not override abstract method') - class OpenStackNetworkObject(OpenStackCloudObject): """ @@ -79,14 +84,13 @@ class OpenStackNetworkObject(OpenStackCloudObject): self._neutron = None def initialize(self): - self._neutron = neutron_utils.neutron_client(self._os_creds) + super(OpenStackNetworkObject, self).initialize() + self._neutron = neutron_utils.neutron_client( + self._os_creds, self._os_session) def create(self): raise NotImplementedError('Do not override abstract method') - def clean(self): - raise NotImplementedError('Do not override abstract method') - class OpenStackIdentityObject(OpenStackCloudObject): """ @@ -99,17 +103,13 @@ class OpenStackIdentityObject(OpenStackCloudObject): :param os_creds: the OpenStack credentials object """ super(OpenStackIdentityObject, self).__init__(os_creds) - self._keystone = None def initialize(self): - self._keystone = keystone_utils.keystone_client(self._os_creds) + super(OpenStackIdentityObject, self).initialize() def create(self): raise NotImplementedError('Do not override abstract method') - def clean(self): - raise NotImplementedError('Do not override abstract method') - class OpenStackVolumeObject(OpenStackCloudObject): """ @@ -125,14 +125,13 @@ class OpenStackVolumeObject(OpenStackCloudObject): self._cinder = None def initialize(self): - self._cinder = cinder_utils.cinder_client(self._os_creds) + super(OpenStackVolumeObject, self).initialize() + self._cinder = cinder_utils.cinder_client( + self._os_creds, self._os_session) def create(self): raise NotImplementedError('Do not override abstract method') - def clean(self): - raise NotImplementedError('Do not override abstract method') - class OpenStackMagnumObject(OpenStackCloudObject): """ @@ -148,10 +147,9 @@ class OpenStackMagnumObject(OpenStackCloudObject): self._magnum = None def initialize(self): - self._magnum = magnum_utils.magnum_client(self._os_creds) + super(OpenStackMagnumObject, self).initialize() + self._magnum = magnum_utils.magnum_client( + self._os_creds, self._os_session) def create(self): raise NotImplementedError('Do not override abstract method') - - def clean(self): - raise NotImplementedError('Do not override abstract method') diff --git a/snaps/openstack/os_credentials.py b/snaps/openstack/os_credentials.py index 2553410..11ef8ff 100644 --- a/snaps/openstack/os_credentials.py +++ b/snaps/openstack/os_credentials.py @@ -63,7 +63,7 @@ class OSCreds: self.project_name = kwargs.get('project_name') if kwargs.get('identity_api_version') is None: - self.identity_api_version = keystone_utils.V2_VERSION_NUM + self.identity_api_version = keystone_utils.V3_VERSION_NUM else: self.identity_api_version = float(kwargs['identity_api_version']) @@ -85,7 +85,9 @@ class OSCreds: if kwargs.get('heat_api_version') is None: self.heat_api_version = 1 else: - self.heat_api_version = float(kwargs['heat_api_version']) + val = kwargs['heat_api_version'] + ver = float(val) + self.heat_api_version = int(ver) if kwargs.get('volume_api_version') is None: self.volume_api_version = cinder_utils.VERSION_2 @@ -169,7 +171,45 @@ class OSCreds: return new_url - @property + def to_dict(self): + """Converts object to a dict that can be used to construct another""" + return {'username': self.username, + 'password': self.password, + 'auth_url': self.auth_url, + 'project_name': self.project_name, + 'identity_api_version': self.identity_api_version, + 'image_api_version': self.image_api_version, + 'network_api_version': self.network_api_version, + 'compute_api_version': self.compute_api_version, + 'heat_api_version': self.heat_api_version, + 'user_domain_id': self.user_domain_id, + 'user_domain_name': self.user_domain_name, + 'project_domain_id': self.project_domain_id, + 'project_domain_name': self.project_domain_name, + 'interface': self.interface, + 'region_name': self.region_name, + 'proxy_settings': self.proxy_settings, + 'cacert': self.cacert} + + def __eq__(self, other): + return (self.username == other.username and + self.password == other.password and + self.auth_url == other.auth_url and + self.project_name == other.project_name and + float(self.identity_api_version) == float(other.identity_api_version) and + float(self.image_api_version) == float(other.image_api_version) and + float(self.network_api_version) == float(other.network_api_version) and + float(self.compute_api_version) == float(other.compute_api_version) and + float(self.heat_api_version) == float(other.heat_api_version) and + self.user_domain_id == other.user_domain_id and + self.user_domain_name == other.user_domain_name and + self.project_domain_id == other.project_domain_id and + self.project_domain_name == other.project_domain_name and + self.interface == other.interface and + self.region_name == other.region_name and + self.proxy_settings == other.proxy_settings and + self.cacert == other.cacert) + def __str__(self): """Converts object to a string""" return ('OSCreds - username=' + str(self.username) + diff --git a/snaps/openstack/tests/cluster_template_tests.py b/snaps/openstack/tests/cluster_template_tests.py index 355467d..eda790d 100644 --- a/snaps/openstack/tests/cluster_template_tests.py +++ b/snaps/openstack/tests/cluster_template_tests.py @@ -54,7 +54,8 @@ class CreateClusterTemplateTests(OSIntegrationTestCase): self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) self.cluster_type_name = self.guid + '-cluster-type' - self.magnum = magnum_utils.magnum_client(self.os_creds) + self.magnum = magnum_utils.magnum_client( + self.os_creds, self.os_session) metadata = self.image_metadata if not metadata: @@ -68,9 +69,10 @@ class CreateClusterTemplateTests(OSIntegrationTestCase): self.image_creator = OpenStackImage(self.os_creds, os_image_settings) - self.flavor_creator = OpenStackFlavor( - self.os_creds, FlavorConfig( - name=self.guid + '-flavor', ram=512, disk=10, vcpus=1)) + flavor_config = openstack_tests.get_flavor_config( + name=self.guid + '-flavor', ram=512, disk=10, + vcpus=1, metadata=self.flavor_metadata) + self.flavor_creator = OpenStackFlavor(self.os_creds, flavor_config) keypair_priv_filepath = 'tmp/' + self.guid keypair_pub_filepath = keypair_priv_filepath + '.pub' diff --git a/snaps/openstack/tests/conf/os_credentials_tests.py b/snaps/openstack/tests/conf/os_credentials_tests.py index 192be86..696ca2d 100644 --- a/snaps/openstack/tests/conf/os_credentials_tests.py +++ b/snaps/openstack/tests/conf/os_credentials_tests.py @@ -142,9 +142,9 @@ class OSCredsUnitTests(unittest.TestCase): project_name='hello') self.assertEqual('foo', os_creds.username) self.assertEqual('bar', os_creds.password) - self.assertEqual('http://foo.bar:5000/v2.0', os_creds.auth_url) + self.assertEqual('http://foo.bar:5000/v3', os_creds.auth_url) self.assertEqual('hello', os_creds.project_name) - self.assertEqual(2, os_creds.identity_api_version) + self.assertEqual(3, os_creds.identity_api_version) self.assertEqual(2, os_creds.image_api_version) self.assertEqual(2, os_creds.compute_api_version) self.assertEqual(1, os_creds.heat_api_version) @@ -165,9 +165,9 @@ class OSCredsUnitTests(unittest.TestCase): 'project_name': 'hello'}) self.assertEqual('foo', os_creds.username) self.assertEqual('bar', os_creds.password) - self.assertEqual('http://foo.bar:5000/v2.0', os_creds.auth_url) + self.assertEqual('http://foo.bar:5000/v3', os_creds.auth_url) self.assertEqual('hello', os_creds.project_name) - self.assertEqual(2, os_creds.identity_api_version) + self.assertEqual(3, os_creds.identity_api_version) self.assertEqual(2, os_creds.image_api_version) self.assertEqual(2, os_creds.compute_api_version) self.assertEqual(1, os_creds.heat_api_version) @@ -243,9 +243,9 @@ class OSCredsUnitTests(unittest.TestCase): project_name='hello', proxy_settings=proxy_settings) self.assertEqual('foo', os_creds.username) self.assertEqual('bar', os_creds.password) - self.assertEqual('http://foo.bar:5000/v2.0', os_creds.auth_url) + self.assertEqual('http://foo.bar:5000/v3', os_creds.auth_url) self.assertEqual('hello', os_creds.project_name) - self.assertEqual(2, os_creds.identity_api_version) + self.assertEqual(3, os_creds.identity_api_version) self.assertEqual(2, os_creds.image_api_version) self.assertEqual(2, os_creds.compute_api_version) self.assertEqual(1, os_creds.heat_api_version) @@ -262,6 +262,11 @@ class OSCredsUnitTests(unittest.TestCase): self.assertIsNone(os_creds.proxy_settings.ssh_proxy_cmd) self.assertIsNone(os_creds.region_name) + creds_dict = os_creds.to_dict() + creds_from_dict = OSCreds(**creds_dict) + + self.assertEqual(os_creds, creds_from_dict) + def test_proxy_settings_obj_kwargs(self): proxy_settings = ProxySettings(host='foo', port=1234) os_creds = OSCreds( @@ -273,9 +278,9 @@ class OSCredsUnitTests(unittest.TestCase): 'project_domain_name': 'domain4'}) self.assertEqual('foo', os_creds.username) self.assertEqual('bar', os_creds.password) - self.assertEqual('http://foo.bar:5000/v2.0', os_creds.auth_url) + self.assertEqual('http://foo.bar:5000/v3', os_creds.auth_url) self.assertEqual('hello', os_creds.project_name) - self.assertEqual(2, os_creds.identity_api_version) + self.assertEqual(3, os_creds.identity_api_version) self.assertEqual(2, os_creds.image_api_version) self.assertEqual(2, os_creds.compute_api_version) self.assertEqual(1, os_creds.heat_api_version) @@ -300,9 +305,9 @@ class OSCredsUnitTests(unittest.TestCase): project_domain_id='domain3', project_domain_name='domain4') self.assertEqual('foo', os_creds.username) self.assertEqual('bar', os_creds.password) - self.assertEqual('http://foo.bar:5000/v2.0', os_creds.auth_url) + self.assertEqual('http://foo.bar:5000/v3', os_creds.auth_url) self.assertEqual('hello', os_creds.project_name) - self.assertEqual(2, os_creds.identity_api_version) + self.assertEqual(3, os_creds.identity_api_version) self.assertEqual(2, os_creds.image_api_version) self.assertEqual(2, os_creds.compute_api_version) self.assertEqual(1, os_creds.heat_api_version) @@ -326,9 +331,9 @@ class OSCredsUnitTests(unittest.TestCase): 'region_name': 'test_region'}) self.assertEqual('foo', os_creds.username) self.assertEqual('bar', os_creds.password) - self.assertEqual('http://foo.bar:5000/v2.0', os_creds.auth_url) + self.assertEqual('http://foo.bar:5000/v3', os_creds.auth_url) self.assertEqual('hello', os_creds.project_name) - self.assertEqual(2, os_creds.identity_api_version) + self.assertEqual(3, os_creds.identity_api_version) self.assertEqual(2, os_creds.image_api_version) self.assertEqual(2, os_creds.compute_api_version) self.assertEqual(1, os_creds.heat_api_version) diff --git a/snaps/openstack/tests/conf/os_env.yaml.template b/snaps/openstack/tests/conf/os_env.yaml.template index 36e3cfd..53d500f 100644 --- a/snaps/openstack/tests/conf/os_env.yaml.template +++ b/snaps/openstack/tests/conf/os_env.yaml.template @@ -14,4 +14,7 @@ #os_auth_url: http://<host>:<port>/ #project_name: admin #identity_api_version: 3 -#ext_net: <external network name>
\ No newline at end of file +#ext_net: <external network name> + +flavor_metadata: + hw:mem_page_size: large diff --git a/snaps/openstack/tests/create_flavor_tests.py b/snaps/openstack/tests/create_flavor_tests.py index f84355d..a69de40 100644 --- a/snaps/openstack/tests/create_flavor_tests.py +++ b/snaps/openstack/tests/create_flavor_tests.py @@ -20,6 +20,7 @@ from snaps.openstack import create_flavor from snaps.openstack.create_flavor import OpenStackFlavor, FlavorSettings from snaps.openstack.tests.os_source_file_test import OSComponentTestCase from snaps.openstack.utils import nova_utils +from snaps.openstack.tests import openstack_tests __author__ = 'spisarski' @@ -274,7 +275,7 @@ class CreateFlavorTests(OSComponentTestCase): guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) self.flavor_name = guid + 'name' - self.nova = nova_utils.nova_client(self.os_creds) + self.nova = nova_utils.nova_client(self.os_creds, self.os_session) # Initialize for cleanup self.flavor_creator = None @@ -286,6 +287,8 @@ class CreateFlavorTests(OSComponentTestCase): if self.flavor_creator: self.flavor_creator.clean() + super(self.__class__, self).__clean__() + def test_create_flavor(self): """ Tests the creation of an OpenStack flavor. @@ -362,10 +365,12 @@ class CreateFlavorTests(OSComponentTestCase): raise any exceptions. """ # Create Flavor - flavor_settings = FlavorConfig( + if self.flavor_metadata: + self.flavor_metadata.update(create_flavor.MEM_PAGE_SIZE_ANY) + flavor_settings = openstack_tests.get_flavor_config( name=self.flavor_name, ram=1, disk=1, vcpus=1, ephemeral=2, swap=3, rxtx_factor=2.2, is_public=False, - metadata=create_flavor.MEM_PAGE_SIZE_ANY) + metadata=self.flavor_metadata) self.flavor_creator = OpenStackFlavor(self.os_creds, flavor_settings) flavor = self.flavor_creator.create() self.assertTrue(validate_flavor(self.nova, flavor_settings, flavor)) diff --git a/snaps/openstack/tests/create_image_tests.py b/snaps/openstack/tests/create_image_tests.py index 9965f87..ae59a67 100644 --- a/snaps/openstack/tests/create_image_tests.py +++ b/snaps/openstack/tests/create_image_tests.py @@ -265,7 +265,8 @@ class CreateImageSuccessTests(OSIntegrationTestCase): guid = uuid.uuid4() self.image_name = self.__class__.__name__ + '-' + str(guid) - self.glance = glance_utils.glance_client(self.os_creds) + self.glance = glance_utils.glance_client( + self.os_creds, self.os_session) self.image_creator = None if self.image_metadata and 'glance_tests' in self.image_metadata: @@ -379,8 +380,8 @@ class CreateImageSuccessTests(OSIntegrationTestCase): clean() does not raise an Exception. """ # Create Image - self.image_creator = create_image.OpenStackImage(self.os_creds, - self.image_settings) + self.image_creator = create_image.OpenStackImage( + self.os_creds, self.image_settings) created_image = self.image_creator.create() self.assertIsNotNone(created_image) @@ -562,7 +563,8 @@ class CreateMultiPartImageTests(OSIntegrationTestCase): guid = uuid.uuid4() self.image_creators = list() self.image_name = self.__class__.__name__ + '-' + str(guid) - self.glance = glance_utils.glance_client(self.os_creds) + self.glance = glance_utils.glance_client( + self.os_creds, self.os_session) self.tmp_dir = 'tmp/' + str(guid) if not os.path.exists(self.tmp_dir): diff --git a/snaps/openstack/tests/create_instance_tests.py b/snaps/openstack/tests/create_instance_tests.py index 55da144..17831b3 100644 --- a/snaps/openstack/tests/create_instance_tests.py +++ b/snaps/openstack/tests/create_instance_tests.py @@ -20,11 +20,10 @@ import unittest import uuid import os -from neutronclient.common.exceptions import InvalidIpForSubnetClient -from novaclient.exceptions import BadRequest +from neutronclient.common.exceptions import ( + InvalidIpForSubnetClient, BadRequest) from snaps import file_utils -from snaps.config.flavor import FlavorConfig from snaps.config.image import ImageConfig from snaps.config.keypair import KeypairConfig from snaps.config.network import PortConfig, NetworkConfig, SubnetConfig @@ -45,10 +44,11 @@ from snaps.openstack.create_network import OpenStackNetwork from snaps.openstack.create_router import OpenStackRouter from snaps.openstack.create_security_group import OpenStackSecurityGroup from snaps.openstack.create_volume import OpenStackVolume +from snaps.openstack.os_credentials import OSCreds from snaps.openstack.tests import openstack_tests, validation_utils from snaps.openstack.tests.os_source_file_test import ( OSIntegrationTestCase, OSComponentTestCase) -from snaps.openstack.utils import nova_utils +from snaps.openstack.utils import nova_utils, keystone_utils, neutron_utils from snaps.openstack.utils.nova_utils import RebootType from snaps.openstack.utils import nova_utils, settings_utils, neutron_utils @@ -294,7 +294,7 @@ class SimpleHealthCheck(OSIntegrationTestCase): """ super(self.__class__, self).__start__() - self.nova = nova_utils.nova_client(self.os_creds) + self.nova = nova_utils.nova_client(self.os_creds, self.os_session) guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) self.vm_inst_name = guid + '-inst' self.port_1_name = guid + 'port-1' @@ -306,6 +306,7 @@ class SimpleHealthCheck(OSIntegrationTestCase): self.inst_creator = None self.priv_net_config = openstack_tests.get_priv_net_config( + project_name=self.os_creds.project_name, net_name=guid + '-priv-net', subnet_name=guid + '-priv-subnet', netconf_override=self.netconf_override) @@ -334,10 +335,11 @@ class SimpleHealthCheck(OSIntegrationTestCase): if (self.flavor_metadata and self.flavor_metadata.get('hw:mem_page_size') == 'large'): self.flavor_ram = 1024 + flavor_config = openstack_tests.get_flavor_config( + name=guid + '-flavor-name', ram=self.flavor_ram, disk=10, + vcpus=1, metadata=self.flavor_metadata) self.flavor_creator = OpenStackFlavor( - self.admin_os_creds, - FlavorConfig(name=guid + '-flavor-name', ram=self.flavor_ram, - disk=10, vcpus=1, metadata=self.flavor_metadata)) + self.admin_os_creds, flavor_config) self.flavor_creator.create() except Exception as e: self.tearDown() @@ -411,23 +413,16 @@ class CreateInstanceSimpleTests(OSIntegrationTestCase): def setUp(self): """ - Instantiates the CreateImage object that is responsible for downloading - and creating an OS image file - within OpenStack + Setup the objects required for the test """ super(self.__class__, self).__start__() - guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) - self.vm_inst_name = guid + '-inst' + 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.neutron = neutron_utils.neutron_client(self.os_creds) os_image_settings = openstack_tests.cirros_image_settings( - name=guid + '-image', image_metadata=self.image_metadata) - - 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, - netconf_override=self.netconf_override) + name=self.guid + '-image', image_metadata=self.image_metadata) # Initialize for tearDown() self.image_creator = None @@ -443,21 +438,13 @@ class CreateInstanceSimpleTests(OSIntegrationTestCase): self.image_creator.create() # Create Flavor + flavor_config = openstack_tests.get_flavor_config( + name=self.guid + '-flavor-name', ram=256, disk=10, + vcpus=2, metadata=self.flavor_metadata) self.flavor_creator = OpenStackFlavor( - self.admin_os_creds, - FlavorConfig(name=guid + '-flavor-name', ram=256, disk=10, - vcpus=2, metadata=self.flavor_metadata)) + self.admin_os_creds, flavor_config) self.flavor_creator.create() - - # Create Network - self.network_creator = OpenStackNetwork( - self.os_creds, net_config.network_settings) - self.network_creator.create() - - self.port_settings = PortConfig( - name=guid + '-port', - network_name=net_config.network_settings.name) - + self.network_creator = None except Exception as e: self.tearDown() raise e @@ -504,6 +491,22 @@ class CreateInstanceSimpleTests(OSIntegrationTestCase): Tests the creation of an OpenStack instance with a single port with a static IP without a Floating IP. """ + # Create Network + net_config = openstack_tests.get_priv_net_config( + project_name=self.os_creds.project_name, + net_name=self.guid + '-pub-net', + subnet_name=self.guid + '-pub-subnet', + router_name=self.guid + '-pub-router', + external_net=self.ext_net_name, + netconf_override=self.netconf_override) + self.network_creator = OpenStackNetwork( + self.os_creds, net_config.network_settings) + self.network_creator.create() + + self.port_settings = PortConfig( + name=self.guid + '-port', + network_name=net_config.network_settings.name) + instance_settings = VmInstanceConfig( name=self.vm_inst_name, flavor=self.flavor_creator.flavor_settings.name, @@ -513,20 +516,174 @@ class CreateInstanceSimpleTests(OSIntegrationTestCase): self.os_creds, instance_settings, self.image_creator.image_settings) - vm_inst = self.inst_creator.create() - self.assertIsNotNone(nova_utils.get_server( - self.nova, self.neutron, vm_inst_settings=instance_settings)) + vm_inst = self.inst_creator.create(block=True) + vm_inst_get = nova_utils.get_server( + self.nova, self.neutron, self.keystone, + vm_inst_settings=instance_settings) + self.assertEqual(vm_inst, vm_inst_get) + + self.assertIsNotNone(self.inst_creator.get_vm_inst().availability_zone) + self.assertIsNone(self.inst_creator.get_vm_inst().compute_host) # Delete instance nova_utils.delete_vm_instance(self.nova, vm_inst) self.assertTrue(self.inst_creator.vm_deleted(block=True)) self.assertIsNone(nova_utils.get_server( - self.nova, self.neutron, vm_inst_settings=instance_settings)) + self.nova, self.neutron, self.keystone, + vm_inst_settings=instance_settings)) # Exception should not be thrown self.inst_creator.clean() + def test_create_admin_instance(self): + """ + Tests the creation of an OpenStack instance with a single port with a + static IP without a Floating IP. + """ + # Create Network + net_config = openstack_tests.get_priv_net_config( + project_name=self.os_creds.project_name, + net_name=self.guid + '-pub-net', + subnet_name=self.guid + '-pub-subnet', + router_name=self.guid + '-pub-router', + external_net=self.ext_net_name, + netconf_override=self.netconf_override) + self.network_creator = OpenStackNetwork( + self.admin_os_creds, net_config.network_settings) + self.network_creator.create() + + self.port_settings = PortConfig( + name=self.guid + '-port', + network_name=net_config.network_settings.name) + + instance_settings = VmInstanceConfig( + name=self.vm_inst_name, + flavor=self.flavor_creator.flavor_settings.name, + port_settings=[self.port_settings]) + + self.inst_creator = OpenStackVmInstance( + self.admin_os_creds, instance_settings, + self.image_creator.image_settings) + + admin_nova = nova_utils.nova_client(self.admin_os_creds) + admin_neutron = neutron_utils.neutron_client(self.admin_os_creds) + admin_key = keystone_utils.keystone_client(self.admin_os_creds) + vm_inst = self.inst_creator.create(block=True) + + self.assertIsNotNone(vm_inst) + vm_inst_get = nova_utils.get_server( + admin_nova, admin_neutron, admin_key, + vm_inst_settings=instance_settings) + self.assertEqual(vm_inst, vm_inst_get) + + self.assertIsNone(nova_utils.get_server( + self.nova, self.neutron, self.keystone, + vm_inst_settings=instance_settings)) + + self.assertIsNotNone(self.inst_creator.get_vm_inst().availability_zone) + self.assertIsNotNone(self.inst_creator.get_vm_inst().compute_host) + + +class CreateInstanceExternalNetTests(OSIntegrationTestCase): + """ + Simple instance creation tests where the network is external + """ + + 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.admin_os_creds) + self.neutron = neutron_utils.neutron_client(self.admin_os_creds) + os_image_settings = openstack_tests.cirros_image_settings( + name=guid + '-image', image_metadata=self.image_metadata) + + # 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, + os_image_settings) + self.image_creator.create() + + # Create Flavor + flavor_config = openstack_tests.get_flavor_config( + name=guid + '-flavor-name', ram=256, disk=10, + vcpus=2, metadata=self.flavor_metadata) + self.flavor_creator = OpenStackFlavor( + self.admin_os_creds, flavor_config) + self.flavor_creator.create() + + self.port_settings = PortConfig( + name=guid + '-port', + network_name=self.ext_net_name) + + 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 ' + '- %s', e) + + if self.flavor_creator: + try: + self.flavor_creator.clean() + except Exception as e: + logger.error( + 'Unexpected exception cleaning flavor with message - %s', + e) + + if self.image_creator and not self.image_creator.image_settings.exists: + try: + self.image_creator.clean() + except Exception as e: + logger.error( + 'Unexpected exception cleaning image with message - %s', e) + + super(self.__class__, self).__clean__() + + def test_create_instance_public_net(self): + """ + Tests the creation of an OpenStack instance with a single port to + the external network. + """ + instance_settings = VmInstanceConfig( + name=self.vm_inst_name, + flavor=self.flavor_creator.flavor_settings.name, + port_settings=[self.port_settings]) + + self.inst_creator = OpenStackVmInstance( + self.admin_os_creds, instance_settings, + self.image_creator.image_settings) + + vm_inst = self.inst_creator.create(block=True) + vm_inst_get = nova_utils.get_server( + self.nova, self.neutron, self.keystone, + vm_inst_settings=instance_settings) + self.assertEqual(vm_inst, vm_inst_get) + ip = self.inst_creator.get_port_ip(self.port_settings.name) + + check_dhcp_lease(self.inst_creator, ip) + class CreateInstanceSingleNetworkTests(OSIntegrationTestCase): """ @@ -538,9 +695,10 @@ class CreateInstanceSingleNetworkTests(OSIntegrationTestCase): Instantiates the CreateImage object that is responsible for downloading and creating an OS image file within OpenStack """ + self.proj_users = ['admin'] super(self.__class__, self).__start__() - self.nova = nova_utils.nova_client(self.os_creds) + self.nova = nova_utils.nova_client(self.os_creds, self.os_session) guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) self.keypair_priv_filepath = 'tmp/' + guid self.keypair_pub_filepath = self.keypair_priv_filepath + '.pub' @@ -560,6 +718,7 @@ class CreateInstanceSingleNetworkTests(OSIntegrationTestCase): self.inst_creators = list() self.pub_net_config = openstack_tests.get_pub_net_config( + project_name=self.os_creds.project_name, net_name=guid + '-pub-net', subnet_name=guid + '-pub-subnet', router_name=guid + '-pub-router', external_net=self.ext_net_name, netconf_override=self.netconf_override) @@ -582,10 +741,11 @@ class CreateInstanceSingleNetworkTests(OSIntegrationTestCase): self.router_creator.create() # Create Flavor + flavor_config = openstack_tests.get_flavor_config( + name=guid + '-flavor-name', ram=256, disk=10, + vcpus=2, metadata=self.flavor_metadata) self.flavor_creator = OpenStackFlavor( - self.admin_os_creds, - FlavorConfig(name=guid + '-flavor-name', ram=256, disk=10, - vcpus=2, metadata=self.flavor_metadata)) + self.admin_os_creds, flavor_config) self.flavor_creator.create() self.keypair_creator = OpenStackKeypair( @@ -816,19 +976,19 @@ class CreateInstanceSingleNetworkTests(OSIntegrationTestCase): # Test default reboot which should be 'SOFT' inst_creator.reboot() # Lag time to allow for shutdown routine to take effect - time.sleep(10) + time.sleep(15) self.assertTrue(check_dhcp_lease(inst_creator, ip)) self.assertTrue(validate_ssh_client(inst_creator)) # Test 'SOFT' reboot inst_creator.reboot(reboot_type=RebootType.soft) - time.sleep(10) + time.sleep(15) self.assertTrue(check_dhcp_lease(inst_creator, ip)) self.assertTrue(validate_ssh_client(inst_creator)) # Test 'HARD' reboot inst_creator.reboot(reboot_type=RebootType.hard) - time.sleep(10) + time.sleep(15) self.assertTrue(check_dhcp_lease(inst_creator, ip)) self.assertTrue(validate_ssh_client(inst_creator)) @@ -897,9 +1057,35 @@ class CreateInstanceSingleNetworkTests(OSIntegrationTestCase): self.assertTrue(inst_creator.vm_active(block=True)) + vm_os_creds = OSCreds( + auth_url=self.admin_os_creds.auth_url, + username=self.admin_os_creds.username, + password=self.admin_os_creds.password, + project_name=self.os_creds.project_name, + identity_api_version=self.os_creds.identity_api_version) derived_inst_creator = create_instance.generate_creator( - self.os_creds, vm_inst, self.image_creator.image_settings, - self.keypair_creator.keypair_settings) + vm_os_creds, vm_inst, self.image_creator.image_settings, + self.os_creds.project_name, self.keypair_creator.keypair_settings) + + # Tests to ensure that a instance can be returned with an invalid + # image config object and admin credentials (when the 'admin' user has + # been added to the project) as this should not matter unless one + # needs to access the machine via ssh and its floating IP + + # Invalid ImageConfig + derived_foo_image_creator = create_instance.generate_creator( + vm_os_creds, vm_inst, ImageConfig( + name='foo', image_user='bar', format='qcow2', + image_file='foo/bar'), + vm_os_creds.project_name) + self.assertIsNotNone(derived_foo_image_creator) + self.assertTrue(derived_foo_image_creator.vm_active()) + + # None ImageConfig + derived_none_image_creator = create_instance.generate_creator( + vm_os_creds, vm_inst, None, vm_os_creds.project_name) + self.assertIsNotNone(derived_none_image_creator) + self.assertTrue(derived_none_image_creator.vm_active()) derived_inst_creator.add_floating_ip(FloatingIpConfig( name=self.floating_ip_name, port_name=self.port_1_name, @@ -966,7 +1152,7 @@ class CreateInstanceIPv6NetworkTests(OSIntegrationTestCase): """ super(self.__class__, self).__start__() - self.nova = nova_utils.nova_client(self.os_creds) + self.nova = nova_utils.nova_client(self.os_creds, self.os_session) self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) self.keypair_priv_filepath = 'tmp/' + self.guid self.keypair_pub_filepath = self.keypair_priv_filepath + '.pub' @@ -991,11 +1177,11 @@ class CreateInstanceIPv6NetworkTests(OSIntegrationTestCase): self.os_creds, os_image_settings) self.image_creator.create() + flavor_config = openstack_tests.get_flavor_config( + name=self.guid + '-flavor-name', ram=256, disk=10, + vcpus=2, metadata=self.flavor_metadata) self.flavor_creator = OpenStackFlavor( - self.admin_os_creds, - FlavorConfig( - name=self.guid + '-flavor-name', ram=256, disk=10, vcpus=2, - metadata=self.flavor_metadata)) + self.admin_os_creds, flavor_config) self.flavor_creator.create() self.keypair_creator = OpenStackKeypair( @@ -1094,7 +1280,10 @@ class CreateInstanceIPv6NetworkTests(OSIntegrationTestCase): name=self.guid + '-net', subnet_settings=[subnet_settings]) router_settings = RouterConfig( name=self.guid + '-router', external_gateway=self.ext_net_name, - internal_subnets=[subnet_settings.name]) + internal_subnets=[{'subnet': { + 'project_name': self.os_creds.project_name, + 'network_name': network_settings.name, + 'subnet_name': subnet_settings.name}}]) # Create Network self.network_creator = OpenStackNetwork( @@ -1142,7 +1331,10 @@ class CreateInstanceIPv6NetworkTests(OSIntegrationTestCase): subnet_settings=[subnet4_settings, subnet6_settings]) router_settings = RouterConfig( name=self.guid + '-router', external_gateway=self.ext_net_name, - internal_subnets=[subnet4_settings.name]) + internal_subnets=[{'subnet': { + 'project_name': self.os_creds.project_name, + 'network_name': network_settings.name, + 'subnet_name': subnet4_settings.name}}]) # Create Network self.network_creator = OpenStackNetwork( @@ -1172,6 +1364,7 @@ class CreateInstanceIPv6NetworkTests(OSIntegrationTestCase): keypair_settings=self.keypair_creator.keypair_settings) self.inst_creator.create(block=True) + self.inst_creator.cloud_init_complete(block=True) ssh_client = self.inst_creator.ssh_client() self.assertIsNotNone(ssh_client) @@ -1189,24 +1382,28 @@ class CreateInstancePortManipulationTests(OSIntegrationTestCase): """ 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' + self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) + 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' # Initialize for tearDown() self.image_creator = None self.network_creator = None + self.network_creator2 = 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, + project_name=self.os_creds.project_name, + net_name=self.guid + '-pub-net', + subnet_name=self.guid + '-pub-subnet', + router_name=self.guid + '-pub-router', + external_net=self.ext_net_name, netconf_override=self.netconf_override) os_image_settings = openstack_tests.cirros_image_settings( - name=guid + '-image', image_metadata=self.image_metadata) + name=self.guid + '-image', image_metadata=self.image_metadata) try: # Create Image @@ -1220,10 +1417,11 @@ class CreateInstancePortManipulationTests(OSIntegrationTestCase): self.network_creator.create() # Create Flavor + flavor_config = openstack_tests.get_flavor_config( + name=self.guid + '-flavor-name', ram=256, disk=10, + vcpus=2, metadata=self.flavor_metadata) self.flavor_creator = OpenStackFlavor( - self.admin_os_creds, - FlavorConfig(name=guid + '-flavor-name', ram=256, disk=10, - vcpus=2, metadata=self.flavor_metadata)) + self.admin_os_creds, flavor_config) self.flavor_creator.create() except Exception as e: self.tearDown() @@ -1257,6 +1455,14 @@ class CreateInstancePortManipulationTests(OSIntegrationTestCase): 'Unexpected exception cleaning network with message - %s', e) + if self.network_creator2: + try: + self.network_creator2.clean() + except Exception as e: + logger.error( + 'Unexpected exception cleaning network with message - %s', + e) + if self.image_creator and not self.image_creator.image_settings.exists: try: self.image_creator.clean() @@ -1293,6 +1499,86 @@ class CreateInstancePortManipulationTests(OSIntegrationTestCase): subnet_name=self.net_config.network_settings.subnet_settings[ 0].name)) + def test_set_one_port_two_ip_one_subnet(self): + """ + Tests the creation of an OpenStack instance with a single port with a + two static IPs on a network with one subnet. + """ + ip1 = '10.55.0.101' + ip2 = '10.55.0.102' + sub_settings = self.net_config.network_settings.subnet_settings + port_settings = PortConfig( + name=self.port_1_name, + network_name=self.net_config.network_settings.name, + ip_addrs=[{'subnet_name': sub_settings[0].name, 'ip': ip1}, + {'subnet_name': sub_settings[0].name, 'ip': ip2}]) + + instance_settings = VmInstanceConfig( + 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) + vm_inst = self.inst_creator.create(block=True) + + self.assertEqual(ip1, vm_inst.ports[0].ips[0]['ip_address']) + self.assertEqual(self.network_creator.get_network().subnets[0].id, + vm_inst.ports[0].ips[0]['subnet_id']) + self.assertEqual(ip2, vm_inst.ports[0].ips[1]['ip_address']) + self.assertEqual(self.network_creator.get_network().subnets[0].id, + vm_inst.ports[0].ips[1]['subnet_id']) + + def test_set_one_port_two_ip_two_subnets(self): + """ + Tests the creation of an OpenStack instance with a single port with a + two static IPs on a network with one subnet. + """ + net2_config = NetworkConfig( + name=self.guid + 'net2', subnets=[ + SubnetConfig(name=self.guid + '-subnet1', cidr='10.55.0.0/24'), + SubnetConfig(name=self.guid + '-subnet2', cidr='10.65.0.0/24'), + ]) + + # Create Network + self.network_creator2 = OpenStackNetwork(self.os_creds, net2_config) + net2 = self.network_creator2.create() + + ip1 = '10.55.0.101' + ip2 = '10.65.0.101' + + port_settings = PortConfig( + name=self.port_1_name, + network_name=net2_config.name, + ip_addrs=[ + {'subnet_name': net2_config.subnet_settings[0].name, + 'ip': ip1}, + {'subnet_name': net2_config.subnet_settings[1].name, + 'ip': ip2}]) + + instance_settings = VmInstanceConfig( + 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) + vm_inst = self.inst_creator.create(block=True) + + subnet1_id = None + subnet2_id = None + for subnet in net2.subnets: + if subnet.name == net2_config.subnet_settings[0].name: + subnet1_id = subnet.id + if subnet.name == net2_config.subnet_settings[1].name: + subnet2_id = subnet.id + self.assertEqual(ip1, vm_inst.ports[0].ips[0]['ip_address']) + self.assertEqual(subnet1_id, vm_inst.ports[0].ips[0]['subnet_id']) + self.assertEqual(ip2, vm_inst.ports[0].ips[1]['ip_address']) + self.assertEqual(subnet2_id, vm_inst.ports[0].ips[1]['subnet_id']) + def test_set_custom_invalid_ip_one_subnet(self): """ Tests the creation of an OpenStack instance with a single port with a @@ -1501,6 +1787,7 @@ class CreateInstanceOnComputeHost(OSIntegrationTestCase): self.inst_creators = list() self.priv_net_config = openstack_tests.get_priv_net_config( + project_name=self.os_creds.project_name, net_name=guid + '-priv-net', subnet_name=guid + '-priv-subnet', netconf_override=self.netconf_override) @@ -1514,10 +1801,11 @@ class CreateInstanceOnComputeHost(OSIntegrationTestCase): self.network_creator.create() # Create Flavor + flavor_config = openstack_tests.get_flavor_config( + name=guid + '-flavor-name', ram=512, disk=1, + vcpus=1, metadata=self.flavor_metadata) self.flavor_creator = OpenStackFlavor( - self.admin_os_creds, - FlavorConfig(name=guid + '-flavor-name', ram=512, disk=1, - vcpus=1, metadata=self.flavor_metadata)) + self.admin_os_creds, flavor_config) self.flavor_creator.create() # Create Image @@ -1571,7 +1859,8 @@ class CreateInstanceOnComputeHost(OSIntegrationTestCase): Tests the creation of OpenStack VM instances to each compute node. """ from snaps.openstack.utils import nova_utils - nova = nova_utils.nova_client(self.admin_os_creds) + nova = nova_utils.nova_client( + self.admin_os_creds, self.admin_os_session) zone_hosts = nova_utils.get_availability_zone_hosts(nova) # Create Instance on each server/zone @@ -1592,7 +1881,11 @@ class CreateInstanceOnComputeHost(OSIntegrationTestCase): self.admin_os_creds, instance_settings, self.image_creator.image_settings) self.inst_creators.append(inst_creator) - inst_creator.create() + inst_creator.create(block=True) + avail_zone = inst_creator.get_vm_inst().availability_zone + self.assertTrue(avail_zone in zone) + compute_host = inst_creator.get_vm_inst().compute_host + self.assertTrue(compute_host in zone) # Validate instances to ensure they've been deployed to the correct # server @@ -1621,7 +1914,7 @@ class InstanceSecurityGroupTests(OSIntegrationTestCase): 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.nova = nova_utils.nova_client(self.os_creds, self.os_session) os_image_settings = openstack_tests.cirros_image_settings( name=self.guid + '-image', image_metadata=self.image_metadata) @@ -1631,6 +1924,7 @@ class InstanceSecurityGroupTests(OSIntegrationTestCase): self.floating_ip_name = self.guid + 'fip1' net_config = openstack_tests.get_priv_net_config( + project_name=self.os_creds.project_name, net_name=self.guid + '-pub-net', subnet_name=self.guid + '-pub-subnet', router_name=self.guid + '-pub-router', @@ -1657,11 +1951,11 @@ class InstanceSecurityGroupTests(OSIntegrationTestCase): self.network_creator.create() # Create Flavor + flavor_config = openstack_tests.get_flavor_config( + name=self.guid + '-flavor-name', ram=256, disk=10, + vcpus=2, metadata=self.flavor_metadata) self.flavor_creator = OpenStackFlavor( - self.admin_os_creds, - FlavorConfig(name=self.guid + '-flavor-name', ram=256, - disk=10, vcpus=2, - metadata=self.flavor_metadata)) + self.admin_os_creds, flavor_config) self.flavor_creator.create() self.port_settings = PortConfig( @@ -1952,9 +2246,10 @@ class CreateInstanceFromThreePartImage(OSIntegrationTestCase): guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) self.image_name = guid self.vm_inst_name = guid + '-inst' - self.nova = nova_utils.nova_client(self.os_creds) + self.nova = nova_utils.nova_client(self.os_creds, self.os_session) net_config = openstack_tests.get_priv_net_config( + project_name=self.os_creds.project_name, net_name=guid + '-pub-net', subnet_name=guid + '-pub-subnet', router_name=guid + '-pub-router', external_net=self.ext_net_name, netconf_override=self.netconf_override) @@ -1993,10 +2288,11 @@ class CreateInstanceFromThreePartImage(OSIntegrationTestCase): self.image_creator.create() # Create Flavor + flavor_config = openstack_tests.get_flavor_config( + name=guid + '-flavor-name', ram=256, disk=10, + vcpus=2, metadata=self.flavor_metadata) self.flavor_creator = OpenStackFlavor( - self.admin_os_creds, - FlavorConfig(name=guid + '-flavor-name', ram=256, disk=10, - vcpus=2, metadata=self.flavor_metadata)) + self.admin_os_creds, flavor_config) self.flavor_creator.create() # Create Network @@ -2097,6 +2393,7 @@ class CreateInstanceMockOfflineTests(OSComponentTestCase): self.inst_creator = None self.priv_net_config = openstack_tests.get_priv_net_config( + project_name=self.os_creds.project_name, net_name=self.guid + '-priv-net', subnet_name=self.guid + '-priv-subnet') self.port_settings = PortConfig( @@ -2114,11 +2411,11 @@ class CreateInstanceMockOfflineTests(OSComponentTestCase): self.network_creator.create() # Create Flavor + flavor_config = openstack_tests.get_flavor_config( + name=self.guid + '-flavor-name', ram=256, disk=10, + vcpus=2, metadata=self.flavor_metadata) self.flavor_creator = OpenStackFlavor( - self.os_creds, - FlavorConfig( - name=self.guid + '-flavor-name', ram=256, disk=10, - vcpus=1)) + self.os_creds, flavor_config) self.flavor_creator.create() except Exception as e: self.tearDown() @@ -2162,6 +2459,8 @@ class CreateInstanceMockOfflineTests(OSComponentTestCase): if os.path.exists(self.tmpDir) and os.path.isdir(self.tmpDir): shutil.rmtree(self.tmpDir) + super(self.__class__, self).__clean__() + def test_inst_from_file_image_simple_flat(self): """ Creates a VM instance from a locally sourced file image using simply @@ -2582,7 +2881,7 @@ class CreateInstanceTwoNetTests(OSIntegrationTestCase): self.ip1 = '10.200.201.5' self.ip2 = '10.200.202.5' - self.nova = nova_utils.nova_client(self.os_creds) + self.nova = nova_utils.nova_client(self.os_creds, self.os_session) # Initialize for tearDown() self.image_creator = None @@ -2616,13 +2915,14 @@ class CreateInstanceTwoNetTests(OSIntegrationTestCase): try: # Create Image - self.image_creator = OpenStackImage(self.os_creds, - os_image_settings) + self.image_creator = OpenStackImage( + self.os_creds, os_image_settings) self.image_creator.create() # First network is public self.network_creators.append(OpenStackNetwork( self.os_creds, self.net_config_1)) + # Second network is private self.network_creators.append(OpenStackNetwork( self.os_creds, self.net_config_2)) @@ -2630,47 +2930,47 @@ class CreateInstanceTwoNetTests(OSIntegrationTestCase): network_creator.create() port_settings = [ - create_network.PortConfig( + PortConfig( name=self.guid + '-router-port1', ip_addrs=[{ 'subnet_name': self.net_config_1.subnet_settings[0].name, 'ip': static_gateway_ip1 }], - network_name=self.net_config_1.name, - project_name=self.os_creds.project_name), - create_network.PortConfig( + network_name=self.net_config_1.name), + PortConfig( name=self.guid + '-router-port2', ip_addrs=[{ 'subnet_name': self.net_config_2.subnet_settings[0].name, 'ip': static_gateway_ip2 }], - network_name=self.net_config_2.name, - project_name=self.os_creds.project_name)] + network_name=self.net_config_2.name)] router_settings = RouterConfig( name=self.guid + '-pub-router', port_settings=port_settings) - self.router_creator = create_router.OpenStackRouter( + self.router_creator = OpenStackRouter( self.os_creds, router_settings) self.router_creator.create() - # Create Flavor + flavor_config = openstack_tests.get_flavor_config( + name=self.guid + '-flavor-name', ram=512, disk=10, + vcpus=2, metadata=self.flavor_metadata) self.flavor_creator = OpenStackFlavor( - self.admin_os_creds, - FlavorConfig(name=self.guid + '-flavor-name', ram=512, - disk=10, vcpus=2, - metadata=self.flavor_metadata)) + self.admin_os_creds, flavor_config) self.flavor_creator.create() - sec_grp_name = self.guid + '-sec-grp' + self.sec_grp_name = self.guid + '-sec-grp' rule1 = SecurityGroupRuleConfig( - sec_grp_name=sec_grp_name, direction=Direction.ingress, + sec_grp_name=self.sec_grp_name, direction=Direction.ingress, + protocol=Protocol.icmp) + rule2 = SecurityGroupRuleConfig( + sec_grp_name=self.sec_grp_name, direction=Direction.egress, protocol=Protocol.icmp) self.sec_grp_creator = OpenStackSecurityGroup( self.os_creds, SecurityGroupConfig( - name=sec_grp_name, rule_settings=[rule1])) + name=self.sec_grp_name, rule_settings=[rule1, rule2])) self.sec_grp_creator.create() except: self.tearDown() @@ -2750,6 +3050,7 @@ class CreateInstanceTwoNetTests(OSIntegrationTestCase): name=self.vm_inst1_name, flavor=self.flavor_creator.flavor_settings.name, userdata=_get_ping_userdata(self.ip2), + security_group_names=self.sec_grp_name, port_settings=[PortConfig( name=self.port_1_name, ip_addrs=[{ @@ -2762,6 +3063,7 @@ class CreateInstanceTwoNetTests(OSIntegrationTestCase): name=self.vm_inst2_name, flavor=self.flavor_creator.flavor_settings.name, userdata=_get_ping_userdata(self.ip1), + security_group_names=self.sec_grp_name, port_settings=[PortConfig( name=self.port_2_name, ip_addrs=[{ @@ -2806,12 +3108,15 @@ class CreateInstanceVolumeTests(OSIntegrationTestCase): guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) self.vm_inst_name = guid + '-inst' - self.nova = nova_utils.nova_client(self.os_creds) - self.neutron = neutron_utils.neutron_client(self.os_creds) + self.nova = nova_utils.nova_client( + self.os_creds, self.os_session) + self.neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) os_image_settings = openstack_tests.cirros_image_settings( name=guid + '-image', image_metadata=self.image_metadata) net_config = openstack_tests.get_priv_net_config( + project_name=self.os_creds.project_name, net_name=guid + '-pub-net', subnet_name=guid + '-pub-subnet', router_name=guid + '-pub-router', external_net=self.ext_net_name, netconf_override=self.netconf_override) @@ -2837,10 +3142,11 @@ class CreateInstanceVolumeTests(OSIntegrationTestCase): self.image_creator.create() # Create Flavor + flavor_config = openstack_tests.get_flavor_config( + name=guid + '-flavor-name', ram=256, disk=1, + vcpus=2, metadata=self.flavor_metadata) self.flavor_creator = OpenStackFlavor( - self.admin_os_creds, - FlavorConfig(name=guid + '-flavor-name', ram=256, disk=1, - vcpus=2, metadata=self.flavor_metadata)) + self.admin_os_creds, flavor_config) self.flavor_creator.create() # Create Network @@ -2933,7 +3239,8 @@ class CreateInstanceVolumeTests(OSIntegrationTestCase): vm_inst = self.inst_creator.create(block=True) self.assertIsNotNone(nova_utils.get_server( - self.nova, self.neutron, vm_inst_settings=instance_settings)) + self.nova, self.neutron, self.keystone, + vm_inst_settings=instance_settings)) self.assertIsNotNone(vm_inst) self.assertEqual(1, len(vm_inst.volume_ids)) @@ -2957,7 +3264,8 @@ class CreateInstanceVolumeTests(OSIntegrationTestCase): vm_inst = self.inst_creator.create(block=True) self.assertIsNotNone(nova_utils.get_server( - self.nova, self.neutron, vm_inst_settings=instance_settings)) + self.nova, self.neutron, self.keystone, + vm_inst_settings=instance_settings)) self.assertIsNotNone(vm_inst) self.assertEqual(2, len(vm_inst.volume_ids)) diff --git a/snaps/openstack/tests/create_keypairs_tests.py b/snaps/openstack/tests/create_keypairs_tests.py index 63e0bcc..44214d0 100644 --- a/snaps/openstack/tests/create_keypairs_tests.py +++ b/snaps/openstack/tests/create_keypairs_tests.py @@ -200,7 +200,7 @@ class CreateKeypairsTests(OSIntegrationTestCase): 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.nova = nova_utils.nova_client(self.os_creds, self.os_session) self.keypair_name = guid self.keypair_creator = None @@ -370,7 +370,7 @@ class CreateKeypairsCleanupTests(OSIntegrationTestCase): 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.nova = nova_utils.nova_client(self.os_creds, self.os_session) self.keypair_name = guid self.keypair_creator = None diff --git a/snaps/openstack/tests/create_network_tests.py b/snaps/openstack/tests/create_network_tests.py index 966cbd0..26c57bd 100644 --- a/snaps/openstack/tests/create_network_tests.py +++ b/snaps/openstack/tests/create_network_tests.py @@ -15,6 +15,8 @@ import unittest import uuid +from neutronclient.common.exceptions import BadRequest + from snaps.config.network import ( NetworkConfig, SubnetConfig, SubnetConfigError, NetworkConfigError, PortConfigError, IPv6Mode) @@ -24,7 +26,7 @@ from snaps.openstack.create_network import ( 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 import neutron_utils, keystone_utils from snaps.openstack.utils.tests import neutron_utils_tests from snaps.openstack.create_network import IPv6Mode as IPv6Mode_old @@ -136,8 +138,7 @@ class SubnetSettingsUnitTests(unittest.TestCase): self.assertIsNone(settings.start) self.assertIsNone(settings.end) self.assertIsNone(settings.enable_dhcp) - self.assertEqual(1, len(settings.dns_nameservers)) - self.assertEqual('8.8.8.8', settings.dns_nameservers[0]) + self.assertEqual(0, len(settings.dns_nameservers)) self.assertIsNone(settings.host_routes) self.assertIsNone(settings.destination) self.assertIsNone(settings.nexthop) @@ -154,8 +155,7 @@ class SubnetSettingsUnitTests(unittest.TestCase): self.assertIsNone(settings.end) self.assertIsNone(settings.gateway_ip) self.assertIsNone(settings.enable_dhcp) - self.assertEqual(1, len(settings.dns_nameservers)) - self.assertEqual('8.8.8.8', settings.dns_nameservers[0]) + self.assertEqual(0, len(settings.dns_nameservers)) self.assertIsNone(settings.host_routes) self.assertIsNone(settings.destination) self.assertIsNone(settings.nexthop) @@ -365,11 +365,15 @@ class CreateNetworkSuccessTests(OSIntegrationTestCase): 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, + project_name=self.os_creds.project_name, + net_name="{}-{}".format(guid, 'pub-net'), mtu=999, + subnet_name="{}-{}".format(guid, 'pub-subnet'), + router_name="{}-{}".format(guid, 'pub-router'), + external_net=self.ext_net_name, netconf_override=self.netconf_override) - self.neutron = neutron_utils.neutron_client(self.os_creds) + self.neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) # Initialize for cleanup self.net_creator = None @@ -392,17 +396,19 @@ class CreateNetworkSuccessTests(OSIntegrationTestCase): Tests the creation of an OpenStack network without a router. """ # Create Network - self.net_creator = OpenStackNetwork(self.os_creds, - self.net_config.network_settings) - self.net_creator.create() + self.net_creator = OpenStackNetwork( + self.os_creds, self.net_config.network_settings) + network = self.net_creator.create() # Validate network was created self.assertTrue(neutron_utils_tests.validate_network( - self.neutron, self.net_creator.network_settings.name, True)) + self.neutron, self.keystone, + self.net_creator.network_settings.name, True, + self.os_creds.project_name, mtu=999)) # Validate subnets self.assertTrue(neutron_utils_tests.validate_subnet( - self.neutron, + self.neutron, network, self.net_creator.network_settings.subnet_settings[0].name, self.net_creator.network_settings.subnet_settings[0].cidr, True)) @@ -411,18 +417,22 @@ class CreateNetworkSuccessTests(OSIntegrationTestCase): Tests the creation of an OpenStack network, it's deletion, then cleanup """ # Create Network - self.net_creator = OpenStackNetwork(self.os_creds, - self.net_config.network_settings) + self.net_creator = OpenStackNetwork( + self.os_creds, self.net_config.network_settings) self.net_creator.create() # Validate network was created self.assertTrue(neutron_utils_tests.validate_network( - self.neutron, self.net_creator.network_settings.name, True)) + self.neutron, self.keystone, + self.net_creator.network_settings.name, True, + self.os_creds.project_name, mtu=999)) - neutron_utils.delete_network(self.neutron, - self.net_creator.get_network()) + neutron_utils.delete_network( + self.neutron, self.net_creator.get_network()) self.assertIsNone(neutron_utils.get_network( - self.neutron, network_settings=self.net_creator.network_settings)) + self.neutron, self.keystone, + network_settings=self.net_creator.network_settings, + project_name=self.os_creds.project_name)) # This shall not throw an exception here self.net_creator.clean() @@ -432,9 +442,9 @@ class CreateNetworkSuccessTests(OSIntegrationTestCase): 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() + self.net_creator = OpenStackNetwork( + self.os_creds, self.net_config.network_settings) + network = self.net_creator.create() # Create Router self.router_creator = create_router.OpenStackRouter( @@ -443,17 +453,21 @@ class CreateNetworkSuccessTests(OSIntegrationTestCase): # Validate network was created self.assertTrue(neutron_utils_tests.validate_network( - self.neutron, self.net_creator.network_settings.name, True)) + self.neutron, self.keystone, + self.net_creator.network_settings.name, True, + self.os_creds.project_name, mtu=999)) # Validate subnets self.assertTrue(neutron_utils_tests.validate_subnet( - self.neutron, + self.neutron, network, 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) + self.neutron, self.keystone, + self.router_creator.router_settings.name, + self.os_creds.project_name, True) neutron_utils_tests.validate_interface_router( self.router_creator.get_internal_router_interface(), @@ -466,12 +480,12 @@ class CreateNetworkSuccessTests(OSIntegrationTestCase): OpenStackNetwork object will not create a second. """ # Create Network - self.net_creator = OpenStackNetwork(self.os_creds, - self.net_config.network_settings) + 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 = OpenStackNetwork( + self.os_creds, self.net_config.network_settings) self.net_creator2.create() self.assertEqual(self.net_creator.get_network().id, @@ -479,31 +493,42 @@ class CreateNetworkSuccessTests(OSIntegrationTestCase): def test_create_network_router_admin_user_to_new_project(self): """ - Tests the creation of an OpenStack network and router with the current - user to the admin project. + Tests the creation of an OpenStack network to the the current using + the credentials to the admin project. """ # Create Network/Subnet where the project names have been changed - admin_project_name = self.admin_os_creds.project_name - self.net_config.network_settings.project_name = admin_project_name - self.net_config.network_settings.subnet_settings[0].project_name = \ - admin_project_name - self.net_creator = OpenStackNetwork(self.os_creds, - self.net_config.network_settings) + project_name = self.os_creds.project_name + config = self.net_config.network_settings + config.project_name = project_name + config.subnet_settings[0].project_name = project_name + + self.net_creator = OpenStackNetwork(self.admin_os_creds, config) self.net_creator.create() retrieved_net = neutron_utils.get_network( - self.neutron, network_settings=self.net_config.network_settings) + self.neutron, self.keystone, + network_name=self.net_config.network_settings.name, + project_name=self.os_creds.project_name) self.assertEqual(self.net_creator.get_network().id, retrieved_net.id) + # Initialize with actual credentials + config.project_name = None + config.subnet_settings[0].project_name = None + proj_net_creator = OpenStackNetwork(self.os_creds, config) + proj_net = proj_net_creator.create() + self.assertEqual(retrieved_net, proj_net) + # Create Router - self.net_config.router_settings.project_name = admin_project_name + self.net_config.router_settings.project_name = project_name self.router_creator = create_router.OpenStackRouter( - self.os_creds, self.net_config.router_settings) + self.admin_os_creds, self.net_config.router_settings) self.router_creator.create() retrieved_router = neutron_utils.get_router( - self.neutron, router_settings=self.router_creator.router_settings) + self.neutron, self.keystone, + router_settings=self.router_creator.router_settings, + project_name=self.os_creds.project_name) self.assertEqual( self.router_creator.get_router().id, retrieved_router.id) @@ -517,12 +542,14 @@ class CreateNetworkSuccessTests(OSIntegrationTestCase): self.net_config.network_settings.project_name = new_project_name self.net_config.network_settings.subnet_settings[0].project_name = \ new_project_name - self.net_creator = OpenStackNetwork(self.admin_os_creds, - self.net_config.network_settings) + self.net_creator = OpenStackNetwork( + self.admin_os_creds, self.net_config.network_settings) self.net_creator.create() retrieved_net = neutron_utils.get_network( - self.neutron, network_settings=self.net_config.network_settings) + self.neutron, self.keystone, + network_settings=self.net_config.network_settings, + project_name=self.os_creds.project_name) self.assertEqual(self.net_creator.get_network().id, retrieved_net.id) @@ -533,14 +560,149 @@ class CreateNetworkSuccessTests(OSIntegrationTestCase): self.router_creator.create() retrieved_router = neutron_utils.get_router( - self.neutron, router_settings=self.router_creator.router_settings) + self.neutron, self.keystone, + router_settings=self.router_creator.router_settings, + project_name=self.os_creds.project_name) self.assertEqual( self.router_creator.get_router().id, retrieved_router.id) +class CreateNetworkGatewayTests(OSIntegrationTestCase): + """ + Test for the CreateNetwork class defined in create_nework.py + """ + + def setUp(self): + """ + Sets up object for test + """ + super(self.__class__, self).__start__() + + self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) + + self.neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) + + self.ip_prfx = '10.1.0.' + + # Initialize for cleanup + self.net_creator = None + + def tearDown(self): + """ + Cleans the network + """ + if self.net_creator: + self.net_creator.clean() + + super(self.__class__, self).__clean__() + + def test_create_subnet_default_gateway_ip(self): + """ + Tests the creation of an OpenStack network with a subnet that has a + default value assigned to the gateway IP. + """ + # Create Network + subnet_config = SubnetConfig( + name=self.guid + '-subnet', cidr=self.ip_prfx + '0/24') + net_config = NetworkConfig( + name=self.guid + '-net', subnets=[subnet_config]) + self.net_creator = OpenStackNetwork( + self.os_creds, net_config) + out_net = self.net_creator.create() + + # Validate network was created + self.assertTrue(neutron_utils_tests.validate_network( + self.neutron, self.keystone, + self.net_creator.network_settings.name, True, + self.os_creds.project_name)) + + # Validate subnets + self.assertTrue(neutron_utils_tests.validate_subnet( + self.neutron, out_net, + self.net_creator.network_settings.subnet_settings[0].name, + self.net_creator.network_settings.subnet_settings[0].cidr, True)) + + self.assertEqual(self.ip_prfx + '1', out_net.subnets[0].gateway_ip) + + def test_create_subnet_valid_gateway_ip(self): + """ + Tests the creation of an OpenStack network with a subnet that has a + valid value assigned to the gateway IP. + """ + # Create Network + subnet_config = SubnetConfig( + name=self.guid + '-subnet', cidr=self.ip_prfx + '0/24', + gateway_ip=self.ip_prfx + '2') + net_config = NetworkConfig( + name=self.guid + '-net', subnets=[subnet_config]) + self.net_creator = OpenStackNetwork( + self.os_creds, net_config) + out_net = self.net_creator.create() + + self.assertIsNotNone(out_net) + + get_net = neutron_utils.get_network_by_id(self.neutron, out_net.id) + self.assertIsNotNone(get_net) + + # Validate subnets + self.assertTrue(neutron_utils_tests.validate_subnet( + self.neutron, out_net, + self.net_creator.network_settings.subnet_settings[0].name, + self.net_creator.network_settings.subnet_settings[0].cidr, True)) + + self.assertEqual(self.ip_prfx + '2', out_net.subnets[0].gateway_ip) + + def test_create_subnet_no_gateway(self): + """ + Tests the creation of an OpenStack network with a subnet that has a + valid value assigned to the gateway IP. + """ + # Create Network + subnet_config = SubnetConfig( + name=self.guid + '-subnet', cidr=self.ip_prfx + '0/24', + gateway_ip='none') + net_config = NetworkConfig( + name=self.guid + '-net', subnets=[subnet_config]) + self.net_creator = OpenStackNetwork( + self.os_creds, net_config) + out_net = self.net_creator.create() + + # Validate network was created + self.assertTrue(neutron_utils_tests.validate_network( + self.neutron, self.keystone, + self.net_creator.network_settings.name, True, + self.os_creds.project_name)) + + # Validate subnets + self.assertTrue(neutron_utils_tests.validate_subnet( + self.neutron, out_net, + self.net_creator.network_settings.subnet_settings[0].name, + self.net_creator.network_settings.subnet_settings[0].cidr, True)) + + self.assertIsNone(out_net.subnets[0].gateway_ip) + + def test_create_subnet_invalid_gateway_ip(self): + """ + Tests the creation of an OpenStack network with a subnet that has an + invalid value assigned to the gateway IP. + """ + # Create Network + subnet_config = SubnetConfig( + name=self.guid + '-subnet', cidr=self.ip_prfx + '0/24', + gateway_ip='foo') + net_config = NetworkConfig( + name=self.guid + '-net', subnets=[subnet_config]) + self.net_creator = OpenStackNetwork( + self.os_creds, net_config) + + with self.assertRaises(BadRequest): + self.net_creator.create() + + class CreateNetworkIPv6Tests(OSIntegrationTestCase): """ - Test for the CreateNetwork class defined in create_nework.py when + Test for the CreateNetwork class defined in create_nework.py when """ def setUp(self): @@ -550,7 +712,8 @@ class CreateNetworkIPv6Tests(OSIntegrationTestCase): super(self.__class__, self).__start__() self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) - self.neutron = neutron_utils.neutron_client(self.os_creds) + self.neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) # Initialize for cleanup self.net_creator = None @@ -580,7 +743,9 @@ class CreateNetworkIPv6Tests(OSIntegrationTestCase): # Validate network was created self.assertTrue(neutron_utils_tests.validate_network( - self.neutron, self.net_creator.network_settings.name, True)) + self.neutron, self.keystone, + self.net_creator.network_settings.name, True, + self.os_creds.project_name)) network = self.net_creator.get_network() self.assertEqual(1, len(network.subnets)) @@ -627,7 +792,7 @@ class CreateNetworkIPv6Tests(OSIntegrationTestCase): self.assertEqual(subnet4_settings.name, subnet4.name) self.assertEqual(subnet4_settings.cidr, subnet4.cidr) self.assertEqual(4, subnet4.ip_version) - self.assertEqual(1, len(subnet4.dns_nameservers)) + self.assertEqual(0, len(subnet4.dns_nameservers)) # Validate IPv6 subnet self.assertEqual(network.id, subnet6.network_id) @@ -649,9 +814,13 @@ class CreateNetworkTypeTests(OSComponentTestCase): """ guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) self.net_config = openstack_tests.get_pub_net_config( + project_name=self.os_creds.project_name, net_name=guid + '-pub-net', subnet_name=guid + '-pub-subnet') - self.neutron = neutron_utils.neutron_client(self.os_creds) + self.neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) + self.keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) # Initialize for cleanup self.net_creator = None @@ -663,6 +832,8 @@ class CreateNetworkTypeTests(OSComponentTestCase): if self.net_creator: self.net_creator.clean() + super(self.__class__, self).__clean__() + def test_create_network_type_vlan(self): """ Tests the creation of an OpenStack network of type vlan. @@ -680,7 +851,8 @@ class CreateNetworkTypeTests(OSComponentTestCase): # Validate network was created self.assertTrue(neutron_utils_tests.validate_network( - self.neutron, net_settings.name, True)) + self.neutron, self.keystone, net_settings.name, True, + self.os_creds.project_name)) self.assertEquals(network_type, network.type) @@ -709,7 +881,8 @@ class CreateNetworkTypeTests(OSComponentTestCase): # Validate network was created self.assertTrue(neutron_utils_tests.validate_network( - self.neutron, net_settings.name, True)) + self.neutron, self.keystone, net_settings.name, True, + self.os_creds.project_name)) self.assertEquals(network_type, network.type) @@ -730,7 +903,8 @@ class CreateNetworkTypeTests(OSComponentTestCase): # Validate network was created self.assertTrue(neutron_utils_tests.validate_network( - self.neutron, net_settings.name, True)) + self.neutron, self.keystone, net_settings.name, True, + self.os_creds.project_name)) self.assertEqual(network_type, network.type) @@ -753,7 +927,8 @@ class CreateNetworkTypeTests(OSComponentTestCase): # Validate network was created self.assertTrue(neutron_utils_tests.validate_network( - self.neutron, net_settings.name, True)) + self.neutron, self.keystone, net_settings.name, True, + self.os_creds.project_name)) self.assertEquals(network_type, network.type) @@ -771,3 +946,94 @@ class CreateNetworkTypeTests(OSComponentTestCase): self.net_creator = OpenStackNetwork(self.os_creds, net_settings) with self.assertRaises(Exception): self.net_creator.create() + + +class CreateMultipleNetworkTests(OSIntegrationTestCase): + """ + Test for the CreateNetwork class and how it interacts with networks + groups within other projects with the same name + """ + + 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( + project_name=self.os_creds.project_name, + net_name=guid + '-pub-net', subnet_name=guid + '-pub-subnet') + + self.neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) + + # Initialize for cleanup + self.admin_net_creator = None + self.proj_net_creator = None + + def tearDown(self): + """ + Cleans the network + """ + if self.admin_net_creator: + self.admin_net_creator.clean() + if self.proj_net_creator: + self.proj_net_creator.clean() + + super(self.__class__, self).__clean__() + + def test_network_same_name_diff_proj(self): + """ + Tests the creation of an OpenStackNetwork with the same name + within a different project/tenant when not configured but implied by + the OSCreds. + """ + # Create Network + + self.admin_net_creator = OpenStackNetwork( + self.admin_os_creds, self.net_config.network_settings) + self.admin_net_creator.create() + + self.proj_net_creator = OpenStackNetwork( + self.os_creds, self.net_config.network_settings) + self.proj_net_creator.create() + + self.assertNotEqual( + self.admin_net_creator.get_network().id, + self.proj_net_creator.get_network().id) + + admin_creator2 = OpenStackNetwork( + self.admin_os_creds, self.net_config.network_settings) + admin_creator2.create() + self.assertEqual( + self.admin_net_creator.get_network(), admin_creator2.get_network()) + + proj_creator2 = OpenStackNetwork( + self.os_creds, self.net_config.network_settings) + proj_creator2.create() + self.assertEqual(self.proj_net_creator.get_network(), + proj_creator2.get_network()) + + def test_network_create_by_admin_to_different_project(self): + """ + Tests the creation of an OpenStackNetwork by the admin user and + initialize again with tenant credentials. + """ + # Create Network + + net_settings = self.net_config.network_settings + + net_settings.project_name = self.os_creds.project_name + + self.admin_net_creator = OpenStackNetwork( + self.admin_os_creds, net_settings) + self.admin_net_creator.create() + + self.proj_net_creator = OpenStackNetwork( + self.os_creds, self.net_config.network_settings) + self.proj_net_creator.create() + + self.assertEqual( + self.admin_net_creator.get_network().id, + self.proj_net_creator.get_network().id) diff --git a/snaps/openstack/tests/create_project_tests.py b/snaps/openstack/tests/create_project_tests.py index 2c10311..1e3a972 100644 --- a/snaps/openstack/tests/create_project_tests.py +++ b/snaps/openstack/tests/create_project_tests.py @@ -99,7 +99,8 @@ class CreateProjectSuccessTests(OSComponentTestCase): name=guid + '-name', domain=self.os_creds.project_domain_name) - self.keystone = keystone_utils.keystone_client(self.os_creds) + self.keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) # Initialize for cleanup self.project_creator = None @@ -111,6 +112,8 @@ class CreateProjectSuccessTests(OSComponentTestCase): if self.project_creator: self.project_creator.clean() + super(self.__class__, self).__clean__() + def test_create_project_bad_domain(self): """ Tests the creation of an OpenStack project with an invalid domain @@ -140,6 +143,38 @@ class CreateProjectSuccessTests(OSComponentTestCase): self.assertTrue(validate_project(self.keystone, self.project_settings, created_project)) + def test_create_project_quota_override(self): + """ + Tests the creation of an OpenStack project with new quotas. + """ + quotas = { + 'cores': 4, 'instances': 5, 'injected_files': 6, + 'injected_file_content_bytes': 60000, 'ram': 70000, 'fixed_ips': 7, + 'key_pairs': 8} + self.project_settings.quotas = quotas + 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_settings=self.project_settings) + self.assertIsNotNone(retrieved_project) + self.assertEqual(created_project, retrieved_project) + self.assertTrue(validate_project(self.keystone, self.project_settings, + created_project)) + + nova = nova_utils.nova_client(self.os_creds, self.os_session) + new_quotas = nova_utils.get_compute_quotas(nova, created_project.id) + + self.assertEqual(4, new_quotas.cores) + self.assertEqual(5, new_quotas.instances) + self.assertEqual(6, new_quotas.injected_files) + self.assertEqual(60000, new_quotas.injected_file_content_bytes) + self.assertEqual(70000, new_quotas.ram) + self.assertEqual(7, new_quotas.fixed_ips) + self.assertEqual(8, new_quotas.key_pairs) + def test_create_project_2x(self): """ Tests the creation of an OpenStack project twice to ensure it only @@ -214,12 +249,12 @@ class CreateProjectSuccessTests(OSComponentTestCase): self.assertEqual(update_network_quotas, self.project_creator.get_network_quotas()) - nova = nova_utils.nova_client(self.os_creds) + nova = nova_utils.nova_client(self.os_creds, self.os_session) new_compute_quotas = nova_utils.get_compute_quotas( nova, self.project_creator.get_project().id) self.assertEqual(update_compute_quotas, new_compute_quotas) - neutron = neutron_utils.neutron_client(self.os_creds) + neutron = neutron_utils.neutron_client(self.os_creds, self.os_session) new_network_quotas = neutron_utils.get_network_quotas( neutron, self.project_creator.get_project().id) self.assertEqual(update_network_quotas, new_network_quotas) @@ -241,7 +276,8 @@ class CreateProjectUserTests(OSComponentTestCase): name=self.guid + '-name', domain=self.os_creds.project_domain_name) - self.keystone = keystone_utils.keystone_client(self.os_creds) + self.keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) # Initialize for cleanup self.project_creator = None @@ -262,6 +298,8 @@ class CreateProjectUserTests(OSComponentTestCase): if self.project_creator: self.project_creator.clean() + super(self.__class__, self).__clean__() + def test_create_project_sec_grp_one_user(self): """ Tests the creation of an OpenStack object to a project with a new users diff --git a/snaps/openstack/tests/create_qos_tests.py b/snaps/openstack/tests/create_qos_tests.py index 68737f8..b3d7979 100644 --- a/snaps/openstack/tests/create_qos_tests.py +++ b/snaps/openstack/tests/create_qos_tests.py @@ -122,12 +122,14 @@ class CreateQoSTests(OSIntegrationTestCase): super(self.__class__, self).__start__() guid = uuid.uuid4() - self.qos_settings = QoSConfig( + qos_settings = QoSConfig( name=self.__class__.__name__ + '-' + str(guid), consumer=Consumer.both) - self.cinder = cinder_utils.cinder_client(self.os_creds) - self.qos_creator = None + self.cinder = cinder_utils.cinder_client( + self.admin_os_creds, self.admin_os_session) + self.qos_creator = create_qos.OpenStackQoS( + self.admin_os_creds, qos_settings) def tearDown(self): """ @@ -143,13 +145,11 @@ class CreateQoSTests(OSIntegrationTestCase): Tests the creation of an OpenStack qos. """ # Create QoS - self.qos_creator = create_qos.OpenStackQoS( - self.os_creds, self.qos_settings) created_qos = self.qos_creator.create() self.assertIsNotNone(created_qos) retrieved_qos = cinder_utils.get_qos( - self.cinder, qos_settings=self.qos_settings) + self.cinder, qos_settings=self.qos_creator.qos_settings) self.assertIsNotNone(retrieved_qos) self.assertEqual(created_qos, retrieved_qos) @@ -160,13 +160,11 @@ class CreateQoSTests(OSIntegrationTestCase): clean() does not raise an Exception. """ # Create QoS - self.qos_creator = create_qos.OpenStackQoS( - self.os_creds, self.qos_settings) created_qos = self.qos_creator.create() self.assertIsNotNone(created_qos) retrieved_qos = cinder_utils.get_qos( - self.cinder, qos_settings=self.qos_settings) + self.cinder, qos_settings=self.qos_creator.qos_settings) self.assertIsNotNone(retrieved_qos) self.assertEqual(created_qos, retrieved_qos) @@ -174,7 +172,7 @@ class CreateQoSTests(OSIntegrationTestCase): cinder_utils.delete_qos(self.cinder, created_qos) self.assertIsNone(cinder_utils.get_qos( - self.cinder, qos_settings=self.qos_settings)) + self.cinder, qos_settings=self.qos_creator.qos_settings)) # Must not raise an exception when attempting to cleanup non-existent # qos @@ -186,16 +184,14 @@ class CreateQoSTests(OSIntegrationTestCase): Tests the creation of an OpenStack qos when one already exists. """ # Create QoS - self.qos_creator = create_qos.OpenStackQoS( - self.os_creds, self.qos_settings) qos1 = self.qos_creator.create() retrieved_qos = cinder_utils.get_qos( - self.cinder, qos_settings=self.qos_settings) + self.cinder, qos_settings=self.qos_creator.qos_settings) self.assertEqual(qos1, retrieved_qos) # Should be retrieving the instance data os_qos_2 = create_qos.OpenStackQoS( - self.os_creds, self.qos_settings) + self.admin_os_creds, self.qos_creator.qos_settings) qos2 = os_qos_2.create() self.assertEqual(qos1, qos2) diff --git a/snaps/openstack/tests/create_router_tests.py b/snaps/openstack/tests/create_router_tests.py index 09471a3..a305cf8 100644 --- a/snaps/openstack/tests/create_router_tests.py +++ b/snaps/openstack/tests/create_router_tests.py @@ -15,19 +15,23 @@ import unittest import uuid -from snaps.config.network import PortConfig, NetworkConfig +from snaps.config.network import PortConfig, NetworkConfig, PortConfigError from snaps.config.router import RouterConfigError, RouterConfig +from snaps.config.security_group import SecurityGroupConfig from snaps.openstack import create_network from snaps.openstack import create_router from snaps.openstack.create_network import OpenStackNetwork -from snaps.openstack.create_router import RouterSettings +from snaps.openstack.create_router import ( + RouterSettings, OpenStackRouter, RouterCreationError) +from snaps.openstack.create_security_group import OpenStackSecurityGroup from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase -from snaps.openstack.utils import neutron_utils, settings_utils +from snaps.openstack.utils import neutron_utils, settings_utils, keystone_utils __author__ = 'mmakati' cidr1 = '10.200.201.0/24' cidr2 = '10.200.202.0/24' +cidr3 = '10.200.203.0/24' static_gateway_ip1 = '10.200.201.1' static_gateway_ip2 = '10.200.202.1' @@ -128,7 +132,8 @@ class CreateRouterSuccessTests(OSIntegrationTestCase): self.router_creator = None self.network_creator1 = None self.network_creator2 = None - self.neutron = neutron_utils.neutron_client(self.os_creds) + self.neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) def tearDown(self): """ @@ -152,12 +157,13 @@ class CreateRouterSuccessTests(OSIntegrationTestCase): router_settings = RouterConfig( 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.OpenStackRouter( + self.os_creds, router_settings) self.router_creator.create() - router = neutron_utils.get_router(self.neutron, - router_settings=router_settings) + router = neutron_utils.get_router( + self.neutron, self.keystone, router_settings=router_settings, + project_name=self.os_creds.project_name) self.assertIsNotNone(router) self.assertEqual(self.router_creator.get_router(), router) @@ -177,32 +183,34 @@ class CreateRouterSuccessTests(OSIntegrationTestCase): self.admin_os_creds, router_settings) self.router_creator.create() - router = neutron_utils.get_router(self.neutron, - router_settings=router_settings) + router = neutron_utils.get_router( + self.neutron, self.keystone, router_settings=router_settings, + project_name=self.os_creds.project_name) self.assertIsNotNone(router) - self.assertEqual(self.router_creator.get_router(), router) + self.assertEqual(self.router_creator.get_router().id, router.id) self.check_router_recreation(router, router_settings) - def test_create_router_new_user_to_admin_project(self): + def test_create_router_new_user_as_admin_project(self): """ Test creation of a most basic router with the new user pointing to the admin project. """ router_settings = RouterConfig( name=self.guid + '-pub-router', external_gateway=self.ext_net_name, - project_name=self.admin_os_creds.project_name) + project_name=self.os_creds.project_name) self.router_creator = create_router.OpenStackRouter( - self.os_creds, router_settings) + self.admin_os_creds, router_settings) self.router_creator.create() - router = neutron_utils.get_router(self.neutron, - router_settings=router_settings) + router = neutron_utils.get_router( + self.neutron, self.keystone, router_settings=router_settings, + project_name=self.os_creds.project_name) self.assertIsNotNone(router) - self.assertEqual(self.router_creator.get_router(), router) + self.assertEqual(self.router_creator.get_router().id, router.id) self.check_router_recreation(router, router_settings) @@ -219,18 +227,67 @@ class CreateRouterSuccessTests(OSIntegrationTestCase): created_router = self.router_creator.create() self.assertIsNotNone(created_router) retrieved_router = neutron_utils.get_router( - self.neutron, router_settings=self.router_settings) + self.neutron, self.keystone, router_settings=self.router_settings, + project_name=self.os_creds.project_name) self.assertIsNotNone(retrieved_router) neutron_utils.delete_router(self.neutron, created_router) retrieved_router = neutron_utils.get_router( - self.neutron, router_settings=self.router_settings) + self.neutron, self.keystone, router_settings=self.router_settings, + project_name=self.os_creds.project_name) self.assertIsNone(retrieved_router) # Should not raise an exception self.router_creator.clean() + def test_create_with_internal_sub(self): + """ + Test internal_subnets works. + """ + network_settings1 = NetworkConfig( + name=self.guid + '-pub-net1', + subnet_settings=[ + create_network.SubnetConfig( + cidr=cidr1, name=self.guid + '-pub-subnet1', + gateway_ip=static_gateway_ip1)]) + self.network_creator1 = OpenStackNetwork(self.os_creds, + network_settings1) + + self.network_creator1.create() + self.router_settings = RouterConfig( + name=self.guid + '-pub-router', external_gateway=self.ext_net_name, + internal_subnets=[network_settings1.subnet_settings[0].name]) + + self.router_creator = create_router.OpenStackRouter( + self.os_creds, self.router_settings) + created_router = self.router_creator.create() + self.assertIsNotNone(created_router) + + def test_create_with_invalid_internal_sub(self): + """ + Test adding an internal subnet owned by admin which should fail. + """ + network_settings1 = NetworkConfig( + name=self.guid + '-pub-net1', + subnet_settings=[ + create_network.SubnetConfig( + cidr=cidr1, name=self.guid + '-pub-subnet1', + gateway_ip=static_gateway_ip1)]) + self.network_creator1 = OpenStackNetwork(self.admin_os_creds, + network_settings1) + + self.network_creator1.create() + self.router_settings = RouterConfig( + name=self.guid + '-pub-router', external_gateway=self.ext_net_name, + internal_subnets=[network_settings1.subnet_settings[0].name]) + + self.router_creator = create_router.OpenStackRouter( + self.os_creds, self.router_settings) + + with self.assertRaises(RouterCreationError): + self.router_creator.create() + def test_create_router_admin_state_false(self): """ Test creation of a basic router with admin state down. @@ -242,8 +299,9 @@ class CreateRouterSuccessTests(OSIntegrationTestCase): router_settings) self.router_creator.create() - router = neutron_utils.get_router(self.neutron, - router_settings=router_settings) + router = neutron_utils.get_router( + self.neutron, self.keystone, router_settings=router_settings, + project_name=self.os_creds.project_name) self.assertIsNotNone(router) self.assertEqual(self.router_creator.get_router(), router) @@ -262,7 +320,8 @@ class CreateRouterSuccessTests(OSIntegrationTestCase): self.router_creator.create() router = neutron_utils.get_router( - self.neutron, router_settings=router_settings) + self.neutron, self.keystone, router_settings=router_settings, + project_name=self.os_creds.project_name) self.assertIsNotNone(router) self.assertEqual(self.router_creator.get_router(), router) @@ -303,25 +362,24 @@ class CreateRouterSuccessTests(OSIntegrationTestCase): network_settings1.subnet_settings[0].name, 'ip': static_gateway_ip1 }], - network_name=network_settings1.name, - project_name=self.os_creds.project_name), + network_name=network_settings1.name), create_network.PortConfig( name=self.guid + '-port2', ip_addrs=[{ 'subnet_name': network_settings2.subnet_settings[0].name, 'ip': static_gateway_ip2 }], - network_name=network_settings2.name, - project_name=self.os_creds.project_name)] + network_name=network_settings2.name)] router_settings = RouterConfig( 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.OpenStackRouter( + self.os_creds, router_settings) self.router_creator.create() router = neutron_utils.get_router( - self.neutron, router_settings=router_settings) + self.neutron, self.keystone, router_settings=router_settings, + project_name=self.os_creds.project_name) self.assertEqual(router, self.router_creator.get_router()) @@ -355,8 +413,7 @@ class CreateRouterSuccessTests(OSIntegrationTestCase): ip_addrs=[{ 'subnet_name': network_settings.subnet_settings[0].name, 'ip': static_gateway_ip1}], - network_name=network_settings.name, - project_name=self.os_creds.project_name)] + network_name=network_settings.name)] router_settings = RouterConfig( name=self.guid + '-pub-router', external_gateway=self.ext_net_name, @@ -366,12 +423,58 @@ class CreateRouterSuccessTests(OSIntegrationTestCase): self.router_creator.create() router = neutron_utils.get_router( - self.neutron, router_settings=router_settings) + self.neutron, self.keystone, router_settings=router_settings, + project_name=self.os_creds.project_name) self.assertEquals(router, self.router_creator.get_router()) self.check_router_recreation(router, router_settings) + def test_create_router_with_ext_port(self): + """ + Test creation of a router with a port to an external network as an + 'admin' user. + """ + port_settings = [ + create_network.PortConfig( + name=self.guid + '-port1', + network_name=self.ext_net_name)] + + router_settings = RouterConfig( + name=self.guid + '-pub-router', port_settings=port_settings) + self.router_creator = create_router.OpenStackRouter( + self.admin_os_creds, router_settings) + self.router_creator.create() + + admin_neutron = neutron_utils.neutron_client( + self.admin_os_creds, self.admin_os_session) + admin_keystone = keystone_utils.keystone_client( + self.admin_os_creds, self.admin_os_session) + router = neutron_utils.get_router( + admin_neutron, admin_keystone, router_settings=router_settings, + project_name=self.admin_os_creds.project_name) + + self.assertIsNotNone(router) + self.assertEquals(router, self.router_creator.get_router()) + + ext_net = neutron_utils.get_network( + admin_neutron, admin_keystone, network_name=self.ext_net_name) + + self.assertIsNotNone(ext_net) + self.assertIsNotNone(router.port_subnets) + + id_found = False + for port, subnets in router.port_subnets: + self.assertIsNotNone(subnets) + self.assertIsNotNone(port) + + if ext_net.id == port.network_id: + id_found = True + for subnet in subnets: + self.assertIsNotNone(subnet) + self.assertEqual(ext_net.id, subnet.network_id) + self.assertTrue(id_found) + def check_router_recreation(self, router, orig_settings): """ Validates the derived RouterConfig with the original @@ -421,6 +524,7 @@ class CreateRouterNegativeTests(OSIntegrationTestCase): super(self.__class__, self).__start__() self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) + self.network_creator = None self.router_creator = None def tearDown(self): @@ -430,6 +534,9 @@ class CreateRouterNegativeTests(OSIntegrationTestCase): if self.router_creator: self.router_creator.clean() + if self.network_creator: + self.network_creator.clean() + super(self.__class__, self).__clean__() def test_create_router_noname(self): @@ -454,3 +561,313 @@ class CreateRouterNegativeTests(OSIntegrationTestCase): self.router_creator = create_router.OpenStackRouter( self.os_creds, router_settings) self.router_creator.create() + + def test_create_router_admin_ports(self): + """ + Test creation of a router with ports to subnets owned by the admin + project + """ + network_settings = NetworkConfig( + name=self.guid + '-pub-net1', + subnet_settings=[ + create_network.SubnetConfig( + cidr=cidr1, name=self.guid + '-pub-subnet1', + gateway_ip=static_gateway_ip1)]) + self.network_creator = OpenStackNetwork( + self.admin_os_creds, network_settings) + self.network_creator.create() + + port_settings = [ + create_network.PortConfig( + 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 = RouterConfig( + 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) + + with self.assertRaises(PortConfigError): + self.router_creator.create() + + +class CreateMultipleRouterTests(OSIntegrationTestCase): + """ + Test for the OpenStackRouter class and how it interacts with routers + groups within other projects with the same name + """ + + def setUp(self): + """ + Initializes objects used for router testing + """ + super(self.__class__, self).__start__() + + self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) + self.admin_router_creator = None + self.proj_router_creator = None + self.neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) + + network_settings = NetworkConfig( + name=self.guid + '-pub-net', shared=True, + subnet_settings=[ + create_network.SubnetConfig( + cidr=cidr1, name=self.guid + '-pub-subnet', + gateway_ip=static_gateway_ip1)]) + + self.network_creator = OpenStackNetwork( + self.admin_os_creds, network_settings) + self.network_creator.create() + + def tearDown(self): + """ + Cleans the remote OpenStack objects used for router testing + """ + if self.admin_router_creator: + self.admin_router_creator.clean() + + if self.proj_router_creator: + self.proj_router_creator.clean() + + if self.network_creator: + self.network_creator.clean() + + super(self.__class__, self).__clean__() + + def test_router_same_name_diff_proj(self): + """ + Tests the creation of an OpenStackNetwork with the same name + within a different project/tenant when not configured but implied by + the OSCreds. + """ + # Create Router + + router_config = RouterConfig(name=self.guid + '-router') + self.admin_router_creator = OpenStackRouter( + self.admin_os_creds, router_config) + self.admin_router_creator.create() + + self.proj_router_creator = OpenStackRouter( + self.os_creds, router_config) + self.proj_router_creator.create() + + self.assertNotEqual( + self.admin_router_creator.get_router().id, + self.proj_router_creator.get_router().id) + + admin_creator2 = OpenStackRouter( + self.admin_os_creds, router_config) + admin_creator2.create() + self.assertEqual( + self.admin_router_creator.get_router(), + admin_creator2.get_router()) + + proj_creator2 = OpenStackRouter(self.os_creds, router_config) + proj_creator2.create() + self.assertEqual(self.proj_router_creator.get_router(), + proj_creator2.get_router()) + + def test_router_create_by_admin_to_different_project(self): + """ + Tests the creation of an OpenStackRouter by the admin user and + initialize again with tenant credentials. + """ + # Create Network + + admin_router_config = RouterConfig( + name=self.guid + '-router', + project_name=self.os_creds.project_name) + + self.admin_router_creator = OpenStackRouter( + self.admin_os_creds, admin_router_config) + self.admin_router_creator.create() + + proj_router_config = RouterConfig( + name=self.guid + '-router', + project_name=self.os_creds.project_name) + + self.proj_router_creator = OpenStackRouter( + self.os_creds, proj_router_config) + self.proj_router_creator.create() + + self.assertEqual( + self.admin_router_creator.get_router().id, + self.proj_router_creator.get_router().id) + + +class CreateRouterSecurityGroupTests(OSIntegrationTestCase): + """ + Class for testing routers with ports containing security groups + """ + + 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_creator = None + + self.sec_grp_creator = OpenStackSecurityGroup( + self.os_creds, SecurityGroupConfig(name=self.guid + '-sec_grp')) + self.sec_grp_creator.create() + + self.neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) + + def tearDown(self): + """ + Cleans the remote OpenStack objects used for router testing + """ + if self.router_creator: + self.router_creator.clean() + + if self.network_creator: + self.network_creator.clean() + + if self.sec_grp_creator: + self.sec_grp_creator.clean() + + super(self.__class__, self).__clean__() + + def test_create_router_secure_port(self): + """ + Test creation of a router with a port that has a security group. + """ + network_settings = NetworkConfig( + name=self.guid + '-pub-net1', + subnet_settings=[ + create_network.SubnetConfig( + cidr=cidr1, name=self.guid + '-pub-subnet1')]) + self.network_creator = OpenStackNetwork( + self.os_creds, network_settings) + self.network_creator.create() + + port_settings = [ + create_network.PortConfig( + name=self.guid + '-port1', + ip_addrs=[{ + 'subnet_name': network_settings.subnet_settings[0].name, + 'ip': static_gateway_ip1}], + network_name=network_settings.name, + security_groups=[self.sec_grp_creator.sec_grp_settings.name])] + + router_settings = RouterConfig( + 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() + + +class CreateRouterSharedNetworksTests(OSIntegrationTestCase): + """ + Class for testing routers external and/or shared networks + """ + + 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 + + ext_network_settings = NetworkConfig( + name=self.guid + '-ext-net', + external=True, + subnet_settings=[ + create_network.SubnetConfig( + cidr=cidr1, name=self.guid + '-ext-subnet1')]) + self.ext_network_creator = OpenStackNetwork( + self.admin_os_creds, ext_network_settings) + self.ext_network_creator.create() + + shared_network_settings = NetworkConfig( + name=self.guid + '-shared-net', + shared=True, + subnet_settings=[ + create_network.SubnetConfig( + cidr=cidr2, name=self.guid + '-shared-subnet1')]) + self.shared_network_creator = OpenStackNetwork( + self.admin_os_creds, shared_network_settings) + self.shared_network_creator.create() + + overlay_network_settings = NetworkConfig( + name=self.guid + '-overlay-net', + subnet_settings=[ + create_network.SubnetConfig( + cidr=cidr3, name=self.guid + '-overlay-subnet1')]) + self.overlay_network_creator = OpenStackNetwork( + self.os_creds, overlay_network_settings) + self.overlay_network_creator.create() + + self.neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) + + def tearDown(self): + """ + Cleans the remote OpenStack objects used for router testing + """ + if self.router_creator: + self.router_creator.clean() + + if self.overlay_network_creator: + self.overlay_network_creator.clean() + + if self.shared_network_creator: + self.shared_network_creator.clean() + + if self.ext_network_creator: + self.ext_network_creator.clean() + + super(self.__class__, self).__clean__() + + def test_create_router_external(self): + """ + Test creation of a router with a custom external network created by + admin. + """ + router_settings = RouterConfig( + name=self.guid + '-pub-router', + external_gateway=self.ext_network_creator.get_network().name) + self.router_creator = create_router.OpenStackRouter( + self.os_creds, router_settings) + self.router_creator.create() + + def test_create_router_port_external(self): + """ + Test creation of a router with a port to an custom external network + created by admin. + """ + router_settings = RouterConfig( + name=self.guid + '-pub-router', + network_name=self.ext_network_creator.get_network().name) + self.router_creator = create_router.OpenStackRouter( + self.os_creds, router_settings) + self.router_creator.create() + + def test_create_router_port_shared(self): + """ + Test creation of a router with a port to an custom shared network + created by admin. + """ + port_settings = [ + create_network.PortConfig( + name=self.guid + '-port1', + network_name=self.shared_network_creator.get_network().name)] + + router_settings = RouterConfig( + name=self.guid + '-pub-router', + port_settings=port_settings) + self.router_creator = create_router.OpenStackRouter( + self.os_creds, router_settings) + self.router_creator.create() diff --git a/snaps/openstack/tests/create_security_group_tests.py b/snaps/openstack/tests/create_security_group_tests.py index 090d736..dc632b7 100644 --- a/snaps/openstack/tests/create_security_group_tests.py +++ b/snaps/openstack/tests/create_security_group_tests.py @@ -21,7 +21,7 @@ from snaps.config.security_group import ( from snaps.openstack import create_security_group from snaps.openstack.create_security_group import ( SecurityGroupSettings, SecurityGroupRuleSettings, Direction, Ethertype, - Protocol) + Protocol, OpenStackSecurityGroup) from snaps.openstack.tests import validation_utils from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase from snaps.openstack.utils import neutron_utils @@ -210,7 +210,8 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) self.sec_grp_name = guid + 'name' - self.neutron = neutron_utils.neutron_client(self.os_creds) + self.neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) # Initialize for cleanup self.sec_grp_creator = None @@ -228,7 +229,7 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): """ Tests the creation of an OpenStack Security Group without custom rules. """ - # Create Image + # Create Security Group sec_grp_settings = SecurityGroupConfig(name=self.sec_grp_name, description='hello group') self.sec_grp_creator = create_security_group.OpenStackSecurityGroup( @@ -236,7 +237,7 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): self.sec_grp_creator.create() sec_grp = neutron_utils.get_security_group( - self.neutron, sec_grp_settings=sec_grp_settings) + self.neutron, self.keystone, sec_grp_settings=sec_grp_settings) self.assertIsNotNone(sec_grp) validation_utils.objects_equivalent( @@ -249,23 +250,24 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): self.assertTrue( validate_sec_grp( - self.neutron, self.sec_grp_creator.sec_grp_settings, + self.neutron, self.keystone, + self.sec_grp_creator.sec_grp_settings, self.sec_grp_creator.get_security_group())) def test_create_group_admin_user_to_new_project(self): """ Tests the creation of an OpenStack Security Group without custom rules. """ - # Create Image + # Create Security Group sec_grp_settings = SecurityGroupConfig( name=self.sec_grp_name, description='hello group', - project_name=self.admin_os_creds.project_name) - self.sec_grp_creator = create_security_group.OpenStackSecurityGroup( - self.os_creds, sec_grp_settings) + project_name=self.os_creds.project_name) + self.sec_grp_creator = OpenStackSecurityGroup( + self.admin_os_creds, sec_grp_settings) self.sec_grp_creator.create() sec_grp = neutron_utils.get_security_group( - self.neutron, sec_grp_settings=sec_grp_settings) + self.neutron, self.keystone, sec_grp_settings=sec_grp_settings) self.assertIsNotNone(sec_grp) validation_utils.objects_equivalent( @@ -278,14 +280,25 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): self.assertTrue( validate_sec_grp( - self.neutron, self.sec_grp_creator.sec_grp_settings, + self.neutron, self.keystone, + self.sec_grp_creator.sec_grp_settings, self.sec_grp_creator.get_security_group(), rules)) + self.assertEqual(self.sec_grp_creator.get_security_group().id, + sec_grp.id) + + proj_creator = OpenStackSecurityGroup( + self.os_creds, SecurityGroupConfig(name=self.sec_grp_name)) + proj_creator.create() + + self.assertEqual(self.sec_grp_creator.get_security_group().id, + proj_creator.get_security_group().id) + def test_create_group_new_user_to_admin_project(self): """ Tests the creation of an OpenStack Security Group without custom rules. """ - # Create Image + # Create Security Group sec_grp_settings = SecurityGroupConfig( name=self.sec_grp_name, description='hello group', project_name=self.os_creds.project_name) @@ -294,7 +307,7 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): self.sec_grp_creator.create() sec_grp = neutron_utils.get_security_group( - self.neutron, sec_grp_settings=sec_grp_settings) + self.neutron, self.keystone, sec_grp_settings=sec_grp_settings) self.assertIsNotNone(sec_grp) validation_utils.objects_equivalent( @@ -307,14 +320,15 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): self.assertTrue( validate_sec_grp( - self.neutron, self.sec_grp_creator.sec_grp_settings, + self.neutron, self.keystone, + self.sec_grp_creator.sec_grp_settings, self.sec_grp_creator.get_security_group(), rules)) def test_create_delete_group(self): """ Tests the creation of an OpenStack Security Group without custom rules. """ - # Create Image + # Create Security Group sec_grp_settings = SecurityGroupConfig(name=self.sec_grp_name, description='hello group') self.sec_grp_creator = create_security_group.OpenStackSecurityGroup( @@ -324,12 +338,13 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): self.assertTrue( validate_sec_grp( - self.neutron, self.sec_grp_creator.sec_grp_settings, + self.neutron, self.keystone, + self.sec_grp_creator.sec_grp_settings, self.sec_grp_creator.get_security_group())) neutron_utils.delete_security_group(self.neutron, created_sec_grp) self.assertIsNone(neutron_utils.get_security_group( - self.neutron, + self.neutron, self.keystone, sec_grp_settings=self.sec_grp_creator.sec_grp_settings)) self.sec_grp_creator.clean() @@ -339,7 +354,7 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): Tests the creation of an OpenStack Security Group with one simple custom rule. """ - # Create Image + # Create Security Group sec_grp_rule_settings = list() sec_grp_rule_settings.append( SecurityGroupRuleConfig( @@ -353,7 +368,7 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): self.sec_grp_creator.create() sec_grp = neutron_utils.get_security_group( - self.neutron, sec_grp_settings=sec_grp_settings) + self.neutron, self.keystone, sec_grp_settings=sec_grp_settings) validation_utils.objects_equivalent( self.sec_grp_creator.get_security_group(), sec_grp) rules = neutron_utils.get_rules_by_security_group( @@ -364,7 +379,8 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): self.assertTrue( validate_sec_grp( - self.neutron, self.sec_grp_creator.sec_grp_settings, + self.neutron, self.keystone, + self.sec_grp_creator.sec_grp_settings, self.sec_grp_creator.get_security_group(), rules)) def test_create_group_with_one_complex_rule(self): @@ -372,7 +388,7 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): Tests the creation of an OpenStack Security Group with one simple custom rule. """ - # Create Image + # Create Security Group sec_grp_rule_settings = list() sec_grp_rule_settings.append( SecurityGroupRuleConfig( @@ -388,7 +404,7 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): self.sec_grp_creator.create() sec_grp = neutron_utils.get_security_group( - self.neutron, sec_grp_settings=sec_grp_settings) + self.neutron, self.keystone, sec_grp_settings=sec_grp_settings) validation_utils.objects_equivalent( self.sec_grp_creator.get_security_group(), sec_grp) rules = neutron_utils.get_rules_by_security_group( @@ -399,7 +415,8 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): self.assertTrue( validate_sec_grp( - self.neutron, self.sec_grp_creator.sec_grp_settings, + self.neutron, self.keystone, + self.sec_grp_creator.sec_grp_settings, self.sec_grp_creator.get_security_group(), rules)) def test_create_group_with_several_rules(self): @@ -407,7 +424,7 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): Tests the creation of an OpenStack Security Group with one simple custom rule. """ - # Create Image + # Create Security Group sec_grp_rule_settings = list() sec_grp_rule_settings.append( SecurityGroupRuleConfig( @@ -432,7 +449,7 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): self.sec_grp_creator.create() sec_grp = neutron_utils.get_security_group( - self.neutron, sec_grp_settings=sec_grp_settings) + self.neutron, self.keystone, sec_grp_settings=sec_grp_settings) validation_utils.objects_equivalent( self.sec_grp_creator.get_security_group(), sec_grp) rules = neutron_utils.get_rules_by_security_group( @@ -443,7 +460,8 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): self.assertTrue( validate_sec_grp( - self.neutron, self.sec_grp_creator.sec_grp_settings, + self.neutron, self.keystone, + self.sec_grp_creator.sec_grp_settings, self.sec_grp_creator.get_security_group(), rules)) def test_add_rule(self): @@ -451,7 +469,7 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): Tests the creation of an OpenStack Security Group with one simple custom rule then adds one after creation. """ - # Create Image + # Create Security Group sec_grp_rule_settings = list() sec_grp_rule_settings.append( SecurityGroupRuleConfig( @@ -465,7 +483,7 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): self.sec_grp_creator.create() sec_grp = neutron_utils.get_security_group( - self.neutron, sec_grp_settings=sec_grp_settings) + self.neutron, self.keystone, sec_grp_settings=sec_grp_settings) validation_utils.objects_equivalent( self.sec_grp_creator.get_security_group(), sec_grp) @@ -474,7 +492,8 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): self.assertTrue( validate_sec_grp( - self.neutron, self.sec_grp_creator.sec_grp_settings, + self.neutron, self.keystone, + self.sec_grp_creator.sec_grp_settings, self.sec_grp_creator.get_security_group(), rules)) rules = neutron_utils.get_rules_by_security_group( @@ -496,7 +515,7 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): Tests the creation of an OpenStack Security Group with two simple custom rules then removes one by the rule ID. """ - # Create Image + # Create Security Group sec_grp_rule_settings = list() sec_grp_rule_settings.append( SecurityGroupRuleConfig( @@ -521,7 +540,7 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): self.sec_grp_creator.create() sec_grp = neutron_utils.get_security_group( - self.neutron, sec_grp_settings=sec_grp_settings) + self.neutron, self.keystone, sec_grp_settings=sec_grp_settings) validation_utils.objects_equivalent( self.sec_grp_creator.get_security_group(), sec_grp) rules = neutron_utils.get_rules_by_security_group( @@ -532,7 +551,8 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): self.assertTrue( validate_sec_grp( - self.neutron, self.sec_grp_creator.sec_grp_settings, + self.neutron, self.keystone, + self.sec_grp_creator.sec_grp_settings, self.sec_grp_creator.get_security_group(), rules)) self.sec_grp_creator.remove_rule( @@ -547,7 +567,7 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): Tests the creation of an OpenStack Security Group with two simple custom rules then removes one by the rule setting object """ - # Create Image + # Create Security Group sec_grp_rule_settings = list() sec_grp_rule_settings.append( SecurityGroupRuleConfig( @@ -572,7 +592,7 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): self.sec_grp_creator.create() sec_grp = neutron_utils.get_security_group( - self.neutron, sec_grp_settings=sec_grp_settings) + self.neutron, self.keystone, sec_grp_settings=sec_grp_settings) validation_utils.objects_equivalent( self.sec_grp_creator.get_security_group(), sec_grp) @@ -584,7 +604,8 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): self.assertTrue( validate_sec_grp( - self.neutron, self.sec_grp_creator.sec_grp_settings, + self.neutron, self.keystone, + self.sec_grp_creator.sec_grp_settings, self.sec_grp_creator.get_security_group(), rules)) self.sec_grp_creator.remove_rule(rule_setting=sec_grp_rule_settings[0]) @@ -594,11 +615,13 @@ class CreateSecurityGroupTests(OSIntegrationTestCase): self.assertEqual(len(rules) - 1, len(rules_after_del)) -def validate_sec_grp(neutron, sec_grp_settings, sec_grp, rules=list()): +def validate_sec_grp(neutron, keystone, sec_grp_settings, sec_grp, + rules=list()): """ Returns True is the settings on a security group are properly contained on the SNAPS SecurityGroup domain object :param neutron: the neutron client + :param keystone: the keystone client :param sec_grp_settings: the security group configuration :param sec_grp: the SNAPS-OO security group object :param rules: collection of SNAPS-OO security group rule objects @@ -607,10 +630,10 @@ def validate_sec_grp(neutron, sec_grp_settings, sec_grp, rules=list()): return (sec_grp.description == sec_grp_settings.description and sec_grp.name == sec_grp_settings.name and validate_sec_grp_rules( - neutron, sec_grp_settings.rule_settings, rules)) + neutron, keystone, sec_grp_settings.rule_settings, rules)) -def validate_sec_grp_rules(neutron, rule_settings, rules): +def validate_sec_grp_rules(neutron, keystone, rule_settings, rules): """ Returns True is the settings on a security group rule are properly contained on the SNAPS SecurityGroupRule domain object. @@ -618,6 +641,7 @@ def validate_sec_grp_rules(neutron, rule_settings, rules): this is the only means to tell if the rule is custom or defaulted by OpenStack :param neutron: the neutron client + :param keystone: the keystone client :param rule_settings: collection of SecurityGroupRuleConfig objects :param rules: a collection of SecurityGroupRule domain objects :return: T/F @@ -628,7 +652,7 @@ def validate_sec_grp_rules(neutron, rule_settings, rules): match = False for rule in rules: sec_grp = neutron_utils.get_security_group( - neutron, sec_grp_name=rule_setting.sec_grp_name) + neutron, keystone, sec_grp_name=rule_setting.sec_grp_name) setting_eth_type = create_security_group.Ethertype.IPv4 if rule_setting.ethertype: @@ -657,3 +681,69 @@ def validate_sec_grp_rules(neutron, rule_settings, rules): return False return True + + +class CreateMultipleSecurityGroupTests(OSIntegrationTestCase): + """ + Test for the CreateSecurityGroup class and how it interacts with security + groups within other projects with the same name + """ + + 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, self.os_session) + + # Initialize for cleanup + self.admin_sec_grp_config = SecurityGroupConfig( + name=self.sec_grp_name, description='hello group') + self.sec_grp_creator_admin = OpenStackSecurityGroup( + self.admin_os_creds, self.admin_sec_grp_config) + self.sec_grp_creator_admin.create() + self.sec_grp_creator_proj = None + + def tearDown(self): + """ + Cleans the image and downloaded image file + """ + if self.sec_grp_creator_admin: + self.sec_grp_creator_admin.clean() + if self.sec_grp_creator_proj: + self.sec_grp_creator_proj.clean() + + super(self.__class__, self).__clean__() + + def test_sec_grp_same_name_diff_proj(self): + """ + Tests the creation of an OpenStack Security Group with the same name + within a different project/tenant. + """ + # Create Security Group + sec_grp_config = SecurityGroupConfig( + name=self.sec_grp_name, description='hello group') + self.sec_grp_creator_proj = OpenStackSecurityGroup( + self.os_creds, sec_grp_config) + self.sec_grp_creator_proj.create() + + self.assertNotEqual( + self.sec_grp_creator_admin.get_security_group().id, + self.sec_grp_creator_proj.get_security_group().id) + + admin_sec_grp_creator = OpenStackSecurityGroup( + self.admin_os_creds, self.admin_sec_grp_config) + admin_sec_grp_creator.create() + self.assertEqual(self.sec_grp_creator_admin.get_security_group().id, + admin_sec_grp_creator.get_security_group().id) + + proj_sec_grp_creator = OpenStackSecurityGroup( + self.os_creds, sec_grp_config) + proj_sec_grp_creator.create() + self.assertEqual(self.sec_grp_creator_proj.get_security_group().id, + proj_sec_grp_creator.get_security_group().id) diff --git a/snaps/openstack/tests/create_stack_tests.py b/snaps/openstack/tests/create_stack_tests.py index 6041735..7da5bc7 100644 --- a/snaps/openstack/tests/create_stack_tests.py +++ b/snaps/openstack/tests/create_stack_tests.py @@ -22,7 +22,8 @@ import snaps from snaps import file_utils from snaps.config.flavor import FlavorConfig from snaps.config.image import ImageConfig -from snaps.config.stack import StackConfigError, StackConfig +from snaps.config.stack import (StackConfigError, StackConfig, + STATUS_UPDATE_COMPLETE) from snaps.openstack.create_flavor import OpenStackFlavor from snaps.openstack.create_image import OpenStackImage @@ -35,11 +36,13 @@ import logging import unittest import uuid +from snaps.openstack import create_stack from snaps.openstack.create_stack import ( StackSettings, StackCreationError, StackError, OpenStackHeatStack) from snaps.openstack.tests import openstack_tests, create_instance_tests from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase -from snaps.openstack.utils import heat_utils, neutron_utils, nova_utils +from snaps.openstack.utils import ( + heat_utils, neutron_utils, nova_utils, keystone_utils) __author__ = 'spisarski' @@ -132,28 +135,27 @@ class CreateStackSuccessTests(OSIntegrationTestCase): """ def setUp(self): + self.user_roles = ['heat_stack_owner'] super(self.__class__, self).__start__() self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) - self.heat_creds = self.admin_os_creds - self.heat_creds.project_name = self.admin_os_creds.project_name - - self.heat_cli = heat_utils.heat_client(self.heat_creds) + self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session) self.stack_creator = None self.image_creator = OpenStackImage( - self.heat_creds, openstack_tests.cirros_image_settings( + self.os_creds, openstack_tests.cirros_image_settings( name=self.guid + '-image', image_metadata=self.image_metadata)) self.image_creator.create() # Create Flavor + flavor_config = openstack_tests.get_flavor_config( + name=self.guid + '-flavor-name', ram=256, disk=10, + vcpus=1, metadata=self.flavor_metadata) self.flavor_creator = OpenStackFlavor( - self.admin_os_creds, - FlavorConfig( - name=self.guid + '-flavor-name', ram=256, disk=10, vcpus=1)) + self.admin_os_creds, flavor_config) self.flavor_creator.create() self.network_name = self.guid + '-net' @@ -206,8 +208,8 @@ class CreateStackSuccessTests(OSIntegrationTestCase): template_path=self.heat_tmplt_path, env_values=self.env_values) self.stack_creator = OpenStackHeatStack( - self.heat_creds, stack_settings) - created_stack = self.stack_creator.create() + self.os_creds, stack_settings) + created_stack = self.stack_creator.create(block=True) self.assertIsNotNone(created_stack) retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli, @@ -217,6 +219,12 @@ class CreateStackSuccessTests(OSIntegrationTestCase): self.assertEqual(created_stack.id, retrieved_stack.id) self.assertEqual(0, len(self.stack_creator.get_outputs())) + derived_creator = create_stack.generate_creator( + self.os_creds, retrieved_stack, + [self.image_creator.image_settings]) + derived_stack = derived_creator.get_stack() + self.assertEqual(retrieved_stack, derived_stack) + def test_create_stack_short_timeout(self): """ Tests the creation of an OpenStack stack from Heat template file. @@ -230,9 +238,9 @@ class CreateStackSuccessTests(OSIntegrationTestCase): env_values=self.env_values, stack_create_timeout=0) self.stack_creator = OpenStackHeatStack( - self.heat_creds, stack_settings) + self.os_creds, stack_settings) with self.assertRaises(StackCreationError): - self.stack_creator.create() + self.stack_creator.create(block=True) def test_create_stack_template_dict(self): """ @@ -248,8 +256,8 @@ class CreateStackSuccessTests(OSIntegrationTestCase): template=template_dict, env_values=self.env_values) self.stack_creator = OpenStackHeatStack( - self.heat_creds, stack_settings) - created_stack = self.stack_creator.create() + self.os_creds, stack_settings) + created_stack = self.stack_creator.create(block=True) self.assertIsNotNone(created_stack) retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli, @@ -272,8 +280,8 @@ class CreateStackSuccessTests(OSIntegrationTestCase): template=template_dict, env_values=self.env_values) self.stack_creator = OpenStackHeatStack( - self.heat_creds, stack_settings) - created_stack = self.stack_creator.create() + self.os_creds, stack_settings) + created_stack = self.stack_creator.create(block=True) self.assertIsNotNone(created_stack) retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli, @@ -316,8 +324,8 @@ class CreateStackSuccessTests(OSIntegrationTestCase): template=template_dict, env_values=self.env_values) self.stack_creator = OpenStackHeatStack( - self.heat_creds, stack_settings) - created_stack1 = self.stack_creator.create() + self.os_creds, stack_settings) + created_stack1 = self.stack_creator.create(block=True) retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli, created_stack1.id) @@ -327,8 +335,8 @@ class CreateStackSuccessTests(OSIntegrationTestCase): self.assertEqual(0, len(self.stack_creator.get_outputs())) # Should be retrieving the instance data - stack_creator2 = OpenStackHeatStack(self.heat_creds, stack_settings) - stack2 = stack_creator2.create() + stack_creator2 = OpenStackHeatStack(self.os_creds, stack_settings) + stack2 = stack_creator2.create(block=True) self.assertEqual(created_stack1.id, stack2.id) def test_retrieve_network_creators(self): @@ -341,8 +349,8 @@ class CreateStackSuccessTests(OSIntegrationTestCase): template_path=self.heat_tmplt_path, env_values=self.env_values) self.stack_creator = OpenStackHeatStack( - self.heat_creds, stack_settings) - created_stack = self.stack_creator.create() + self.os_creds, stack_settings) + created_stack = self.stack_creator.create(block=True) self.assertIsNotNone(created_stack) net_creators = self.stack_creator.get_network_creators() @@ -350,9 +358,14 @@ class CreateStackSuccessTests(OSIntegrationTestCase): self.assertEqual(1, len(net_creators)) self.assertEqual(self.network_name, net_creators[0].get_network().name) - neutron = neutron_utils.neutron_client(self.os_creds) + # Need to use 'admin' creds as heat creates objects under it's own + # project/tenant + neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) + keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) net_by_name = neutron_utils.get_network( - neutron, network_name=net_creators[0].get_network().name) + neutron, keystone, network_name=net_creators[0].get_network().name) self.assertEqual(net_creators[0].get_network(), net_by_name) self.assertIsNotNone(neutron_utils.get_network_by_id( neutron, net_creators[0].get_network().id)) @@ -360,7 +373,7 @@ class CreateStackSuccessTests(OSIntegrationTestCase): self.assertEqual(1, len(net_creators[0].get_network().subnets)) subnet = net_creators[0].get_network().subnets[0] subnet_by_name = neutron_utils.get_subnet( - neutron, subnet_name=subnet.name) + neutron, net_creators[0].get_network(), subnet_name=subnet.name) self.assertEqual(subnet, subnet_by_name) subnet_by_id = neutron_utils.get_subnet_by_id(neutron, subnet.id) @@ -377,8 +390,8 @@ class CreateStackSuccessTests(OSIntegrationTestCase): template_path=self.heat_tmplt_path, env_values=self.env_values) self.stack_creator = OpenStackHeatStack( - self.heat_creds, stack_settings) - created_stack = self.stack_creator.create() + self.os_creds, stack_settings) + created_stack = self.stack_creator.create(block=True) self.assertIsNotNone(created_stack) vm_inst_creators = self.stack_creator.get_vm_inst_creators() @@ -387,13 +400,16 @@ class CreateStackSuccessTests(OSIntegrationTestCase): self.assertEqual(self.vm_inst_name, vm_inst_creators[0].get_vm_inst().name) - nova = nova_utils.nova_client(self.admin_os_creds) - neutron = neutron_utils.neutron_client(self.admin_os_creds) + nova = nova_utils.nova_client(self.os_creds, self.os_session) + neutron = neutron_utils.neutron_client(self.os_creds, self.os_session) + keystone = keystone_utils.keystone_client(self.os_creds, self.os_session) vm_inst_by_name = nova_utils.get_server( - nova, neutron, server_name=vm_inst_creators[0].get_vm_inst().name) + nova, neutron, keystone, + server_name=vm_inst_creators[0].get_vm_inst().name) + self.assertEqual(vm_inst_creators[0].get_vm_inst(), vm_inst_by_name) self.assertIsNotNone(nova_utils.get_server_object_by_id( - nova, neutron, vm_inst_creators[0].get_vm_inst().id)) + nova, neutron, keystone, vm_inst_creators[0].get_vm_inst().id)) class CreateStackFloatingIpTests(OSIntegrationTestCase): @@ -403,19 +419,17 @@ class CreateStackFloatingIpTests(OSIntegrationTestCase): """ def setUp(self): + self.user_roles = ['heat_stack_owner', 'admin'] super(self.__class__, self).__start__() self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) - self.heat_creds = self.admin_os_creds - self.heat_creds.project_name = self.admin_os_creds.project_name - - self.heat_cli = heat_utils.heat_client(self.heat_creds) + self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session) self.stack_creator = None self.image_creator = OpenStackImage( - self.heat_creds, openstack_tests.cirros_image_settings( + self.os_creds, openstack_tests.cirros_image_settings( name=self.guid + '-image', image_metadata=self.image_metadata)) self.image_creator.create() @@ -434,6 +448,7 @@ class CreateStackFloatingIpTests(OSIntegrationTestCase): 'image2_name': self.image_creator.image_settings.name, 'flavor1_name': self.flavor1_name, 'flavor2_name': self.flavor2_name, + 'flavor_extra_specs': self.flavor_metadata, 'net_name': self.network_name, 'subnet_name': self.subnet_name, 'inst1_name': self.vm_inst1_name, @@ -487,9 +502,9 @@ class CreateStackFloatingIpTests(OSIntegrationTestCase): template_path=self.heat_tmplt_path, env_values=self.env_values) self.stack_creator = OpenStackHeatStack( - self.heat_creds, stack_settings, + self.os_creds, stack_settings, [self.image_creator.image_settings]) - created_stack = self.stack_creator.create() + created_stack = self.stack_creator.create(block=True) self.assertIsNotNone(created_stack) self.vm_inst_creators = self.stack_creator.get_vm_inst_creators( @@ -505,6 +520,39 @@ class CreateStackFloatingIpTests(OSIntegrationTestCase): vm_settings = vm_inst_creator.instance_settings self.assertEqual(0, len(vm_settings.floating_ip_settings)) + def test_connect_via_ssh_heat_vm_derived(self): + """ + Tests the the retrieval of two VM instance creators from a derived + OpenStackHeatStack object and attempt to connect via + SSH to the first one with a floating IP. + """ + stack_settings = StackConfig( + name=self.__class__.__name__ + '-' + str(self.guid) + '-stack', + template_path=self.heat_tmplt_path, + env_values=self.env_values) + self.stack_creator = OpenStackHeatStack( + self.os_creds, stack_settings, + [self.image_creator.image_settings]) + created_stack = self.stack_creator.create(block=True) + self.assertIsNotNone(created_stack) + + derived_stack = create_stack.generate_creator( + self.os_creds, created_stack, + [self.image_creator.image_settings]) + + self.vm_inst_creators = derived_stack.get_vm_inst_creators( + heat_keypair_option='private_key') + self.assertIsNotNone(self.vm_inst_creators) + self.assertEqual(2, len(self.vm_inst_creators)) + + for vm_inst_creator in self.vm_inst_creators: + if vm_inst_creator.get_vm_inst().name == self.vm_inst1_name: + self.assertTrue( + create_instance_tests.validate_ssh_client(vm_inst_creator)) + else: + vm_settings = vm_inst_creator.instance_settings + self.assertEqual(0, len(vm_settings.floating_ip_settings)) + class CreateStackNestedResourceTests(OSIntegrationTestCase): """ @@ -512,19 +560,125 @@ class CreateStackNestedResourceTests(OSIntegrationTestCase): """ def setUp(self): + self.user_roles = ['heat_stack_owner'] super(self.__class__, self).__start__() self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) - self.heat_creds = self.admin_os_creds - self.heat_creds.project_name = self.admin_os_creds.project_name + self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session) + self.stack_creator = None + + self.image_creator = OpenStackImage( + self.os_creds, openstack_tests.cirros_image_settings( + name="{}-{}".format(self.guid, 'image'), + image_metadata=self.image_metadata)) + self.image_creator.create() + + flavor_config = openstack_tests.get_flavor_config( + name="{}-{}".format(self.guid, 'flavor-name'), ram=256, disk=10, + vcpus=1, metadata=self.flavor_metadata) + self.flavor_creator = OpenStackFlavor( + self.admin_os_creds, flavor_config) + self.flavor_creator.create() + + env_values = { + 'network_name': self.guid + '-network', + 'public_network': self.ext_net_name, + 'agent_image': self.image_creator.image_settings.name, + 'agent_flavor': self.flavor_creator.flavor_settings.name, + 'key_name': self.guid + '-key', + } + + heat_tmplt_path = pkg_resources.resource_filename( + 'snaps.openstack.tests.heat', 'agent-group.yaml') + heat_resource_path = pkg_resources.resource_filename( + 'snaps.openstack.tests.heat', 'agent.yaml') + + stack_settings = StackConfig( + name="{}-{}".format( + self.__class__.__name__, str(self.guid) + '-stack'), + template_path=heat_tmplt_path, + resource_files=[heat_resource_path], + env_values=env_values) + + self.stack_creator = OpenStackHeatStack( + self.os_creds, stack_settings, + [self.image_creator.image_settings]) + + self.vm_inst_creators = list() + + def tearDown(self): + """ + Cleans the stack and downloaded stack file + """ + if self.stack_creator: + try: + self.stack_creator.clean() + except: + pass + + if self.image_creator: + try: + self.image_creator.clean() + except: + pass - self.heat_cli = heat_utils.heat_client(self.heat_creds) + if self.flavor_creator: + try: + self.flavor_creator.clean() + except: + pass + + for vm_inst_creator in self.vm_inst_creators: + try: + keypair_settings = vm_inst_creator.keypair_settings + if keypair_settings and keypair_settings.private_filepath: + expanded_path = os.path.expanduser( + keypair_settings.private_filepath) + os.chmod(expanded_path, 0o755) + os.remove(expanded_path) + except: + pass + + super(self.__class__, self).__clean__() + + def test_nested(self): + """ + Tests the creation of an OpenStack stack from Heat template file and + the retrieval of two VM instance creators and attempt to connect via + SSH to the first one with a floating IP. + """ + created_stack = self.stack_creator.create(block=True) + self.assertIsNotNone(created_stack) + + self.vm_inst_creators = self.stack_creator.get_vm_inst_creators( + heat_keypair_option='private_key') + self.assertIsNotNone(self.vm_inst_creators) + self.assertEqual(1, len(self.vm_inst_creators)) + + for vm_inst_creator in self.vm_inst_creators: + self.assertTrue( + create_instance_tests.validate_ssh_client(vm_inst_creator)) + + +class CreateStackUpdateTests(OSIntegrationTestCase): + """ + Tests to ensure that stack update commands work + """ + + def setUp(self): + self.user_roles = ['heat_stack_owner'] + + super(self.__class__, self).__start__() + + self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) + + self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session) self.stack_creator = None self.image_creator = OpenStackImage( - self.heat_creds, openstack_tests.cirros_image_settings( + self.os_creds, openstack_tests.cirros_image_settings( name=self.guid + '-image', image_metadata=self.image_metadata)) self.image_creator.create() @@ -536,9 +690,11 @@ class CreateStackNestedResourceTests(OSIntegrationTestCase): self.flavor_creator.create() env_values = { + 'network_name': self.guid + '-network', 'public_network': self.ext_net_name, 'agent_image': self.image_creator.image_settings.name, 'agent_flavor': self.flavor_creator.flavor_settings.name, + 'key_name': self.guid + '-key', } heat_tmplt_path = pkg_resources.resource_filename( @@ -553,7 +709,7 @@ class CreateStackNestedResourceTests(OSIntegrationTestCase): env_values=env_values) self.stack_creator = OpenStackHeatStack( - self.heat_creds, stack_settings, + self.os_creds, stack_settings, [self.image_creator.image_settings]) self.vm_inst_creators = list() @@ -593,13 +749,14 @@ class CreateStackNestedResourceTests(OSIntegrationTestCase): super(self.__class__, self).__clean__() - def test_nested(self): + def test_update(self): """ - Tests the creation of an OpenStack stack from Heat template file and + Tests the update of an OpenStack stack from Heat template file + by changing the number of VM instances from 1 to 2, and the retrieval of two VM instance creators and attempt to connect via SSH to the first one with a floating IP. """ - created_stack = self.stack_creator.create() + created_stack = self.stack_creator.create(block=True) self.assertIsNotNone(created_stack) self.vm_inst_creators = self.stack_creator.get_vm_inst_creators( @@ -611,6 +768,28 @@ class CreateStackNestedResourceTests(OSIntegrationTestCase): self.assertTrue( create_instance_tests.validate_ssh_client(vm_inst_creator)) + env_values = { + 'network_name': self.guid + '-network', + 'public_network': self.ext_net_name, + 'agent_count': 2, + 'agent_image': self.image_creator.image_settings.name, + 'agent_flavor': self.flavor_creator.flavor_settings.name, + 'key_name': self.guid + '-key', + } + + updated_stack = self.stack_creator.update(env_values, block=True) + self.assertIsNotNone(updated_stack) + self.assertEqual(STATUS_UPDATE_COMPLETE, updated_stack.status) + + self.vm_inst_creators = self.stack_creator.get_vm_inst_creators( + heat_keypair_option='private_key') + self.assertIsNotNone(self.vm_inst_creators) + self.assertEqual(2, len(self.vm_inst_creators)) + + for vm_inst_creator in self.vm_inst_creators: + self.assertTrue( + create_instance_tests.validate_ssh_client(vm_inst_creator)) + class CreateStackRouterTests(OSIntegrationTestCase): """ @@ -623,15 +802,15 @@ class CreateStackRouterTests(OSIntegrationTestCase): Instantiates the CreateStack object that is responsible for downloading and creating an OS stack file within OpenStack """ + self.user_roles = ['heat_stack_owner'] + super(self.__class__, self).__start__() self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) - self.heat_creds = self.admin_os_creds - self.heat_creds.project_name = self.admin_os_creds.project_name - - self.heat_cli = heat_utils.heat_client(self.heat_creds) - self.neutron = neutron_utils.neutron_client(self.os_creds) + self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session) + self.neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) self.stack_creator = None self.net_name = self.guid + '-net' @@ -652,8 +831,8 @@ class CreateStackRouterTests(OSIntegrationTestCase): template_path=self.heat_tmplt_path, env_values=self.env_values) self.stack_creator = OpenStackHeatStack( - self.heat_creds, stack_settings) - self.created_stack = self.stack_creator.create() + self.os_creds, stack_settings) + self.created_stack = self.stack_creator.create(block=True) self.assertIsNotNone(self.created_stack) def tearDown(self): @@ -682,7 +861,7 @@ class CreateStackRouterTests(OSIntegrationTestCase): router = creator.get_router() ext_net = neutron_utils.get_network( - self.neutron, network_name=self.ext_net_name) + self.neutron, self.keystone, network_name=self.ext_net_name) self.assertEqual(ext_net.id, router.external_network_id) @@ -694,14 +873,13 @@ class CreateStackVolumeTests(OSIntegrationTestCase): def setUp(self): + self.user_roles = ['heat_stack_owner', 'admin'] + super(self.__class__, self).__start__() self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) - self.heat_creds = self.admin_os_creds - self.heat_creds.project_name = self.admin_os_creds.project_name - - self.heat_cli = heat_utils.heat_client(self.heat_creds) + self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session) self.stack_creator = None self.volume_name = self.guid + '-volume' @@ -719,8 +897,8 @@ class CreateStackVolumeTests(OSIntegrationTestCase): template_path=self.heat_tmplt_path, env_values=self.env_values) self.stack_creator = OpenStackHeatStack( - self.heat_creds, stack_settings) - self.created_stack = self.stack_creator.create() + self.os_creds, stack_settings) + self.created_stack = self.stack_creator.create(block=True) self.assertIsNotNone(self.created_stack) def tearDown(self): @@ -789,14 +967,13 @@ class CreateStackFlavorTests(OSIntegrationTestCase): def setUp(self): + self.user_roles = ['heat_stack_owner', 'admin'] + super(self.__class__, self).__start__() self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) - self.heat_creds = self.admin_os_creds - self.heat_creds.project_name = self.admin_os_creds.project_name - - self.heat_cli = heat_utils.heat_client(self.heat_creds) + self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session) self.stack_creator = None self.heat_tmplt_path = pkg_resources.resource_filename( @@ -806,8 +983,8 @@ class CreateStackFlavorTests(OSIntegrationTestCase): name=self.guid + '-stack', template_path=self.heat_tmplt_path) self.stack_creator = OpenStackHeatStack( - self.heat_creds, stack_settings) - self.created_stack = self.stack_creator.create() + self.os_creds, stack_settings) + self.created_stack = self.stack_creator.create(block=True) self.assertIsNotNone(self.created_stack) def tearDown(self): @@ -849,15 +1026,14 @@ class CreateStackKeypairTests(OSIntegrationTestCase): def setUp(self): + self.user_roles = ['heat_stack_owner'] + super(self.__class__, self).__start__() self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) - self.heat_creds = self.admin_os_creds - self.heat_creds.project_name = self.admin_os_creds.project_name - - self.heat_cli = heat_utils.heat_client(self.heat_creds) - self.nova = nova_utils.nova_client(self.heat_creds) + self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session) + self.nova = nova_utils.nova_client(self.os_creds, self.os_session) self.stack_creator = None self.keypair_name = self.guid + '-kp' @@ -873,8 +1049,8 @@ class CreateStackKeypairTests(OSIntegrationTestCase): template_path=self.heat_tmplt_path, env_values=self.env_values) self.stack_creator = OpenStackHeatStack( - self.heat_creds, stack_settings) - self.created_stack = self.stack_creator.create() + self.os_creds, stack_settings) + self.created_stack = self.stack_creator.create(block=True) self.assertIsNotNone(self.created_stack) self.keypair_creators = list() @@ -934,15 +1110,14 @@ class CreateStackSecurityGroupTests(OSIntegrationTestCase): Instantiates the CreateStack object that is responsible for downloading and creating an OS stack file within OpenStack """ + self.user_roles = ['heat_stack_owner'] + super(self.__class__, self).__start__() self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) - self.heat_creds = self.admin_os_creds - self.heat_creds.project_name = self.admin_os_creds.project_name - - self.heat_cli = heat_utils.heat_client(self.heat_creds) - self.nova = nova_utils.nova_client(self.heat_creds) + self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session) + self.nova = nova_utils.nova_client(self.os_creds, self.os_session) self.stack_creator = None self.security_group_name = self.guid + '-sec-grp' @@ -958,8 +1133,8 @@ class CreateStackSecurityGroupTests(OSIntegrationTestCase): template_path=self.heat_tmplt_path, env_values=self.env_values) self.stack_creator = OpenStackHeatStack( - self.heat_creds, stack_settings) - self.created_stack = self.stack_creator.create() + self.os_creds, stack_settings) + self.created_stack = self.stack_creator.create(block=True) self.assertIsNotNone(self.created_stack) def tearDown(self): @@ -1024,12 +1199,10 @@ class CreateStackNegativeTests(OSIntegrationTestCase): """ def setUp(self): + self.user_roles = ['heat_stack_owner'] super(self.__class__, self).__start__() - self.heat_creds = self.admin_os_creds - self.heat_creds.project_name = self.admin_os_creds.project_name - self.stack_name = self.__class__.__name__ + '-' + str(uuid.uuid4()) self.stack_creator = None self.heat_tmplt_path = pkg_resources.resource_filename( @@ -1038,6 +1211,7 @@ class CreateStackNegativeTests(OSIntegrationTestCase): def tearDown(self): if self.stack_creator: self.stack_creator.clean() + super(self.__class__, self).__clean__() def test_missing_dependencies(self): @@ -1047,9 +1221,9 @@ class CreateStackNegativeTests(OSIntegrationTestCase): stack_settings = StackConfig(name=self.stack_name, template_path=self.heat_tmplt_path) self.stack_creator = OpenStackHeatStack( - self.heat_creds, stack_settings) + self.os_creds, stack_settings) with self.assertRaises(HTTPBadRequest): - self.stack_creator.create() + self.stack_creator.create(block=True) def test_bad_stack_file(self): """ @@ -1058,9 +1232,9 @@ class CreateStackNegativeTests(OSIntegrationTestCase): stack_settings = StackConfig( name=self.stack_name, template_path='foo') self.stack_creator = OpenStackHeatStack( - self.heat_creds, stack_settings) + self.os_creds, stack_settings) with self.assertRaises(IOError): - self.stack_creator.create() + self.stack_creator.create(block=True) class CreateStackFailureTests(OSIntegrationTestCase): @@ -1071,21 +1245,19 @@ class CreateStackFailureTests(OSIntegrationTestCase): """ def setUp(self): + self.user_roles = ['heat_stack_owner'] super(self.__class__, self).__start__() self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) - self.heat_creds = self.admin_os_creds - self.heat_creds.project_name = self.admin_os_creds.project_name - - self.heat_cli = heat_utils.heat_client(self.heat_creds) + self.heat_cli = heat_utils.heat_client(self.os_creds, self.os_session) self.stack_creator = None self.tmp_file = file_utils.save_string_to_file( ' ', str(uuid.uuid4()) + '-bad-image') self.image_creator = OpenStackImage( - self.heat_creds, ImageConfig( + self.os_creds, ImageConfig( name=self.guid + 'image', image_file=self.tmp_file.name, image_user='foo', img_format='qcow2')) self.image_creator.create() @@ -1155,11 +1327,11 @@ class CreateStackFailureTests(OSIntegrationTestCase): template_path=self.heat_tmplt_path, env_values=self.env_values) self.stack_creator = OpenStackHeatStack( - self.heat_creds, stack_settings) + self.os_creds, stack_settings) with self.assertRaises(StackError): try: - self.stack_creator.create() + self.stack_creator.create(block=True) except StackError: resources = heat_utils.get_resources( self.heat_cli, self.stack_creator.get_stack().id) diff --git a/snaps/openstack/tests/create_user_tests.py b/snaps/openstack/tests/create_user_tests.py index d3eb4a6..c15a71f 100644 --- a/snaps/openstack/tests/create_user_tests.py +++ b/snaps/openstack/tests/create_user_tests.py @@ -106,10 +106,11 @@ class CreateUserSuccessTests(OSComponentTestCase): self.user_settings = UserConfig( name=guid + '-name', password=guid + '-password', - roles={'admin': self.os_creds.project_name}, + roles={'admin': self.os_creds.project_name, + 'Admin': self.os_creds.project_name}, domain_name=self.os_creds.user_domain_name) - self.keystone = keystone_utils.keystone_client(self.os_creds) + self.keystone = keystone_utils.keystone_client(self.os_creds, self.os_session) # Initialize for cleanup self.user_creator = None @@ -121,6 +122,8 @@ class CreateUserSuccessTests(OSComponentTestCase): if self.user_creator: self.user_creator.clean() + super(self.__class__, self).__clean__() + def test_create_user(self): """ Tests the creation of an OpenStack user. @@ -181,6 +184,8 @@ class CreateUserSuccessTests(OSComponentTestCase): self.assertEqual(created_user, retrieved_user) role = keystone_utils.get_role_by_name(self.keystone, 'admin') + if not role: + role = keystone_utils.get_role_by_name(self.keystone, 'Admin') self.assertIsNotNone(role) os_proj = keystone_utils.get_project( diff --git a/snaps/openstack/tests/create_volume_tests.py b/snaps/openstack/tests/create_volume_tests.py index ca13860..3c9a346 100644 --- a/snaps/openstack/tests/create_volume_tests.py +++ b/snaps/openstack/tests/create_volume_tests.py @@ -32,7 +32,7 @@ import uuid from snaps.openstack.create_volume import ( VolumeSettings, OpenStackVolume) from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase -from snaps.openstack.utils import cinder_utils +from snaps.openstack.utils import cinder_utils, keystone_utils __author__ = 'spisarski' @@ -129,7 +129,10 @@ class CreateSimpleVolumeSuccessTests(OSIntegrationTestCase): self.volume_settings = VolumeConfig( name=self.__class__.__name__ + '-' + str(guid)) - self.cinder = cinder_utils.cinder_client(self.os_creds) + self.cinder = cinder_utils.cinder_client( + self.os_creds, self.os_session) + self.keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) self.volume_creator = None def tearDown(self): @@ -152,7 +155,8 @@ class CreateSimpleVolumeSuccessTests(OSIntegrationTestCase): self.assertIsNotNone(created_volume) retrieved_volume = cinder_utils.get_volume( - self.cinder, volume_settings=self.volume_settings) + self.cinder, self.keystone, volume_settings=self.volume_settings, + project_name=self.os_creds.project_name) self.assertIsNotNone(retrieved_volume) self.assertEqual(created_volume.id, retrieved_volume.id) @@ -170,7 +174,8 @@ class CreateSimpleVolumeSuccessTests(OSIntegrationTestCase): self.assertIsNotNone(created_volume) retrieved_volume = cinder_utils.get_volume( - self.cinder, volume_settings=self.volume_settings) + self.cinder, self.keystone, volume_settings=self.volume_settings, + project_name=self.os_creds.project_name) self.assertIsNotNone(retrieved_volume) self.assertEqual(created_volume, retrieved_volume) @@ -178,7 +183,8 @@ class CreateSimpleVolumeSuccessTests(OSIntegrationTestCase): self.volume_creator.clean() self.assertIsNone(cinder_utils.get_volume( - self.cinder, volume_settings=self.volume_settings)) + self.cinder, self.keystone, volume_settings=self.volume_settings, + project_name=self.os_creds.project_name)) # Must not throw an exception when attempting to cleanup non-existent # volume @@ -195,7 +201,8 @@ class CreateSimpleVolumeSuccessTests(OSIntegrationTestCase): volume1 = self.volume_creator.create(block=True) retrieved_volume = cinder_utils.get_volume( - self.cinder, volume_settings=self.volume_settings) + self.cinder, self.keystone, volume_settings=self.volume_settings, + project_name=self.os_creds.project_name) self.assertEqual(volume1, retrieved_volume) # Should be retrieving the instance data @@ -218,7 +225,8 @@ class CreateSimpleVolumeFailureTests(OSIntegrationTestCase): super(self.__class__, self).__start__() self.guid = uuid.uuid4() - self.cinder = cinder_utils.cinder_client(self.os_creds) + self.cinder = cinder_utils.cinder_client( + self.os_creds, self.os_session) self.volume_creator = None def tearDown(self): @@ -274,21 +282,6 @@ class CreateSimpleVolumeFailureTests(OSIntegrationTestCase): with self.assertRaises(BadRequest): self.volume_creator.create(block=True) - def test_create_volume_bad_zone(self): - """ - Tests the creation of an OpenStack volume with an availability zone - that does not exist to ensure it raises a BadRequest exception. - """ - volume_settings = VolumeConfig( - name=self.__class__.__name__ + '-' + str(self.guid), - availability_zone='foo') - - # Create Volume - self.volume_creator = OpenStackVolume(self.os_creds, volume_settings) - - with self.assertRaises(BadRequest): - self.volume_creator.create(block=True) - class CreateVolumeWithTypeTests(OSIntegrationTestCase): """ @@ -304,7 +297,7 @@ class CreateVolumeWithTypeTests(OSIntegrationTestCase): self.volume_type_name = guid + '-vol-type' self.volume_type_creator = OpenStackVolumeType( - self.os_creds, VolumeTypeConfig(name=self.volume_type_name)) + self.admin_os_creds, VolumeTypeConfig(name=self.volume_type_name)) self.volume_type_creator.create() self.volume_creator = None @@ -332,8 +325,7 @@ class CreateVolumeWithTypeTests(OSIntegrationTestCase): Expect a NotFound to be raised when the volume type does not exist """ self.volume_creator = OpenStackVolume( - self.os_creds, - VolumeConfig( + self.admin_os_creds, VolumeConfig( name=self.volume_name, type_name=self.volume_type_name)) created_volume = self.volume_creator.create(block=True) @@ -349,7 +341,8 @@ class CreateVolumeWithImageTests(OSIntegrationTestCase): def setUp(self): super(self.__class__, self).__start__() - self.cinder = cinder_utils.cinder_client(self.os_creds) + self.cinder = cinder_utils.cinder_client( + self.os_creds, self.os_session) guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) self.volume_name = guid + '-vol' @@ -408,3 +401,87 @@ class CreateVolumeWithImageTests(OSIntegrationTestCase): self.cinder, created_volume.id) self.assertEqual(created_volume, retrieved_volume) + + +class CreateVolMultipleCredsTests(OSIntegrationTestCase): + """ + Test for the OpenStackVolume class and how it interacts with volumes + created with differenct credentials and to other projects with the same + name + """ + def setUp(self): + super(self.__class__, self).__start__() + + self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) + self.volume_creators = list() + + def tearDown(self): + for volume_creator in self.volume_creators: + volume_creator.clean() + + super(self.__class__, self).__clean__() + + # TODO - activate after cinder API bug has been fixed + # see https://bugs.launchpad.net/cinder/+bug/1641982 as to why this test + # is not activated + # def test_create_by_admin_to_other_proj(self): + # """ + # Creates a volume as admin to the project of os_creds then instantiates + # a creator object with the os_creds project to ensure it initializes + # without creation + # """ + # self.volume_creators.append(OpenStackVolume( + # self.admin_os_creds, VolumeConfig( + # name=self.guid + '-vol', + # project_name=self.os_creds.project_name))) + # admin_vol = self.volume_creators[0].create(block=True) + # + # self.volume_creators.append(OpenStackVolume( + # self.os_creds, VolumeConfig(name=self.guid + '-vol'))) + # proj_vol = self.volume_creators[1].create(block=True) + # + # self.assertEqual(admin_vol, proj_vol) + + def test_create_two_vol_same_name_diff_proj(self): + """ + Creates a volume as admin to the project of os_creds then instantiates + a creator object with the os_creds project to ensure it initializes + without creation + """ + vol_name = self.guid + '-vol' + self.volume_creators.append(OpenStackVolume( + self.admin_os_creds, VolumeConfig(name=vol_name))) + admin_vol = self.volume_creators[0].create(block=True) + self.assertIsNotNone(admin_vol) + + admin_key = keystone_utils.keystone_client( + self.admin_os_creds, self.admin_os_session) + admin_proj = keystone_utils.get_project( + admin_key, project_name=self.admin_os_creds.project_name) + self.assertEqual(admin_vol.project_id, admin_proj.id) + + admin_cinder = cinder_utils.cinder_client( + self.admin_os_creds, self.admin_os_session) + admin_vol_get = cinder_utils.get_volume( + admin_cinder, admin_key, volume_name=vol_name, + project_name=self.admin_os_creds.project_name) + self.assertIsNotNone(admin_vol_get) + self.assertEqual(admin_vol, admin_vol_get) + + self.volume_creators.append(OpenStackVolume( + self.os_creds, VolumeConfig(name=vol_name))) + proj_vol = self.volume_creators[1].create(block=True) + self.assertIsNotNone(proj_vol) + + self.assertNotEqual(admin_vol, proj_vol) + + proj_key = keystone_utils.keystone_client( + self.os_creds, self.os_session) + proj_cinder = cinder_utils.cinder_client( + self.os_creds, self.os_session) + proj_vol_get = cinder_utils.get_volume( + proj_cinder, proj_key, volume_name=vol_name, + project_name=self.os_creds.project_name) + + self.assertIsNotNone(proj_vol_get) + self.assertEqual(proj_vol, proj_vol_get) diff --git a/snaps/openstack/tests/create_volume_type_tests.py b/snaps/openstack/tests/create_volume_type_tests.py index 70c40cc..b797274 100644 --- a/snaps/openstack/tests/create_volume_type_tests.py +++ b/snaps/openstack/tests/create_volume_type_tests.py @@ -29,7 +29,7 @@ import uuid from snaps.openstack import create_volume_type from snaps.openstack.create_volume_type import ( - VolumeTypeSettings, VolumeTypeEncryptionSettings) + VolumeTypeSettings, VolumeTypeEncryptionSettings, OpenStackVolumeType) from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase from snaps.openstack.utils import cinder_utils @@ -127,8 +127,10 @@ class CreateSimpleVolumeTypeSuccessTests(OSIntegrationTestCase): self.volume_type_settings = VolumeTypeConfig( name=self.__class__.__name__ + '-' + str(guid)) - self.cinder = cinder_utils.cinder_client(self.os_creds) - self.volume_type_creator = None + self.cinder = cinder_utils.cinder_client( + self.admin_os_creds, self.admin_os_session) + self.volume_type_creator = OpenStackVolumeType( + self.admin_os_creds, self.volume_type_settings) def tearDown(self): """ @@ -144,8 +146,6 @@ class CreateSimpleVolumeTypeSuccessTests(OSIntegrationTestCase): Tests the creation of an OpenStack volume. """ # Create VolumeType - self.volume_type_creator = create_volume_type.OpenStackVolumeType( - self.os_creds, self.volume_type_settings) created_volume_type = self.volume_type_creator.create() self.assertIsNotNone(created_volume_type) self.assertEqual(self.volume_type_settings.name, @@ -166,8 +166,6 @@ class CreateSimpleVolumeTypeSuccessTests(OSIntegrationTestCase): clean() does not raise an Exception. """ # Create VolumeType - self.volume_type_creator = create_volume_type.OpenStackVolumeType( - self.os_creds, self.volume_type_settings) created_volume_type = self.volume_type_creator.create() self.assertIsNotNone(created_volume_type) @@ -192,8 +190,6 @@ class CreateSimpleVolumeTypeSuccessTests(OSIntegrationTestCase): Tests the creation of an OpenStack volume_type when one already exists. """ # Create VolumeType - self.volume_type_creator = create_volume_type.OpenStackVolumeType( - self.os_creds, self.volume_type_settings) volume_type1 = self.volume_type_creator.create() retrieved_volume_type = cinder_utils.get_volume_type( @@ -202,7 +198,7 @@ class CreateSimpleVolumeTypeSuccessTests(OSIntegrationTestCase): # Should be retrieving the instance data os_volume_type_2 = create_volume_type.OpenStackVolumeType( - self.os_creds, self.volume_type_settings) + self.admin_os_creds, self.volume_type_settings) volume_type2 = os_volume_type_2.create() self.assertEqual(volume_type2, volume_type2) @@ -216,7 +212,8 @@ class CreateVolumeTypeComplexTests(OSIntegrationTestCase): def setUp(self): super(self.__class__, self).__start__() - self.cinder = cinder_utils.cinder_client(self.os_creds) + self.cinder = cinder_utils.cinder_client( + self.admin_os_creds, self.admin_os_session) guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) @@ -225,7 +222,7 @@ class CreateVolumeTypeComplexTests(OSIntegrationTestCase): qos_settings = QoSConfig( name=guid + '-qos-spec', consumer=Consumer.both) - self.qos_creator = OpenStackQoS(self.os_creds, qos_settings) + self.qos_creator = OpenStackQoS(self.admin_os_creds, qos_settings) self.qos_creator.create() def tearDown(self): @@ -241,8 +238,8 @@ class CreateVolumeTypeComplexTests(OSIntegrationTestCase): """ Creates a Volume Type object with an associated QoS Spec """ - self.volume_type_creator = create_volume_type.OpenStackVolumeType( - self.os_creds, + self.volume_type_creator = OpenStackVolumeType( + self.admin_os_creds, VolumeTypeConfig( name=self.volume_type_name, qos_spec_name=self.qos_creator.qos_settings.name)) @@ -270,8 +267,8 @@ class CreateVolumeTypeComplexTests(OSIntegrationTestCase): encryption_settings = VolumeTypeEncryptionConfig( name='foo', provider_class='bar', control_location=ControlLocation.back_end) - self.volume_type_creator = create_volume_type.OpenStackVolumeType( - self.os_creds, + self.volume_type_creator = OpenStackVolumeType( + self.admin_os_creds, VolumeTypeConfig( name=self.volume_type_name, encryption=encryption_settings)) @@ -299,8 +296,8 @@ class CreateVolumeTypeComplexTests(OSIntegrationTestCase): encryption_settings = VolumeTypeEncryptionConfig( name='foo', provider_class='bar', control_location=ControlLocation.back_end) - self.volume_type_creator = create_volume_type.OpenStackVolumeType( - self.os_creds, + self.volume_type_creator = OpenStackVolumeType( + self.admin_os_creds, VolumeTypeConfig( name=self.volume_type_name, encryption=encryption_settings, diff --git a/snaps/openstack/tests/heat/agent-group.yaml b/snaps/openstack/tests/heat/agent-group.yaml index 540ea93..494da0a 100644 --- a/snaps/openstack/tests/heat/agent-group.yaml +++ b/snaps/openstack/tests/heat/agent-group.yaml @@ -41,6 +41,10 @@ parameters: availability_zone: type: string default: nova + network_name: + type: string + key_name: + type: string resources: slaves: @@ -66,7 +70,7 @@ resources: network: type: OS::Neutron::Net properties: - name: network + name: { get_param: network_name } subnet: type: OS::Neutron::Subnet @@ -91,7 +95,7 @@ resources: type: OS::Nova::KeyPair properties: save_private_key: true - name: agent_keypair + name: { get_param: key_name } open_security_group: type: OS::Neutron::SecurityGroup diff --git a/snaps/openstack/tests/heat/agent.yaml b/snaps/openstack/tests/heat/agent.yaml index 014b14f..8ac0660 100644 --- a/snaps/openstack/tests/heat/agent.yaml +++ b/snaps/openstack/tests/heat/agent.yaml @@ -25,7 +25,6 @@ parameters: default: 'Ubuntu 16.04' key_name: type: string - default: test_key username: type: string default: test_user diff --git a/snaps/openstack/tests/heat/floating_ip_heat_template.yaml b/snaps/openstack/tests/heat/floating_ip_heat_template.yaml index a191acc..e64c7fc 100644 --- a/snaps/openstack/tests/heat/floating_ip_heat_template.yaml +++ b/snaps/openstack/tests/heat/floating_ip_heat_template.yaml @@ -41,6 +41,10 @@ parameters: label: Instance Flavor for second VM description: Flavor name for the second instance default: m1.med + flavor_extra_specs: + type: json + description: Instance Flavor extra specs + default: {} net_name: type: string label: Test network name @@ -88,12 +92,14 @@ resources: ram: 1024 vcpus: 2 disk: 2 + extra_specs: { get_param: flavor_extra_specs } flavor2: type: OS::Nova::Flavor properties: ram: 1024 vcpus: 2 disk: 2 + extra_specs: { get_param: flavor_extra_specs } network: type: OS::Neutron::Net @@ -140,10 +146,10 @@ resources: floating_network: { get_param: external_net_name } floating_ip_association: - type: OS::Nova::FloatingIPAssociation + type: OS::Neutron::FloatingIPAssociation properties: - floating_ip: { get_resource: floating_ip } - server_id: {get_resource: vm1} + floatingip_id: { get_resource: floating_ip } + port_id: {get_resource: port1} keypair: type: OS::Nova::KeyPair @@ -151,21 +157,28 @@ resources: name: { get_param: keypair_name } save_private_key: True + port1: + type: OS::Neutron::Port + properties: + network: { get_resource: network } + security_groups: [{ get_resource: server_security_group }] + fixed_ips: + - subnet_id: { get_resource: subnet } + vm1: type: OS::Nova::Server - depends_on: [subnet, keypair, flavor1] + depends_on: [subnet, keypair, flavor1, port1] properties: name: { get_param: inst1_name } image: { get_param: image1_name } flavor: { get_resource: flavor1 } key_name: {get_resource: keypair} - security_groups: [{ get_resource: server_security_group }] networks: - - network: { get_resource: network } + - port: { get_resource: port1 } vm2: type: OS::Nova::Server - depends_on: [subnet, flavor2] + depends_on: [subnet, keypair, flavor2] properties: name: { get_param: inst2_name } image: { get_param: image2_name } diff --git a/snaps/openstack/tests/openstack_tests.py b/snaps/openstack/tests/openstack_tests.py index a3dec11..f3a1df7 100644 --- a/snaps/openstack/tests/openstack_tests.py +++ b/snaps/openstack/tests/openstack_tests.py @@ -15,8 +15,8 @@ import logging import re -import pkg_resources from snaps import file_utils +from snaps.config.flavor import FlavorConfig from snaps.config.image import ImageConfig from snaps.config.network import NetworkConfig, SubnetConfig from snaps.config.router import RouterConfig @@ -139,6 +139,10 @@ def get_credentials(os_env_file=None, proxy_settings_str=None, if overrides and isinstance(overrides, dict): creds_dict.update(overrides) + for key, value in creds_dict.items(): + if value is not None and isinstance(value, str): + creds_dict[key] = value.replace('"', '').replace('\'', '') + os_creds = OSCreds(**creds_dict) logger.info('OS Credentials = %s', os_creds.__str__) return os_creds @@ -309,22 +313,81 @@ def ubuntu_image_settings(name, url=None, image_metadata=None, public=public) -def get_priv_net_config(net_name, subnet_name, router_name=None, - cidr='10.55.0.0/24', external_net=None, - netconf_override=None): - return OSNetworkConfig(net_name, subnet_name, cidr, router_name, - external_gateway=external_net, - netconf_override=netconf_override) +def get_priv_net_config(project_name, net_name, mtu=None, subnet_name=None, + router_name=None, cidr='10.55.0.0/24', + external_net=None, netconf_override=None): + return OSNetworkConfig( + project_name, net_name, mtu, subnet_name, cidr, router_name, + external_gateway=external_net, netconf_override=netconf_override) -def get_pub_net_config(net_name, subnet_name=None, router_name=None, - cidr='10.55.1.0/24', external_net=None, - netconf_override=None): - return OSNetworkConfig(net_name, subnet_name, cidr, router_name, - external_gateway=external_net, +def get_pub_net_config( + project_name, net_name, mtu=None, subnet_name=None, router_name=None, + cidr='10.55.1.0/24', external_net=None, netconf_override=None): + return OSNetworkConfig(project_name, net_name, mtu, subnet_name, cidr, + router_name, external_gateway=external_net, netconf_override=netconf_override) +def get_flavor_config(name, ram, disk, vcpus, ephemeral=None, swap=None, + rxtx_factor=None, is_public=None, metadata=None): + """This method replaces the hard coded basic element (e.g. ram, vcpu, disk + etc) with those are included in the new freeform dict() of metadata + parameter. + + :param name: the flavor name (required) + :param ram: memory in MB to allocate to VM (required) + :param disk: disk storage in GB (required) + :param vcpus: the number of CPUs to allocate to VM (required) + :param ephemeral: the size of the ephemeral disk in GB (default=0) + :param swap: the size of the swap disk in GB (default=0) + :param rxtx_factor: the receive/transmit factor to be set on ports + if backend supports QoS extension (default=1.0) + :param is_public: flag that denotes whether or not other projects + can access image (default=True) + :param metadata: - freeform dict() for special metadata (optional) + - freeform dict() for values of basic elements + (e.g. ram, vcpu, disk, etc) could be added. + As result the hard coded values of those elements will be + overwritten by the new ones (optional) + :return: The FlavorConfig replacing the hard coded basic element values + (e.g. ram, vcpu, disk etc) with those are included in the metadata + dict [optional]. The metadata parameter in the FlavorConfig + consist of the metadata data only. + """ + + metadata_excl = metadata + if metadata: + if 'ram' in metadata: + ram = metadata['ram'] + del metadata_excl['ram'] + if 'disk' in metadata: + disk = metadata['disk'] + del metadata_excl['disk'] + if 'vcpus' in metadata: + vcpus = metadata['vcpus'] + del metadata_excl['vcpus'] + if 'ephemeral' in metadata: + ephemeral = metadata['ephemeral'] + del metadata_excl['ephemeral'] + if 'swap' in metadata: + swap = metadata['swap'] + del metadata_excl['swap'] + if 'rxtx_factor' in metadata: + rxtx_factor = metadata['rxtx_factor'] + del metadata_excl['rxtx_factor'] + if 'is_public' in metadata: + is_public = metadata['is_public'] + del metadata_excl['is_public'] + if 'metadata' in metadata: + metadata_excl = metadata['metadata'] + + return FlavorConfig( + name=name, ram=ram, disk=disk, vcpus=vcpus, ephemeral=ephemeral, + swap=swap, rxtx_factor=rxtx_factor, is_public=is_public, + metadata=metadata_excl) + + class OSNetworkConfig: """ Represents the settings required for the creation of a network in OpenStack @@ -332,18 +395,17 @@ class OSNetworkConfig: physical_network and segmentation_id """ - def __init__(self, net_name, subnet_name=None, subnet_cidr=None, - router_name=None, external_gateway=None, + def __init__(self, project_name, net_name, mtu=None, subnet_name=None, + subnet_cidr=None, router_name=None, external_gateway=None, netconf_override=None): """ - :param netconf_override: dict() containing the reconfigured network_type, - physical_network and segmentation_id + :param netconf_override: dict() containing the reconfigured + network_type, physical_network and + segmentation_id """ - - network_conf = None if subnet_name and subnet_cidr: network_conf = NetworkConfig( - name=net_name, subnet_settings=[ + name=net_name, mtu=mtu, subnet_settings=[ SubnetConfig(cidr=subnet_cidr, name=subnet_name)]) else: network_conf = NetworkConfig(name=net_name) @@ -359,7 +421,10 @@ class OSNetworkConfig: if subnet_name: self.router_settings = RouterConfig( name=router_name, external_gateway=external_gateway, - internal_subnets=[subnet_name]) + internal_subnets=[{'subnet': { + 'project_name': project_name, + 'network_name': net_name, + 'subnet_name': subnet_name}}]) else: self.router_settings = RouterConfig( 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 index 7e910a4..8b0a7b4 100644 --- a/snaps/openstack/tests/os_source_file_test.py +++ b/snaps/openstack/tests/os_source_file_test.py @@ -31,7 +31,8 @@ dev_os_env_file = pkg_resources.resource_filename( class OSComponentTestCase(unittest.TestCase): def __init__(self, method_name='runTest', os_creds=None, ext_net_name=None, - image_metadata=None, log_level=logging.DEBUG): + flavor_metadata=None, image_metadata=None, + log_level=logging.DEBUG): """ Super for test classes requiring a connection to OpenStack :param method_name: default 'runTest' @@ -39,6 +40,8 @@ class OSComponentTestCase(unittest.TestCase): 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 flavor_metadata: dict() to be sent directly into the Nova client + generally used for page sizes :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) @@ -47,23 +50,36 @@ class OSComponentTestCase(unittest.TestCase): logging.basicConfig(level=log_level) + self.ext_net_name = None + self.flavor_metadata = None + 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.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') + if file_utils.file_exists(dev_os_env_file): + self.os_creds = openstack_tests.get_credentials( + dev_os_env_file=dev_os_env_file) + test_conf = file_utils.read_yaml(dev_os_env_file) + self.ext_net_name = test_conf.get('ext_net') + os_env_dict = file_utils.read_yaml(dev_os_env_file) + flavor_metadata = os_env_dict.get('flavor_metadata') + if flavor_metadata: + self.flavor_metadata = {'metadata': flavor_metadata} + else: + raise Exception('Unable to obtain OSCreds') + + self.os_session = keystone_utils.keystone_session(self.os_creds) self.image_metadata = image_metadata + if not self.ext_net_name: + self.ext_net_name = ext_net_name + if not self.flavor_metadata: + self.flavor_metadata = flavor_metadata @staticmethod def parameterize(testcase_klass, os_creds, ext_net_name, - image_metadata=None, log_level=logging.DEBUG): + flavor_metadata=None, image_metadata=None, + log_level=logging.DEBUG): """ Create a suite containing all tests taken from the given subclass, passing them the parameter 'param'. """ @@ -71,10 +87,18 @@ 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, flavor_metadata, image_metadata, + log_level)) return suite + def __clean__(self): + """ + Cleans up keystone session. + """ + if self.os_session: + keystone_utils.close_session(self.os_session) + class OSIntegrationTestCase(OSComponentTestCase): @@ -104,16 +128,17 @@ class OSIntegrationTestCase(OSComponentTestCase): """ 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) + ext_net_name=ext_net_name, flavor_metadata=flavor_metadata, + image_metadata=image_metadata, log_level=log_level) self.netconf_override = netconf_override self.use_keystone = use_keystone self.keystone = None - self.flavor_metadata = flavor_metadata + self.user_roles = None + self.proj_users = None @staticmethod def parameterize(testcase_klass, os_creds, ext_net_name, - use_keystone=False, flavor_metadata=None, + use_keystone=True, flavor_metadata=None, image_metadata=None, netconf_override=None, log_level=logging.DEBUG): """ @@ -143,10 +168,11 @@ class OSIntegrationTestCase(OSComponentTestCase): self.project_creator = None self.user_creator = None self.admin_os_creds = self.os_creds - self.role = None + self.admin_os_session = self.os_session + self.keystone = keystone_utils.keystone_client( + self.admin_os_creds, self.admin_os_session) if self.use_keystone: - self.keystone = keystone_utils.keystone_client(self.admin_os_creds) guid = self.__class__.__name__ + '-' + str(uuid.uuid4())[:-19] project_name = guid + '-proj' self.project_creator = deploy_utils.create_project( @@ -154,19 +180,31 @@ class OSIntegrationTestCase(OSComponentTestCase): name=project_name, domain=self.admin_os_creds.project_domain_name)) + # Set by implementing class for setting the user's roles + roles = dict() + if self.user_roles and isinstance(self.user_roles, list): + for user_role in self.user_roles: + roles[user_role] = project_name + self.user_creator = deploy_utils.create_user( self.admin_os_creds, UserConfig( name=guid + '-user', password=guid, - project_name=project_name, roles={ - 'admin': self.project_creator.project_settings.name}, + project_name=project_name, roles=roles, domain_name=self.admin_os_creds.user_domain_name)) self.os_creds = self.user_creator.get_os_creds( self.project_creator.project_settings.name) + self.os_session = keystone_utils.keystone_session(self.os_creds) # add user to project self.project_creator.assoc_user(self.user_creator.get_user()) + if self.proj_users and isinstance(self.proj_users, list): + for user_name in self.proj_users: + user = keystone_utils.get_user(self.keystone, user_name) + if user: + self.project_creator.assoc_user(user) + def __clean__(self): """ Cleans up test user and project. @@ -174,11 +212,13 @@ class OSIntegrationTestCase(OSComponentTestCase): 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() + + if self.admin_os_session: + keystone_utils.close_session(self.admin_os_session) + + super(OSIntegrationTestCase, self).__clean__() diff --git a/snaps/openstack/utils/__init__.py b/snaps/openstack/utils/__init__.py index 7f92908..5435f8f 100644 --- a/snaps/openstack/utils/__init__.py +++ b/snaps/openstack/utils/__init__.py @@ -12,4 +12,5 @@ # 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'
\ No newline at end of file + +__author__ = 'spisarski' diff --git a/snaps/openstack/utils/cinder_utils.py b/snaps/openstack/utils/cinder_utils.py index c50a166..61abe22 100644 --- a/snaps/openstack/utils/cinder_utils.py +++ b/snaps/openstack/utils/cinder_utils.py @@ -33,37 +33,55 @@ Utilities for basic neutron API calls """ -def cinder_client(os_creds): +def cinder_client(os_creds, session=None): """ Creates and returns a cinder client object + :param os_creds: the credentials for connecting to the OpenStack remote API + :param session: the keystone session object (optional) :return: the cinder client """ + if not session: + session = keystone_utils.keystone_session(os_creds) + return Client(version=os_creds.volume_api_version, - session=keystone_utils.keystone_session(os_creds), + session=session, region_name=os_creds.region_name) -def get_volume(cinder, volume_name=None, volume_settings=None): +def get_volume(cinder, keystone=None, volume_name=None, volume_settings=None, + project_name=None): """ Returns an OpenStack volume object for a given name :param cinder: the Cinder client + :param keystone: the Keystone client (required if project_name or + volume_settings.project_name is not None :param volume_name: the volume name to lookup :param volume_settings: the volume settings used for lookups + :param project_name: the name of the project associated with the volume :return: the volume object or None """ if volume_settings: volume_name = volume_settings.name volumes = cinder.volumes.list() - for volume in volumes: - if volume.name == volume_name: - return Volume( - name=volume.name, volume_id=volume.id, - description=volume.description, size=volume.size, - vol_type=volume.volume_type, - availability_zone=volume.availability_zone, - multi_attach=volume.multiattach, - attachments=volume.attachments) + for os_volume in volumes: + if os_volume.name == volume_name: + project_id = None + if hasattr(os_volume, 'os-vol-tenant-attr:tenant_id'): + project_id = getattr( + os_volume, 'os-vol-tenant-attr:tenant_id') + + if volume_settings and volume_settings.project_name: + project_name = volume_settings.project_name + + if project_name: + project = keystone_utils.get_project_by_id( + keystone, project_id) + + if project and project.name == project_name: + return __map_os_volume_to_domain(os_volume) + else: + return __map_os_volume_to_domain(os_volume) def __get_os_volume_by_id(cinder, volume_id): @@ -83,12 +101,29 @@ def get_volume_by_id(cinder, volume_id): :param volume_id: the volume ID to lookup :return: the SNAPS-OO Domain Volume object or None """ - volume = __get_os_volume_by_id(cinder, volume_id) + os_volume = __get_os_volume_by_id(cinder, volume_id) + return __map_os_volume_to_domain(os_volume) + + +def __map_os_volume_to_domain(os_volume): + """ + Returns a SNAPS-OO domain Volume object that is created by an OpenStack + Volume object + :param os_volume: the OpenStack volume object + :return: Volume domain object + """ + project_id = None + if hasattr(os_volume, 'os-vol-tenant-attr:tenant_id'): + project_id = getattr( + os_volume, 'os-vol-tenant-attr:tenant_id') + return Volume( - name=volume.name, volume_id=volume.id, description=volume.description, - size=volume.size, vol_type=volume.volume_type, - availability_zone=volume.availability_zone, - multi_attach=volume.multiattach, attachments=volume.attachments) + name=os_volume.name, volume_id=os_volume.id, + project_id=project_id, description=os_volume.description, + size=os_volume.size, vol_type=os_volume.volume_type, + availability_zone=os_volume.availability_zone, + multi_attach=os_volume.multiattach, + attachments=os_volume.attachments) def get_volume_status(cinder, volume): @@ -102,27 +137,36 @@ def get_volume_status(cinder, volume): return os_volume.status -def create_volume(cinder, volume_settings): +def create_volume(cinder, keystone, volume_settings): """ Creates and returns OpenStack volume object with an external URL :param cinder: the cinder client + :param keystone: the keystone client :param volume_settings: the volume settings object :return: the OpenStack volume object :raise Exception if using a file and it cannot be found """ - volume = cinder.volumes.create( - name=volume_settings.name, description=volume_settings.description, - size=volume_settings.size, imageRef=volume_settings.image_name, + project_id = None + if volume_settings.project_name: + project = keystone_utils.get_project( + keystone, project_name=volume_settings.project_name) + if project: + project_id = project.id + else: + raise KeystoneUtilsException( + 'Project cannot be found with name - ' + + volume_settings.project_name) + os_volume = cinder.volumes.create( + name=volume_settings.name, + project_id=project_id, + description=volume_settings.description, + size=volume_settings.size, + imageRef=volume_settings.image_name, volume_type=volume_settings.type_name, availability_zone=volume_settings.availability_zone, multiattach=volume_settings.multi_attach) - return Volume( - name=volume.name, volume_id=volume.id, - description=volume.description, - size=volume.size, vol_type=volume.volume_type, - availability_zone=volume.availability_zone, - multi_attach=volume.multiattach, attachments=volume.attachments) + return __map_os_volume_to_domain(os_volume) def delete_volume(cinder, volume): @@ -367,3 +411,9 @@ def delete_qos(cinder, qos): """ logger.info('Deleting QoS named - %s', qos.name) cinder.qos_specs.delete(qos.id) + + +class KeystoneUtilsException(Exception): + """ + Exception when calls to the Keystone client cannot be served properly + """ diff --git a/snaps/openstack/utils/glance_utils.py b/snaps/openstack/utils/glance_utils.py index a127ad3..db20f2f 100644 --- a/snaps/openstack/utils/glance_utils.py +++ b/snaps/openstack/utils/glance_utils.py @@ -34,13 +34,18 @@ Utilities for basic neutron API calls """ -def glance_client(os_creds): +def glance_client(os_creds, session=None): """ Creates and returns a glance client object + :param os_creds: the credentials for connecting to the OpenStack remote API + :param session: the keystone session object (optional) :return: the glance client """ + if not session: + session = keystone_utils.keystone_session(os_creds) + return Client(version=os_creds.image_api_version, - session=keystone_utils.keystone_session(os_creds), + session=session, region_name=os_creds.region_name) diff --git a/snaps/openstack/utils/heat_utils.py b/snaps/openstack/utils/heat_utils.py index e440717..17de020 100644 --- a/snaps/openstack/utils/heat_utils.py +++ b/snaps/openstack/utils/heat_utils.py @@ -15,32 +15,35 @@ import logging import os -import yaml from heatclient.client import Client from heatclient.common.template_format import yaml_loader from novaclient.exceptions import NotFound from oslo_serialization import jsonutils +import yaml from snaps import file_utils from snaps.domain.stack import Stack, Resource, Output - from snaps.openstack.utils import ( keystone_utils, neutron_utils, nova_utils, cinder_utils) +from snaps.thread_utils import worker_pool + __author__ = 'spisarski' logger = logging.getLogger('heat_utils') -def heat_client(os_creds): +def heat_client(os_creds, session=None): """ Retrieves the Heat client :param os_creds: the OpenStack credentials :return: the client """ logger.debug('Retrieving Heat Client') + if not session: + session = keystone_utils.keystone_session(os_creds) return Client(os_creds.heat_api_version, - session=keystone_utils.keystone_session(os_creds), + session=session, region_name=os_creds.region_name) @@ -65,7 +68,11 @@ def get_stack(heat_cli, stack_settings=None, stack_name=None): stacks = heat_cli.stacks.list(**stack_filter) for stack in stacks: - return Stack(name=stack.identifier, stack_id=stack.id) + return Stack( + name=stack.stack_name, stack_id=stack.id, + stack_project_id=stack.stack_user_project_id, + status=stack.stack_status, + status_reason=stack.stack_status_reason) def get_stack_by_id(heat_cli, stack_id): @@ -76,7 +83,11 @@ def get_stack_by_id(heat_cli, stack_id): :return: the Stack domain object else None """ stack = heat_cli.stacks.get(stack_id) - return Stack(name=stack.identifier, stack_id=stack.id) + return Stack( + name=stack.stack_name, stack_id=stack.id, + stack_project_id=stack.stack_user_project_id, + status=stack.stack_status, + status_reason=stack.stack_status_reason) def get_stack_status(heat_cli, stack_id): @@ -133,6 +144,24 @@ def create_stack(heat_cli, stack_settings): return get_stack_by_id(heat_cli, stack_id=stack['stack']['id']) +def update_stack(heat_cli, stack, env_vals): + """ + Updates the specified parameters in the stack + :param heat_cli: the OpenStack heat client object + :param stack_settings: the stack configuration + """ + args = dict() + + args['stack_name'] = stack.name + args['existing'] = True + + if env_vals: + args['parameters'] = env_vals + heat_cli.stacks.update(stack.id, **args) + else: + logger.warn('Stack not updated, env_vals are None') + + def delete_stack(heat_cli, stack): """ Deletes the Heat stack @@ -210,8 +239,14 @@ def get_stack_networks(heat_cli, neutron, stack): out = list() resources = get_resources(heat_cli, stack.id, 'OS::Neutron::Net') + workers = [] for resource in resources: - network = neutron_utils.get_network_by_id(neutron, resource.id) + worker = worker_pool().apply_async(neutron_utils.get_network_by_id, + (neutron, resource.id)) + workers.append(worker) + + for worker in workers: + network = worker.get() if network: out.append(network) @@ -229,8 +264,14 @@ def get_stack_routers(heat_cli, neutron, stack): out = list() resources = get_resources(heat_cli, stack.id, 'OS::Neutron::Router') + workers = [] for resource in resources: - router = neutron_utils.get_router_by_id(neutron, resource.id) + worker = worker_pool().apply_async(neutron_utils.get_router_by_id, + (neutron, resource.id)) + workers.append(worker) + + for worker in workers: + router = worker.get() if router: out.append(router) @@ -248,47 +289,68 @@ def get_stack_security_groups(heat_cli, neutron, stack): out = list() resources = get_resources(heat_cli, stack.id, 'OS::Neutron::SecurityGroup') + workers = [] for resource in resources: - security_group = neutron_utils.get_security_group_by_id( - neutron, resource.id) + worker = worker_pool().apply_async( + neutron_utils.get_security_group_by_id, + (neutron, resource.id)) + workers.append(worker) + + for worker in workers: + security_group = worker.get() if security_group: out.append(security_group) return out -def get_stack_servers(heat_cli, nova, neutron, stack): +def get_stack_servers(heat_cli, nova, neutron, keystone, stack, project_name): """ Returns a list of VMInst domain objects associated with a Stack :param heat_cli: the OpenStack heat client object :param nova: the OpenStack nova client object :param neutron: the OpenStack neutron client object + :param keystone: the OpenStack keystone client object :param stack: the SNAPS-OO Stack domain object + :param project_name: the associated project ID :return: a list of VMInst domain objects """ out = list() srvr_res = get_resources(heat_cli, stack.id, 'OS::Nova::Server') + workers = [] for resource in srvr_res: + worker = worker_pool().apply_async( + nova_utils.get_server_object_by_id, + (nova, neutron, keystone, resource.id, project_name)) + workers.append((resource.id, worker)) + + for worker in workers: + resource_id = worker[0] try: - server = nova_utils.get_server_object_by_id( - nova, neutron, resource.id) + server = worker[1].get() if server: out.append(server) except NotFound: - logger.warn('VmInst cannot be located with ID %s', resource.id) + logger.warn('VmInst cannot be located with ID %s', resource_id) res_grps = get_resources(heat_cli, stack.id, 'OS::Heat::ResourceGroup') for res_grp in res_grps: res_ress = get_resources(heat_cli, res_grp.id) + workers = [] for res_res in res_ress: res_res_srvrs = get_resources( heat_cli, res_res.id, 'OS::Nova::Server') for res_srvr in res_res_srvrs: - server = nova_utils.get_server_object_by_id( - nova, neutron, res_srvr.id) - if server: - out.append(server) + worker = worker_pool().apply_async( + nova_utils.get_server_object_by_id, + (nova, neutron, keystone, res_srvr.id, project_name)) + workers.append(worker) + + for worker in workers: + server = worker.get() + if server: + out.append(server) return out @@ -304,13 +366,20 @@ def get_stack_keypairs(heat_cli, nova, stack): out = list() resources = get_resources(heat_cli, stack.id, 'OS::Nova::KeyPair') + workers = [] for resource in resources: + worker = worker_pool().apply_async( + nova_utils.get_keypair_by_id, (nova, resource.id)) + workers.append((resource.id, worker)) + + for worker in workers: + resource_id = worker[0] try: - keypair = nova_utils.get_keypair_by_id(nova, resource.id) + keypair = worker[1].get() if keypair: out.append(keypair) except NotFound: - logger.warn('Keypair cannot be located with ID %s', resource.id) + logger.warn('Keypair cannot be located with ID %s', resource_id) return out @@ -326,13 +395,20 @@ def get_stack_volumes(heat_cli, cinder, stack): out = list() resources = get_resources(heat_cli, stack.id, 'OS::Cinder::Volume') + workers = [] for resource in resources: + worker = worker_pool().apply_async( + cinder_utils.get_volume_by_id, (cinder, resource.id)) + workers.append((resource.id, worker)) + + for worker in workers: + resource_id = worker[0] try: - server = cinder_utils.get_volume_by_id(cinder, resource.id) + server = worker[1].get() if server: out.append(server) except NotFound: - logger.warn('Volume cannot be located with ID %s', resource.id) + logger.warn('Volume cannot be located with ID %s', resource_id) return out @@ -348,13 +424,20 @@ def get_stack_volume_types(heat_cli, cinder, stack): out = list() resources = get_resources(heat_cli, stack.id, 'OS::Cinder::VolumeType') + workers = [] for resource in resources: + worker = worker_pool().apply_async( + cinder_utils.get_volume_type_by_id, (cinder, resource.id)) + workers.append((resource.id, worker)) + + for worker in workers: + resource_id = worker[0] try: - vol_type = cinder_utils.get_volume_type_by_id(cinder, resource.id) + vol_type = worker[1].get() if vol_type: out.append(vol_type) except NotFound: - logger.warn('VolumeType cannot be located with ID %s', resource.id) + logger.warn('VolumeType cannot be located with ID %s', resource_id) return out @@ -371,13 +454,20 @@ def get_stack_flavors(heat_cli, nova, stack): out = list() resources = get_resources(heat_cli, stack.id, 'OS::Nova::Flavor') + workers = [] for resource in resources: + worker = worker_pool().apply_async( + nova_utils.get_flavor_by_id, (nova, resource.id)) + workers.append((resource.id, worker)) + + for worker in workers: + resource_id = worker[0] try: - flavor = nova_utils.get_flavor_by_id(nova, resource.id) + flavor = worker[1].get() if flavor: out.append(flavor) except NotFound: - logger.warn('Flavor cannot be located with ID %s', resource.id) + logger.warn('Flavor cannot be located with ID %s', resource_id) return out diff --git a/snaps/openstack/utils/keystone_utils.py b/snaps/openstack/utils/keystone_utils.py index b8769c0..4d3c0d3 100644 --- a/snaps/openstack/utils/keystone_utils.py +++ b/snaps/openstack/utils/keystone_utils.py @@ -14,10 +14,12 @@ # limitations under the License. import logging +import keystoneauth1 from keystoneclient.client import Client from keystoneauth1.identity import v3, v2 from keystoneauth1 import session import requests +from keystoneclient.exceptions import NotFound from snaps.domain.project import Project, Domain from snaps.domain.role import Role @@ -26,6 +28,7 @@ from snaps.domain.user import User logger = logging.getLogger('keystone_utils') V2_VERSION_NUM = 2.0 +V3_VERSION_NUM = 3 V2_VERSION_STR = 'v' + str(V2_VERSION_NUM) @@ -77,15 +80,29 @@ def keystone_session(os_creds): verify=os_creds.cacert) -def keystone_client(os_creds): +def close_session(session): + """ + Closes a keystone session + :param session: a session.Session object + """ + if isinstance(session, keystoneauth1.session.Session): + session.session.close() + + +def keystone_client(os_creds, session=None): """ Returns the keystone client :param os_creds: the OpenStack credentials (OSCreds) object + :param session: the keystone session object (optional) :return: the client """ + + if not session: + session = keystone_session(os_creds) + return Client( version=os_creds.identity_api_version, - session=keystone_session(os_creds), + session=session, interface=os_creds.interface, region_name=os_creds.region_name) @@ -105,26 +122,16 @@ def get_endpoint(os_creds, service_type, interface='public'): interface=interface) -def get_project(keystone=None, os_creds=None, project_settings=None, - project_name=None): +def get_project(keystone=None, project_settings=None, project_name=None): """ Returns the first project where the project_settings is used for the query if not None, else the project_name parameter is used for the query. If both parameters are None, None is returned :param keystone: the Keystone client - :param os_creds: the OpenStack credentials used to obtain the Keystone - client if the keystone parameter is None :param project_settings: a ProjectConfig object :param project_name: the name to query :return: the SNAPS-OO Project domain object or None """ - if not keystone: - if os_creds: - keystone = keystone_client(os_creds) - else: - raise KeystoneException( - 'Cannot lookup project without the proper credentials') - proj_filter = dict() if project_name: @@ -152,6 +159,26 @@ def get_project(keystone=None, os_creds=None, project_settings=None, domain_id=domain_id) +def get_project_by_id(keystone, proj_id): + """ + Returns the first project where the project_settings is used for the query + if not None, else the project_name parameter is used for the query. If both + parameters are None, None is returned + :param keystone: the Keystone client + :param proj_id: the project ID + """ + if proj_id and len(proj_id) > 0: + try: + os_proj = keystone.projects.get(proj_id) + if os_proj: + return Project(name=os_proj.name, project_id=os_proj.id, + domain_id=os_proj) + except NotFound: + pass + except KeyError: + pass + + def create_project(keystone, project_settings): """ Creates a project @@ -184,7 +211,7 @@ def create_project(keystone, project_settings): def delete_project(keystone, project): """ Deletes a project - :param keystone: the Keystone clien + :param keystone: the Keystone client :param project: the SNAPS-OO Project domain object """ logger.info('Deleting project with name - %s', project.name) @@ -237,8 +264,8 @@ def create_user(keystone, user_settings): """ project = None if user_settings.project_name: - project = get_project(keystone=keystone, - project_name=user_settings.project_name) + project = get_project( + keystone=keystone, project_name=user_settings.project_name) if keystone.version == V2_VERSION_STR: project_id = None @@ -381,9 +408,10 @@ def get_domain_by_id(keystone, domain_id): :param domain_id: the domain ID to retrieve :return: the SNAPS-OO Domain domain object """ - domain = keystone.domains.get(domain_id) - if domain: - return Domain(name=domain.name, domain_id=domain.id) + if keystone.version != V2_VERSION_STR: + domain = keystone.domains.get(domain_id) + if domain: + return Domain(name=domain.name, domain_id=domain.id) def __get_os_domain_by_name(keystone, domain_name): diff --git a/snaps/openstack/utils/launch_utils.py b/snaps/openstack/utils/launch_utils.py index e10cf48..ddaad12 100644 --- a/snaps/openstack/utils/launch_utils.py +++ b/snaps/openstack/utils/launch_utils.py @@ -24,6 +24,7 @@ import os import time from keystoneauth1.exceptions import Unauthorized +from snaps import file_utils from snaps.config.flavor import FlavorConfig from snaps.config.image import ImageConfig from snaps.config.keypair import KeypairConfig @@ -48,9 +49,14 @@ from snaps.openstack.create_user import OpenStackUser from snaps.openstack.create_volume import OpenStackVolume from snaps.openstack.create_volume_type import OpenStackVolumeType from snaps.openstack.os_credentials import OSCreds, ProxySettings -from snaps.openstack.utils import deploy_utils, neutron_utils +from snaps.openstack.utils import deploy_utils, neutron_utils, keystone_utils +from snaps.openstack.utils.nova_utils import RebootType from snaps.provisioning import ansible_utils +from warnings import warn +warn('This utility will be removed in a subsequent release', + DeprecationWarning) + logger = logging.getLogger('lanuch_utils') DEFAULT_CREDS_KEY = 'admin' @@ -118,7 +124,7 @@ def launch_config(config, tmplt_file, deploy, clean, clean_image): users_dict) creators.append(vol_type_dict) - # Create volume types + # Create volumes vol_dict = __create_instances( os_creds_dict, OpenStackVolume, VolumeConfig, os_config.get('volumes'), 'volume', clean, users_dict) @@ -307,6 +313,8 @@ def __create_instances(os_creds_dict, creator_class, config_class, config, creator.create() out[inst_config['name']] = creator + else: + raise Exception('Unable to instantiate creator') logger.info('Initialized configured %ss', config_key) @@ -415,10 +423,9 @@ def __apply_ansible_playbooks(ansible_configs, os_creds_dict, vm_dict, 'SSH requests') return False - os_creds = os_creds_dict.get('admin-creds') __apply_ansible_playbook( - ansible_config, os_creds, vm_dict, image_dict, flavor_dict, - networks_dict, routers_dict) + ansible_config, os_creds_dict, vm_dict, image_dict, + flavor_dict, networks_dict, routers_dict) # Return to original directory os.chdir(orig_cwd) @@ -426,12 +433,13 @@ def __apply_ansible_playbooks(ansible_configs, os_creds_dict, vm_dict, return True -def __apply_ansible_playbook(ansible_config, os_creds, vm_dict, image_dict, - flavor_dict, networks_dict, routers_dict): +def __apply_ansible_playbook(ansible_config, os_creds_dict, vm_dict, + image_dict, flavor_dict, networks_dict, + routers_dict): """ Applies an Ansible configuration setting :param ansible_config: the configuration settings - :param os_creds: the OpenStack admin credentials object + :param os_creds_dict: dict where the key is the name and value is OSCreds :param vm_dict: the dictionary of newly instantiated VMs where the name is the key :param image_dict: the dictionary of newly instantiated images where the @@ -457,20 +465,16 @@ def __apply_ansible_playbook(ansible_config, os_creds, vm_dict, image_dict, 'completed') variables = __get_variables( - ansible_config.get('variables'), os_creds, vm_dict, image_dict, - flavor_dict, networks_dict, routers_dict) + ansible_config.get('variables'), os_creds_dict, vm_dict, + image_dict, flavor_dict, networks_dict, routers_dict) - retval = ansible_utils.apply_playbook( + ansible_utils.apply_playbook( ansible_config['playbook_location'], floating_ips, remote_user, - private_key_filepath, + ssh_priv_key_file_path=private_key_filepath, variables=variables, proxy_setting=proxy_settings) - if retval != 0: - # Not a fatal type of event - raise Exception( - 'Error applying playbook found at location - %s', - ansible_config.get('playbook_location')) - elif ansible_config.get('post_processing'): + + if 'post_processing' in ansible_config: post_proc_config = ansible_config['post_processing'] if 'sleep' in post_proc_config: time.sleep(post_proc_config['sleep']) @@ -478,9 +482,7 @@ def __apply_ansible_playbook(ansible_config, os_creds, vm_dict, image_dict, for vm_name in post_proc_config['reboot']: if vm_name in vm_dict: logger.info('Rebooting VM - %s', vm_name) - vm_dict[vm_name].reboot() - - return retval + vm_dict[vm_name].reboot(RebootType.hard) def __get_connection_info(ansible_config, vm_dict): @@ -526,13 +528,13 @@ def __get_connection_info(ansible_config, vm_dict): return None -def __get_variables(var_config, os_creds, vm_dict, image_dict, flavor_dict, - networks_dict, routers_dict): +def __get_variables(var_config, os_creds_dict, vm_dict, image_dict, + flavor_dict, networks_dict, routers_dict): """ Returns a dictionary of substitution variables to be used for Ansible templates :param var_config: the variable configuration settings - :param os_creds: the OpenStack admin credentials object + :param os_creds_dict: dict where the key is the name and value is OSCreds :param vm_dict: the dictionary of newly instantiated VMs where the name is the key :param image_dict: the dictionary of newly instantiated images where the @@ -549,7 +551,7 @@ def __get_variables(var_config, os_creds, vm_dict, image_dict, flavor_dict, variables = dict() for key, value in var_config.items(): value = __get_variable_value( - value, os_creds, vm_dict, image_dict, flavor_dict, + value, os_creds_dict, vm_dict, image_dict, flavor_dict, networks_dict, routers_dict) if key and value: variables[key] = value @@ -564,13 +566,13 @@ def __get_variables(var_config, os_creds, vm_dict, image_dict, flavor_dict, return None -def __get_variable_value(var_config_values, os_creds, vm_dict, image_dict, +def __get_variable_value(var_config_values, os_creds_dict, vm_dict, image_dict, flavor_dict, networks_dict, routers_dict): """ Returns the associated variable value for use by Ansible for substitution purposes :param var_config_values: the configuration dictionary - :param os_creds: the OpenStack admin credentials object + :param os_creds_dict: dict where the key is the name and value is OSCreds :param vm_dict: the dictionary of newly instantiated VMs where the name is the key :param image_dict: the dictionary of newly instantiated images where the @@ -588,12 +590,14 @@ def __get_variable_value(var_config_values, os_creds, vm_dict, image_dict, if var_config_values['type'] == 'vm-attr': return __get_vm_attr_variable_value(var_config_values, vm_dict) if var_config_values['type'] == 'os_creds': - return __get_os_creds_variable_value(var_config_values, os_creds) + return __get_os_creds_variable_value(var_config_values, os_creds_dict) + if var_config_values['type'] == 'os_creds_dict': + return str(__get_os_creds_dict(var_config_values, os_creds_dict)) if var_config_values['type'] == 'network': return __get_network_variable_value(var_config_values, networks_dict) if var_config_values['type'] == 'router': return __get_router_variable_value(var_config_values, routers_dict, - os_creds) + os_creds_dict) if var_config_values['type'] == 'port': return __get_vm_port_variable_value(var_config_values, vm_dict) if var_config_values['type'] == 'floating_ip': @@ -602,6 +606,8 @@ def __get_variable_value(var_config_values, os_creds, vm_dict, image_dict, return __get_image_variable_value(var_config_values, image_dict) if var_config_values['type'] == 'flavor': return __get_flavor_variable_value(var_config_values, flavor_dict) + if var_config_values['type'] == 'vm-yaml': + return __create_yaml(var_config_values, vm_dict) return None @@ -630,13 +636,19 @@ def __get_vm_attr_variable_value(var_config_values, vm_dict): return vm.get_image_user() -def __get_os_creds_variable_value(var_config_values, os_creds): +def __get_os_creds_variable_value(var_config_values, os_creds_dict): """ Returns the associated OS credentials value :param var_config_values: the configuration dictionary - :param os_creds: the admin OpenStack OSCreds object + :param os_creds_dict: dict of OpenStack credentials where the key is the + name :return: the value """ + if 'creds_name' in var_config_values: + os_creds = os_creds_dict.get[var_config_values['creds_name']] + else: + os_creds = os_creds_dict.get('admin-creds') + if os_creds: if var_config_values['value'] == 'username': logger.info("Returning OS username") @@ -652,6 +664,21 @@ def __get_os_creds_variable_value(var_config_values, os_creds): return os_creds.project_name +def __get_os_creds_dict(var_config_values, os_creds_dict): + """ + Returns the associated OS credentials as a dict + :param var_config_values: the configuration dictionary + :param os_creds_dict: dict of creds where the key is the username + :return: the value dict + """ + if 'creds_name' in var_config_values: + os_creds = os_creds_dict.get[var_config_values['creds_name']] + else: + os_creds = os_creds_dict.get('admin-creds') + if os_creds: + return os_creds.to_dict() + + def __get_network_variable_value(var_config_values, networks_dict): """ Returns the associated network value @@ -675,6 +702,12 @@ def __get_network_variable_value(var_config_values, networks_dict): return subnet.gateway_ip if 'ip_range' == var_config_values['value']: return subnet.start + ' ' + subnet.end + if 'ip_range_start' == var_config_values['value']: + return subnet.start + if 'ip_range_end' == var_config_values['value']: + return subnet.end + if 'cidr' == var_config_values['value']: + return subnet.cidr if 'cidr_ip' == var_config_values['value']: cidr_split = subnet.cidr.split('/') return cidr_split[0] @@ -693,32 +726,43 @@ def __get_network_variable_value(var_config_values, networks_dict): return broadcast_ip -def __get_router_variable_value(var_config_values, routers_dict, os_creds): +def __get_router_variable_value(var_config_values, routers_dict, + os_creds_dict): """ Returns the associated network value :param var_config_values: the configuration dictionary :param routers_dict: the dictionary containing all networks where the key is the network name - :param os_creds: the admin OpenStack credentials + :param os_creds_dict: dict of OpenStack credentials where the key is the + name :return: the value """ + if 'creds_name' in var_config_values: + os_creds = os_creds_dict.get[var_config_values['creds_name']] + else: + os_creds = os_creds_dict.get('admin-creds') + router_name = var_config_values.get('router_name') router_creator = routers_dict[router_name] if router_creator: if 'external_fixed_ip' == var_config_values.get('attr'): - neutron = neutron_utils.neutron_client(os_creds) - ext_nets = neutron_utils.get_external_networks(neutron) + session = keystone_utils.keystone_session(os_creds) + neutron = neutron_utils.neutron_client(os_creds, session) + try: + ext_nets = neutron_utils.get_external_networks(neutron) - subnet_name = var_config_values.get('subnet_name') + subnet_name = var_config_values.get('subnet_name') - for ext_net in ext_nets: - for subnet in ext_net.subnets: - if subnet_name == subnet.name: - router = router_creator.get_router() - for fixed_ips in router.external_fixed_ips: - if subnet.id == fixed_ips['subnet_id']: - return fixed_ips['ip_address'] + for ext_net in ext_nets: + for subnet in ext_net.subnets: + if subnet_name == subnet.name: + router = router_creator.get_router() + for fixed_ips in router.external_fixed_ips: + if subnet.id == fixed_ips['subnet_id']: + return fixed_ips['ip_address'] + finally: + keystone_utils.close_session(session) def __get_vm_port_variable_value(var_config_values, vm_dict): @@ -801,6 +845,36 @@ def __get_flavor_variable_value(var_config_values, flavor_dict): return flavor_creator.get_flavor().id +def __create_yaml(var_config_values, vm_dict): + """ + Creates a yaml file containing an OpenStack pod's credentials with a list + of server IDs that can be used for obtaining SNAPS-OO instances for + manipulation such as rebooting + :param var_config_values: the configuration dictionary + :param vm_dict: the dictionary containing all vm creators where the + key is the name + :return: the name of the generated file + """ + out_dict = dict() + out_dict['vms'] = list() + req_vm_names = var_config_values.get('vms') + + for name, vm_creator in vm_dict.items(): + vm_inst = vm_creator.get_vm_inst() + if vm_inst and vm_inst.name in req_vm_names: + out_dict['vms'].append({ + 'name': str(vm_inst.name), + 'id': str(vm_inst.id), + 'os_creds': vm_creator.get_os_creds().to_dict() + }) + + out_file = file_utils.persist_dict_to_yaml( + out_dict, var_config_values.get('file_name')) + + if out_file: + return out_file.name + + def __cleanup(creators, clean_image=False): """ Cleans up environment diff --git a/snaps/openstack/utils/magnum_utils.py b/snaps/openstack/utils/magnum_utils.py index 96ba6d1..1f39cfe 100644 --- a/snaps/openstack/utils/magnum_utils.py +++ b/snaps/openstack/utils/magnum_utils.py @@ -24,15 +24,18 @@ __author__ = 'spisarski' logger = logging.getLogger('magnum_utils') -def magnum_client(os_creds): +def magnum_client(os_creds, session=None): """ Retrieves the Magnum client :param os_creds: the OpenStack credentialsf + :param session: the keystone session object (optional) :return: the client """ logger.debug('Retrieving Magnum Client') - return Client(str(os_creds.magnum_api_version), - session=keystone_utils.keystone_session(os_creds)) + if not session: + session = keystone_utils.keystone_session(os_creds) + + return Client(str(os_creds.magnum_api_version), session=session) def get_cluster_template(magnum, template_config=None, template_name=None): diff --git a/snaps/openstack/utils/neutron_utils.py b/snaps/openstack/utils/neutron_utils.py index e94a40e..f1a5ac2 100644 --- a/snaps/openstack/utils/neutron_utils.py +++ b/snaps/openstack/utils/neutron_utils.py @@ -33,15 +33,18 @@ Utilities for basic neutron API calls """ -def neutron_client(os_creds): +def neutron_client(os_creds, session=None): """ Instantiates and returns a client for communications with OpenStack's Neutron server :param os_creds: the credentials for connecting to the OpenStack remote API + :param session: the keystone session object (optional) :return: the client object """ + if not session: + session = keystone_utils.keystone_session(os_creds) return Client(api_version=os_creds.network_api_version, - session=keystone_utils.keystone_session(os_creds), + session=session, region_name=os_creds.region_name) @@ -101,17 +104,18 @@ def delete_network(neutron, network): neutron.delete_network(network.id) -def get_network(neutron, network_settings=None, network_name=None, - project_id=None): +def get_network(neutron, keystone, network_settings=None, network_name=None, + project_name=None): """ Returns Network SNAPS-OO domain object the first network found with either the given attributes from the network_settings object if not None, else the query will use just the name from the network_name parameter. - When the project_id is included, that will be added to the query filter. - :param neutron: the client + When the project_name is included, that will be added to the query filter. + :param neutron: the Neutron client + :param keystone: the Keystone client :param network_settings: the NetworkConfig object used to create filter :param network_name: the name of the network to retrieve - :param project_id: the id of the network's project + :param project_name: the name of the network's project :return: a SNAPS-OO Network domain object """ net_filter = dict() @@ -120,13 +124,20 @@ def get_network(neutron, network_settings=None, network_name=None, elif network_name: net_filter['name'] = network_name - if project_id: - net_filter['project_id'] = project_id - networks = neutron.list_networks(**net_filter) for network, netInsts in networks.items(): for inst in netInsts: - return __map_network(neutron, inst) + if project_name: + if 'project_id' in inst.keys(): + project = keystone_utils.get_project_by_id( + keystone, inst['project_id']) + else: + project = keystone_utils.get_project_by_id( + keystone, inst['tenant_id']) + if project and project.name == project_name: + return __map_network(neutron, inst) + else: + return __map_network(neutron, inst) def __get_os_network_by_id(neutron, network_id): @@ -199,16 +210,17 @@ def delete_subnet(neutron, subnet): neutron.delete_subnet(subnet.id) -def get_subnet(neutron, subnet_settings=None, subnet_name=None): +def get_subnet(neutron, network, subnet_settings=None, subnet_name=None): """ Returns the first subnet object that fits the query else None including if subnet_settings or subnet_name parameters are None. :param neutron: the client + :param network: the associated SNAPS-OO Network domain object :param subnet_settings: the subnet settings of the object to retrieve :param subnet_name: the name of the subnet to retrieve :return: a SNAPS-OO Subnet domain object or None """ - sub_filter = dict() + sub_filter = {'network_id': network.id} if subnet_settings: sub_filter['name'] = subnet_settings.name sub_filter['cidr'] = subnet_settings.cidr @@ -239,6 +251,30 @@ def get_subnet(neutron, subnet_settings=None, subnet_name=None): return Subnet(**subnet) +def get_subnet_by_name(neutron, keystone, subnet_name, project_name=None): + """ + Returns the first subnet object that fits the query else None including + if subnet_settings or subnet_name parameters are None. + :param neutron: the Neutron client + :param keystone: the Keystone client + :param subnet_name: the name of the subnet to retrieve + :param project_name: the name of the associated project to the subnet to + retrieve + :return: a SNAPS-OO Subnet domain object or None + """ + sub_filter = {'name': subnet_name} + subnets = neutron.list_subnets(**sub_filter) + for subnet in subnets['subnets']: + subnet = Subnet(**subnet) + if project_name: + project = keystone_utils.get_project_by_id( + keystone, subnet.project_id) + if project and project.name == project_name: + return subnet + else: + return subnet + + def get_subnet_by_id(neutron, subnet_id): """ Returns a SNAPS-OO Subnet domain object for a given ID @@ -321,14 +357,17 @@ def get_router_by_id(neutron, router_id): return __map_router(neutron, router['router']) -def get_router(neutron, router_settings=None, router_name=None): +def get_router(neutron, keystone, router_settings=None, router_name=None, + project_name=None): """ Returns the first router object (dictionary) found the given the settings values if not None, else finds the first with the value of the router_name parameter, else None - :param neutron: the client + :param neutron: the Neutron client + :param keystone: the Keystone client :param router_settings: the RouterConfig object :param router_name: the name of the network to retrieve + :param project_name: the name of the router's project :return: a SNAPS-OO Router domain object """ router_filter = dict() @@ -341,12 +380,17 @@ def get_router(neutron, router_settings=None, router_name=None): else: return None - routers = neutron.list_routers(**router_filter) - - for routerInst in routers['routers']: - return __map_router(neutron, routerInst) - - return None + os_routers = neutron.list_routers(**router_filter) + for os_router in os_routers['routers']: + if project_name: + if 'project_id' in os_router.keys(): + project = keystone_utils.get_project_by_id( + keystone, os_router['project_id']) + else: + project = keystone_utils.get_project_by_id( + keystone, os_router['tenant_id']) + if project and project.name == project_name: + return __map_router(neutron, os_router) def __map_router(neutron, os_router): @@ -460,10 +504,7 @@ def create_port(neutron, os_creds, port_settings): logger.info('Creating port for network with name - %s', port_settings.network_name) os_port = neutron.create_port(body=json_body)['port'] - return Port(name=os_port['name'], id=os_port['id'], - ips=os_port['fixed_ips'], - mac_address=os_port['mac_address'], - allowed_address_pairs=os_port['allowed_address_pairs']) + return Port(**os_port) def delete_port(neutron, port): @@ -476,13 +517,16 @@ def delete_port(neutron, port): neutron.delete_port(port.id) -def get_port(neutron, port_settings=None, port_name=None): +def get_port(neutron, keystone, port_settings=None, port_name=None, + project_name=None): """ Returns the first port object (dictionary) found for the given query - :param neutron: the client + :param neutron: the Neutron client + :param keystone: the Keystone client :param port_settings: the PortConfig object used for generating the query :param port_name: if port_settings is None, this name is the value to place into the query + :param project_name: the associated project name :return: a SNAPS-OO Port domain object """ port_filter = dict() @@ -496,9 +540,15 @@ def get_port(neutron, port_settings=None, port_name=None): port_filter['device_id'] = port_settings.device_id if port_settings.mac_address: port_filter['mac_address'] = port_settings.mac_address + if port_settings.project_name: + project_name = port_settings.project_name if port_settings.network_name: - network = get_network(neutron, - network_name=port_settings.network_name) + network = get_network( + neutron, keystone, network_name=port_settings.network_name) + if network and not (network.shared or network.external): + network = get_network( + neutron, keystone, network_name=port_settings.network_name, + project_name=project_name) if network: port_filter['network_id'] = network.id elif port_name: @@ -506,7 +556,17 @@ def get_port(neutron, port_settings=None, port_name=None): ports = neutron.list_ports(**port_filter) for port in ports['ports']: - return Port(**port) + if project_name: + if 'project_id' in port.keys(): + project = keystone_utils.get_project_by_id( + keystone, port['project_id']) + else: + project = keystone_utils.get_project_by_id( + keystone, port['tenant_id']) + if project and project.name == project_name: + return Port(**port) + else: + return Port(**port) return None @@ -572,30 +632,31 @@ def delete_security_group(neutron, sec_grp): neutron.delete_security_group(sec_grp.id) -def get_security_group(neutron, sec_grp_settings=None, sec_grp_name=None, - project_id=None): +def get_security_group(neutron, keystone, sec_grp_settings=None, + sec_grp_name=None, project_name=None): """ Returns the first security group for a given query. The query gets built from the sec_grp_settings parameter if not None, else only the name of the security group will be used, else if the query parameters are None then None will be returned - :param neutron: the client + :param neutron: the neutron client + :param keystone: the keystone client :param sec_grp_settings: an instance of SecurityGroupConfig object :param sec_grp_name: the name of security group object to retrieve - :param project_id: the ID of the project/tentant object that owns the + :param project_name: the name of the project/tentant object that owns the secuity group to retrieve :return: a SNAPS-OO SecurityGroup domain object or None if not found """ sec_grp_filter = dict() - if project_id: - sec_grp_filter['tenant_id'] = project_id if sec_grp_settings: sec_grp_filter['name'] = sec_grp_settings.name if sec_grp_settings.description: sec_grp_filter['description'] = sec_grp_settings.description + if sec_grp_settings.project_name: + project_name = sec_grp_settings.project_name elif sec_grp_name: sec_grp_filter['name'] = sec_grp_name else: @@ -603,7 +664,17 @@ def get_security_group(neutron, sec_grp_settings=None, sec_grp_name=None, groups = neutron.list_security_groups(**sec_grp_filter) for group in groups['security_groups']: - return __map_os_security_group(neutron, group) + if project_name: + if 'project_id' in group.keys(): + project = keystone_utils.get_project_by_id( + keystone, group['project_id']) + else: + project = keystone_utils.get_project_by_id( + keystone, group['tenant_id']) + if project and project_name == project.name: + return __map_os_security_group(neutron, group) + else: + return __map_os_security_group(neutron, group) def __map_os_security_group(neutron, os_sec_grp): @@ -635,17 +706,35 @@ def get_security_group_by_id(neutron, sec_grp_id): return None -def create_security_group_rule(neutron, sec_grp_rule_settings): +def list_security_groups(neutron): + + """ + Lists the available security groups + :param neutron: the neutron client + """ + logger.info('Listing the available security groups') + sec_groups = [] + response = neutron.list_security_groups() + for sg in response['security_groups']: + sec_groups.append(__map_os_security_group(neutron, sg)) + + return sec_groups + + +def create_security_group_rule(neutron, keystone, sec_grp_rule_settings, + proj_name): """ Creates a security group rule in OpenStack - :param neutron: the client + :param neutron: the neutron client + :param keystone: the keystone client :param sec_grp_rule_settings: the security group rule settings + :param proj_name: the default project name :return: a SNAPS-OO SecurityGroupRule domain object """ logger.info('Creating security group to security group - %s', sec_grp_rule_settings.sec_grp_name) os_rule = neutron.create_security_group_rule( - sec_grp_rule_settings.dict_for_neutron(neutron)) + sec_grp_rule_settings.dict_for_neutron(neutron, keystone, proj_name)) return SecurityGroupRule(**os_rule['security_group_rule']) @@ -716,14 +805,11 @@ def get_external_networks(neutron): return out -def get_floating_ips(neutron, ports=None): +def get_port_floating_ips(neutron, ports): """ - Returns all of the floating IPs - When ports is not None, FIPs returned must be associated with one of the - ports in the list and a tuple 2 where the first element being the port's - ID and the second being the FloatingIp SNAPS-OO domain object. - When ports is None, all known FloatingIp SNAPS-OO domain objects will be - returned in a list + Returns all of the floating IPs associated with the ports returned in a + list of tuples where the port object is in the first position and the + floating IP object is in the second :param neutron: the Neutron client :param ports: a list of tuple 2 where index 0 is the port name and index 1 is the SNAPS-OO Port object @@ -733,31 +819,44 @@ def get_floating_ips(neutron, ports=None): out = list() fips = neutron.list_floatingips() for fip in fips['floatingips']: - if ports: - for port_name, port in ports: - if port and port.id == fip['port_id']: - out.append((port.id, FloatingIp(**fip))) - break - else: - out.append(FloatingIp(**fip)) + for port_name, port in ports: + if port and port.id == fip['port_id']: + out.append((port.id, FloatingIp(**fip))) + break + return out + +def get_floating_ips(neutron): + """ + Returns a list of all of the floating IPs + :param neutron: the Neutron client + """ + out = list() + fips = neutron.list_floatingips() + for fip in fips['floatingips']: + out.append(FloatingIp(**fip)) return out -def create_floating_ip(neutron, ext_net_name): +def create_floating_ip(neutron, keystone, ext_net_name, port_id=None): """ Returns the floating IP object that was created with this call :param neutron: the Neutron client + :param keystone: the Keystone client :param ext_net_name: the name of the external network on which to apply the floating IP address + :param port_id: the ID of the port to which the floating IP will be + associated :return: the SNAPS FloatingIp object """ logger.info('Creating floating ip to external network - ' + ext_net_name) - ext_net = get_network(neutron, network_name=ext_net_name) + ext_net = get_network(neutron, keystone, network_name=ext_net_name) if ext_net: - fip = neutron.create_floatingip( - body={'floatingip': - {'floating_network_id': ext_net.id}}) + body = {'floatingip': {'floating_network_id': ext_net.id}} + if port_id: + body['floatingip']['port_id'] = port_id + + fip = neutron.create_floatingip(body=body) return FloatingIp(id=fip['floatingip']['id'], ip=fip['floatingip']['floating_ip_address']) @@ -791,7 +890,7 @@ def __get_os_floating_ip(neutron, floating_ip): """ logger.debug('Attempting to retrieve existing floating ip with IP - %s', floating_ip.ip) - fips = neutron.list_floatingips(ip=floating_ip.id) + fips = neutron.list_floatingips(floating_ip_address=floating_ip.ip) for fip in fips['floatingips']: if fip['id'] == floating_ip.id: diff --git a/snaps/openstack/utils/nova_utils.py b/snaps/openstack/utils/nova_utils.py index e15484c..005b56f 100644 --- a/snaps/openstack/utils/nova_utils.py +++ b/snaps/openstack/utils/nova_utils.py @@ -35,33 +35,41 @@ __author__ = 'spisarski' logger = logging.getLogger('nova_utils') +POLL_INTERVAL = 3 + """ Utilities for basic OpenStack Nova API calls """ -def nova_client(os_creds): +def nova_client(os_creds, session=None): """ Instantiates and returns a client for communications with OpenStack's Nova server :param os_creds: The connection credentials to the OpenStack API + :param session: the keystone session object (optional) :return: the client object """ logger.debug('Retrieving Nova Client') + if not session: + session = keystone_utils.keystone_session(os_creds) + return Client(os_creds.compute_api_version, - session=keystone_utils.keystone_session(os_creds), + session=session, region_name=os_creds.region_name) -def create_server(nova, neutron, glance, instance_config, image_config, - keypair_config=None): +def create_server(nova, keystone, neutron, glance, instance_config, + image_config, project_name, keypair_config=None): """ Creates a VM instance :param nova: the nova client (required) + :param keystone: the keystone client for retrieving projects (required) :param neutron: the neutron client for retrieving ports (required) :param glance: the glance client (required) :param instance_config: the VMInstConfig object (required) :param image_config: the VM's ImageConfig object (required) + :param project_name: the associated project name (required) :param keypair_config: the VM's KeypairConfig object (optional) :return: a snaps.domain.VmInst object """ @@ -69,8 +77,13 @@ def create_server(nova, neutron, glance, instance_config, image_config, ports = list() for port_setting in instance_config.port_settings: - ports.append(neutron_utils.get_port( - neutron, port_settings=port_setting)) + port = neutron_utils.get_port( + neutron, keystone, port_settings=port_setting, + project_name=project_name) + if port: + ports.append(port) + else: + raise Exception('Cannot find port named - ' + port_setting.name) nics = [] for port in ports: kv = dict() @@ -115,22 +128,26 @@ def create_server(nova, neutron, glance, instance_config, image_config, server = nova.servers.create(**args) - return __map_os_server_obj_to_vm_inst(neutron, server) + return __map_os_server_obj_to_vm_inst( + neutron, keystone, server, project_name) else: raise NovaException( 'Cannot create instance, image cannot be located with name %s', image_config.name) -def get_server(nova, neutron, vm_inst_settings=None, server_name=None): +def get_server(nova, neutron, keystone, vm_inst_settings=None, + server_name=None, project_id=None): """ Returns a VmInst object for the first server instance found. :param nova: the Nova client :param neutron: the Neutron client + :param keystone: the Keystone client :param vm_inst_settings: the VmInstanceConfig object from which to build the query if not None :param server_name: the server with this name to return if vm_inst_settings is not None + :param project_id: the assocaited project ID :return: a snaps.domain.VmInst object or None if not found """ search_opts = dict() @@ -141,7 +158,8 @@ def get_server(nova, neutron, vm_inst_settings=None, server_name=None): servers = nova.servers.list(search_opts=search_opts) for server in servers: - return __map_os_server_obj_to_vm_inst(neutron, server) + return __map_os_server_obj_to_vm_inst( + neutron, keystone, server, project_id) def get_server_connection(nova, vm_inst_settings=None, server_name=None): @@ -165,11 +183,14 @@ def get_server_connection(nova, vm_inst_settings=None, server_name=None): return server.links[0] -def __map_os_server_obj_to_vm_inst(neutron, os_server): +def __map_os_server_obj_to_vm_inst(neutron, keystone, os_server, + project_name=None): """ Returns a VmInst object for an OpenStack Server object - :param neutron: the Neutron client (when None, ports will be empty) + :param neutron: the Neutron client + :param keystone: the Keystone client :param os_server: the OpenStack server object + :param project_name: the associated project name :return: an equivalent SNAPS-OO VmInst domain object """ sec_grp_names = list() @@ -182,10 +203,17 @@ def __map_os_server_obj_to_vm_inst(neutron, os_server): out_ports = list() if len(os_server.networks) > 0: for net_name, ips in os_server.networks.items(): - network = neutron_utils.get_network(neutron, network_name=net_name) - ports = neutron_utils.get_ports(neutron, network, ips) - for port in ports: - out_ports.append(port) + network = neutron_utils.get_network( + neutron, keystone, network_name=net_name, + project_name=project_name) + if network: + ports = neutron_utils.get_ports(neutron, network, ips) + for port in ports: + out_ports.append(port) + else: + raise NovaException( + 'Unable to locate network in project {} with ' + 'name {}'.format(project_name, net_name)) volumes = None if hasattr(os_server, 'os-extended-volumes:volumes_attached'): @@ -195,7 +223,9 @@ def __map_os_server_obj_to_vm_inst(neutron, os_server): name=os_server.name, inst_id=os_server.id, image_id=os_server.image['id'], flavor_id=os_server.flavor['id'], ports=out_ports, keypair_name=os_server.key_name, - sec_grp_names=sec_grp_names, volume_ids=volumes) + sec_grp_names=sec_grp_names, volume_ids=volumes, + compute_host=os_server._info.get('OS-EXT-SRV-ATTR:host'), + availability_zone=os_server._info.get('OS-EXT-AZ:availability_zone')) def __get_latest_server_os_object(nova, server): @@ -244,28 +274,35 @@ def get_server_console_output(nova, server): return None -def get_latest_server_object(nova, neutron, server): +def get_latest_server_object(nova, neutron, keystone, server, project_name): """ Returns a server with a given id :param nova: the Nova client :param neutron: the Neutron client + :param keystone: the Keystone client :param server: the old server object + :param project_name: the associated project name :return: the list of servers or None if not found """ server = __get_latest_server_os_object(nova, server) - return __map_os_server_obj_to_vm_inst(neutron, server) + return __map_os_server_obj_to_vm_inst( + neutron, keystone, server, project_name) -def get_server_object_by_id(nova, neutron, server_id): +def get_server_object_by_id(nova, neutron, keystone, server_id, + project_name=None): """ Returns a server with a given id :param nova: the Nova client :param neutron: the Neutron client + :param keystone: the Keystone client :param server_id: the server's id + :param project_name: the associated project name :return: an SNAPS-OO VmInst object or None if not found """ server = __get_latest_server_os_object_by_id(nova, server_id) - return __map_os_server_obj_to_vm_inst(neutron, server) + return __map_os_server_obj_to_vm_inst( + neutron, keystone, server, project_name) def get_server_security_group_names(nova, server): @@ -277,8 +314,9 @@ def get_server_security_group_names(nova, server): """ out = list() os_vm_inst = __get_latest_server_os_object(nova, server) - for sec_grp_dict in os_vm_inst.security_groups: - out.append(sec_grp_dict['name']) + if hasattr(os_vm_inst, 'security_groups'): + for sec_grp_dict in os_vm_inst.security_groups: + out.append(sec_grp_dict['name']) return out @@ -673,18 +711,6 @@ def remove_security_group(nova, vm, security_group): nova.servers.remove_security_group(str(vm.id), security_group.name) -def add_floating_ip_to_server(nova, vm, floating_ip, ip_addr): - """ - Adds a floating IP to a server instance - :param nova: the nova client - :param vm: VmInst domain object - :param floating_ip: FloatingIp domain object - :param ip_addr: the IP to which to bind the floating IP to - """ - vm = __get_latest_server_os_object(nova, vm) - vm.add_floating_ip(floating_ip.ip, ip_addr) - - def get_compute_quotas(nova, project_id): """ Returns a list of all available keypairs @@ -720,60 +746,73 @@ def update_quotas(nova, project_id, compute_quotas): return nova.quotas.update(project_id, **update_values) -def attach_volume(nova, neutron, server, volume, timeout=None): +def attach_volume(nova, neutron, keystone, server, volume, project_name, + timeout=120): """ - Attaches a volume to a server + Attaches a volume to a server. When the timeout parameter is used, a VmInst + object with the proper volume updates is returned unless it has not been + updated in the allotted amount of time then an Exception will be raised. :param nova: the nova client :param neutron: the neutron client + :param keystone: the neutron client :param server: the VMInst domain object :param volume: the Volume domain object + :param project_name: the associated project name :param timeout: denotes the amount of time to block to determine if the - has been properly attached. When None, do not wait. - :return: the value from the nova call + has been properly attached. + :return: updated VmInst object """ nova.volumes.create_server_volume(server.id, volume.id) - if timeout: - start_time = time.time() - while time.time() < start_time + timeout: - vm = get_server_object_by_id(nova, neutron, server.id) - for vol_dict in vm.volume_ids: - if volume.id == vol_dict['id']: - return vm + start_time = time.time() + while time.time() < start_time + timeout: + vm = get_server_object_by_id( + nova, neutron, keystone, server.id, project_name) + for vol_dict in vm.volume_ids: + if volume.id == vol_dict['id']: + return vm + time.sleep(POLL_INTERVAL) - return None - else: - return get_server_object_by_id(nova, neutron, server.id) + raise NovaException( + 'Attach failed on volume - {} and server - {}'.format( + volume.id, server.id)) -def detach_volume(nova, neutron, server, volume, timeout=None): +def detach_volume(nova, neutron, keystone, server, volume, project_name, + timeout=120): """ - Attaches a volume to a server + Detaches a volume to a server. When the timeout parameter is used, a VmInst + object with the proper volume updates is returned unless it has not been + updated in the allotted amount of time then an Exception will be raised. :param nova: the nova client :param neutron: the neutron client + :param keystone: the keystone client :param server: the VMInst domain object :param volume: the Volume domain object + :param project_name: the associated project name :param timeout: denotes the amount of time to block to determine if the - has been properly detached. When None, do not wait. - :return: the value from the nova call + has been properly detached. + :return: updated VmInst object """ nova.volumes.delete_server_volume(server.id, volume.id) - if timeout: - start_time = time.time() - while time.time() < start_time + timeout: - vm = get_server_object_by_id(nova, neutron, server.id) - found = False + start_time = time.time() + while time.time() < start_time + timeout: + vm = get_server_object_by_id( + nova, neutron, keystone, server.id, project_name) + if len(vm.volume_ids) == 0: + return vm + else: + ids = list() for vol_dict in vm.volume_ids: - if volume.id == vol_dict['id']: - found = True - - if not found: + ids.append(vol_dict['id']) + if volume.id not in ids: return vm + time.sleep(POLL_INTERVAL) - return None - else: - return get_server_object_by_id(nova, neutron, server.id) + raise NovaException( + 'Detach failed on volume - {} server - {}'.format( + volume.id, server.id)) class RebootType(enum.Enum): diff --git a/snaps/openstack/utils/settings_utils.py b/snaps/openstack/utils/settings_utils.py index 2cf6047..e43f8f5 100644 --- a/snaps/openstack/utils/settings_utils.py +++ b/snaps/openstack/utils/settings_utils.py @@ -224,14 +224,16 @@ def create_keypair_config(heat_cli, stack, keypair, pk_output_key): return KeypairConfig(name=keypair.name) -def create_vm_inst_config(nova, neutron, server): +def create_vm_inst_config(nova, keystone, neutron, server, project_name): """ Returns a VmInstanceConfig object note: if the server instance is not active, the PortSettings objects will not be generated resulting in an invalid configuration :param nova: the nova client + :param keystone: the keystone client :param neutron: the neutron client :param server: a SNAPS-OO VmInst domain object + :param project_name: the associated project name :return: """ @@ -244,7 +246,7 @@ def create_vm_inst_config(nova, neutron, server): kwargs['port_settings'] = __create_port_configs(neutron, server.ports) kwargs['security_group_names'] = server.sec_grp_names kwargs['floating_ip_settings'] = __create_floatingip_config( - neutron, kwargs['port_settings']) + neutron, keystone, kwargs['port_settings'], project_name) return VmInstanceConfig(**kwargs) @@ -281,11 +283,12 @@ def __create_port_configs(neutron, ports): return out -def __create_floatingip_config(neutron, port_settings): +def __create_floatingip_config(neutron, keystone, port_settings, project_name): """ Returns a list of FloatingIpConfig objects as they pertain to an existing deployed server instance :param neutron: the neutron client + :param keystone: the keystone client :param port_settings: list of SNAPS-OO PortConfig objects :return: a list of FloatingIpConfig objects or an empty list if no floating IPs have been created @@ -296,10 +299,11 @@ def __create_floatingip_config(neutron, port_settings): fip_ports = list() for port_setting in port_settings: - setting_port = neutron_utils.get_port(neutron, port_setting) + setting_port = neutron_utils.get_port( + neutron, keystone, port_setting, project_name=project_name) if setting_port: network = neutron_utils.get_network( - neutron, network_name=port_setting.network_name) + neutron, keystone, network_name=port_setting.network_name) network_ports = neutron_utils.get_ports(neutron, network) if network_ports: for setting_port in network_ports: @@ -307,7 +311,7 @@ def __create_floatingip_config(neutron, port_settings): fip_ports.append((port_setting.name, setting_port)) break - floating_ips = neutron_utils.get_floating_ips(neutron, fip_ports) + floating_ips = neutron_utils.get_port_floating_ips(neutron, fip_ports) for port_id, floating_ip in floating_ips: router = neutron_utils.get_router_by_id(neutron, floating_ip.router_id) diff --git a/snaps/openstack/utils/tests/cinder_utils_tests.py b/snaps/openstack/utils/tests/cinder_utils_tests.py index b624b09..073d574 100644 --- a/snaps/openstack/utils/tests/cinder_utils_tests.py +++ b/snaps/openstack/utils/tests/cinder_utils_tests.py @@ -26,7 +26,7 @@ from snaps.openstack import create_volume from snaps.openstack.create_qos import Consumer from snaps.openstack.tests import validation_utils from snaps.openstack.tests.os_source_file_test import OSComponentTestCase -from snaps.openstack.utils import cinder_utils +from snaps.openstack.utils import cinder_utils, keystone_utils __author__ = 'spisarski' @@ -43,7 +43,7 @@ class CinderSmokeTests(OSComponentTestCase): """ Tests to ensure that the proper credentials can connect. """ - cinder = cinder_utils.cinder_client(self.os_creds) + cinder = cinder_utils.cinder_client(self.os_creds, self.os_session) volumes = cinder.volumes.list() self.assertIsNotNone(volumes) self.assertTrue(isinstance(volumes, list)) @@ -74,7 +74,10 @@ class CinderUtilsVolumeTests(OSComponentTestCase): guid = uuid.uuid4() self.volume_name = self.__class__.__name__ + '-' + str(guid) self.volume = None - self.cinder = cinder_utils.cinder_client(self.os_creds) + self.cinder = cinder_utils.cinder_client( + self.os_creds, self.os_session) + self.keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) def tearDown(self): """ @@ -94,14 +97,15 @@ class CinderUtilsVolumeTests(OSComponentTestCase): """ volume_settings = VolumeConfig(name=self.volume_name) self.volume = cinder_utils.create_volume( - self.cinder, volume_settings) + self.cinder, self.keystone, volume_settings) self.assertIsNotNone(self.volume) self.assertEqual(self.volume_name, self.volume.name) self.assertTrue(volume_active(self.cinder, self.volume)) volume = cinder_utils.get_volume( - self.cinder, volume_settings=volume_settings) + self.cinder, self.keystone, volume_settings=volume_settings, + project_name=self.os_creds.project_name) self.assertIsNotNone(volume) validation_utils.objects_equivalent(self.volume, volume) @@ -111,21 +115,24 @@ class CinderUtilsVolumeTests(OSComponentTestCase): """ volume_settings = VolumeConfig(name=self.volume_name) self.volume = cinder_utils.create_volume( - self.cinder, volume_settings) + self.cinder, self.keystone, volume_settings) self.assertIsNotNone(self.volume) self.assertEqual(self.volume_name, self.volume.name) self.assertTrue(volume_active(self.cinder, self.volume)) volume = cinder_utils.get_volume( - self.cinder, volume_settings=volume_settings) + self.cinder, self.keystone, volume_settings=volume_settings, + project_name=self.os_creds.project_name) self.assertIsNotNone(volume) validation_utils.objects_equivalent(self.volume, volume) cinder_utils.delete_volume(self.cinder, self.volume) self.assertTrue(volume_deleted(self.cinder, self.volume)) self.assertIsNone( - cinder_utils.get_volume(self.cinder, volume_settings)) + cinder_utils.get_volume( + self.cinder, self.keystone, volume_settings, + project_name=self.os_creds.project_name)) def volume_active(cinder, volume): @@ -181,7 +188,8 @@ class CinderUtilsQoSTests(OSComponentTestCase): self.qos_name = self.__class__.__name__ + '-' + str(guid) self.specs = {'foo': 'bar '} self.qos = None - self.cinder = cinder_utils.cinder_client(self.os_creds) + self.cinder = cinder_utils.cinder_client( + self.os_creds, self.os_session) def tearDown(self): """ @@ -193,6 +201,8 @@ class CinderUtilsQoSTests(OSComponentTestCase): except NotFound: pass + super(self.__class__, self).__clean__() + def test_create_qos_both(self): """ Tests the cinder_utils.create_qos() @@ -278,7 +288,8 @@ class CinderUtilsSimpleVolumeTypeTests(OSComponentTestCase): volume_type_name = self.__class__.__name__ + '-' + str(guid) self.volume_type_settings = VolumeTypeConfig(name=volume_type_name) self.volume_type = None - self.cinder = cinder_utils.cinder_client(self.os_creds) + self.cinder = cinder_utils.cinder_client( + self.os_creds, self.os_session) def tearDown(self): """ @@ -290,6 +301,8 @@ class CinderUtilsSimpleVolumeTypeTests(OSComponentTestCase): except NotFound: pass + super(self.__class__, self).__clean__() + def test_create_simple_volume_type(self): """ Tests the cinder_utils.create_volume_type(), get_volume_type(), and @@ -346,7 +359,8 @@ class CinderUtilsAddEncryptionTests(OSComponentTestCase): self.encryption_name = self.__class__.__name__ + '-' + str(guid) self.encryption = None - self.cinder = cinder_utils.cinder_client(self.os_creds) + self.cinder = cinder_utils.cinder_client( + self.os_creds, self.os_session) volume_type_name = self.__class__.__name__ + '-' + str(guid) + '-type' self.volume_type = cinder_utils.create_volume_type( @@ -369,6 +383,8 @@ class CinderUtilsAddEncryptionTests(OSComponentTestCase): except NotFound: pass + super(self.__class__, self).__clean__() + def test_create_simple_encryption(self): """ Tests the cinder_utils.create_volume_encryption(), @@ -463,7 +479,8 @@ class CinderUtilsVolumeTypeCompleteTests(OSComponentTestCase): self.qos_name = self.__class__.__name__ + '-' + str(guid) + '-qos' self.vol_type_name = self.__class__.__name__ + '-' + str(guid) self.specs = {'foo': 'bar'} - self.cinder = cinder_utils.cinder_client(self.os_creds) + self.cinder = cinder_utils.cinder_client( + self.os_creds, self.os_session) qos_settings = QoSConfig( name=self.qos_name, specs=self.specs, consumer=Consumer.both) self.qos = cinder_utils.create_qos(self.cinder, qos_settings) @@ -491,6 +508,8 @@ class CinderUtilsVolumeTypeCompleteTests(OSComponentTestCase): except NotFound: pass + super(self.__class__, self).__clean__() + def test_create_with_encryption(self): """ Tests the cinder_utils.create_volume_type() where encryption has been diff --git a/snaps/openstack/utils/tests/glance_utils_tests.py b/snaps/openstack/utils/tests/glance_utils_tests.py index e61b795..e761ed8 100644 --- a/snaps/openstack/utils/tests/glance_utils_tests.py +++ b/snaps/openstack/utils/tests/glance_utils_tests.py @@ -39,7 +39,7 @@ class GlanceSmokeTests(OSComponentTestCase): """ Tests to ensure that the proper credentials can connect. """ - glance = glance_utils.glance_client(self.os_creds) + glance = glance_utils.glance_client(self.os_creds, self.os_session) image = glance_utils.get_image(glance, image_name='foo') self.assertIsNone(image) @@ -69,7 +69,8 @@ class GlanceUtilsTests(OSComponentTestCase): guid = uuid.uuid4() self.image_name = self.__class__.__name__ + '-' + str(guid) self.image = None - self.glance = glance_utils.glance_client(self.os_creds) + self.glance = glance_utils.glance_client( + self.os_creds, self.os_session) if self.image_metadata: self.glance_test_meta = self.image_metadata.get('glance_tests') else: @@ -89,6 +90,8 @@ class GlanceUtilsTests(OSComponentTestCase): 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_minimal_url(self): """ Tests the glance_utils.create_image() function with a URL unless the diff --git a/snaps/openstack/utils/tests/heat_utils_tests.py b/snaps/openstack/utils/tests/heat_utils_tests.py index 67fbdec..fa240bd 100644 --- a/snaps/openstack/utils/tests/heat_utils_tests.py +++ b/snaps/openstack/utils/tests/heat_utils_tests.py @@ -31,7 +31,7 @@ from snaps.openstack.tests import openstack_tests from snaps.openstack.tests.os_source_file_test import OSComponentTestCase from snaps.openstack.utils import ( heat_utils, neutron_utils, nova_utils, settings_utils, glance_utils, - cinder_utils) + cinder_utils, keystone_utils) __author__ = 'spisarski' @@ -47,12 +47,12 @@ class HeatSmokeTests(OSComponentTestCase): """ Tests to ensure that the proper credentials can connect. """ - heat = heat_utils.heat_client(self.os_creds) + heat = heat_utils.heat_client(self.os_creds, self.os_session) # This should not throw an exception stacks = heat.stacks.list() for stack in stacks: - print stack + logger.info('Stack - %s', stack) def test_heat_connect_fail(self): """ @@ -70,7 +70,7 @@ class HeatSmokeTests(OSComponentTestCase): # This should throw an exception with self.assertRaises(Exception): for stack in stacks: - print stack + logger.info('Stack - %s', stack) class HeatUtilsCreateSimpleStackTests(OSComponentTestCase): @@ -115,7 +115,8 @@ class HeatUtilsCreateSimpleStackTests(OSComponentTestCase): env_values=env_values) self.stack1 = None self.stack2 = None - self.heat_client = heat_utils.heat_client(self.os_creds) + self.heat_client = heat_utils.heat_client( + self.os_creds, self.os_session) def tearDown(self): """ @@ -145,6 +146,8 @@ class HeatUtilsCreateSimpleStackTests(OSComponentTestCase): except: pass + super(self.__class__, self).__clean__() + def test_create_stack(self): """ Tests the creation of an OpenStack Heat stack1 that does not exist. @@ -174,7 +177,7 @@ class HeatUtilsCreateSimpleStackTests(OSComponentTestCase): self.assertTrue(stack_active(self.heat_client, self.stack1)) - neutron = neutron_utils.neutron_client(self.os_creds) + neutron = neutron_utils.neutron_client(self.os_creds, self.os_session) networks = heat_utils.get_stack_networks( self.heat_client, neutron, self.stack1) self.assertIsNotNone(networks) @@ -185,9 +188,12 @@ class HeatUtilsCreateSimpleStackTests(OSComponentTestCase): self.assertEqual(1, len(subnets)) self.assertEqual(self.subnet_name, subnets[0].name) - nova = nova_utils.nova_client(self.os_creds) + nova = nova_utils.nova_client(self.os_creds, self.os_session) + keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) servers = heat_utils.get_stack_servers( - self.heat_client, nova, neutron, self.stack1) + self.heat_client, nova, neutron, keystone, self.stack1, + self.os_creds.project_name) self.assertIsNotNone(servers) self.assertEqual(1, len(servers)) self.assertEqual(self.vm_inst_name, servers[0].name) @@ -275,7 +281,8 @@ class HeatUtilsCreateComplexStackTests(OSComponentTestCase): stack_settings = StackConfig( name=stack_name, template_path=heat_tmplt_path, env_values=env_values) - self.heat_client = heat_utils.heat_client(self.os_creds) + self.heat_client = heat_utils.heat_client( + self.os_creds, self.os_session) self.stack = heat_utils.create_stack(self.heat_client, stack_settings) self.assertTrue(stack_active(self.heat_client, self.stack)) @@ -307,14 +314,22 @@ class HeatUtilsCreateComplexStackTests(OSComponentTestCase): time.sleep(3) if not is_deleted: - nova = nova_utils.nova_client(self.os_creds) - neutron = neutron_utils.neutron_client(self.os_creds) - glance = glance_utils.glance_client(self.os_creds) + nova = nova_utils.nova_client( + self.os_creds, self.os_session) + keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) + neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) + glance = glance_utils.glance_client( + self.os_creds, self.os_session) + servers = heat_utils.get_stack_servers( - self.heat_client, nova, neutron, self.stack) + self.heat_client, nova, neutron, keystone, self.stack, + self.os_creds.project_name) for server in servers: vm_settings = settings_utils.create_vm_inst_config( - nova, neutron, server) + nova, keystone, neutron, server, + self.os_creds.project_name) img_settings = settings_utils.determine_image_config( glance, server, [self.image_creator1.image_settings, @@ -354,6 +369,8 @@ class HeatUtilsCreateComplexStackTests(OSComponentTestCase): os.chmod(expanded_path, 0o755) os.remove(expanded_path) + super(self.__class__, self).__clean__() + def test_get_settings_from_stack(self): """ Tests that a heat template with floating IPs and can have the proper @@ -361,13 +378,13 @@ class HeatUtilsCreateComplexStackTests(OSComponentTestCase): """ resources = heat_utils.get_resources(self.heat_client, self.stack.id) self.assertIsNotNone(resources) - self.assertEqual(12, len(resources)) + self.assertEqual(13, len(resources)) options = heat_utils.get_outputs(self.heat_client, self.stack) self.assertIsNotNone(options) self.assertEqual(1, len(options)) - neutron = neutron_utils.neutron_client(self.os_creds) + neutron = neutron_utils.neutron_client(self.os_creds, self.os_session) networks = heat_utils.get_stack_networks( self.heat_client, neutron, self.stack) self.assertIsNotNone(networks) @@ -379,11 +396,13 @@ class HeatUtilsCreateComplexStackTests(OSComponentTestCase): self.assertIsNotNone(network_settings) self.assertEqual(self.network_name, network_settings.name) - nova = nova_utils.nova_client(self.os_creds) - glance = glance_utils.glance_client(self.os_creds) - + nova = nova_utils.nova_client(self.os_creds, self.os_session) + glance = glance_utils.glance_client(self.os_creds, self.os_session) + keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) servers = heat_utils.get_stack_servers( - self.heat_client, nova, neutron, self.stack) + self.heat_client, nova, neutron, keystone, self.stack, + self.os_creds.project_name) self.assertIsNotNone(servers) self.assertEqual(2, len(servers)) @@ -452,8 +471,10 @@ class HeatUtilsRouterTests(OSComponentTestCase): name=stack_name, template_path=heat_tmplt_path, env_values=env_values) self.stack = None - self.heat_client = heat_utils.heat_client(self.os_creds) - self.neutron = neutron_utils.neutron_client(self.os_creds) + self.heat_client = heat_utils.heat_client( + self.os_creds, self.os_session) + self.neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) def tearDown(self): """ @@ -465,6 +486,8 @@ class HeatUtilsRouterTests(OSComponentTestCase): except: pass + super(self.__class__, self).__clean__() + def test_create_router_with_stack(self): """ Tests the creation of an OpenStack router with Heat and the retrieval @@ -498,8 +521,10 @@ class HeatUtilsRouterTests(OSComponentTestCase): router = routers[0] self.assertEqual(self.router_name, router.name) + keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) ext_net = neutron_utils.get_network( - self.neutron, network_name=self.ext_net_name) + self.neutron, keystone, network_name=self.ext_net_name) self.assertEqual(ext_net.id, router.external_network_id) @@ -527,8 +552,10 @@ class HeatUtilsVolumeTests(OSComponentTestCase): name=stack_name, template_path=heat_tmplt_path, env_values=env_values) self.stack = None - self.heat_client = heat_utils.heat_client(self.os_creds) - self.cinder = cinder_utils.cinder_client(self.os_creds) + self.heat_client = heat_utils.heat_client( + self.os_creds, self.os_session) + self.cinder = cinder_utils.cinder_client( + self.os_creds, self.os_session) def tearDown(self): """ @@ -540,6 +567,8 @@ class HeatUtilsVolumeTests(OSComponentTestCase): except: pass + super(self.__class__, self).__clean__() + def test_create_vol_with_stack(self): """ Tests the creation of an OpenStack volume with Heat. @@ -607,8 +636,10 @@ class HeatUtilsFlavorTests(OSComponentTestCase): self.stack_settings = StackConfig( name=stack_name, template_path=heat_tmplt_path) self.stack = None - self.heat_client = heat_utils.heat_client(self.os_creds) - self.nova = nova_utils.nova_client(self.os_creds) + self.heat_client = heat_utils.heat_client( + self.os_creds, self.os_session) + self.nova = nova_utils.nova_client( + self.os_creds, self.os_session) def tearDown(self): """ @@ -620,6 +651,8 @@ class HeatUtilsFlavorTests(OSComponentTestCase): except: pass + super(self.__class__, self).__clean__() + def test_create_flavor_with_stack(self): """ Tests the creation of an OpenStack volume with Heat. @@ -666,8 +699,10 @@ class HeatUtilsKeypairTests(OSComponentTestCase): name=stack_name, template_path=heat_tmplt_path, env_values=env_values) self.stack = None - self.heat_client = heat_utils.heat_client(self.os_creds) - self.nova = nova_utils.nova_client(self.os_creds) + self.heat_client = heat_utils.heat_client( + self.os_creds, self.os_session) + self.nova = nova_utils.nova_client( + self.os_creds, self.os_session) def tearDown(self): """ @@ -679,6 +714,8 @@ class HeatUtilsKeypairTests(OSComponentTestCase): except: pass + super(self.__class__, self).__clean__() + def test_create_keypair_with_stack(self): """ Tests the creation of an OpenStack keypair with Heat. @@ -729,8 +766,10 @@ class HeatUtilsSecurityGroupTests(OSComponentTestCase): name=stack_name, template_path=heat_tmplt_path, env_values=env_values) self.stack = None - self.heat_client = heat_utils.heat_client(self.os_creds) - self.neutron = neutron_utils.neutron_client(self.os_creds) + self.heat_client = heat_utils.heat_client( + self.os_creds, self.os_session) + self.neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) def tearDown(self): """ @@ -742,6 +781,8 @@ class HeatUtilsSecurityGroupTests(OSComponentTestCase): except: pass + super(self.__class__, self).__clean__() + def test_create_security_group_with_stack(self): """ Tests the creation of an OpenStack SecurityGroup with Heat. diff --git a/snaps/openstack/utils/tests/keystone_utils_tests.py b/snaps/openstack/utils/tests/keystone_utils_tests.py index b7f024d..cc3a8ad 100644 --- a/snaps/openstack/utils/tests/keystone_utils_tests.py +++ b/snaps/openstack/utils/tests/keystone_utils_tests.py @@ -31,7 +31,8 @@ class KeystoneSmokeTests(OSComponentTestCase): """ Tests to ensure that the proper credentials can connect. """ - keystone = keystone_utils.keystone_client(self.os_creds) + keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) users = keystone.users.list() self.assertIsNotNone(users) @@ -66,17 +67,19 @@ class KeystoneUtilsTests(OSComponentTestCase): self.project_name = self.guid + '-projName' self.project = None self.role = None - self.keystone = keystone_utils.keystone_client(self.os_creds) + self.keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) def tearDown(self): """ Cleans the remote OpenStack objects """ if self.project: - neutron = neutron_utils.neutron_client(self.os_creds) + neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) default_sec_grp = neutron_utils.get_security_group( - neutron, sec_grp_name='default', - project_id=self.project.id) + neutron, self.keystone, sec_grp_name='default', + project_name=self.os_creds.project_name) if default_sec_grp: try: neutron_utils.delete_security_group( @@ -92,6 +95,8 @@ class KeystoneUtilsTests(OSComponentTestCase): if self.role: keystone_utils.delete_role(self.keystone, self.role) + super(self.__class__, self).__clean__() + def test_create_user_minimal(self): """ Tests the keystone_utils.create_user() function @@ -124,8 +129,11 @@ class KeystoneUtilsTests(OSComponentTestCase): domain = keystone_utils.get_domain_by_id( self.keystone, project.domain_id) - self.assertIsNotNone(domain) - self.assertEqual(domain.id, project.domain_id) + if self.keystone.version == keystone_utils.V2_VERSION_STR: + self.assertIsNone(domain) + else: + self.assertIsNotNone(domain) + self.assertEqual(domain.id, project.domain_id) def test_get_endpoint_success(self): """ diff --git a/snaps/openstack/utils/tests/magnum_utils_tests.py b/snaps/openstack/utils/tests/magnum_utils_tests.py index f841c48..d87d97a 100644 --- a/snaps/openstack/utils/tests/magnum_utils_tests.py +++ b/snaps/openstack/utils/tests/magnum_utils_tests.py @@ -44,7 +44,8 @@ class MagnumSmokeTests(OSComponentTestCase): """ Tests to ensure that the proper credentials can connect. """ - magnum = magnum_utils.magnum_client(self.os_creds) + magnum = magnum_utils.magnum_client( + self.os_creds, self.os_session) # This should not throw an exception self.assertIsNotNone(magnum.clusters.list()) @@ -70,7 +71,8 @@ class MagnumUtilsClusterTypeTests(OSComponentTestCase): def setUp(self): self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) self.cluster_type_name = self.guid + '-cluster-type' - self.magnum = magnum_utils.magnum_client(self.os_creds) + self.magnum = magnum_utils.magnum_client( + self.os_creds, self.os_session) metadata = self.image_metadata if not metadata: @@ -130,6 +132,8 @@ class MagnumUtilsClusterTypeTests(OSComponentTestCase): except: pass + super(self.__class__, self).__clean__() + def test_create_cluster_template_simple(self): config = ClusterTemplateConfig( name=self.cluster_type_name, diff --git a/snaps/openstack/utils/tests/neutron_utils_tests.py b/snaps/openstack/utils/tests/neutron_utils_tests.py index 38faf71..a55f779 100644 --- a/snaps/openstack/utils/tests/neutron_utils_tests.py +++ b/snaps/openstack/utils/tests/neutron_utils_tests.py @@ -41,7 +41,7 @@ class NeutronSmokeTests(OSComponentTestCase): """ Tests to ensure that the proper credentials can connect. """ - neutron = neutron_utils.neutron_client(self.os_creds) + neutron = neutron_utils.neutron_client(self.os_creds, self.os_session) networks = neutron.list_networks() @@ -70,7 +70,7 @@ class NeutronSmokeTests(OSComponentTestCase): configured self.ext_net_name is contained within the returned list :return: """ - neutron = neutron_utils.neutron_client(self.os_creds) + neutron = neutron_utils.neutron_client(self.os_creds, self.os_session) ext_networks = neutron_utils.get_external_networks(neutron) found = False for network in ext_networks: @@ -88,9 +88,13 @@ class NeutronUtilsNetworkTests(OSComponentTestCase): def setUp(self): guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) self.port_name = str(guid) + '-port' - self.neutron = neutron_utils.neutron_client(self.os_creds) + self.neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) + self.keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) self.network = None self.net_config = openstack_tests.get_pub_net_config( + project_name=self.os_creds.project_name, net_name=guid + '-pub-net') def tearDown(self): @@ -100,6 +104,8 @@ class NeutronUtilsNetworkTests(OSComponentTestCase): if self.network: neutron_utils.delete_network(self.neutron, self.network) + super(self.__class__, self).__clean__() + def test_create_network(self): """ Tests the neutron_utils.create_network() function @@ -109,7 +115,9 @@ class NeutronUtilsNetworkTests(OSComponentTestCase): self.assertEqual(self.net_config.network_settings.name, self.network.name) self.assertTrue(validate_network( - self.neutron, self.net_config.network_settings.name, True)) + self.neutron, self.keystone, + self.net_config.network_settings.name, True, + self.os_creds.project_name)) self.assertEqual(len(self.net_config.network_settings.subnet_settings), len(self.network.subnets)) @@ -142,9 +150,13 @@ class NeutronUtilsSubnetTests(OSComponentTestCase): def setUp(self): guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) self.port_name = str(guid) + '-port' - self.neutron = neutron_utils.neutron_client(self.os_creds) + self.neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) + self.keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) self.network = None self.net_config = openstack_tests.get_pub_net_config( + project_name=self.os_creds.project_name, net_name=guid + '-pub-net', subnet_name=guid + '-pub-subnet', external_net=self.ext_net_name) @@ -158,6 +170,8 @@ class NeutronUtilsSubnetTests(OSComponentTestCase): except: pass + super(self.__class__, self).__clean__() + def test_create_subnet(self): """ Tests the neutron_utils.create_network() function @@ -167,14 +181,17 @@ class NeutronUtilsSubnetTests(OSComponentTestCase): self.assertEqual(self.net_config.network_settings.name, self.network.name) self.assertTrue(validate_network( - self.neutron, self.net_config.network_settings.name, True)) + self.neutron, self.keystone, + self.net_config.network_settings.name, True, + self.os_creds.project_name)) subnet_setting = self.net_config.network_settings.subnet_settings[0] self.assertTrue(validate_subnet( - self.neutron, subnet_setting.name, subnet_setting.cidr, True)) + self.neutron, self.network, subnet_setting.name, + subnet_setting.cidr, True)) subnet_query1 = neutron_utils.get_subnet( - self.neutron, subnet_name=subnet_setting.name) + self.neutron, self.network, subnet_name=subnet_setting.name) self.assertEqual(self.network.subnets[0], subnet_query1) subnet_query2 = neutron_utils.get_subnets_by_network(self.neutron, @@ -183,6 +200,12 @@ class NeutronUtilsSubnetTests(OSComponentTestCase): self.assertEqual(1, len(subnet_query2)) self.assertEqual(self.network.subnets[0], subnet_query2[0]) + subnet_query3 = neutron_utils.get_subnet_by_name( + self.neutron, self.keystone, subnet_setting.name, + self.os_creds.project_name) + self.assertIsNotNone(subnet_query3) + self.assertEqual(self.network.subnets[0], subnet_query3) + def test_create_subnet_null_name(self): """ Tests the neutron_utils.create_neutron_subnet() function for an @@ -193,7 +216,9 @@ class NeutronUtilsSubnetTests(OSComponentTestCase): self.assertEqual(self.net_config.network_settings.name, self.network.name) self.assertTrue(validate_network( - self.neutron, self.net_config.network_settings.name, True)) + self.neutron, self.keystone, + self.net_config.network_settings.name, True, + self.os_creds.project_name)) with self.assertRaises(Exception): SubnetConfig(cidr=self.net_config.subnet_cidr) @@ -208,20 +233,23 @@ class NeutronUtilsSubnetTests(OSComponentTestCase): self.assertEqual(self.net_config.network_settings.name, self.network.name) self.assertTrue(validate_network( - self.neutron, self.net_config.network_settings.name, True)) + self.neutron, self.keystone, + self.net_config.network_settings.name, True, + self.os_creds.project_name)) subnet_setting = self.net_config.network_settings.subnet_settings[0] self.assertTrue(validate_subnet( - self.neutron, subnet_setting.name, subnet_setting.cidr, True)) + self.neutron, self.network, subnet_setting.name, + subnet_setting.cidr, True)) self.assertFalse(validate_subnet( - self.neutron, '', subnet_setting.cidr, True)) + self.neutron, self.network, '', subnet_setting.cidr, True)) subnet_query1 = neutron_utils.get_subnet( - self.neutron, subnet_name=subnet_setting.name) + self.neutron, self.network, subnet_name=subnet_setting.name) self.assertEqual(self.network.subnets[0], subnet_query1) - subnet_query2 = neutron_utils.get_subnets_by_network(self.neutron, - self.network) + subnet_query2 = neutron_utils.get_subnets_by_network( + self.neutron, self.network) self.assertIsNotNone(subnet_query2) self.assertEqual(1, len(subnet_query2)) self.assertEqual(self.network.subnets[0], subnet_query2[0]) @@ -254,7 +282,8 @@ class NeutronUtilsIPv6Tests(OSComponentTestCase): def setUp(self): self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) - self.neutron = neutron_utils.neutron_client(self.os_creds) + self.neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) self.network = None def tearDown(self): @@ -267,6 +296,8 @@ class NeutronUtilsIPv6Tests(OSComponentTestCase): except: pass + super(self.__class__, self).__clean__() + def test_create_network_slaac(self): """ Tests the neutron_utils.create_network() with an IPv6 subnet where DHCP @@ -455,7 +486,7 @@ class NeutronUtilsIPv6Tests(OSComponentTestCase): """ sub_setting = SubnetConfig( name=self.guid + '-subnet', cidr='1:1::/48', ip_version=6, - gateway_ip='1:2::1') + gateway_ip='192.168.0.1') self.network_settings = NetworkConfig( name=self.guid + '-net', subnet_settings=[sub_setting]) @@ -487,12 +518,16 @@ class NeutronUtilsRouterTests(OSComponentTestCase): def setUp(self): guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) self.port_name = str(guid) + '-port' - self.neutron = neutron_utils.neutron_client(self.os_creds) + self.neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) + self.keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) self.network = None self.port = None self.router = None self.interface_router = None self.net_config = openstack_tests.get_pub_net_config( + project_name=self.os_creds.project_name, net_name=guid + '-pub-net', subnet_name=guid + '-pub-subnet', router_name=guid + '-pub-router', external_net=self.ext_net_name) @@ -507,7 +542,9 @@ class NeutronUtilsRouterTests(OSComponentTestCase): if self.router: try: neutron_utils.delete_router(self.neutron, self.router) - validate_router(self.neutron, self.router.name, False) + validate_router( + self.neutron, self.keystone, self.router.name, + self.os_creds.project_name, False) except: pass @@ -520,14 +557,17 @@ class NeutronUtilsRouterTests(OSComponentTestCase): if self.network: neutron_utils.delete_network(self.neutron, self.network) + super(self.__class__, self).__clean__() + def test_create_router_simple(self): """ Tests the neutron_utils.create_router() """ self.router = neutron_utils.create_router( self.neutron, self.os_creds, self.net_config.router_settings) - validate_router(self.neutron, self.net_config.router_settings.name, - True) + validate_router( + self.neutron, self.keystone, self.net_config.router_settings.name, + self.os_creds.project_name, True) def test_create_router_with_public_interface(self): """ @@ -535,18 +575,19 @@ class NeutronUtilsRouterTests(OSComponentTestCase): """ subnet_setting = self.net_config.network_settings.subnet_settings[0] self.net_config = openstack_tests.OSNetworkConfig( - self.net_config.network_settings.name, - subnet_setting.name, - subnet_setting.cidr, - self.net_config.router_settings.name, - self.ext_net_name) + project_name=self.os_creds.project_name, + net_name=self.net_config.network_settings.name, + subnet_name=subnet_setting.name, subnet_cidr=subnet_setting.cidr, + router_name=self.net_config.router_settings.name, + external_gateway=self.ext_net_name) self.router = neutron_utils.create_router( self.neutron, self.os_creds, self.net_config.router_settings) - validate_router(self.neutron, self.net_config.router_settings.name, - True) + validate_router( + self.neutron, self.keystone, self.net_config.router_settings.name, + self.os_creds.project_name, True) ext_net = neutron_utils.get_network( - self.neutron, network_name=self.ext_net_name) + self.neutron, self.keystone, network_name=self.ext_net_name) self.assertEqual(self.router.external_network_id, ext_net.id) def test_add_interface_router(self): @@ -558,16 +599,20 @@ class NeutronUtilsRouterTests(OSComponentTestCase): self.assertEqual(self.net_config.network_settings.name, self.network.name) self.assertTrue(validate_network( - self.neutron, self.net_config.network_settings.name, True)) + self.neutron, self.keystone, + self.net_config.network_settings.name, True, + self.os_creds.project_name)) subnet_setting = self.net_config.network_settings.subnet_settings[0] self.assertTrue(validate_subnet( - self.neutron, subnet_setting.name, subnet_setting.cidr, True)) + self.neutron, self.network, subnet_setting.name, + subnet_setting.cidr, True)) self.router = neutron_utils.create_router( self.neutron, self.os_creds, self.net_config.router_settings) - validate_router(self.neutron, self.net_config.router_settings.name, - True) + validate_router( + self.neutron, self.keystone, self.net_config.router_settings.name, + self.os_creds.project_name, True) self.interface_router = neutron_utils.add_interface_router( self.neutron, self.router, self.network.subnets[0]) @@ -584,11 +629,14 @@ class NeutronUtilsRouterTests(OSComponentTestCase): self.assertEqual(self.net_config.network_settings.name, self.network.name) self.assertTrue(validate_network( - self.neutron, self.net_config.network_settings.name, True)) + self.neutron, self.keystone, + self.net_config.network_settings.name, True, + self.os_creds.project_name)) subnet_setting = self.net_config.network_settings.subnet_settings[0] self.assertTrue(validate_subnet( - self.neutron, subnet_setting.name, subnet_setting.cidr, True)) + self.neutron, self.network, subnet_setting.name, + subnet_setting.cidr, True)) with self.assertRaises(NeutronException): self.interface_router = neutron_utils.add_interface_router( @@ -604,12 +652,15 @@ class NeutronUtilsRouterTests(OSComponentTestCase): self.assertEqual(self.net_config.network_settings.name, self.network.name) self.assertTrue(validate_network( - self.neutron, self.net_config.network_settings.name, True)) + self.neutron, self.keystone, + self.net_config.network_settings.name, True, + self.os_creds.project_name)) self.router = neutron_utils.create_router( self.neutron, self.os_creds, self.net_config.router_settings) - validate_router(self.neutron, self.net_config.router_settings.name, - True) + validate_router( + self.neutron, self.keystone, self.net_config.router_settings.name, + self.os_creds.project_name, True) with self.assertRaises(NeutronException): self.interface_router = neutron_utils.add_interface_router( @@ -625,12 +676,15 @@ class NeutronUtilsRouterTests(OSComponentTestCase): self.assertEqual(self.net_config.network_settings.name, self.network.name) self.assertTrue(validate_network( - self.neutron, self.net_config.network_settings.name, True)) + self.neutron, self.keystone, + self.net_config.network_settings.name, True, + self.os_creds.project_name)) self.router = neutron_utils.create_router( self.neutron, self.os_creds, self.net_config.router_settings) - validate_router(self.neutron, self.net_config.router_settings.name, - True) + validate_router( + self.neutron, self.keystone, self.net_config.router_settings.name, + self.os_creds.project_name, True) for subnet in self.network.subnets: neutron_utils.delete_subnet(self.neutron, subnet) @@ -648,11 +702,14 @@ class NeutronUtilsRouterTests(OSComponentTestCase): self.assertEqual(self.net_config.network_settings.name, self.network.name) self.assertTrue(validate_network( - self.neutron, self.net_config.network_settings.name, True)) + self.neutron, self.keystone, + self.net_config.network_settings.name, True, + self.os_creds.project_name)) subnet_setting = self.net_config.network_settings.subnet_settings[0] self.assertTrue(validate_subnet( - self.neutron, subnet_setting.name, subnet_setting.cidr, True)) + self.neutron, self.network, subnet_setting.name, + subnet_setting.cidr, True)) self.port = neutron_utils.create_port( self.neutron, self.os_creds, PortConfig( @@ -672,11 +729,14 @@ class NeutronUtilsRouterTests(OSComponentTestCase): self.assertEqual(self.net_config.network_settings.name, self.network.name) self.assertTrue(validate_network( - self.neutron, self.net_config.network_settings.name, True)) + self.neutron, self.keystone, + self.net_config.network_settings.name, True, + self.os_creds.project_name)) subnet_setting = self.net_config.network_settings.subnet_settings[0] - self.assertTrue(validate_subnet(self.neutron, subnet_setting.name, - subnet_setting.cidr, True)) + self.assertTrue(validate_subnet( + self.neutron, self.network, subnet_setting.name, + subnet_setting.cidr, True)) self.port = neutron_utils.create_port( self.neutron, self.os_creds, PortConfig( @@ -696,11 +756,14 @@ class NeutronUtilsRouterTests(OSComponentTestCase): self.assertEqual(self.net_config.network_settings.name, self.network.name) self.assertTrue(validate_network( - self.neutron, self.net_config.network_settings.name, True)) + self.neutron, self.keystone, + self.net_config.network_settings.name, True, + self.os_creds.project_name)) subnet_setting = self.net_config.network_settings.subnet_settings[0] self.assertTrue(validate_subnet( - self.neutron, subnet_setting.name, subnet_setting.cidr, True)) + self.neutron, self.network, subnet_setting.name, + subnet_setting.cidr, True)) self.port = neutron_utils.create_port( self.neutron, self.os_creds, @@ -740,11 +803,14 @@ class NeutronUtilsRouterTests(OSComponentTestCase): self.assertEqual(self.net_config.network_settings.name, self.network.name) self.assertTrue(validate_network( - self.neutron, self.net_config.network_settings.name, True)) + self.neutron, self.keystone, + self.net_config.network_settings.name, True, + self.os_creds.project_name)) subnet_setting = self.net_config.network_settings.subnet_settings[0] self.assertTrue(validate_subnet( - self.neutron, subnet_setting.name, subnet_setting.cidr, True)) + self.neutron, self.network, subnet_setting.name, + subnet_setting.cidr, True)) with self.assertRaises(Exception): self.port = neutron_utils.create_port( @@ -766,11 +832,14 @@ class NeutronUtilsRouterTests(OSComponentTestCase): self.assertEqual(self.net_config.network_settings.name, self.network.name) self.assertTrue(validate_network( - self.neutron, self.net_config.network_settings.name, True)) + self.neutron, self.keystone, + self.net_config.network_settings.name, True, + self.os_creds.project_name)) subnet_setting = self.net_config.network_settings.subnet_settings[0] self.assertTrue(validate_subnet( - self.neutron, subnet_setting.name, subnet_setting.cidr, True)) + self.neutron, self.network, subnet_setting.name, + subnet_setting.cidr, True)) with self.assertRaises(Exception): self.port = neutron_utils.create_port( @@ -792,11 +861,14 @@ class NeutronUtilsRouterTests(OSComponentTestCase): self.assertEqual(self.net_config.network_settings.name, self.network.name) self.assertTrue(validate_network( - self.neutron, self.net_config.network_settings.name, True)) + self.neutron, self.keystone, + self.net_config.network_settings.name, True, + self.os_creds.project_name)) subnet_setting = self.net_config.network_settings.subnet_settings[0] self.assertTrue(validate_subnet( - self.neutron, subnet_setting.name, subnet_setting.cidr, True)) + self.neutron, self.network, subnet_setting.name, + subnet_setting.cidr, True)) with self.assertRaises(Exception): self.port = neutron_utils.create_port( @@ -820,8 +892,10 @@ class NeutronUtilsSecurityGroupTests(OSComponentTestCase): self.security_groups = list() self.security_group_rules = list() - self.neutron = neutron_utils.neutron_client(self.os_creds) - self.keystone = keystone_utils.keystone_client(self.os_creds) + self.neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) + self.keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) def tearDown(self): """ @@ -837,26 +911,27 @@ class NeutronUtilsSecurityGroupTests(OSComponentTestCase): except: pass + super(self.__class__, self).__clean__() + def test_create_delete_simple_sec_grp(self): """ Tests the neutron_utils.create_security_group() function """ sec_grp_settings = SecurityGroupConfig(name=self.sec_grp_name) - security_group = neutron_utils.create_security_group(self.neutron, - self.keystone, - sec_grp_settings) + security_group = neutron_utils.create_security_group( + self.neutron, self.keystone, sec_grp_settings) self.assertTrue(sec_grp_settings.name, security_group.name) sec_grp_get = neutron_utils.get_security_group( - self.neutron, sec_grp_settings=sec_grp_settings) + self.neutron, self.keystone, sec_grp_settings=sec_grp_settings) self.assertIsNotNone(sec_grp_get) self.assertTrue(validation_utils.objects_equivalent( security_group, sec_grp_get)) neutron_utils.delete_security_group(self.neutron, security_group) sec_grp_get = neutron_utils.get_security_group( - self.neutron, sec_grp_settings=sec_grp_settings) + self.neutron, self.keystone, sec_grp_settings=sec_grp_settings) self.assertIsNone(sec_grp_get) def test_create_sec_grp_no_name(self): @@ -869,9 +944,8 @@ class NeutronUtilsSecurityGroupTests(OSComponentTestCase): with self.assertRaises(Exception): sec_grp_settings = SecurityGroupConfig() self.security_groups.append( - neutron_utils.create_security_group(self.neutron, - self.keystone, - sec_grp_settings)) + neutron_utils.create_security_group( + self.neutron, self.keystone, sec_grp_settings)) def test_create_sec_grp_no_rules(self): """ @@ -880,14 +954,14 @@ class NeutronUtilsSecurityGroupTests(OSComponentTestCase): sec_grp_settings = SecurityGroupConfig( name=self.sec_grp_name, description='hello group') self.security_groups.append( - neutron_utils.create_security_group(self.neutron, self.keystone, - sec_grp_settings)) + neutron_utils.create_security_group( + self.neutron, self.keystone, sec_grp_settings)) self.assertTrue(sec_grp_settings.name, self.security_groups[0].name) self.assertEqual(sec_grp_settings.name, self.security_groups[0].name) sec_grp_get = neutron_utils.get_security_group( - self.neutron, sec_grp_settings=sec_grp_settings) + self.neutron, self.keystone, sec_grp_settings=sec_grp_settings) self.assertIsNotNone(sec_grp_get) self.assertEqual(self.security_groups[0], sec_grp_get) @@ -903,23 +977,26 @@ class NeutronUtilsSecurityGroupTests(OSComponentTestCase): rule_settings=[sec_grp_rule_settings]) self.security_groups.append( - neutron_utils.create_security_group(self.neutron, self.keystone, - sec_grp_settings)) + neutron_utils.create_security_group( + self.neutron, self.keystone, sec_grp_settings)) free_rules = neutron_utils.get_rules_by_security_group( self.neutron, self.security_groups[0]) for free_rule in free_rules: self.security_group_rules.append(free_rule) + keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) self.security_group_rules.append( neutron_utils.create_security_group_rule( - self.neutron, sec_grp_settings.rule_settings[0])) + self.neutron, keystone, sec_grp_settings.rule_settings[0], + self.os_creds.project_name)) # Refresh object so it is populated with the newly added rule security_group = neutron_utils.get_security_group( - self.neutron, sec_grp_settings=sec_grp_settings) + self.neutron, self.keystone, sec_grp_settings=sec_grp_settings) - rules = neutron_utils.get_rules_by_security_group(self.neutron, - security_group) + rules = neutron_utils.get_rules_by_security_group( + self.neutron, security_group) self.assertTrue( validation_utils.objects_equivalent( @@ -928,7 +1005,7 @@ class NeutronUtilsSecurityGroupTests(OSComponentTestCase): self.assertTrue(sec_grp_settings.name, security_group.name) sec_grp_get = neutron_utils.get_security_group( - self.neutron, sec_grp_settings=sec_grp_settings) + self.neutron, self.keystone, sec_grp_settings=sec_grp_settings) self.assertIsNotNone(sec_grp_get) self.assertEqual(security_group, sec_grp_get) @@ -954,6 +1031,33 @@ class NeutronUtilsSecurityGroupTests(OSComponentTestCase): self.assertEqual(self.security_groups[0].id, sec_grp_1b.id) self.assertEqual(self.security_groups[1].id, sec_grp_2b.id) + def test_create_list_sec_grp_no_rules(self): + """ + Tests the neutron_utils.create_security_group() and + list_security_groups function + """ + sec_grp_settings = SecurityGroupConfig( + name=self.sec_grp_name + "-1", description='hello group') + self.security_groups.append(neutron_utils.create_security_group( + self.neutron, self.keystone, sec_grp_settings)) + + sec_grp_settings2 = SecurityGroupConfig( + name=self.sec_grp_name + "-2", description='hola group') + self.security_groups.append(neutron_utils.create_security_group( + self.neutron, self.keystone, sec_grp_settings2)) + + returned_sec_groups = neutron_utils.list_security_groups(self.neutron) + + self.assertIsNotNone(returned_sec_groups) + worked = 0 + for sg in returned_sec_groups: + if sec_grp_settings.name == sg.name: + worked += 1 + elif sec_grp_settings2.name == sg.name: + worked += 1 + + self.assertEqual(worked, 2) + class NeutronUtilsFloatingIpTests(OSComponentTestCase): """ @@ -965,7 +1069,10 @@ class NeutronUtilsFloatingIpTests(OSComponentTestCase): Instantiates the CreateImage object that is responsible for downloading and creating an OS image file within OpenStack """ - self.neutron = neutron_utils.neutron_client(self.os_creds) + self.neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) + self.keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) self.floating_ip = None def tearDown(self): @@ -979,6 +1086,8 @@ class NeutronUtilsFloatingIpTests(OSComponentTestCase): except: pass + super(self.__class__, self).__clean__() + def test_floating_ips(self): """ Tests the creation of a floating IP @@ -986,8 +1095,8 @@ class NeutronUtilsFloatingIpTests(OSComponentTestCase): """ initial_fips = neutron_utils.get_floating_ips(self.neutron) - self.floating_ip = neutron_utils.create_floating_ip(self.neutron, - self.ext_net_name) + self.floating_ip = neutron_utils.create_floating_ip( + self.neutron, self.keystone, self.ext_net_name) all_fips = neutron_utils.get_floating_ips(self.neutron) self.assertEqual(len(initial_fips) + 1, len(all_fips)) returned = neutron_utils.get_floating_ip(self.neutron, @@ -1001,36 +1110,43 @@ Validation routines """ -def validate_network(neutron, name, exists): +def validate_network(neutron, keystone, name, exists, project_name, mtu=None): """ Returns true if a network for a given name DOES NOT exist if the exists parameter is false conversely true. Returns false if a network for a given name DOES exist if the exists parameter is true conversely false. :param neutron: The neutron client + :param keystone: The keystone client :param name: The expected network name :param exists: Whether or not the network name should exist or not + :param project_name: the associated project name :return: True/False """ - network = neutron_utils.get_network(neutron, network_name=name) + network = neutron_utils.get_network( + neutron, keystone, network_name=name, project_name=project_name) if exists and network: return True if not exists and not network: return True + if mtu: + return mtu == network.mtu return False -def validate_subnet(neutron, name, cidr, exists): +def validate_subnet(neutron, network, name, cidr, exists): """ Returns true if a subnet for a given name DOES NOT exist if the exists parameter is false conversely true. Returns false if a subnet for a given name DOES exist if the exists parameter is true conversely false. :param neutron: The neutron client + :param network: The SNAPS-OO Network domain object :param name: The expected subnet name :param cidr: The expected CIDR value :param exists: Whether or not the network name should exist or not :return: True/False """ - subnet = neutron_utils.get_subnet(neutron, subnet_name=name) + subnet = neutron_utils.get_subnet( + neutron, network, subnet_name=name) if exists and subnet and subnet.name == name: return subnet.cidr == cidr if not exists and not subnet: @@ -1038,17 +1154,21 @@ def validate_subnet(neutron, name, cidr, exists): return False -def validate_router(neutron, name, exists): +def validate_router(neutron, keystone, name, project_name, exists): """ Returns true if a router for a given name DOES NOT exist if the exists parameter is false conversely true. Returns false if a router for a given name DOES exist if the exists parameter is true conversely false. :param neutron: The neutron client + :param keystone: The keystone client :param name: The expected router name + :param project_name: The name of the project in which the router should + exist :param exists: Whether or not the network name should exist or not :return: True/False """ - router = neutron_utils.get_router(neutron, router_name=name) + router = neutron_utils.get_router( + neutron, keystone, router_name=name, project_name=project_name) if exists and router: return True return False diff --git a/snaps/openstack/utils/tests/nova_utils_tests.py b/snaps/openstack/utils/tests/nova_utils_tests.py index 8cb0812..7c343f8 100644 --- a/snaps/openstack/utils/tests/nova_utils_tests.py +++ b/snaps/openstack/utils/tests/nova_utils_tests.py @@ -32,7 +32,8 @@ from snaps.openstack.create_volume import OpenStackVolume from snaps.openstack.tests import openstack_tests from snaps.openstack.tests.os_source_file_test import OSComponentTestCase from snaps.openstack.utils import ( - nova_utils, neutron_utils, glance_utils, cinder_utils) + nova_utils, neutron_utils, glance_utils, cinder_utils, keystone_utils) +from snaps.openstack.utils.nova_utils import NovaException __author__ = 'spisarski' @@ -48,7 +49,7 @@ class NovaSmokeTests(OSComponentTestCase): """ Tests to ensure that the proper credentials can connect. """ - nova = nova_utils.nova_client(self.os_creds) + nova = nova_utils.nova_client(self.os_creds, self.os_session) # This should not throw an exception nova.flavors.list() @@ -57,7 +58,7 @@ class NovaSmokeTests(OSComponentTestCase): """ Tests to ensure that get_hypervisors() function works. """ - nova = nova_utils.nova_client(self.os_creds) + nova = nova_utils.nova_client(self.os_creds, self.os_session) hosts = nova_utils.get_hypervisor_hosts(nova) # This should not throw an exception @@ -94,7 +95,7 @@ class NovaUtilsKeypairTests(OSComponentTestCase): self.priv_key_file_path = 'tmp/' + guid self.pub_key_file_path = self.priv_key_file_path + '.pub' - self.nova = nova_utils.nova_client(self.os_creds) + self.nova = nova_utils.nova_client(self.os_creds, self.os_session) self.keys = nova_utils.create_keys() self.public_key = nova_utils.public_key_openssh(self.keys) self.keypair_name = guid @@ -122,6 +123,8 @@ class NovaUtilsKeypairTests(OSComponentTestCase): except: pass + super(self.__class__, self).__clean__() + def test_create_keypair(self): """ Tests the creation of an OpenStack keypair that does not exist. @@ -175,7 +178,7 @@ class NovaUtilsFlavorTests(OSComponentTestCase): self.flavor_settings = FlavorConfig( name=guid + '-name', flavor_id=guid + '-id', ram=1, disk=1, vcpus=1, ephemeral=1, swap=2, rxtx_factor=3.0, is_public=False) - self.nova = nova_utils.nova_client(self.os_creds) + self.nova = nova_utils.nova_client(self.os_creds, self.os_session) self.flavor = None def tearDown(self): @@ -188,6 +191,8 @@ class NovaUtilsFlavorTests(OSComponentTestCase): except: pass + super(self.__class__, self).__clean__() + def test_create_flavor(self): """ Tests the creation of an OpenStack keypair that does not exist. @@ -241,9 +246,14 @@ class NovaUtilsInstanceTests(OSComponentTestCase): guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) - self.nova = nova_utils.nova_client(self.os_creds) - self.neutron = neutron_utils.neutron_client(self.os_creds) - self.glance = glance_utils.glance_client(self.os_creds) + self.nova = nova_utils.nova_client( + self.os_creds, self.os_session) + self.keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) + self.neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) + self.glance = glance_utils.glance_client( + self.os_creds, self.os_session) self.image_creator = None self.network_creator = None @@ -259,15 +269,17 @@ class NovaUtilsInstanceTests(OSComponentTestCase): self.image_creator.create() network_settings = openstack_tests.get_priv_net_config( - guid + '-net', guid + '-subnet').network_settings + project_name=self.os_creds.project_name, + net_name="{}-{}".format(guid, 'net'), + subnet_name="{}-{}".format(guid, 'subnet')).network_settings self.network_creator = OpenStackNetwork( self.os_creds, network_settings) self.network_creator.create() - self.flavor_creator = OpenStackFlavor( - self.os_creds, - FlavorConfig( - name=guid + '-flavor-name', ram=256, disk=10, vcpus=1)) + flavor_config = openstack_tests.get_flavor_config( + name="{}-{}".format(guid, 'flavor-name'), ram=256, disk=10, + vcpus=1, metadata=self.flavor_metadata) + self.flavor_creator = OpenStackFlavor(self.os_creds, flavor_config) self.flavor_creator.create() port_settings = PortConfig( @@ -314,6 +326,8 @@ class NovaUtilsInstanceTests(OSComponentTestCase): except: pass + super(self.__class__, self).__clean__() + def test_create_instance(self): """ Tests the nova_utils.create_server() method @@ -321,26 +335,30 @@ class NovaUtilsInstanceTests(OSComponentTestCase): """ self.vm_inst = nova_utils.create_server( - self.nova, self.neutron, self.glance, self.instance_settings, - self.image_creator.image_settings) + self.nova, self.keystone, self.neutron, self.glance, + self.instance_settings, self.image_creator.image_settings, + self.os_creds.project_name) self.assertIsNotNone(self.vm_inst) # Wait until instance is ACTIVE iters = 0 active = False + status = None while iters < 60: - if create_instance.STATUS_ACTIVE == nova_utils.get_server_status( - self.nova, self.vm_inst): + status = nova_utils.get_server_status(self.nova, self.vm_inst) + if create_instance.STATUS_ACTIVE == status: active = True break time.sleep(3) iters += 1 - self.assertTrue(active) + self.assertTrue(active, msg='VM {} status {} is not {}'.format( + self.vm_inst.name, status, create_instance.STATUS_ACTIVE)) vm_inst = nova_utils.get_latest_server_object( - self.nova, self.neutron, self.vm_inst) + self.nova, self.neutron, self.keystone, self.vm_inst, + self.os_creds.project_name) self.assertEqual(self.vm_inst.name, vm_inst.name) self.assertEqual(self.vm_inst.id, vm_inst.id) @@ -359,8 +377,9 @@ class NovaUtilsInstanceVolumeTests(OSComponentTestCase): guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) - self.nova = nova_utils.nova_client(self.os_creds) - self.cinder = cinder_utils.cinder_client(self.os_creds) + self.nova = nova_utils.nova_client(self.os_creds, self.os_session) + self.cinder = cinder_utils.cinder_client( + self.os_creds, self.os_session) self.image_creator = None self.network_creator = None @@ -376,15 +395,19 @@ class NovaUtilsInstanceVolumeTests(OSComponentTestCase): self.image_creator.create() network_settings = openstack_tests.get_priv_net_config( - guid + '-net', guid + '-subnet').network_settings + project_name=self.os_creds.project_name, + net_name="{}-{}".format(guid, 'net'), + subnet_name="{}-{}".format(guid, 'subnet')).network_settings + self.network_creator = OpenStackNetwork( self.os_creds, network_settings) self.network_creator.create() + flavor_settings = openstack_tests.get_flavor_config( + name=guid + '-flavor', ram=256, disk=10, vcpus=1, + metadata=self.flavor_metadata) self.flavor_creator = OpenStackFlavor( - self.os_creds, - FlavorConfig( - name=guid + '-flavor-name', ram=256, disk=10, vcpus=1)) + self.os_creds, flavor_settings) self.flavor_creator.create() # Create Volume @@ -438,9 +461,12 @@ class NovaUtilsInstanceVolumeTests(OSComponentTestCase): except: pass + super(self.__class__, self).__clean__() + def test_add_remove_volume(self): """ - Tests the nova_utils.create_server() method + Tests the nova_utils.attach_volume() and detach_volume functions with + a timeout value :return: """ @@ -448,29 +474,36 @@ class NovaUtilsInstanceVolumeTests(OSComponentTestCase): self.assertEqual(0, len(self.volume_creator.get_volume().attachments)) # Attach volume to VM - neutron = neutron_utils.neutron_client(self.os_creds) - nova_utils.attach_volume( - self.nova, neutron, self.instance_creator.get_vm_inst(), - self.volume_creator.get_volume()) - - time.sleep(10) - - vol_attach = cinder_utils.get_volume_by_id( - self.cinder, self.volume_creator.get_volume().id) - vm_attach = nova_utils.get_server_object_by_id( - self.nova, neutron, self.instance_creator.get_vm_inst().id) + neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) + keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) + self.assertIsNotNone(nova_utils.attach_volume( + self.nova, neutron, keystone, self.instance_creator.get_vm_inst(), + self.volume_creator.get_volume(), self.os_creds.project_name)) + + vol_attach = None + vol_detach = None + attached = False + start_time = time.time() + while time.time() < start_time + 120: + vol_attach = cinder_utils.get_volume_by_id( + self.cinder, self.volume_creator.get_volume().id) + + if len(vol_attach.attachments) > 0: + attached = True + break - # Detach volume to VM - nova_utils.detach_volume( - self.nova, neutron, self.instance_creator.get_vm_inst(), - self.volume_creator.get_volume()) + time.sleep(3) - time.sleep(10) + self.assertTrue(attached) + self.assertIsNotNone(vol_attach) - vol_detach = cinder_utils.get_volume_by_id( - self.cinder, self.volume_creator.get_volume().id) - vm_detach = nova_utils.get_server_object_by_id( - self.nova, neutron, self.instance_creator.get_vm_inst().id) + keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) + vm_attach = nova_utils.get_server_object_by_id( + self.nova, neutron, keystone, + self.instance_creator.get_vm_inst().id, self.os_creds.project_name) # Validate Attachment self.assertIsNotNone(vol_attach) @@ -479,8 +512,103 @@ class NovaUtilsInstanceVolumeTests(OSComponentTestCase): self.assertEqual(vm_attach.volume_ids[0]['id'], vol_attach.attachments[0]['volume_id']) + # Detach volume to VM + self.assertIsNotNone(nova_utils.detach_volume( + self.nova, neutron, keystone, self.instance_creator.get_vm_inst(), + self.volume_creator.get_volume(), self.os_creds.project_name)) + + start_time = time.time() + while time.time() < start_time + 120: + vol_detach = cinder_utils.get_volume_by_id( + self.cinder, self.volume_creator.get_volume().id) + if len(vol_detach.attachments) == 0: + attached = False + break + + time.sleep(3) + + self.assertFalse(attached) + self.assertIsNotNone(vol_detach) + + vm_detach = nova_utils.get_server_object_by_id( + self.nova, neutron, keystone, + self.instance_creator.get_vm_inst().id, self.os_creds.project_name) + # Validate Detachment self.assertIsNotNone(vol_detach) self.assertEqual(self.volume_creator.get_volume().id, vol_detach.id) + self.assertEqual(0, len(vol_detach.attachments)) self.assertEqual(0, len(vm_detach.volume_ids)) + + def test_attach_volume_nowait(self): + """ + Tests the nova_utils.attach_volume() with a timeout value that is too + small to have the volume attachment data to be included on the VmInst + object that was supposed to be returned + """ + + self.assertIsNotNone(self.volume_creator.get_volume()) + self.assertEqual(0, len(self.volume_creator.get_volume().attachments)) + + # Attach volume to VM + neutron = neutron_utils.neutron_client(self.os_creds, self.os_session) + keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) + with self.assertRaises(NovaException): + nova_utils.attach_volume( + self.nova, neutron, keystone, + self.instance_creator.get_vm_inst(), + self.volume_creator.get_volume(), self.os_creds.project_name, + 0) + + def test_detach_volume_nowait(self): + """ + Tests the nova_utils.detach_volume() with a timeout value that is too + small to have the volume attachment data to be included on the VmInst + object that was supposed to be returned + """ + + self.assertIsNotNone(self.volume_creator.get_volume()) + self.assertEqual(0, len(self.volume_creator.get_volume().attachments)) + + # Attach volume to VM + neutron = neutron_utils.neutron_client(self.os_creds, self.os_session) + keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) + nova_utils.attach_volume( + self.nova, neutron, keystone, self.instance_creator.get_vm_inst(), + self.volume_creator.get_volume(), self.os_creds.project_name) + + # Check VmInst for attachment + keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) + latest_vm = nova_utils.get_server_object_by_id( + self.nova, neutron, keystone, + self.instance_creator.get_vm_inst().id, self.os_creds.project_name) + self.assertEqual(1, len(latest_vm.volume_ids)) + + # Check Volume for attachment + vol_attach = None + attached = False + start_time = time.time() + while time.time() < start_time + 120: + vol_attach = cinder_utils.get_volume_by_id( + self.cinder, self.volume_creator.get_volume().id) + + if len(vol_attach.attachments) > 0: + attached = True + break + + time.sleep(3) + + self.assertTrue(attached) + self.assertIsNotNone(vol_attach) + + # Detach volume + with self.assertRaises(NovaException): + nova_utils.detach_volume( + self.nova, neutron, keystone, + self.instance_creator.get_vm_inst(), + self.volume_creator.get_volume(), self.os_creds.project_name, + 0) diff --git a/snaps/openstack/utils/tests/settings_utils_tests.py b/snaps/openstack/utils/tests/settings_utils_tests.py index cbd78d8..3d080d4 100644 --- a/snaps/openstack/utils/tests/settings_utils_tests.py +++ b/snaps/openstack/utils/tests/settings_utils_tests.py @@ -37,7 +37,7 @@ from snaps.openstack.create_security_group import OpenStackSecurityGroup from snaps.openstack.tests import openstack_tests from snaps.openstack.tests.os_source_file_test import OSComponentTestCase from snaps.openstack.utils import ( - neutron_utils, settings_utils, nova_utils, glance_utils) + neutron_utils, settings_utils, nova_utils, glance_utils, keystone_utils) __author__ = 'spisarski' @@ -58,7 +58,8 @@ class SettingsUtilsNetworkingTests(OSComponentTestCase): self.network_name = guid + '-net' self.subnet_name = guid + '-subnet' self.net_creator = None - self.neutron = neutron_utils.neutron_client(self.os_creds) + self.neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) def tearDown(self): """ @@ -70,6 +71,8 @@ class SettingsUtilsNetworkingTests(OSComponentTestCase): except: pass + super(self.__class__, self).__clean__() + def test_derive_net_settings_no_subnet(self): """ Validates the utility function settings_utils#create_network_config @@ -154,9 +157,14 @@ class SettingsUtilsVmInstTests(OSComponentTestCase): Instantiates the CreateImage object that is responsible for downloading and creating an OS image file within OpenStack """ - self.nova = nova_utils.nova_client(self.os_creds) - self.glance = glance_utils.glance_client(self.os_creds) - self.neutron = neutron_utils.neutron_client(self.os_creds) + self.nova = nova_utils.nova_client( + self.os_creds, self.os_session) + self.keystone = keystone_utils.keystone_client( + self.os_creds, self.os_session) + self.glance = glance_utils.glance_client( + self.os_creds, self.os_session) + self.neutron = neutron_utils.neutron_client( + self.os_creds, self.os_session) guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) self.keypair_priv_filepath = 'tmp/' + guid @@ -188,6 +196,7 @@ class SettingsUtilsVmInstTests(OSComponentTestCase): # First network is public self.pub_net_config = openstack_tests.get_pub_net_config( + project_name=self.os_creds.project_name, net_name=guid + '-pub-net', subnet_name=guid + '-pub-subnet', router_name=guid + '-pub-router', external_net=self.ext_net_name) @@ -314,7 +323,7 @@ class SettingsUtilsVmInstTests(OSComponentTestCase): if os.path.isfile(self.test_file_local_path): os.remove(self.test_file_local_path) - # super(self.__class__, self).__clean__() + super(self.__class__, self).__clean__() def test_derive_vm_inst_config(self): """ @@ -324,10 +333,11 @@ class SettingsUtilsVmInstTests(OSComponentTestCase): self.inst_creator.create(block=True) server = nova_utils.get_server( - self.nova, self.neutron, + self.nova, self.neutron, self.keystone, vm_inst_settings=self.inst_creator.instance_settings) derived_vm_settings = settings_utils.create_vm_inst_config( - self.nova, self.neutron, server) + self.nova, self.keystone, self.neutron, server, + self.os_creds.project_name) self.assertIsNotNone(derived_vm_settings) self.assertIsNotNone(derived_vm_settings.port_settings) self.assertIsNotNone(derived_vm_settings.floating_ip_settings) @@ -340,7 +350,7 @@ class SettingsUtilsVmInstTests(OSComponentTestCase): self.inst_creator.create(block=True) server = nova_utils.get_server( - self.nova, self.neutron, + self.nova, self.neutron, self.keystone, vm_inst_settings=self.inst_creator.instance_settings) derived_image_settings = settings_utils.determine_image_config( self.glance, server, [self.image_creator.image_settings]) @@ -356,8 +366,9 @@ class SettingsUtilsUnitTests(unittest.TestCase): def test_vol_settings_from_vol(self): volume = Volume( - name='vol-name', volume_id='vol-id', description='desc', size=99, - vol_type='vol-type', availability_zone='zone1', multi_attach=True) + name='vol-name', volume_id='vol-id', project_id='proj-id', + description='desc', size=99, vol_type='vol-type', + availability_zone='zone1', multi_attach=True) settings = settings_utils.create_volume_config(volume) self.assertEqual(volume.name, settings.name) self.assertEqual(volume.description, settings.description) diff --git a/snaps/playbook_runner.py b/snaps/playbook_runner.py index 87321f5..7b10390 100644 --- a/snaps/playbook_runner.py +++ b/snaps/playbook_runner.py @@ -15,9 +15,13 @@ import argparse import ast import logging +import os import re +import yaml +from jinja2 import Environment, FileSystemLoader + from snaps.openstack.os_credentials import ProxySettings from snaps.provisioning import ansible_utils @@ -41,41 +45,59 @@ def main(parsed_args): ssh_proxy_cmd=parsed_args.ssh_proxy_cmd) # Ensure can get an SSH client - ssh = ansible_utils.ssh_client(parsed_args.ip_addr, parsed_args.host_user, - parsed_args.priv_key, proxy_settings) + ssh = ansible_utils.ssh_client( + parsed_args.ip_addr, parsed_args.host_user, + private_key_filepath=parsed_args.priv_key, + proxy_settings=proxy_settings) if ssh: ssh.close() - vars = dict() - if args.vars: - vars = ast.literal_eval(args.vars) - if not isinstance(vars, dict): - vars = dict() + env = Environment(loader=FileSystemLoader( + searchpath=os.path.dirname(parsed_args.env_file))) + template = env.get_template(os.path.basename(parsed_args.env_file)) + + env_dict = dict() + if parsed_args.vars: + env_dict = ast.literal_eval(parsed_args.vars) + + output = template.render(**env_dict) + + variables = yaml.load(output) + + if not variables.get('env_file'): + variables['env_file'] = parsed_args.env_file - retval = ansible_utils.apply_playbook( + ansible_utils.apply_playbook( parsed_args.playbook, [parsed_args.ip_addr], parsed_args.host_user, - parsed_args.priv_key, variables=vars, + ssh_priv_key_file_path=parsed_args.priv_key, + password=parsed_args.password, variables=variables, proxy_setting=proxy_settings) - exit(retval) if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('-a', '--ip-addr', dest='ip_addr', required=True, help='The Host IP Address') - parser.add_argument('-k', '--priv-key', dest='priv_key', required=True, - help='The location of the private key file') parser.add_argument('-u', '--host-user', dest='host_user', required=True, help='Host user account') + parser.add_argument('-k', '--priv-key', dest='priv_key', required=False, + help='The location of the private key file') + parser.add_argument('-pw', '--password', dest='password', required=False, + help='The host-user password') parser.add_argument('-b', '--playbook', dest='playbook', required=True, help='Playbook Location') parser.add_argument('-p', '--http-proxy', dest='http_proxy', required=False, help='<host>:<port>') parser.add_argument('-s', '--ssh-proxy-cmd', dest='ssh_proxy_cmd', required=False) + parser.add_argument('-e', '--env-file', dest='env_file', + help='Yaml file containing playbook substitution vals', + required=False) parser.add_argument('-v', '--vars', dest='vars', + help='String renditon of a dict to pass into ' + 'playbook for additional subtitution values not ' + 'found in env_file', required=False) args = parser.parse_args() main(args) - diff --git a/snaps/provisioning/ansible_utils.py b/snaps/provisioning/ansible_utils.py index 63f26e1..019a8e7 100644 --- a/snaps/provisioning/ansible_utils.py +++ b/snaps/provisioning/ansible_utils.py @@ -21,52 +21,80 @@ import paramiko try: from ansible.parsing.dataloader import DataLoader - from ansible.vars import VariableManager - from ansible.inventory import Inventory + from ansible.vars.manager import VariableManager + from ansible.inventory.manager import InventoryManager from ansible.executor.playbook_executor import PlaybookExecutor except: pass __author__ = 'spisarski' +from warnings import warn +warn('This utility will be removed in a subsequent release', + DeprecationWarning) + logger = logging.getLogger('ansible_utils') -def apply_playbook(playbook_path, hosts_inv, host_user, ssh_priv_key_file_path, - variables=None, proxy_setting=None): +def apply_playbook(playbook_path, hosts_inv=None, host_user=None, + ssh_priv_key_file_path=None, password=None, variables=None, + proxy_setting=None): """ Executes an Ansible playbook to the given host :param playbook_path: the (relative) path to the Ansible playbook :param hosts_inv: a list of hostnames/ip addresses to which to apply the - Ansible playbook + Ansible playbook (not required when PB is configured for + localhost) :param host_user: A user for the host instances (must be a password-less - sudo user if playbook has "sudo: yes" - :param ssh_priv_key_file_path: the file location of the ssh key + sudo user if playbook has "sudo: yes") (not required when + PB is configured for localhost) + :param ssh_priv_key_file_path: the file location of the ssh key. Required + if password is None (not required when PB is + configured for localhost) + :param password: the file location of the ssh key. Required if + ssh_priv_key_file_path is None (not required when PB is + configured for localhost) :param variables: a dictionary containing any substitution variables needed by the Jinga 2 templates :param proxy_setting: instance of os_credentials.ProxySettings class - :return: the results + :raises AnsibleException when the return code from the Ansible library is + not 0 + :return: the return code from the Ansible library only when 0. + Implementation now raises an exception otherwise """ if not os.path.isfile(playbook_path): - raise AnsibleException('Requested playbook not found - ' + playbook_path) + raise AnsibleException( + 'Requested playbook not found - ' + playbook_path) + + pk_file_path = None + if ssh_priv_key_file_path: + pk_file_path = os.path.expanduser(ssh_priv_key_file_path) + if not password: + if not os.path.isfile(pk_file_path): + raise AnsibleException( + 'Requested private SSH key not found - ' + pk_file_path) - pk_file_path = os.path.expanduser(ssh_priv_key_file_path) - if not os.path.isfile(pk_file_path): - raise AnsibleException('Requested private SSH key not found - ' + - pk_file_path) + passwords = None + if password: + passwords = {'conn_pass': password, 'become_pass': password} import ansible.constants ansible.constants.HOST_KEY_CHECKING = False - variable_manager = VariableManager() + loader = DataLoader() + inventory = InventoryManager(loader=loader) + if hosts_inv: + for host in hosts_inv: + inventory.add_host(host=host, group='ungrouped') + connection = 'ssh' + else: + connection = 'local' + + variable_manager = VariableManager(loader=loader, inventory=inventory) + if variables: variable_manager.extra_vars = variables - loader = DataLoader() - inventory = Inventory(loader=loader, variable_manager=variable_manager, - host_list=hosts_inv) - variable_manager.set_inventory(inventory) - ssh_extra_args = None if proxy_setting and proxy_setting.ssh_proxy_cmd: ssh_extra_args = '-o ProxyCommand=\'%s\'' % proxy_setting.ssh_proxy_cmd @@ -76,14 +104,15 @@ def apply_playbook(playbook_path, hosts_inv, host_user, ssh_priv_key_file_path, 'connection', 'module_path', 'forks', 'remote_user', 'private_key_file', 'ssh_common_args', 'ssh_extra_args', 'become', 'become_method', 'become_user', 'verbosity', - 'check', 'timeout']) + 'check', 'timeout', 'diff']) ansible_opts = options( listtags=False, listtasks=False, listhosts=False, syntax=False, - connection='ssh', module_path=None, forks=100, remote_user=host_user, - private_key_file=pk_file_path, ssh_common_args=None, - ssh_extra_args=ssh_extra_args, become=None, become_method=None, - become_user=None, verbosity=11111, check=False, timeout=30) + connection=connection, module_path=None, forks=100, + remote_user=host_user, private_key_file=pk_file_path, + ssh_common_args=None, ssh_extra_args=ssh_extra_args, become=None, + become_method=None, become_user=None, verbosity=11111, check=False, + timeout=30, diff=None) logger.debug('Setting up Ansible Playbook Executor for playbook - ' + playbook_path) @@ -93,18 +122,28 @@ def apply_playbook(playbook_path, hosts_inv, host_user, ssh_priv_key_file_path, variable_manager=variable_manager, loader=loader, options=ansible_opts, - passwords=None) + passwords=passwords) logger.debug('Executing Ansible Playbook - ' + playbook_path) - return executor.run() + ret_val = executor.run() + + if ret_val != 0: + raise AnsibleException( + 'Error applying playbook [{}] with value [{}] using the connection' + ' type of [{}]'.format( + playbook_path, ret_val, connection)) + return ret_val -def ssh_client(ip, user, private_key_filepath, proxy_settings=None): + +def ssh_client(ip, user, private_key_filepath=None, password=None, + proxy_settings=None): """ Retrieves and attemts an SSH connection :param ip: the IP of the host to connect :param user: the user with which to connect - :param private_key_filepath: the path to the private key file + :param private_key_filepath: when None, password is required + :param password: when None, private_key_filepath is required :param proxy_settings: instance of os_credentials.ProxySettings class (optional) :return: the SSH client if can connect else false @@ -120,12 +159,17 @@ def ssh_client(ip, user, private_key_filepath, proxy_settings=None): proxy_cmd_str = proxy_cmd_str.replace("%p", '22') proxy_cmd = paramiko.ProxyCommand(proxy_cmd_str) - pk_abs_path = os.path.expanduser(private_key_filepath) - ssh.connect(ip, username=user, key_filename=pk_abs_path, - sock=proxy_cmd) + pk_abs_path = None + if not password and private_key_filepath: + pk_abs_path = os.path.expanduser(private_key_filepath) + + ssh.connect( + ip, username=user, key_filename=pk_abs_path, password=password, + sock=proxy_cmd) + logger.info('Obtained SSH connection to %s', ip) return ssh except Exception as e: - logger.warning('Unable to connect via SSH with message - ' + str(e)) + logger.debug('Unable to connect via SSH with message - ' + str(e)) class AnsibleException(Exception): diff --git a/snaps/provisioning/tests/ansible_utils_tests.py b/snaps/provisioning/tests/ansible_utils_tests.py index 7600002..b6ace31 100644 --- a/snaps/provisioning/tests/ansible_utils_tests.py +++ b/snaps/provisioning/tests/ansible_utils_tests.py @@ -19,7 +19,6 @@ import os import pkg_resources from scp import SCPClient -from snaps.config.flavor import FlavorConfig from snaps.config.keypair import KeypairConfig from snaps.config.network import PortConfig from snaps.config.security_group import ( @@ -58,7 +57,7 @@ class AnsibleProvisioningTests(OSIntegrationTestCase): """ super(self.__class__, self).__start__() - self.nova = nova_utils.nova_client(self.os_creds) + self.nova = nova_utils.nova_client(self.os_creds, self.os_session) guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) self.keypair_priv_filepath = 'tmp/' + guid @@ -84,13 +83,15 @@ class AnsibleProvisioningTests(OSIntegrationTestCase): os_image_settings = openstack_tests.ubuntu_image_settings( name=guid + '-' + '-image', image_metadata=self.image_metadata) - self.image_creator = create_image.OpenStackImage(self.os_creds, - os_image_settings) + self.image_creator = create_image.OpenStackImage( + self.os_creds, os_image_settings) self.image_creator.create() # First network is public self.pub_net_config = openstack_tests.get_pub_net_config( - net_name=guid + '-pub-net', subnet_name=guid + '-pub-subnet', + project_name=self.os_creds.project_name, + net_name=guid + '-pub-net', + mtu=1442, subnet_name=guid + '-pub-subnet', router_name=guid + '-pub-router', external_net=self.ext_net_name) @@ -104,11 +105,12 @@ class AnsibleProvisioningTests(OSIntegrationTestCase): self.router_creator.create() # Create Flavor + flavor_config = openstack_tests.get_flavor_config( + name=guid + '-flavor-name', ram=2048, disk=10, + vcpus=2, metadata=self.flavor_metadata) + self.flavor_creator = create_flavor.OpenStackFlavor( - self.admin_os_creds, - FlavorConfig( - name=guid + '-flavor-name', ram=2048, disk=10, vcpus=2, - metadata=self.flavor_metadata)) + self.admin_os_creds, flavor_config) self.flavor_creator.create() # Create Key/Pair @@ -263,11 +265,11 @@ class AnsibleProvisioningTests(OSIntegrationTestCase): relative_pb_path = pkg_resources.resource_filename( 'snaps.provisioning.tests.playbooks', 'simple_playbook.yml') - retval = self.inst_creator.apply_ansible_playbook(relative_pb_path) - self.assertEqual(0, retval) + self.inst_creator.apply_ansible_playbook(relative_pb_path) - ssh = ansible_utils.ssh_client(ip, user, priv_key, - self.os_creds.proxy_settings) + ssh = ansible_utils.ssh_client( + ip, user, private_key_filepath=priv_key, + proxy_settings=self.os_creds.proxy_settings) self.assertIsNotNone(ssh) scp = None try: @@ -329,13 +331,12 @@ class AnsibleProvisioningTests(OSIntegrationTestCase): relative_pb_path = pkg_resources.resource_filename( 'snaps.provisioning.tests.playbooks', 'template_playbook.yml') - retval = self.inst_creator.apply_ansible_playbook(relative_pb_path, - variables={ - 'name': 'Foo'}) - self.assertEqual(0, retval) + self.inst_creator.apply_ansible_playbook( + relative_pb_path, variables={'name': 'Foo'}) - ssh = ansible_utils.ssh_client(ip, user, priv_key, - self.os_creds.proxy_settings) + ssh = ansible_utils.ssh_client( + ip, user, private_key_filepath=priv_key, + proxy_settings=self.os_creds.proxy_settings) self.assertIsNotNone(ssh) scp = None diff --git a/snaps/test_runner.py b/snaps/test_runner.py index d3c1fd6..d46fe86 100644 --- a/snaps/test_runner.py +++ b/snaps/test_runner.py @@ -13,11 +13,12 @@ # See the License for the specific language governing permissions and # limitations under the License. import argparse -import json import logging import unittest +from concurrencytest import ConcurrentTestSuite, fork_for_tests -from snaps import test_suite_builder, file_utils +from snaps import file_utils +from snaps import test_suite_builder as tsb from snaps.openstack.tests import openstack_tests __author__ = 'spisarski' @@ -30,14 +31,13 @@ LOG_LEVELS = {'FATAL': logging.FATAL, 'CRITICAL': logging.CRITICAL, 'INFO': logging.INFO, 'DEBUG': logging.DEBUG} -def __create_test_suite( +def __create_concurrent_test_suite( source_filename, ext_net_name, proxy_settings, ssh_proxy_cmd, run_unit_tests, run_connection_tests, run_api_tests, run_integration_tests, run_staging_tests, flavor_metadata, - image_metadata, use_keystone, use_floating_ips, continuous_integration, - log_level): + image_metadata, use_floating_ips, continuous_integration, log_level): """ - Compiles the tests that should run + Compiles the tests that can be run concurrently :param source_filename: the OpenStack credentials file (required) :param ext_net_name: the name of the external network to use for floating IPs (required) @@ -56,9 +56,6 @@ def __create_test_suite( created for test VM instance :param image_metadata: dict() object containing the metadata for overriding default images within the tests - :param use_keystone: when true, tests creating users and projects will be - exercised and must be run on a host that - has access to the cloud's administrative network :param use_floating_ips: when true, tests requiring floating IPs will be executed :param continuous_integration: when true, tests for CI will be run @@ -73,43 +70,100 @@ def __create_test_suite( # Tests that do not require a remote connection to an OpenStack cloud if run_unit_tests: - test_suite_builder.add_unit_tests(suite) + tsb.add_unit_tests(suite) # Basic connection tests if run_connection_tests: - test_suite_builder.add_openstack_client_tests( + tsb.add_openstack_client_tests( suite=suite, os_creds=os_creds, ext_net_name=ext_net_name, - use_keystone=use_keystone, log_level=log_level) + use_keystone=True, log_level=log_level) # Tests the OpenStack API calls if run_api_tests: - test_suite_builder.add_openstack_api_tests( + tsb.add_openstack_api_tests( suite=suite, os_creds=os_creds, ext_net_name=ext_net_name, - use_keystone=use_keystone, image_metadata=image_metadata, - log_level=log_level) + use_keystone=True, flavor_metadata=flavor_metadata, + image_metadata=image_metadata, log_level=log_level) # Long running integration type tests if run_integration_tests: - test_suite_builder.add_openstack_integration_tests( + tsb.add_openstack_integration_tests( suite=suite, os_creds=os_creds, ext_net_name=ext_net_name, - use_keystone=use_keystone, flavor_metadata=flavor_metadata, + use_keystone=True, flavor_metadata=flavor_metadata, image_metadata=image_metadata, use_floating_ips=use_floating_ips, log_level=log_level) if run_staging_tests: - test_suite_builder.add_openstack_staging_tests( + tsb.add_openstack_staging_tests( suite=suite, os_creds=os_creds, ext_net_name=ext_net_name, log_level=log_level) if continuous_integration: - test_suite_builder.add_openstack_ci_tests( + tsb.add_openstack_ci_tests( suite=suite, os_creds=os_creds, ext_net_name=ext_net_name, - use_keystone=use_keystone, flavor_metadata=flavor_metadata, + use_keystone=True, flavor_metadata=flavor_metadata, image_metadata=image_metadata, use_floating_ips=use_floating_ips, log_level=log_level) return suite +def __create_sequential_test_suite( + source_filename, ext_net_name, proxy_settings, ssh_proxy_cmd, + run_integration_tests, flavor_metadata, image_metadata, + use_floating_ips, log_level): + """ + Compiles the tests that cannot be run in parallel + :param source_filename: the OpenStack credentials file (required) + :param ext_net_name: the name of the external network to use for floating + IPs (required) + :param run_integration_tests: when true, the integration tests are executed + :param proxy_settings: <host>:<port> of the proxy server (optional) + :param ssh_proxy_cmd: the command used to connect via SSH over some proxy + server (optional) + :param flavor_metadata: dict() object containing the metadata for flavors + created for test VM instance + :param image_metadata: dict() object containing the metadata for overriding + default images within the tests + :param use_floating_ips: when true, tests requiring floating IPs will be + executed + :param log_level: the logging level + :return: + """ + if use_floating_ips and run_integration_tests: + suite = unittest.TestSuite() + + os_creds = openstack_tests.get_credentials( + os_env_file=source_filename, proxy_settings_str=proxy_settings, + ssh_proxy_cmd=ssh_proxy_cmd) + + tsb.add_ansible_integration_tests( + suite=suite, os_creds=os_creds, ext_net_name=ext_net_name, + use_keystone=True, flavor_metadata=flavor_metadata, + image_metadata=image_metadata, log_level=log_level) + + return suite + + +def __output_results(results): + """ + Sends the test results to the logger + :param results: + :return: + """ + + if results.errors: + logger.error('Number of errors in test suite - %s', + len(results.errors)) + for test, message in results.errors: + logger.error(str(test) + " ERROR with " + message) + + if results.failures: + logger.error('Number of failures in test suite - %s', + len(results.failures)) + for test, message in results.failures: + logger.error(str(test) + " FAILED with " + message) + + def main(arguments): """ Begins running unit tests. @@ -123,13 +177,16 @@ def main(arguments): flavor_metadata = None if arguments.flavor_metadata: - flavor_metadata = json.loads(arguments.flavor_metadata) + flavor_metadata = { + 'metadata': {'hw:mem_page_size': arguments.flavor_metadata}} image_metadata = None if arguments.image_metadata_file: image_metadata = file_utils.read_yaml(arguments.image_metadata_file) - suite = None + concurrent_suite = None + sequential_suite = None + if arguments.env and arguments.ext_net: unit = arguments.include_unit != ARG_NOT_SET connection = arguments.include_connection != ARG_NOT_SET @@ -144,40 +201,61 @@ def main(arguments): api = True integration = True - suite = __create_test_suite( + concurrent_suite = __create_concurrent_test_suite( arguments.env, arguments.ext_net, arguments.proxy, arguments.ssh_proxy_cmd, unit, connection, api, integration, staging, flavor_metadata, image_metadata, - arguments.use_keystone != ARG_NOT_SET, arguments.floating_ips != ARG_NOT_SET, ci, log_level) + + if (arguments.include_integration != ARG_NOT_SET + and arguments.floating_ips != ARG_NOT_SET): + sequential_suite = __create_sequential_test_suite( + arguments.env, arguments.ext_net, arguments.proxy, + arguments.ssh_proxy_cmd, integration, flavor_metadata, + image_metadata, + arguments.floating_ips != ARG_NOT_SET, log_level) else: logger.error('Environment file or external network not defined') exit(1) i = 0 while i < int(arguments.num_runs): - result = unittest.TextTestRunner(verbosity=2).run(suite) i += 1 - if result.errors: - logger.error('Number of errors in test suite - %s', - len(result.errors)) - for test, message in result.errors: - logger.error(str(test) + " ERROR with " + message) - - if result.failures: - logger.error('Number of failures in test suite - %s', - len(result.failures)) - for test, message in result.failures: - logger.error(str(test) + " FAILED with " + message) - - if ((result.errors and len(result.errors) > 0) - or (result.failures and len(result.failures) > 0)): - logger.error('See above for test failures') - exit(1) - else: - logger.info('All tests completed successfully in run #%s', i) + if concurrent_suite: + logger.info('Running Concurrent Tests') + concurrent_runner = unittest.TextTestRunner(verbosity=2) + concurrent_suite = ConcurrentTestSuite( + concurrent_suite, fork_for_tests(int(arguments.threads))) + concurrent_results = concurrent_runner.run(concurrent_suite) + __output_results(concurrent_results) + + if ((concurrent_results.errors + and len(concurrent_results.errors) > 0) + or (concurrent_results.failures + and len(concurrent_results.failures) > 0)): + logger.error('See above for test failures') + exit(1) + else: + logger.info( + 'Concurrent tests completed successfully in run #%s', i) + + if sequential_suite: + logger.info('Running Sequential Tests') + sequential_runner = unittest.TextTestRunner(verbosity=2) + sequential_results = sequential_runner.run(sequential_suite) + __output_results(sequential_results) + + if ((sequential_results.errors + and len(sequential_results.errors) > 0) + or (sequential_results.failures + and len(sequential_results.failures) > 0)): + logger.error('See above for test failures') + exit(1) + else: + logger.info( + 'Sequential tests completed successfully in run #%s', i) logger.info('Successful completion of %s test runs', i) exit(0) @@ -227,15 +305,9 @@ if __name__ == '__main__': nargs='?', help='When argument is set, all integration tests requiring' ' Floating IPs will be executed') parser.add_argument( - '-k', '--use-keystone', dest='use_keystone', default=ARG_NOT_SET, - nargs='?', - help='When argument is set, the tests will exercise the keystone APIs ' - 'and must be run on a machine that has access to the admin ' - 'network and is able to create users and groups') - parser.add_argument( '-fm', '--flavor-meta', dest='flavor_metadata', - help='JSON string to be used as flavor metadata for all test instances' - ' created') + help='hw:mem_page_size flavor setting value (i.e. large). ' + 'Required for DPDK') parser.add_argument( '-im', '--image-meta', dest='image_metadata_file', default=None, help='Location of YAML file containing the image metadata') @@ -247,6 +319,9 @@ if __name__ == '__main__': parser.add_argument( '-r', '--num-runs', dest='num_runs', default=1, help='Number of test runs to execute (default 1)') + parser.add_argument( + '-t', '--threads', dest='threads', default=4, + help='Number of threads to execute the tests (default 4)') args = parser.parse_args() diff --git a/snaps/test_suite_builder.py b/snaps/test_suite_builder.py index 7b3ece7..52008a6 100644 --- a/snaps/test_suite_builder.py +++ b/snaps/test_suite_builder.py @@ -65,18 +65,19 @@ from snaps.openstack.tests.create_image_tests import ( CreateImageSuccessTests, CreateImageNegativeTests, CreateMultiPartImageTests) from snaps.openstack.tests.create_instance_tests import ( - CreateInstanceSingleNetworkTests, CreateInstanceOnComputeHost, + CreateInstanceSingleNetworkTests, CreateInstanceOnComputeHost, CreateInstanceSimpleTests, FloatingIpSettingsUnitTests, InstanceSecurityGroupTests, VmInstanceSettingsUnitTests, CreateInstancePortManipulationTests, SimpleHealthCheck, CreateInstanceFromThreePartImage, CreateInstanceMockOfflineTests, CreateInstanceTwoNetTests, CreateInstanceVolumeTests, - CreateInstanceIPv6NetworkTests) + CreateInstanceIPv6NetworkTests, CreateInstanceExternalNetTests) from snaps.openstack.tests.create_keypairs_tests import ( CreateKeypairsTests, KeypairSettingsUnitTests, CreateKeypairsCleanupTests) from snaps.openstack.tests.create_network_tests import ( CreateNetworkSuccessTests, NetworkSettingsUnitTests, PortSettingsUnitTests, - SubnetSettingsUnitTests, CreateNetworkTypeTests, CreateNetworkIPv6Tests) + SubnetSettingsUnitTests, CreateNetworkTypeTests, CreateNetworkIPv6Tests, + CreateMultipleNetworkTests, CreateNetworkGatewayTests) from snaps.openstack.tests.create_project_tests import ( CreateProjectSuccessTests, ProjectSettingsUnitTests, CreateProjectUserTests) @@ -84,21 +85,23 @@ from snaps.openstack.tests.create_qos_tests import ( QoSSettingsUnitTests, CreateQoSTests) from snaps.openstack.tests.create_router_tests import ( CreateRouterSuccessTests, CreateRouterNegativeTests, - RouterSettingsUnitTests) + RouterSettingsUnitTests, CreateMultipleRouterTests, + CreateRouterSecurityGroupTests, CreateRouterSharedNetworksTests) from snaps.openstack.tests.create_security_group_tests import ( CreateSecurityGroupTests, SecurityGroupRuleSettingsUnitTests, - SecurityGroupSettingsUnitTests) + SecurityGroupSettingsUnitTests, CreateMultipleSecurityGroupTests) from snaps.openstack.tests.create_stack_tests import ( StackSettingsUnitTests, CreateStackSuccessTests, CreateStackNegativeTests, CreateStackFlavorTests, CreateStackFloatingIpTests, CreateStackNestedResourceTests, CreateStackKeypairTests, - CreateStackVolumeTests, CreateStackSecurityGroupTests) + CreateStackVolumeTests, CreateStackSecurityGroupTests, + CreateStackUpdateTests) from snaps.openstack.tests.create_user_tests import ( UserSettingsUnitTests, CreateUserSuccessTests) from snaps.openstack.tests.create_volume_tests import ( VolumeSettingsUnitTests, CreateSimpleVolumeSuccessTests, CreateVolumeWithTypeTests, CreateVolumeWithImageTests, - CreateSimpleVolumeFailureTests) + CreateSimpleVolumeFailureTests, CreateVolMultipleCredsTests) from snaps.openstack.tests.create_volume_type_tests import ( VolumeTypeSettingsUnitTests, CreateSimpleVolumeTypeSuccessTests, CreateVolumeTypeComplexTests) @@ -315,7 +318,8 @@ def add_openstack_client_tests(suite, os_creds, ext_net_name, def add_openstack_api_tests(suite, os_creds, ext_net_name, use_keystone=True, - image_metadata=None, log_level=logging.INFO): + flavor_metadata=None, image_metadata=None, + log_level=logging.INFO): """ Adds tests written to exercise all existing OpenStack APIs :param suite: the unittest.TestSuite object to which to add the tests @@ -326,6 +330,9 @@ def add_openstack_api_tests(suite, os_creds, ext_net_name, use_keystone=True, :param use_keystone: when True, tests requiring direct access to Keystone are added as these need to be running on a host that has access to the cloud's private network + :param flavor_metadata: dict() object containing the metadata required by + your flavor based on your configuration: + (i.e. {'hw:mem_page_size': 'any'}) :param image_metadata: dict() object containing metadata for creating an image with custom config (see YAML files in examples/image-metadata) @@ -381,7 +388,7 @@ def add_openstack_api_tests(suite, os_creds, ext_net_name, use_keystone=True, suite.addTest(OSComponentTestCase.parameterize( NovaUtilsInstanceVolumeTests, os_creds=os_creds, ext_net_name=ext_net_name, log_level=log_level, - image_metadata=image_metadata)) + flavor_metadata=flavor_metadata, image_metadata=image_metadata)) suite.addTest(OSComponentTestCase.parameterize( CreateFlavorTests, os_creds=os_creds, ext_net_name=ext_net_name, log_level=log_level)) @@ -468,6 +475,11 @@ def add_openstack_integration_tests(suite, os_creds, ext_net_name, flavor_metadata=flavor_metadata, image_metadata=image_metadata, log_level=log_level)) suite.addTest(OSIntegrationTestCase.parameterize( + CreateMultipleSecurityGroupTests, os_creds=os_creds, + ext_net_name=ext_net_name, use_keystone=use_keystone, + flavor_metadata=flavor_metadata, image_metadata=image_metadata, + log_level=log_level)) + suite.addTest(OSIntegrationTestCase.parameterize( CreateImageSuccessTests, os_creds=os_creds, ext_net_name=ext_net_name, use_keystone=use_keystone, flavor_metadata=flavor_metadata, image_metadata=image_metadata, @@ -499,11 +511,21 @@ def add_openstack_integration_tests(suite, os_creds, ext_net_name, flavor_metadata=flavor_metadata, image_metadata=image_metadata, log_level=log_level)) suite.addTest(OSIntegrationTestCase.parameterize( + CreateNetworkGatewayTests, os_creds=os_creds, + ext_net_name=ext_net_name, use_keystone=use_keystone, + flavor_metadata=flavor_metadata, image_metadata=image_metadata, + log_level=log_level)) + suite.addTest(OSIntegrationTestCase.parameterize( CreateNetworkIPv6Tests, os_creds=os_creds, ext_net_name=ext_net_name, use_keystone=use_keystone, flavor_metadata=flavor_metadata, image_metadata=image_metadata, log_level=log_level)) suite.addTest(OSIntegrationTestCase.parameterize( + CreateMultipleNetworkTests, os_creds=os_creds, + ext_net_name=ext_net_name, use_keystone=use_keystone, + flavor_metadata=flavor_metadata, image_metadata=image_metadata, + log_level=log_level)) + suite.addTest(OSIntegrationTestCase.parameterize( CreateRouterSuccessTests, os_creds=os_creds, ext_net_name=ext_net_name, use_keystone=use_keystone, flavor_metadata=flavor_metadata, image_metadata=image_metadata, @@ -514,6 +536,21 @@ def add_openstack_integration_tests(suite, os_creds, ext_net_name, flavor_metadata=flavor_metadata, image_metadata=image_metadata, log_level=log_level)) suite.addTest(OSIntegrationTestCase.parameterize( + CreateMultipleRouterTests, os_creds=os_creds, + ext_net_name=ext_net_name, use_keystone=use_keystone, + flavor_metadata=flavor_metadata, image_metadata=image_metadata, + log_level=log_level)) + suite.addTest(OSIntegrationTestCase.parameterize( + CreateRouterSecurityGroupTests, os_creds=os_creds, + ext_net_name=ext_net_name, use_keystone=use_keystone, + flavor_metadata=flavor_metadata, image_metadata=image_metadata, + log_level=log_level)) + suite.addTest(OSIntegrationTestCase.parameterize( + CreateRouterSharedNetworksTests, os_creds=os_creds, + ext_net_name=ext_net_name, use_keystone=use_keystone, + flavor_metadata=flavor_metadata, image_metadata=image_metadata, + log_level=log_level)) + suite.addTest(OSIntegrationTestCase.parameterize( CreateQoSTests, os_creds=os_creds, ext_net_name=ext_net_name, use_keystone=use_keystone, flavor_metadata=flavor_metadata, image_metadata=image_metadata, @@ -548,6 +585,11 @@ def add_openstack_integration_tests(suite, os_creds, ext_net_name, ext_net_name=ext_net_name, use_keystone=use_keystone, flavor_metadata=flavor_metadata, image_metadata=image_metadata, log_level=log_level)) + suite.addTest(OSIntegrationTestCase.parameterize( + CreateVolMultipleCredsTests, os_creds=os_creds, + ext_net_name=ext_net_name, use_keystone=use_keystone, + flavor_metadata=flavor_metadata, image_metadata=image_metadata, + log_level=log_level)) # VM Instances suite.addTest(OSIntegrationTestCase.parameterize( @@ -566,6 +608,11 @@ def add_openstack_integration_tests(suite, os_creds, ext_net_name, flavor_metadata=flavor_metadata, image_metadata=image_metadata, log_level=log_level)) suite.addTest(OSIntegrationTestCase.parameterize( + CreateInstanceExternalNetTests, os_creds=os_creds, + ext_net_name=ext_net_name, use_keystone=use_keystone, + flavor_metadata=flavor_metadata, image_metadata=image_metadata, + log_level=log_level)) + suite.addTest(OSIntegrationTestCase.parameterize( CreateInstancePortManipulationTests, os_creds=os_creds, ext_net_name=ext_net_name, use_keystone=use_keystone, flavor_metadata=flavor_metadata, image_metadata=image_metadata, @@ -643,12 +690,43 @@ def add_openstack_integration_tests(suite, os_creds, ext_net_name, flavor_metadata=flavor_metadata, image_metadata=image_metadata, log_level=log_level)) suite.addTest(OSIntegrationTestCase.parameterize( - AnsibleProvisioningTests, os_creds=os_creds, + CreateStackUpdateTests, os_creds=os_creds, ext_net_name=ext_net_name, use_keystone=use_keystone, flavor_metadata=flavor_metadata, image_metadata=image_metadata, log_level=log_level)) +def add_ansible_integration_tests(suite, os_creds, ext_net_name, + use_keystone=True, flavor_metadata=None, + image_metadata=None, log_level=logging.INFO): + """ + Adds tests written to exercise all long-running OpenStack integration tests + meaning they will be creating VM instances and potentially performing some + SSH functions through floatingIPs + :param suite: the unittest.TestSuite object to which to add the tests + :param os_creds: and instance of OSCreds that holds the credentials + required by OpenStack + :param ext_net_name: the name of an external network on the cloud under + test + :param use_keystone: when True, tests requiring direct access to Keystone + are added as these need to be running on a host that + has access to the cloud's private network + :param image_metadata: dict() object containing metadata for creating an + image with custom config + (see YAML files in examples/image-metadata) + :param flavor_metadata: dict() object containing the metadata required by + your flavor based on your configuration: + (i.e. {'hw:mem_page_size': 'large'}) + :param log_level: the logging level + :return: None as the tests will be adding to the 'suite' parameter object + """ + suite.addTest(OSIntegrationTestCase.parameterize( + AnsibleProvisioningTests, os_creds=os_creds, + ext_net_name=ext_net_name, use_keystone=use_keystone, + flavor_metadata=flavor_metadata, image_metadata=image_metadata, + log_level=log_level)) + + def add_openstack_ci_tests( suite, os_creds, ext_net_name, use_keystone=True, flavor_metadata=None, image_metadata=None, use_floating_ips=True, log_level=logging.INFO): diff --git a/snaps/tests/file_utils_tests.py b/snaps/tests/file_utils_tests.py index befe37a..e09a9cc 100644 --- a/snaps/tests/file_utils_tests.py +++ b/snaps/tests/file_utils_tests.py @@ -31,7 +31,7 @@ class FileUtilsTests(unittest.TestCase): def setUp(self): guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) - self.tmp_dir = '.tmp/' + self.tmp_dir = 'tmp/' self.test_dir = self.tmp_dir + str(guid) if not os.path.exists(self.test_dir): os.makedirs(self.test_dir) @@ -44,7 +44,7 @@ class FileUtilsTests(unittest.TestCase): self.tmp_file_opened.close() if os.path.exists(self.test_dir) and os.path.isdir(self.test_dir): - shutil.rmtree(self.tmp_dir) + shutil.rmtree(self.test_dir) def testFileIsDirectory(self): """ diff --git a/snaps/thread_utils.py b/snaps/thread_utils.py new file mode 100644 index 0000000..3a3eb4d --- /dev/null +++ b/snaps/thread_utils.py @@ -0,0 +1,27 @@ +# Copyright (c) 2017 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 multiprocessing.pool import ThreadPool + +_pool = None + + +# Define a thread pool with a limit for how many simultaneous API requests +# can be in progress at once. +def worker_pool(size=5): + global _pool + if _pool is None: + _pool = ThreadPool(processes=size) + return _pool @@ -0,0 +1,17 @@ +[tox] +minversion = 1.6 +envlist = + docs, + docs-linkcheck +skipsdist = true + +[testenv:docs] +deps = -rdocs/requirements.txt +commands = + sphinx-build -b html -n -d {envtmpdir}/doctrees ./docs/ {toxinidir}/docs/_build/html + echo "Generated docs available in {toxinidir}/docs/_build/html" +whitelist_externals = echo + +[testenv:docs-linkcheck] +deps = -rdocs/requirements.txt +commands = sphinx-build -b linkcheck -d {envtmpdir}/doctrees ./docs/ {toxinidir}/docs/_build/linkcheck |