sudo pip install virtualenv
virtualenv ./vpy
source ./vpy/bin/activate
-pip install -e ../
+pip install -chttps://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/pike -e ../
# $1 is the IP to the pod's build server
# $2 is the IP to the pod's control server
Ensures that a Nova client can be obtained as well as the proper
exceptions thrown with the wrong credentials.
+cinder_utils_tests.py - CinderSmokeTests
+----------------------------------------
+
+Ensures that a Cinder client can be obtained as well as the proper
+exceptions thrown with the wrong credentials.
+
heat_utils_tests.py - HeatSmokeTests
------------------------------------
+---------------------------------------+---------------+-----------------------------------------------------------+
| Test Name | Neutron API | Description |
+=======================================+===============+===========================================================+
-| test_create_subnet | 2 | Ensures neutron_utils.create_subnet() can properly create |
+| test_create_subnet | 2 | Ensures neutron_utils.create_network() can properly create|
| | | an OpenStack subnet object |
+---------------------------------------+---------------+-----------------------------------------------------------+
-| test_create_subnet_null_name | 2 | Ensures neutron_utils.create_subnet() raises an exception |
+| test_create_subnet_null_name | 2 | Ensures neutron_utils.create_network() raises an exception|
| | | when the subnet name is None |
+---------------------------------------+---------------+-----------------------------------------------------------+
-| test_create_subnet_empty_name | 2 | Ensures neutron_utils.create_subnet() raises an exception |
+| test_create_subnet_empty_name | 2 | Ensures neutron_utils.create_network() raises an exception|
| | | when the subnet name is an empty string |
+---------------------------------------+---------------+-----------------------------------------------------------+
-| test_create_subnet_null_cidr | 2 | Ensures neutron_utils.create_subnet() raises an exception |
+| test_create_subnet_null_cidr | 2 | Ensures neutron_utils.create_network() raises an exception|
| | | when the subnet CIDR is None |
+---------------------------------------+---------------+-----------------------------------------------------------+
-| test_create_subnet_empty_cidr | 2 | Ensures neutron_utils.create_subnet() raises an exception |
+| test_create_subnet_empty_cidr | 2 | Ensures neutron_utils.create_network() raises an exception|
| | | when the subnet CIDR is an empty string |
+---------------------------------------+---------------+-----------------------------------------------------------+
+neutron_utils_tests.py - NeutronUtilsIPv6Tests
+----------------------------------------------
+
++---------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Neutron API | Description |
++=======================================+===============+===========================================================+
+| test_create_network_slaac | 2 | Ensures neutron_utils.create_network() can properly create|
+| | | an OpenStack network with an IPv6 subnet when DHCP is True|
+| | | and modes are 'slaac' |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_network_stateful | 2 | Ensures neutron_utils.create_network() can properly create|
+| | | an OpenStack network with an IPv6 subnet when DHCP is True|
+| | | and modes are 'stateful' |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_network_stateless | 2 | Ensures neutron_utils.create_network() can properly create|
+| | | an OpenStack network with an IPv6 subnet when DHCP is True|
+| | | and modes are 'stateless' |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_network_no_dhcp_slaac | 2 | Ensures neutron_utils.create_network() raises a BadRequest|
+| | | exception when deploying the network with an IPv6 subnet |
+| | | when DHCP is False and modes are 'slaac' |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_network_invalid_start_ip | 2 | Ensures neutron_utils.create_network() sets the start IP |
+| | | address to the minimum value when the start configuration |
+| | | parameter is some garbage value |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_network_invalid_end_ip | 2 | Ensures neutron_utils.create_network() sets the end IP |
+| | | address to the maximum value when the end configuration |
+| | | parameter is some garbage value |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_network_with_bad_cidr | 2 | Ensures neutron_utils.create_network() raises a BadRequest|
+| | | exception when the IPv6 CIDR is incorrect |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_network_invalid_gateway_ip| 2 | Ensures neutron_utils.create_network() raises a BadRequest|
+| | | exception when the IPv6 gateway IP does not match the CIDR|
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_network_with_bad_dns | 2 | Ensures neutron_utils.create_network() raises a BadRequest|
+| | | exception when the IPv6 DNS IP address is not a valid IPv6|
+| | | address |
++---------------------------------------+---------------+-----------------------------------------------------------+
+
neutron_utils_tests.py - NeutronUtilsRouterTests
------------------------------------------------
| face | | an OpenStack router object with an interface to the |
| | | external network |
+---------------------------------------+---------------+-----------------------------------------------------------+
-| test_create_router_empty_name | 2 | Ensures neutron_utils.create_router() raises an exception |
-| | | when the name is an empty string |
-+---------------------------------------+---------------+-----------------------------------------------------------+
-| test_create_router_null_name | 2 | Ensures neutron_utils.create_router() raises an exception |
-| | | when the name is None |
-+---------------------------------------+---------------+-----------------------------------------------------------+
| test_add_interface_router | 2 | Ensures neutron_utils.add_interface_router() properly adds|
| | | an interface to another subnet |
+---------------------------------------+---------------+-----------------------------------------------------------+
| test_add_interface_router_null_subnet | 2 | Ensures neutron_utils.add_interface_router() raises an |
| | | exception when the subnet object is None |
+---------------------------------------+---------------+-----------------------------------------------------------+
+| test_add_interface_router_missing_sub | 2 | Ensures neutron_utils.add_interface_router() raises an |
+| net | | exception when the subnet object had been deleted |
++---------------------------------------+---------------+-----------------------------------------------------------+
| test_create_port | 2 | Ensures neutron_utils.create_port() can properly create an|
| | | OpenStack port object |
+---------------------------------------+---------------+-----------------------------------------------------------+
| test_floating_ips | 2 | Ensures that a floating IP can be created |
+---------------------------------------+---------------+-----------------------------------------------------------+
+cinder_utils_tests.py - CinderUtilsQoSTests
+-------------------------------------------
+
++---------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Cinder API | Description |
++=======================================+===============+===========================================================+
+| test_create_qos_both | 2 & 3 | Ensures that a QoS Spec can be created with a Consumer |
+| | | value of 'both' |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_qos_front | 2 & 3 | Ensures that a QoS Spec can be created with a Consumer |
+| | | value of 'front-end' |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_qos_back | 2 & 3 | Ensures that a QoS Spec can be created with a Consumer |
+| | | value of 'back-end' |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_delete_qos | 2 & 3 | Ensures that a QoS Spec can be created and deleted |
++---------------------------------------+---------------+-----------------------------------------------------------+
+
+cinder_utils_tests.py - CinderUtilsSimpleVolumeTypeTests
+--------------------------------------------------------
+
++---------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Cinder API | Description |
++=======================================+===============+===========================================================+
+| test_create_simple_volume_type | 2 & 3 | Tests the creation of a simple volume type with the |
+| | | function cinder_utils#create_volume_type() |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_delete_volume_type | 2 & 3 | Tests the creation of a simple volume type with the |
+| | | function cinder_utils#create_volume_type() then deletes |
+| | | with the function cinder_utils#delete_volume_type() |
++---------------------------------------+---------------+-----------------------------------------------------------+
+
+cinder_utils_tests.py - CinderUtilsAddEncryptionTests
+-----------------------------------------------------
+
++---------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Cinder API | Description |
++=======================================+===============+===========================================================+
+| test_create_simple_encryption | 2 & 3 | Tests the creation of a simple volume type encryption |
+| | | with the function cinder_utils#create_volume_encryption() |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_delete_encryption | 2 & 3 | Tests the creation of a simple volume type encryption |
+| | | with the function cinder_utils#create_volume_encryption() |
+| | | then deletes with the function |
+| | | cinder_utils#delete_volume_type_encryption() |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_with_all_attrs | 2 & 3 | Tests the creation of a simple volume type encryption |
+| | | with the function cinder_utils#create_volume_encryption() |
+| | | where all configuration attributes have been set |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_bad_key_size | 2 & 3 | Tests to ensure that the function |
+| | | cinder_utils#create_volume_encryption() raises a |
+| | | BadRequest exception when the key_size attribute is -1 |
++---------------------------------------+---------------+-----------------------------------------------------------+
+
+cinder_utils_tests.py - CinderUtilsVolumeTypeCompleteTests
+----------------------------------------------------------
+
++---------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Cinder API | Description |
++=======================================+===============+===========================================================+
+| test_create_with_encryption | 2 & 3 | Tests the creation of a volume type with encryption |
+| | | with the function cinder_utils#create_volume_type() |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_with_qos | 2 & 3 | Tests the creation of a volume type with a QoS Spec |
+| | | with the function cinder_utils#create_volume_type() |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_with_invalid_qos | 2 & 3 | Tests the creation of a volume type with an invalid QoS |
+| | | Spec with the function cinder_utils#create_volume_type() |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_with_qos_and_encryption | 2 & 3 | Tests the creation of a volume type with a QoS Spec and |
+| | | encryption with the function |
+| | | cinder_utils#create_volume_type() |
++---------------------------------------+---------------+-----------------------------------------------------------+
+
+cinder_utils_tests.py - CinderUtilsVolumeTests
+----------------------------------------------
+
++---------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Cinder API | Description |
++=======================================+===============+===========================================================+
+| test_create_simple_volume | 2 & 3 | Tests the creation of a simple volume with the function |
+| | | cinder_utils#create_volume() |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_delete_volume | 2 & 3 | Tests the creation of a volume with the function |
+| | | cinder_utils#create_volume() then deletion with the |
+| | | function cinder_utils#delete_volume() |
++---------------------------------------+---------------+-----------------------------------------------------------+
+
nova_utils_tests.py - NovaUtilsKeypairTests
-------------------------------------------
| | | nova_utils.create_server() |
+---------------------------------------+---------------+-----------------------------------------------------------+
+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 |
++---------------------------------------+---------------+-----------------------------------------------------------+
+
create_flavor_tests.py - CreateFlavorTests
------------------------------------------
+---------------------------------------+---------------+-----------------------------------------------------------+
| Test Name | Heat API | Description |
+=======================================+===============+===========================================================+
-| test_create_stack | 1 | Tests the heat_utils.create_stack() with a test template |
+| test_create_stack | 1-3 | Tests the heat_utils.create_stack() with a test template |
+---------------------------------------+---------------+-----------------------------------------------------------+
-| test_create_stack_x2 | 1 | Tests the heat_utils.create_stack() with a test template |
+| test_create_stack_x2 | 1-3 | Tests the heat_utils.create_stack() with a test template |
| | | and attempts to deploy a second time w/o actually |
| | | deploying any objects |
+---------------------------------------+---------------+-----------------------------------------------------------+
+---------------------------------------+---------------+-----------------------------------------------------------+
| Test Name | Heat API | Description |
+=======================================+===============+===========================================================+
-| test_get_settings_from_stack | 1 | Tests the heat_utils functions that are responsible for |
+| test_get_settings_from_stack | 1-3 | Tests the heat_utils functions that are responsible for |
| | | reverse engineering settings objects of the types deployed|
| | | by Heat |
+---------------------------------------+---------------+-----------------------------------------------------------+
+heat_utils_tests.py - HeatUtilsRouterTests
+------------------------------------------
+
++---------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Heat API | Description |
++=======================================+===============+===========================================================+
+| test_create_router_with_stack | 1-3 | Tests ability of the function |
+| | | heat_utils.get_stack_routers() to return the correct |
+| | | OpenStackRouter instance |
++---------------------------------------+---------------+-----------------------------------------------------------+
+
+heat_utils_tests.py - HeatUtilsVolumeTests
+------------------------------------------
+
++---------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Heat API | Description |
++=======================================+===============+===========================================================+
+| test_create_vol_with_stack | 1-3 | Tests ability of the function |
+| | | heat_utils.create_stack() to return the correct |
+| | | Volume domain objects deployed with Heat |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_vol_types_with_stack | 1-3 | Tests ability of the function |
+| | | heat_utils.get_stack_volumes_types() to return the correct|
+| | | VolumeType domain objects deployed with Heat |
++---------------------------------------+---------------+-----------------------------------------------------------+
+
+heat_utils_tests.py - HeatUtilsKeypairTests
+-------------------------------------------
+
++---------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Heat API | Description |
++=======================================+===============+===========================================================+
+| test_create_keypair_with_stack | 1-3 | Tests ability of the function |
+| | | heat_utils.get_stack_keypairs() to return the correct |
+| | | Keypair domain objects deployed with Heat |
++---------------------------------------+---------------+-----------------------------------------------------------+
+
+heat_utils_tests.py - HeatUtilsSecurityGroupTests
+-------------------------------------------------
+
++---------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Heat API | Description |
++=======================================+===============+===========================================================+
+| test_create_security_group_with_stack | 1-3 | Tests ability of the function |
+| | | heat_utils.get_stack_security_groups() to return the |
+| | | correct SecurityGroup domain objects deployed with Heat |
++---------------------------------------+---------------+-----------------------------------------------------------+
+
+heat_utils_tests.py - HeatUtilsFlavorTests
+------------------------------------------
+
++---------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Heat API | Description |
++=======================================+===============+===========================================================+
+| test_create_flavor_with_stack | 1-3 | Tests ability of the function |
+| | | heat_utils.get_stack_flavors() to return the correct |
+| | | Flavor domain objects deployed with Heat |
++---------------------------------------+---------------+-----------------------------------------------------------+
+
+magnum_utils_tests.py - MagnumUtilsTests
+----------------------------------------
+
++---------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Magnum API | Description |
++=======================================+===============+===========================================================+
+| test_create_cluster_template_simple | 1 | Tests ability of the function |
+| | | magnum_utils.create_cluster_template() to create a simple |
+| | | cluster template OpenStack object with minimal config |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_cluster_template_all | 1 | Tests ability of the function |
+| | | magnum_utils.create_cluster_template() to create a |
+| | | cluster template OpenStack object with maximum config |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_cluster_template_bad_image| 1 | Ensures the function |
+| | | magnum_utils.create_cluster_template() will raise a |
+| | | BadRequest exception when the image does not exist |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_cluster_template_bad_ext | 1 | Ensures the function |
+| _net | | magnum_utils.create_cluster_template() will raise a |
+| | | BadRequest exception when the external network does not |
+| | | exist |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_cluster_template_bad | 1 | Ensures the function |
+| _flavor | | magnum_utils.create_cluster_template() will raise a |
+| | | BadRequest exception when the flavor does not exist |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_cluster_template_bad | 1 | Ensures the function |
+| _master_flavor | | magnum_utils.create_cluster_template() will raise a |
+| | | BadRequest exception when the master flavor does not exist|
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_cluster_template_bad | 1 | Ensures the function |
+| _network_driver | | magnum_utils.create_cluster_template() will raise a |
+| | | BadRequest exception when the network driver is invalid |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_cluster_template_bad | 1 | Ensures the function |
+| _volume_driver | | magnum_utils.create_cluster_template() will raise a |
+| | | BadRequest exception when the volume driver is invalid |
++---------------------------------------+---------------+-----------------------------------------------------------+
+
settings_utils_tests.py - SettingsUtilsNetworkingTests
------------------------------------------------------
+---------------------------------------+---------------+-----------------------------------------------------------+
| Test Name | API | Description |
+=======================================+===============+===========================================================+
-| test_derive_net_settings_no_subnet | Neutron 2 | Tests to ensure that derived NetworkSettings from an |
+| test_derive_net_settings_no_subnet | Neutron 2 | Tests to ensure that derived NetworkConfig from an |
| | | OpenStack network are correct without a subnet |
+---------------------------------------+---------------+-----------------------------------------------------------+
-| test_derive_net_settings_two_subnets | Neutron 2 | Tests to ensure that derived NetworkSettings from an |
+| test_derive_net_settings_two_subnets | Neutron 2 | Tests to ensure that derived NetworkConfig from an |
| | | OpenStack network are correct with two subnets |
+---------------------------------------+---------------+-----------------------------------------------------------+
+---------------------------------------+---------------+-----------------------------------------------------------+
| Test Name | API | Description |
+=======================================+===============+===========================================================+
-| test_derive_vm_inst_settings | Neutron 2 | Tests to ensure that derived VmInstanceSettings from an |
+| test_derive_vm_inst_config | Neutron 2 | Tests to ensure that derived VmInstanceSettings from an |
| | | OpenStack VM instance is correct |
+---------------------------------------+---------------+-----------------------------------------------------------+
-| test_derive_image_settings | Neutron 2 | Tests to ensure that derived ImageSettings from an |
+| test_derive_image_settings | Neutron 2 | Tests to ensure that derived ImageConfig from an |
| | | OpenStack VM instance is correct |
+---------------------------------------+---------------+-----------------------------------------------------------+
sudo pip install -e <path to repo>/snaps/
(note: on CentOS 7 and Ubuntu 14.04 you may have to try the previous command several times)
+SNAPS is now hosted on the Python Package Manager (PyPI).
+
+::
+
+ pip install snaps
+
+This will install the stable Euphrates version.
+
The install should now be complete and you can start using the SNAPS-OO libraries.
| Test Name | Glance API | Description |
+=======================================+===============+===========================================================+
| test_bad_image_name | 1 & 2 | Ensures OpenStackImage.create() results in an Exception |
-| | | being raised when the ImageSettings.name attribute has |
+| | | being raised when the ImageConfig.name attribute has |
| | | not been set |
+---------------------------------------+---------------+-----------------------------------------------------------+
| test_bad_image_url | 1 & 2 | Ensures OpenStackImage.create() results in an Exception |
| | | 'admin' project ID |
+---------------------------------------+---------------+-----------------------------------------------------------+
+create_network_tests.py - CreateNetworkIPv6Tests
+------------------------------------------------
+
++---------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Neutron API | Description |
++=======================================+===============+===========================================================+
+| test_create_network_one_ipv6_subnet | 2 | Ensures that a network can be created with an IPv6 subnet |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_network_ipv4_ipv6_subnet | 2 | Ensures that a network can be created with an IPv4 and |
+| | | IPv6 subnet |
++---------------------------------------+---------------+-----------------------------------------------------------+
+
create_router_tests.py - CreateRouterSuccessTests
-------------------------------------------------
| | | create a router to an external network that does not exist|
+----------------------------------------+---------------+-----------------------------------------------------------+
+create_qos_tests.py - CreateQoSTests
+------------------------------------
+
++----------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Cinder API | Description |
++========================================+===============+===========================================================+
+| test_create_qos | 2 & 3 | Tests the creation of a QoS Spec with the class |
+| | | OpenStackQoS |
++----------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_delete_qos | 2 & 3 | Tests the creation of a QoS Spec with the class |
+| | | OpenStackQoS, its deletion with cinder_utils.py the |
+| | | the attempts to use the clean() method to ensure an |
+| | | exception is not called |
++----------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_same_qos | 2 & 3 | Tests the creation of a QoS Spec with the class |
+| | | OpenStackQoS then instantiates another OpenStackQoS |
+| | | object with the same configuration to ensure the second |
+| | | instance returns the ID of the original |
++----------------------------------------+---------------+-----------------------------------------------------------+
+
+create_volume_type_tests.py - CreateSimpleVolumeTypeSuccessTests
+----------------------------------------------------------------
+
++----------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Cinder API | Description |
++========================================+===============+===========================================================+
+| test_create_volume_type | 2 & 3 | Tests the creation of a Volume Type with the class |
+| | | OpenStackVolumeType |
++----------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_delete_volume_type | 2 & 3 | Tests the creation of a Volume Type with the class |
+| | | OpenStackVolumeType, its deletion with cinder_utils.py, |
+| | | then attempts to use the clean() method to ensure an |
+| | | exception is not raised |
++----------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_same_volume_type | 2 & 3 | Tests the creation of a Volume Type with the class |
+| | | OpenStackVolumeType then instantiates another |
+| | | OpenStackVolumeType object with the same configuration to |
+| | | ensure the second instance returns the ID of the original |
++----------------------------------------+---------------+-----------------------------------------------------------+
+
+create_volume_type_tests.py - CreateSimpleVolumeTypeComplexTests
+----------------------------------------------------------------
+
++-----------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Cinder API | Description |
++=========================================+===============+===========================================================+
+| test_volume_type_with_qos | 2 & 3 | Tests the creation of a Volume Type with the class |
+| | | OpenStackVolumeType with a QoSSpec |
++-----------------------------------------+---------------+-----------------------------------------------------------+
+| test_volume_type_with_encryption | 2 & 3 | Tests the creation of a Volume Type with the class |
+| | | OpenStackVolumeType with encryption |
++-----------------------------------------+---------------+-----------------------------------------------------------+
+| test_volume_type_with_qos_and_encryption| 2 & 3 | Tests the creation of a Volume Type with the class |
+| | | OpenStackVolumeType with encryption and QoS Spec |
++-----------------------------------------+---------------+-----------------------------------------------------------+
+
+create_volume_tests.py - CreateSimpleVolumeSuccessTests
+-------------------------------------------------------
+
++----------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Cinder API | Description |
++========================================+===============+===========================================================+
+| test_create_volume_simple | 2 & 3 | Tests the creation of a Volume Type with the class |
+| | | OpenStackVolume |
++----------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_delete_volume | 2 & 3 | Tests the creation of a Volume with the class |
+| | | OpenStackVolume, its deletion with cinder_utils.py, then |
+| | | attempts to use the clean() method to ensure an |
+| | | exception is not raised |
++----------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_same_volume | 2 & 3 | Tests the creation of a Volume with the class |
+| | | OpenStackVolume then instantiates another |
+| | | OpenStackVolume object with the same configuration to |
+| | | ensure the second instance returns the ID of the original |
++----------------------------------------+---------------+-----------------------------------------------------------+
+
+create_volume_tests.py - CreateSimpleVolumeFailureTests
+-------------------------------------------------------
+
++----------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Cinder API | Description |
++========================================+===============+===========================================================+
+| test_create_volume_bad_size | 2 & 3 | Tests to ensure that attempting to create a volume with a |
+| | | size of -1 raises a BadRequest exception |
++----------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_volume_bad_type | 2 & 3 | Tests to ensure that attempting to create a volume with a |
+| | | type that does not exist raises a NotFound exception |
++----------------------------------------+---------------+-----------------------------------------------------------+
+| 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
+--------------------------------------------------
+
++----------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Cinder API | Description |
++========================================+===============+===========================================================+
+| test_bad_volume_type | 2 & 3 | Tests to ensure the creation of a Volume with the |
+| | | OpenStackVolume#create() method raises a NotFound |
+| | | exception when attempting to apply a VolumeType that does |
+| | | not exist |
++----------------------------------------+---------------+-----------------------------------------------------------+
+| test_valid_volume_type | 2 & 3 | Tests to ensure the creation of a Volume with the |
+| | | OpenStackVolume#create() method properly creates the |
+| | | volume when associating with a valid VolumeType |
++----------------------------------------+---------------+-----------------------------------------------------------+
+
+create_volume_tests.py - CreateVolumeWithImageTests
+---------------------------------------------------
+
++----------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Cinder API | Description |
++========================================+===============+===========================================================+
+| test_bad_image_name | 2 & 3 | Tests to ensure the creation of a Volume with the |
+| | | OpenStackVolume#create() method raises a BadRequest |
+| | | exception when attempting to apply an image that does not |
+| | | exist |
++----------------------------------------+---------------+-----------------------------------------------------------+
+| test_valid_volume_image | 2 & 3 | Tests to ensure the creation of a Volume with the |
+| | | OpenStackVolume#create() method properly creates the |
+| | | volume when associating with a valid image |
++----------------------------------------+---------------+-----------------------------------------------------------+
+
create_stack_tests.py - CreateStackSuccessTests
-----------------------------------------------
+---------------------------------------+---------------+-----------------------------------------------------------+
-| Test Name | Neutron API | Description |
+| Test Name | Heat API | Description |
+=======================================+===============+===========================================================+
-| test_create_stack_template_file | 2 | Ensures that a Heat stack can be created with a file-based|
+| test_create_stack_template_file | 1-3 | Ensures that a Heat stack can be created with a file-based|
| | | Heat template file |
+---------------------------------------+---------------+-----------------------------------------------------------+
-| test_create_stack_template_dict | 2 | Ensures that a Heat stack can be created with a dictionary|
+| test_create_stack_template_dict | 1-3 | Ensures that a Heat stack can be created with a dictionary|
| | | Heat template |
+---------------------------------------+---------------+-----------------------------------------------------------+
-| test_create_delete_stack | 2 | Ensures that a Heat stack can be created and deleted |
+| test_create_delete_stack | 1-3 | Ensures that a Heat stack can be created and deleted |
| | | while having clean() called 2x without an exception |
+---------------------------------------+---------------+-----------------------------------------------------------+
-| test_create_same_stack | 2 | Ensures that a Heat stack with the same name cannot be |
+| test_create_same_stack | 1-3 | Ensures that a Heat stack with the same name cannot be |
| | | created 2x |
+---------------------------------------+---------------+-----------------------------------------------------------+
-| test_retrieve_network_creators | 2 | Ensures that an OpenStackHeatStack instance can return an |
+| test_retrieve_network_creators | 1-3 | Ensures that an OpenStackHeatStack instance can return an |
| | | OpenStackNetwork instance configured as deployed |
+---------------------------------------+---------------+-----------------------------------------------------------+
-| test_retrieve_vm_inst_creators | 2 | Ensures that an OpenStackHeatStack instance can return an |
+| test_retrieve_vm_inst_creators | 1-3 | Ensures that an OpenStackHeatStack instance can return an |
| | | OpenStackVmInstance instance configured as deployed |
+---------------------------------------+---------------+-----------------------------------------------------------+
+create_stack_tests.py - CreateStackVolumeTests
+----------------------------------------------
+
++---------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Heat API | Description |
++=======================================+===============+===========================================================+
+| test_retrieve_volume_creator | 1-3 | Ensures that an OpenStackHeatStack instance can return a |
+| | | OpenStackVolume instance that it was responsible for |
+| | | deploying |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_retrieve_volume_type_creator | 1-3 | Ensures that an OpenStackHeatStack instance can return a |
+| | | OpenStackVolumeType instance that it was responsible for |
+| | | deploying |
++---------------------------------------+---------------+-----------------------------------------------------------+
+
+create_stack_tests.py - CreateStackFloatingIpTests
+--------------------------------------------------
+
++---------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Heat API | Description |
++=======================================+===============+===========================================================+
+| test_connect_via_ssh_heat_vm | 1 | Ensures that an OpenStackHeatStack instance can create a |
+| | | VM with a floating IP that can be accessed via |
+| | | OpenStackVmInstance |
++---------------------------------------+---------------+-----------------------------------------------------------+
+
+create_stack_tests.py - CreateStackNestedResourceTests
+------------------------------------------------------
+
++---------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Heat API | Description |
++=======================================+===============+===========================================================+
+| test_nested | 1 | Ensures that an OpenStackHeatStack with an external |
+| | | resource file with VMs with floating IPs can be accessed |
+| | | in the class OpenStackVmInstance and return the associated|
+| | | initialized OpenStackVmInstance objects |
++---------------------------------------+---------------+-----------------------------------------------------------+
+
+create_stack_tests.py - CreateStackRouterTests
+----------------------------------------------
+
++---------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Heat API | Description |
++=======================================+===============+===========================================================+
+| test_retrieve_router_creator | 1 | Ensures that an OpenStackHeatStack instance can return a |
+| | | OpenStackRouter instance that it was responsible for |
+| | | deploying |
++---------------------------------------+---------------+-----------------------------------------------------------+
+
+create_stack_tests.py - CreateStackFlavorTests
+----------------------------------------------
+
++---------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Heat API | Description |
++=======================================+===============+===========================================================+
+| test_retrieve_flavor_creator | 1-3 | Ensures that an OpenStackHeatStack instance can return a |
+| | | OpenStackFlavor instance that it was responsible for |
+| | | deploying |
++---------------------------------------+---------------+-----------------------------------------------------------+
+
+create_stack_tests.py - CreateStackKeypairTests
+-----------------------------------------------
+
++---------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Heat API | Description |
++=======================================+===============+===========================================================+
+| test_retrieve_keypair_creator | 1-3 | Ensures that an OpenStackHeatStack instance can return a |
+| | | OpenStackKeypair instance that it was responsible for |
+| | | deploying |
++---------------------------------------+---------------+-----------------------------------------------------------+
+
+create_stack_tests.py - CreateStackSecurityGroupTests
+-----------------------------------------------------
+
++---------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Heat API | Description |
++=======================================+===============+===========================================================+
+| test_retrieve_security_group_creator | 1-3 | Ensures that an OpenStackHeatStack instance can return a |
+| | | OpenStackSecurityGroup instance that it was responsible |
+| | | for deploying |
++---------------------------------------+---------------+-----------------------------------------------------------+
+
create_stack_tests.py - CreateComplexStackTests
-----------------------------------------------
+---------------------------------------+---------------+-----------------------------------------------------------+
-| Test Name | Neutron API | Description |
+| Test Name | Heat API | Description |
+=======================================+===============+===========================================================+
-| test_connect_via_ssh_heat_vm | 2 | Ensures that two OpenStackHeatStack instances can return |
+| test_connect_via_ssh_heat_vm | 1-3 | Ensures that two OpenStackHeatStack instances can return |
| | | OpenStackVmInstance instances one configured with a |
| | | floating IP and keypair and can be access via SSH |
+---------------------------------------+---------------+-----------------------------------------------------------+
------------------------------------------------
+----------------------------------------+---------------+-----------------------------------------------------------+
-| Test Name | Neutron API | Description |
+| Test Name | Heat API | Description |
+========================================+===============+===========================================================+
-| test_missing_dependencies | 2 | Ensures that a Heat template fails to deploy when expected|
+| test_missing_dependencies | 1-3 | Ensures that a Heat template fails to deploy when expected|
| | | dependencies are missing |
+----------------------------------------+---------------+-----------------------------------------------------------+
-| test_bad_stack_file | 2 | Ensures that a Heat template fails to deploy when the Heat|
+| test_bad_stack_file | 1-3 | Ensures that a Heat template fails to deploy when the Heat|
| | | template file does not exist |
+----------------------------------------+---------------+-----------------------------------------------------------+
+create_stack_tests.py - CreateStackFailureTests
+-----------------------------------------------
+
++----------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Heat API | Description |
++========================================+===============+===========================================================+
+| test_stack_failure | 1-3 | Ensures that a Heat template fails to deploy when expected|
+| | | dependencies are missing |
++----------------------------------------+---------------+-----------------------------------------------------------+
+
create_instance_tests.py - CreateInstanceSimpleTests
----------------------------------------------------
| test_ssh_client_fip_after_active | Nova 2 | Ensures that an instance can be reached over SSH when the |
| | Neutron 2 | floating IP is assigned after to the VM becoming ACTIVE |
+---------------------------------------+---------------+-----------------------------------------------------------+
+| test_ssh_client_fip_after_init | Nova 2 | Ensures that an instance can have a floating IP assigned |
+| | Neutron 2 | added after initialization |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_ssh_client_fip_reverse_engineer | Nova 2 | Ensures that an instance can be reverse engineered and |
+| | Neutron 2 | allows for a floating IP to be added after initialization |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_ssh_client_fip_after_reboot | Nova 2 | Ensures that an instance can be reached over SSH after |
+| | Neutron 2 | a reboot call has been issued |
++---------------------------------------+---------------+-----------------------------------------------------------+
| test_ssh_client_fip_second_creator | Nova 2 | Ensures that an instance can be reached over SSH via a |
| | Neutron 2 | second identical creator object |
+---------------------------------------+---------------+-----------------------------------------------------------+
| | Neutron 2 | delete it when using a 3-part image |
+-----------------------------------------------------+---------------+-----------------------------------------------------------+
-create_instance_tests.py - CreateInstancePubPrivNetTests
---------------------------------------------------------
+create_instance_tests.py - CreateInstanceIPv6NetworkTests (Staging)
+-------------------------------------------------------------------
+---------------------------------------+---------------+-----------------------------------------------------------+
| Test Name | API Versions | Description |
+=======================================+===============+===========================================================+
-| test_dual_ports_dhcp | Nova 2 | Ensures that a VM with two ports/NICs can have its second |
-| | Neutron 2 | NIC configured via SSH/Ansible after startup |
+| test_v4fip_v6overlay | Nova 2 | Expects a BadRequest exception to be raised when |
+| | Neutron 2 | attempting to add an IPv4 floating IP to a VM with an IPv6|
+| | | port |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_fip_v4and6_overlay | Nova 2 | Connects to a VM via a floating IP joined to a port that |
+| | Neutron 2 | has been confiured with both IPv4 and IPv6 addresses |
+---------------------------------------+---------------+-----------------------------------------------------------+
create_instance_tests.py - InstanceSecurityGroupTests
| | Neutron 2 | that has already been added to the instance |
+---------------------------------------+---------------+-----------------------------------------------------------+
+create_instance_tests.py - CreateInstanceVolumeTests
+----------------------------------------------------
+
++---------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | API Versions | Description |
++=======================================+===============+===========================================================+
+| test_create_instance_with_one_volume | Nova 2 | Ensures that a VM instance can have one volume attached |
+| | Cinder 2 & 3 | to it |
++---------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_instance_with_two_volumes | Nova 2 | Ensures that a VM instance can have two volumes attached |
+| | Cinder 2 & 3 | to it |
++---------------------------------------+---------------+-----------------------------------------------------------+
+
ansible_utils_tests.py - AnsibleProvisioningTests
-------------------------------------------------
| | Neutron 2 | apply a Ansible playbook containing Jinga2 substitution |
| | | values |
+---------------------------------------+---------------+-----------------------------------------------------------+
+
+cluster_template_tests.py - CreateClusterTemplateTests
+------------------------------------------------------
+
++----------------------------------------+---------------+-----------------------------------------------------------+
+| Test Name | Magnum API | Description |
++========================================+===============+===========================================================+
+| test_create_cluster_template | 1 | Tests the creation of a Cluster template with the class |
+| | | OpenStackClusterTemplate |
++----------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_delete_cluster_template | 1 | Tests the creation and deletiong of a Cluster template |
+| | | with the class OpenStackClusterTemplate |
++----------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_same_cluster_template | 1 | Tests the creation of a Cluster template 2x using the same|
+| | | config object to ensure it was only created once |
++----------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_cluster_template_bad_flavor| 1 | Tests to ensure OpenStackClusterTemplate#create() will |
+| | | raise an exception when the flavor is invalid |
++----------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_cluster_template_bad_master| 1 | Tests to ensure OpenStackClusterTemplate#create() will |
+| _flavor | | raise an exception when the master flavor is invalid |
++----------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_cluster_template_bad_image | 1 | Tests to ensure OpenStackClusterTemplate#create() will |
+| | | raise an exception when the image is invalid |
++----------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_cluster_template_bad | 1 | Tests to ensure OpenStackClusterTemplate#create() will |
+| _network_driver | | raise an exception when the network driver is invalid |
++----------------------------------------+---------------+-----------------------------------------------------------+
+| test_create_cluster_template_bad | 1 | Tests to ensure OpenStackClusterTemplate#create() will |
+| _volume_driver | | raise an exception when the volume driver is invalid |
++----------------------------------------+---------------+-----------------------------------------------------------+
+
- auth\_url
- project\_name (aka. tenant\_name)
- identity\_api\_version (for obtaining Keystone authorization token.
- Versions 2.0 & v3 only validated.)
-- image\_api\_version (Glance version 1 & 2 only validated)
+ default = 2, Versions 2.0 & v3 only validated.)
+- image\_api\_version (default = 2, Glance version 1 & 2 only validated)
- network\_api\_version (Neutron version 2 currently only validated)
- compute\_api\_version (Nova version 2 currently only validated)
- heat\_api\_version (Heat version 1 currently only validated)
+- volume\_api\_version (default = 2, Heat versions 2 & 3 currently only validated)
- user\_domain\_id (default='default')
-- user\_domain\_name (default='default')
+- user\_domain\_name (default='Default')
- project\_domain\_id (default='default')
-- project\_domain\_name (default='default')
+- project\_domain\_name (default='Default')
- interface (default='admin', used to specify the endpoint type for keystone: public, admin, internal)
- cacert (default=False, expected values T|F to denote server certificate verification, else value contains the path to an HTTPS certificate)
- region_name (The region name default=None)
-----------
- User - snaps.openstack.create\_user.OpenStackUser
- - snaps.openstack.create\_user.UserSettings
+ - snaps.openstack.user.UserConfig
- name - the username (required)
- password - the user's password (required)
- email - the user's email address (optional)
- enabled - flag to determine whether or not the user should be
enabled (default=True)
+ - roles - dict where key is the role's name and value is the name
+ the project to associate with the role (optional)
.. code:: python
- from snaps.openstack.create_user import UserSettings, OpenStackUser
- user_settings = UserSettings(name='username', password='password')
+ from snaps.config.user import UserConfig
+ from snaps.openstack.create_user import OpenStackUser
+ user_settings = UserConfig(name='username', password='password')
user_creator = OpenStackUser(os_creds, user_settings)
user_creator.create()
--------------
- Project - snaps.openstack.create\_project.OpenStackProject
- - snaps.openstack.create\_project.ProjectSettings
+ - snaps.openstack.project.ProjectConfig
- name - the project name (required)
- domain - the project's domain (default='default')
- description - the project's description (optional)
- - enables - flag to determine whether or not the project should
+ - enabled - flag to determine whether or not the project should
be enabled (default=True)
.. code:: python
- from snaps.openstack.create_project import ProjectSettings, OpenStackProject
- project_settings = ProjectSettings(name='username', password='password')
+ from snaps.openstack.project import ProjectConfig
+ from snaps.openstack.create_project import OpenStackProject
+ project_settings = ProjectConfig(name='username', password='password')
project_creator = OpenStackProject(os_creds, project_settings)
project_creator.create()
-------------
- Flavor - snaps.openstack.create\_flavor.OpenStackFlavor
- - snaps.openstack.create\_flavor.FlavorSettings
+ - snaps.config.flavor.FlavorConfig
- name - the flavor name (required)
- flavor\_id - the flavor's string ID (default='auto')
if backend supports QoS extension (default=1.0)
- is\_public - flag that denotes whether or not other projects
can access image (default=True)
+ - metadata - freeform dict() for special metadata (optional)
.. code:: python
- from snaps.openstack.create_flavor import FlavorSettings, OpenStackFlavor
- flavor_settings = FlavorSettings(name='flavor-name', ram=4, disk=10, vcpus=2)
+ from snaps.config.flavor import FlavorConfig
+ from snaps.openstack.create_flavor import OpenStackFlavor
+ flavor_settings = FlavorConfig(name='flavor-name', ram=4, disk=10, vcpus=2)
flavor_creator = OpenStackFlavor(os_creds, flavor_settings)
flavor_creator.create()
------------
- Image - snaps.openstack.create\_image.OpenStackImage
- - snaps.openstack.create\_image.ImageSettings
+ - snaps.config.image.ImageConfig
- name - the image name (required)
- image\_user - the default image user generally used by
OpenStackVMInstance class for obtaining an SSH connection
(required)
- - img\_format - the image's format (i.e. qcow2) (required)
+ - img\_format or format - the image's format (i.e. qcow2) (required)
- url - the download URL to obtain the image file (this or
image\_file must be configured, not both)
- image\_file - the location of the file to be sourced from the
local filesystem (this or url must be configured, not both)
+ - extra\_properties - dict() object containing extra parameters to
+ pass when loading the image (i.e. ids of kernel and initramfs images)
- nic\_config\_pb\_loc - the location of the ansible playbook
that can configure additional NICs. Floating IPs are required
- to perform this operation. (optional)
+ to perform this operation. (optional and deprecated)
+ - kernel\_image\_settings - the image settings for a kernel image (optional)
+ - ramdisk\_image\_settings - the image settings for a ramdisk image (optional)
+ - public - image will be created with public visibility when True (default = False)
.. code:: python
- from snaps.openstack.create_image import ImageSettings, OpenStackImage
- image_settings = ImageSettings(name='image-name', image_user='ubuntu', img_format='qcow2',
- url='http://uec-images.ubuntu.com/releases/trusty/14.04/ubuntu-14.04-server-cloudimg-amd64-disk1.img')
+ from snaps.openstack.create_image import OpenStackImage
+ from snaps.config.image import ImageConfig
+ image_settings = ImageConfig(name='image-name', image_user='ubuntu', img_format='qcow2',
+ url='http://uec-images.ubuntu.com/releases/trusty/14.04/ubuntu-14.04-server-cloudimg-amd64-disk1.img')
image_creator = OpenStackImage(os_creds, image_settings)
image_creator.create()
--------------
- Keypair - snaps.openstack.create\_keypair.OpenStackKeypair
- - snaps.openstack.create\_keypair.KeypairSettings
+ - snaps.openstack.keypair.KeypairConfig
- name - the keypair name (required)
- public\_filepath - the file location to where the public key is
file is to be written or currently resides (optional but highly
recommended to leverage or the private key will be lost
forever)
+ - key\_size - The number of bytes for the key size when it needs to
+ be generated (value must be >=512, default = 1024)
+ - delete\_on\_clean - when True, the key files will be deleted when
+ OpenStackKeypair#clean() is called (default = False)
.. code:: python
- from snaps.openstack.create_keypairs import KeypairSettings, OpenStackKeypair
- keypair_settings = KeypairSettings(name='kepair-name', private_filepath='/tmp/priv-kp')
+ from snaps.openstack.keypair.KeypairConfig
+ from snaps.openstack.create_keypairs import OpenStackKeypair
+ keypair_settings = KeypairConfig(name='kepair-name', private_filepath='/tmp/priv-kp')
keypair_creator = OpenStackKeypair(os_creds, keypair_settings)
keypair_creator.create()
- Network - snaps.openstack.create\_network.OpenStackNetwork
- - snaps.openstack.create\_network.NetworkSettings
+ - snaps.config_network.NetworkConfig
- name - the name of the network (required)
- admin\_state\_up - flag denoting the administrative status of
- network\_type - the type of network (i.e. vlan\|vxlan\|flat)
- physical\_network - the name of the physical network (required
when network\_type is 'flat')
+ - segmentation\_id - the id of the segmentation (required
+ when network\_type is 'vlan')
- subnet\_settings (list of optional
- snaps.openstack.create\_network.SubnetSettings objects)
+ snaps.config.network.SubnetConfig objects)
- cidr - the subnet's CIDR (required)
- ip\_version - 4 or 6 (default=4)
.. code:: python
- from snaps.openstack.create_network import NetworkSettings, SubnetSettings, OpenStackNetwork
+ from snaps.config.network import NetworkConfig, SubnetConfig
+ from snaps.openstack.create_network import OpenStackNetwork
- subnet_settings = SubnetSettings(name='subnet-name', cidr='10.0.0.0/24')
- network_settings = NetworkSettings(name='network-name', subnet_settings=[subnet_settings])
+ subnet_settings = SubnetConfig(name='subnet-name', cidr='10.0.0.0/24')
+ network_settings = NetworkConfig(name='network-name', subnet_settings=[subnet_settings])
network_creator = OpenStackNetwork(os_creds, network_settings)
network_creator.create()
.. code:: python
+ from snaps.config.network import SubnetConfig
+ from snaps.config.rule import RuleConfig
from snaps.openstack.create_security_group import SecurityGroupSettings, SecurityGroupRuleSettings, Direction, OpenStackSecurityGroup
- rule_settings = SubnetSettings(name='subnet-name', cidr='10.0.0.0/24')
- network_settings = NetworkSettings(name='network-name', subnet_settings=[subnet_settings])
+ rule_settings = RuleConfig(name='subnet-name', cidr='10.0.0.0/24')
+ network_settings = SubnetConfig(name='network-name', subnet_settings=[subnet_settings])
sec_grp_name = 'sec-grp-name'
rule_settings = SecurityGroupRuleSettings(name=sec_grp_name, direction=Direction.ingress)
- Router - snaps.openstack.create\_router.OpenStackRouter
- - snaps.openstack.create\_router.RouterSettings
+ - snaps.openstack.router.RouterConfig
- name - the router name (required)
- project\_name - the name of the project (optional - can only be
- internal\_subnets - list of subnet names to which this router
will connect (optional)
- port\_settings (list of optional
- snaps.openstack.create\_router.PortSettings objects) - creates
+ snaps.config.network.PortConfig objects) - creates
custom ports to internal subnets (similar to internal\_subnets
with more control)
- - name
- - network\_name
- - admin\_state\_up
+ - name - the port's display name
+ - network\_name - the name of the network on which to create the port
+ - admin\_state\_up - A boolean value denoting the administrative
+ status of the port (default = True)
- project\_name - the name of the project (optional - can only
be set by admin users)
- - mac\_address
- - ip\_addrs
- - fixed\_ips
- - security\_groups
- - allowed\_address\_pairs
- - opt\_value
- - opt\_name
- - device\_owner
- - device\_id
+ - mac\_address - the port's MAC address to set (optional and
+ recommended not to set this configuration value)
+ - ip\_addrs - list of dict() objects containing two keys 'subnet_name'
+ and 'ip' where the value of the 'ip' entry is the expected IP
+ address assigned. This value gets mapped to the fixed\_ips
+ attribute (optional)
+ - fixed\_ips - dict() where the key is the subnet ID and value is the
+ associated IP address to assign to the port (optional)
+ - security\_groups - list of security group IDs (not tested)
+ - allowed\_address\_pairs - A dictionary containing a set of zero or
+ more allowed address pairs. An address pair contains an IP address
+ and MAC address (optional)
+ - opt\_value - the extra DHCP option value (optional)
+ - opt\_name - the extra DHCP option name (optional)
+ - device\_owner - The ID of the entity that uses this port.
+ For example, a DHCP agent (optional)
+ - device\_id - The ID of the device that uses this port.
+ For example, a virtual server (optional)
.. code:: python
- from snaps.openstack.create_router import RouterSettings, OpenStackRouter
+ from snaps.config.router import RouterConfig
+ from snaps.openstack.create_router import OpenStackRouter
- router_settings = RouterSettings(name='router-name', external_gateway='external')
+ router_settings = RouterConfig(name='router-name', external_gateway='external')
router_creator = OpenStackRouter(os_creds, router_settings)
router_creator.create()
# Cleanup
router_creator.clean()
+Create QoS Spec
+---------------
+
+- Volume Type - snaps.openstack.create\_qos.OpenStackQoS
+
+ - snaps.openstack.qos.QoSConfig
+
+ - name - the volume type's name (required)
+ - consumer - the qos's consumer type of the enum type Consumer (required)
+ - specs - freeform dict() to be added as 'specs' (optional)
+
+.. code:: python
+
+ from snaps.openstack.qos import QoSConfig
+ from snaps.openstack.create_qos import OpenStackQoS
+
+ qos_settings = QoSConfig(name='stack-name', consumer=Consumer.front-end)
+ qos_creator = OpenStackQoS(os_creds, vol_type_settings)
+ qos_creator.create()
+
+ # Perform logic
+ ...
+
+ # Cleanup
+ qos_creator.clean()
+
+Create Volume Type
+------------------
+
+- Volume Type - snaps.openstack.create\_volume\_type.OpenStackVolumeType
+
+ - snaps.config.volume\_type.VolumeTypeConfig
+
+ - name - the volume type's name (required)
+ - description - the volume type's description (optional)
+ - encryption - instance or config for VolumeTypeEncryptionConfig (optional)
+ - qos\_spec\_name - name of the QoS Spec to associate (optional)
+ - public - instance or config for VolumeTypeEncryptionConfig (optional)
+
+.. code:: python
+
+ from snaps.config.volume_type import VolumeTypeConfig
+ from snaps.openstack.create_volume_type import OpenStackVolumeType
+
+ vol_type_settings = VolumeTypeConfig(name='stack-name')
+ vol_type_creator = OpenStackHeatStack(os_creds, vol_type_settings)
+ vol_type_creator.create()
+
+ # Perform logic
+ ...
+
+ # Cleanup
+ vol_type_creator.clean()
+
+Create Volume
+-------------
+
+- Volume - snaps.openstack.create\_volume.OpenStackVolume
+
+ - snaps.config.volume.VolumeConfig
+
+ - name - the volume type's name (required)
+ - description - the volume type's description (optional)
+ - size - size of volume in GB (default = 1)
+ - image_name - when a glance image is used for the image source (optional)
+ - type\_name - the associated volume's type name (optional)
+ - availability\_zone - the name of the compute server on which to
+ deploy the volume (optional)
+ - multi_attach - when true, volume can be attached to more than one
+ server (default = False)
+
+.. code:: python
+
+ from snaps.config.volume import VolumeConfig
+ from snaps.openstack.create\_volume import OpenStackVolume
+
+ vol_settings = VolumeConfig(name='stack-name')
+ vol_creator = OpenStackVolume(os_creds, vol_settings)
+ vol_creator.create()
+
+ # Perform logic
+ ...
+
+ # Cleanup
+ vol_type_creator.clean()
+
+Create Heat Stack
+-----------------
+
+- Heat Stack - snaps.openstack.create\_stack.OpenStackHeatStack
+
+ - snaps.config.stack.StackConfig
+
+ - name - the stack's name (required)
+ - template - the heat template in dict() format (required when
+ template_path is None)
+ - template\_path - the location of the heat template file (required
+ when template is None)
+ - env\_values - dict() of strings for substitution of template
+ default values (optional)
+
+.. code:: python
+
+ from snaps.config.stack import StackConfig
+ from snaps.openstack.create_stack import OpenStackHeatStack
+
+ stack_settings = StackConfig(name='stack-name', template_path='/tmp/template.yaml')
+ stack_creator = OpenStackHeatStack(os_creds, stack_settings)
+ stack_creator.create()
+
+ # Perform logic
+ ...
+
+ # Cleanup
+ stack_creator.clean()
+
Create VM Instance
------------------
- name - the name of the VM (required)
- flavor - the name of the flavor (required)
- port\_settings - list of
- snaps.openstack.create\_network.PortSettings objects where each
+ snaps.config.network.PortConfig objects where each
denote a NIC (see above in create router section for details)
API does not require, but newer NFVIs now require VMs have at
least one network
- userdata - the cloud-init script to execute after VM has been
started
- - image\_settings - see snaps.openstack.create\_image.ImageSettings
+ - image\_settings - see snaps.config.image.ImageConfig
above (required)
- keypair\_settings - see
- snaps.openstack.create\_keypairs.KeypairSettings above (optional)
+ snaps.openstack.keypair.KeypairConfig above (optional)
.. code:: python
from snaps.openstack.create_instance import VmInstanceSettings, FloatingIpSettings, OpenStackVmInstance
- from snaps.openstack.create_network import PortSettings
+ from snaps.config.network import PortConfig
- port_settings = PortSettings(name='port-name', network_name=network_settings.name)
+ port_settings = PortConfig(name='port-name', network_name=network_settings.name)
floating_ip_settings = FloatingIpSettings(name='fip1', port_name=port_settings.name, router_name=router_settings.name)
instance_settings = VmInstanceSettings(name='vm-name', flavor='flavor_settings.name', port_settings=[port_settings],
floating_ip_settings=[floating_ip_settings])
supporting more than one version)
- snaps.openstack.utils.keystone\_utils - for calls to the Keystone
- APIs
+ APIs (support for versions 2 & 3)
- snaps.openstack.utils.glance\_utils - for calls to the Glance APIs
+ (support for versions 1 & 2)
- snaps.openstack.utils.neutron\_utils - for calls to the Neutron APIs
-- snaps.openstack.utils.nova\_utils - for calls to the Nova APIs
+ (version 2)
+- snaps.openstack.utils.nova\_utils - for calls to the Nova APIs (version 2)
+- snaps.openstack.utils.heat\_utils - for calls to the Heat APIs (version 1)
+- snaps.openstack.utils.cinder\_utils - for calls to the Cinder APIs
+ (support for versions 2 & 3)
Ensures that all required members are included when constructing a
OSCreds object
+SecurityGroupRuleConfigUnitTests
+--------------------------------
+
+Ensures that all required members are included when constructing a
+SecurityGroupRuleConfig object
+
SecurityGroupRuleSettingsUnitTests
----------------------------------
Ensures that all required members are included when constructing a
-SecurityGroupRuleSettings object
+deprecated SecurityGroupRuleSettings object
SecurityGroupRuleDomainObjectTests
----------------------------------
Ensures that all required members are included when constructing a
SecurityGroupRule domain object
+SecurityGroupConfigUnitTests
+----------------------------
+
+Ensures that all required members are included when constructing a
+SecuirtyGroupConfig object
+
SecurityGroupSettingsUnitTests
------------------------------
Ensures that all required members are included when constructing a
-SecuirtyGroupSettings object
+deprecated SecuirtyGroupSettings object
SecurityGroupDomainObjectTests
------------------------------
Ensures that all required members are included when constructing a
SecurityGroup domain object
+ImageConfigUnitTests
+--------------------
+
+Ensures that all required members are included when constructing a
+ImageConfig object
+
ImageSettingsUnitTests
----------------------
Ensures that all required members are included when constructing a
-ImageSettings object
+ImageSettings object (deprecated see ImageConfigUnitTests)
ImageDomainObjectTests
----------------------
Ensures that all required members are included when constructing a
Image domain object
+FlavorConfigUnitTests
+---------------------
+
+Ensures that all required members are included when constructing a
+FlavorConfig object
+
FlavorSettingsUnitTests
-----------------------
Ensures that all required members are included when constructing a
-FlavorSettings object
+deprecated FlavorSettings object
FlavorDomainObjectTests
-----------------------
Ensures that all required members are included when constructing a
Flavor domain object
+KeypairConfigUnitTests
+----------------------
+
+Ensures that all required members are included when constructing a
+KeypairConfig object
+
KeypairSettingsUnitTests
------------------------
Ensures that all required members are included when constructing a
-KeypairSettings object
+deprecated KeypairSettings object
KeypairDomainObjectTests
------------------------
Ensures that all required members are included when constructing a
Keypair domain object
+UserConfigUnitTests
+-------------------
+
+Ensures that all required members are included when constructing a
+UserConfig object
+
UserSettingsUnitTests
---------------------
Ensures that all required members are included when constructing a
-UserSettings object
+deprecated UserSettings object
UserDomainObjectTests
---------------------
Ensures that all required members are included when constructing a
User domain object
+ProjectConfigUnitTests
+----------------------
+
+Ensures that all required members are included when constructing a
+ProjectConfig object
+
ProjectSettingsUnitTests
------------------------
Ensures that all required members are included when constructing a
-ProjectSettings object
+deprecated ProjectSettings object
ProjectDomainObjectTests
------------------------
Ensures that all required members are included when constructing a
Role domain object
+NetworkConfigUnitTests
+----------------------
+
+Ensures that all required members are included when constructing a
+NetworkConfig object
+
NetworkSettingsUnitTests
------------------------
Ensures that all required members are included when constructing a
-NetworkSettings object
+deprecated NetworkSettings object
NetworkObjectTests
------------------
Ensures that all required members are included when constructing a
Network domain object
+SubnetConfigUnitTests
+---------------------
+
+Ensures that all required members are included when constructing a
+SubnetConfig object
+
SubnetSettingsUnitTests
-----------------------
Ensures that all required members are included when constructing a
-SubnetSettings object
+deprecated SubnetSettings object
SubnetObjectTests
-----------------
Ensures that all required members are included when constructing a
Subnet domain object
+PortConfigUnitTests
+-------------------
+
+Ensures that all required members are included when constructing a
+PortConfig object
+
PortSettingsUnitTests
---------------------
Ensures that all required members are included when constructing a
-PortSettings object
+deprecated PortSettings object
PortDomainObjectTests
---------------------
Ensures that all required members are included when constructing a
Port domain object
+RouterConfigUnitTests
+---------------------
+
+Ensures that all required members are included when constructing a
+RouterConfig object
+
RouterSettingsUnitTests
-----------------------
Ensures that all required members are included when constructing a
-RouterSettings object
+deprecated RouterSettings object
RouterDomainObjectTests
-----------------------
Ensures that all required members are included when constructing a
InterfaceRouter domain object
+StackConfigUnitTests
+--------------------
+
+Ensures that all required members are included when constructing a
+StackConfig object
+
StackSettingsUnitTests
----------------------
Ensures that all required members are included when constructing a
-StackSettings object
+deprecated StackSettings object
StackDomainObjectTests
----------------------
Ensures that all required members are included when constructing a
Output domain object (for Heat)
+VolumeConfigUnitTests
+---------------------
+
+Ensures that all required members are included when constructing a
+VolumeConfig object
+
+VolumeSettingsUnitTests
+-----------------------
+
+Ensures that all required members are included when constructing a
+deprecated VolumeSettings object
+
+VolumeDomainObjectTests
+-----------------------
+
+Ensures that all required members are included when constructing a
+Volume domain object (for Cinder)
+
+VolumeTypeConfigUnitTests
+-------------------------
+
+Ensures that all required members are included when constructing a
+VolumeTypeConfig object
+
+VolumeTypeSettingsUnitTests
+---------------------------
+
+Ensures that all required members are included when constructing a
+deprecated VolumeTypeSettings object
+
+VolumeTypeDomainObjectTests
+---------------------------
+
+Ensures that all required members are included when constructing a
+VolumeType domain object (for Cinder)
+
+VolumeTypeEncryptionObjectTests
+-------------------------------
+
+Ensures that all required members are included when constructing a
+VolumeTypeEncryption domain object (for Cinder)
+
+QoSConfigUnitTests
+------------------
+
+Ensures that all required members are included when constructing a
+QoSConfig object
+
+QoSSettingsUnitTests
+--------------------
+
+Ensures that all required members are included when constructing a
+deprecated QoSSettings object
+
+QoSSpecDomainObjectTests
+------------------------
+
+Ensures that all required members are included when constructing a
+QoSSpec domain object (for Cinder)
+
+VolumeDomainObjectTests
+-----------------------
+
+Ensures that all required members are included when constructing a
+Volume domain object (for Cinder)
+
+FloatingIpConfigUnitTests
+-------------------------
+
+Ensures that all required members are included when constructing a
+FloatingIpConfig object
+
FloatingIpSettingsUnitTests
---------------------------
Ensures that all required members are included when constructing a
-FloatingIpSettings object
+depecated FloatingIpSettings object
FloatingIpDomainObjectTests
---------------------------
Ensures that all required members are included when constructing a
FloatingIp domain object
+VmInstanceConfigUnitTests
+-------------------------
+
+Ensures that all required members are included when constructing a
+VmInstanceConfig object
+
VmInstanceSettingsUnitTests
---------------------------
Ensures that all required members are included when constructing a
-VmInstanceSettings object
+deprecated VmInstanceSettings object
VmInstDomainObjectTests
-----------------------
Ensures that all required members are included when constructing a
VmInst domain object
+
+ClusterTemplateConfigUnitTests
+------------------------------
+
+Ensures that all required members are included when constructing a
+ClusterTemplateConfig object
+
+ClusterTemplateUnitTests
+------------------------
+
+Ensures that all required members are included when constructing a
+ClusterTemplate object
+
+SettingsUtilsUnitTests
+----------------------
+
+Ensures that the settings_utils.py#create_volume_config() function properly
+maps a snaps.domain.Volume object correctly to a
+snaps.config.volume.VolumeConfig object as well as a
+snaps.domain.VolumeType object to a
+snaps.config.volume.VolumeConfig object
+
+
+Ensures that the settings_utils.py#create_flavor_config() function properly
+maps a snaps.domain.Flavor object correctly to a
+snaps.config.flavor.FlavorConfig object
\ No newline at end of file
Use launcher.py to deploy and clean up example environments. These examples are described in YAML files.
-#. Add your OpenStack connection information to the deploy-complex-network.yaml.
+#. Add your OpenStack connection information.
- Edit <path to repo>/examples/complex-network/deploy-complex-network.yaml
+ Edit <path to repo>/examples/inst-w-volume/deploy-env.yaml with your OpenStack
+ credentials and authorization URL
- openstack: the top level tag that denotes configuration for the OpenStack components
- auth\_url: - the URL to the OpenStack APIs (required)
- project\_name: - the name of the OpenStack project for the user
(required)
- - http\_proxy: - the {{ host }}:{{ port }} of the proxy server the
- HTTPPhotoman01(optional)
+ - http\_proxy: - the {{ host }}:{{ port }} of the proxy server (optional)
#. Go to the examples directory.
::
- python launch.py -t ./complex-network/deploy-complex-network.yaml -d
+ python launch.py -t ./inst-w-volume/deploy-vm-with-volume.yaml -e ./inst-w-volume/deploy-env.yaml -d
#. Clean the deployment.
::
- python launch.py -t ./complex-network/deploy-complex-network.yaml -c
+ python launch.py -t ./complex-network/deploy-complex-network.yaml -e ./inst-w-volume/deploy-env.yaml -c
#. Customize the deployment by changing the yaml file.
- openstack: the top level tag that denotes configuration for the
OpenStack components
- - connection: - contains the credentials and endpoints required to
- connect with OpenStack
- - username: - the project's user (required)
- - password: - the tentant's user password (required)
- - auth\_url: - the URL to the OpenStack APIs (required)
- - project\_name: - the name of the OpenStack project for the user
- (required)
- - http\_proxy: - the {{ host }}:{{ port }} of the proxy server the
- HTTPPhotoman01(optional)
- - images: - describes each image
- - image:
-
- - name: The unique image name. If the name already exists for
- your project, a new one will not be created (required)
- - format: The format type of the image i.e. qcow2 (required)
- - download\_url: The HTTP download location of the image file
- (required)
- - nic\_config\_pb\_loc: The file location relative to the CWD
- (python directory) to the Ansible Playbook used to configure
- VMs with more than one port. VMs get their first NIC configured
- for free while subsequent ones are not. This value/script will
- only be leveraged when necessary. Centos has been supported
- with
- "provisioning/ansible/centos-network-setup/configure\_host.yml".
+ - connections: the different connections/credentials to be used by the
+ launcher application
+
+ - connection: the credentials and endpoints required to connect to an
+ OpenStack project/tenant
+
+ - name: the name of the credentials for use when creating objects (required)
+ - username: the project's user (required)
+ - password: the tentant's user password (required)
+ - auth\_url: the URL to the OpenStack APIs (required)
+ - project\_name: the name of the OpenStack project for the user
+ (required)
+ - identity\_api\_version: the Keystone client version to use (default = 2)
+ - image\_api\_version: the Glance client version to use (default = 2)
+ - network\_api\_version: the Neutron client version to use (default = 2)
+ - compute\_api\_version: the Nova client version to use (default = 2)
+ - heat\_api\_version: the Heat client version to use (default = 1)
+ - volume\_api\_version: the Cinder client version to use (default = 2)
+ - user\_domain\_id: the user domain ID to use (default = 'default')
+ - user\_domain\_name: the user domain name to use (default = 'Default')
+ - project\_domain\_id: the project domain ID to use (default = 'default')
+ - project\_domain\_name: the project domain name to use (default = 'Default')
+ - interface: Used to specify the endpoint type for keystone (default = 'public')
+ - cacert: True for https or the certification file location (default = False)
+ - region\_name: the region (default = None)
+ - proxy\_settings: for accessing APIs hidden behind an HTTP proxy
+
+ - host: hostname or IP of HTTP proxy host (required)
+ - port: port number of the HTTP proxy server (required)
+ - http\_host: hostname or IP of HTTPS proxy host (default = host)
+ - port: port number of the HTTPS proxy server (default = port)
+ - ssh\_proxy\_cmd: the OpenSSH command used to access the SSH port
+ of a VM (optional)
+
+ - projects: the projects/tenants to create
+
+ - project: a project/tenant to create (admin user credentials required)
+
+ - os\_creds\_name: the connection name (default = 'default'
+ required or use "os\_user" below instead)
+ - name: the project's name (required)
+ - domain or domain_name: the project's domain name (default = 'Default')
+ - description: the description (optional)
+ - users: a list of users to associate to the project (optional)
+ - enabled: when True the project will be enabled on creation (default = True)
+
+ - users: the users to create
+
+ - user: a user to create (admin user credentials required)
+
+ - os\_creds\_name: the connection name (required)
+ - name: the username (required)
+ - password: the user's password (required)
+ - project\_name: the user's primary project name (optional)
+ - domain\_name: the user's domain name (default = 'Default')
+ - email: the user's email address (optional)
+ - roles: dict where key is the role's name and value is the name
+ of the project to associate with the role (optional)
+
+ - flavors: the flavors to create
+
+ - flavor: a flavor to create (admin user credentials required)
+
+ - os\_creds\_name: the connection name (default = 'default'
+ required or use "os\_user" below instead)
+ - name: the name (required)
+ - flavor\_id: the string ID (default 'auto')
+ - ram: the required RAM in MB (required)
+ - disk: the size of the root disk in GB (required)
+ - vcpus: the number of virtual CPUs (required)
+ - ephemeral: the size of the ephemeral disk in GB (default 0)
+ - swap: the size of the dedicated swap disk in GB (default 0)
+ - 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)
+
+ - qos_specs: the QoS Specs to create
+
+ - qos_spec: a QoS Spec to create (admin user credentials required)
+
+ - os\_creds\_name: the connection name (default = 'default'
+ required or use "os\_user" below instead)
+ - name: the name (required)
+ - consumer: enumerations: 'front-end', 'back-end', 'both' (required)
+ - specs: dict of custom values (optional)
+
+ - volume_types: the Volume Type to create
+
+ - volume_type: a Volume Type to create (admin user credentials required)
+
+ - os\_creds\_name: the connection name (default = 'default'
+ required or use "os\_user" below instead)
+ - name: the name (required)
+ - description: the description (optional)
+ - qos_spec_name: the name of the associate QoS Spec (optional)
+ - public: visibility (default - False)
+ - encryption: the encryption settings (optional)
+
+ - name: the name (required)
+ - provider_class: the provider class (required i.e. LuksEncryptor)
+ - control_location: enumerations: 'front-end', 'back-end' (required)
+ - cipher: the encryption algorithm/mode to use (optional)
+ - key_size: the size of the encryption key, in bits (optional)
+
+ - volumes: the Volume to create
+
+ - volume: a Volume to create
+
+ - os\_creds\_name: the connection name (default = 'default'
+ required or use "os\_user" below instead)
+ - os\_user: the connection from a new user defined in template
+ (required or use "os\_creds\_name" above
+
+ - name: the user's name (required)
+ - project\_name: the project name to use
+
+ - name: the name (required)
+ - description: the description (optional)
+ - size: the volume size in GB (default = 1)
+ - image_name: the image name to leverage (optional)
+ - type_name: the volume type name to associate (optional)
+ - availability_zone: the zone name on which to deploy (optional)
+ - multi_attach: when true, volume can be attached to more than one VM
+ (default = False)
+
+ - images: describes each image to create
+
+ - image:
+
+ - os\_creds\_name: the connection name (default = 'default'
+ required or use "os\_user" below instead)
+ - os\_user: the connection from a new user defined in template
+ (required or use "os\_creds\_name" above
+
+ - name: the user's name (required)
+ - project\_name: the project name to use
+
+ - name: The unique image name. If the name already exists for
+ your project, a new one will not be created (required)
+ - image\_user: the image's default sudo user (required)
+ - format or img\_format: the image format type (required i.e. qcow2)
+ - url or download\_url: The HTTP download location of the image file
+ (required when "image_file" below has not been configured)
+ - image\_file: the image file location (required when "url" has not
+ been configured)
+ - kernel\_image\_settings: the settings for a kernel image (optional)
+ - ramdisk\_image\_settings: the settings for a kernel image (optional)
+ - public: publically visibile when True (default = True)
- networks:
- - network:
-
- - name: The name of the network to be created. If one already
- exists, a new one will not be created (required)
- - admin\_state\_up: T\|F (default True)
- - shared: (optional)
- - project\_name: Name of the project who owns the network. Note:
- only administrative users can specify projects other than their
- own (optional)
- - external: T\|F whether or not network is external (default
- False)
- - network\_type: The type of network to create. (optional)
- - subnets:
- - subnet:
-
- - name: The name of the network to be created. If one already
- exists, a new one will not be created. Note: although
- OpenStack allows for multiple subnets to be applied to any
- given network, we have not included support as our current
- use cases does not utilize this functionality (required)
- - cidr: The subnet mask value (required)
- - dns\_nameservers: A list of IP values used for DNS
- resolution (default: 8.8.8.8)
- - ip\_version: 4\|6 (default: 4)
- - project\_name: Name of the project who owns the network.
- Note: only administrative users can specify projects other
- than their own (optional)
- - start: The start address for allocation\_pools (optional)
- - end: The ending address for allocation\_pools (optional)
- - gateway\_ip: The IP address to the gateway (optional)
- - enable\_dhcp: T\|F (optional)
- - dns\_nameservers: List of DNS server IPs
- - host\_routes: A list of host route dictionaries (optional)
- i.e.:
- ``yaml "host_routes":[ { "destination":"0.0.0.0/0", "nexthop":"123.456.78.9" }, { "destination":"192.168.0.0/24", "nexthop":"192.168.0.1" } ]``
- - destination: The destination for a static route (optional)
- - nexthop: The next hop for the destination (optional)
- - ipv6\_ra\_mode: Valid values: "dhcpv6-stateful",
- "dhcpv6-stateless", and "slaac" (optional)
- - ipv6\_address\_mode: Valid values: "dhcpv6-stateful",
- "dhcpv6-stateless", and "slaac" (optional)
+ - network:
+
+ - os\_creds\_name: the connection name (default = 'default'
+ required or use "os\_user" below instead)
+ - os\_user: the connection from a new user defined in template
+ (required or use "os\_creds\_name" above
+
+ - name: the user's name (required)
+ - project\_name: the project name to use
+
+ - name: The name of the network to be created. If one already
+ exists, a new one will not be created (required)
+ - admin\_state\_up: T\|F (default True)
+ - shared: (optional)
+ - project\_name: Name of the project who owns the network. Note:
+ only administrative users can specify projects other than their
+ own (optional)
+ - external: T\|F whether or not network is external (default False)
+ - network\_type: The type of network to create (optional)
+ - physical\_network: the name of the physical network
+ (required when network_type is 'flat')
+ - segmentation\_id: the id of the segmentation
+ (required when network_type is 'vlan')
+ - subnets:
+ - subnet:
+
+ - name: The name of the network to be created. If one already
+ exists, a new one will not be created. Note: although
+ OpenStack allows for multiple subnets to be applied to any
+ given network, we have not included support as our current
+ use cases does not utilize this functionality (required)
+ - cidr: The subnet mask value (required)
+ - dns\_nameservers: A list of IP values used for DNS
+ resolution (default: 8.8.8.8)
+ - ip\_version: 4\|6 (default: 4)
+ - project\_name: Name of the project who owns the network.
+ Note: only administrative users can specify projects other
+ than their own (optional)
+ - start: The start address for allocation\_pools (optional)
+ - end: The ending address for allocation\_pools (optional)
+ - gateway\_ip: The IP address to the gateway (optional)
+ - enable\_dhcp: T\|F (optional)
+ - dns\_nameservers: List of DNS server IPs (default = ['8.8.8.8']
+ - host\_routes: A list of host route dictionaries (optional)
+ i.e.:
+ ``yaml "host_routes":[ { "destination":"0.0.0.0/0", "nexthop":"123.456.78.9" }, { "destination":"192.168.0.0/24", "nexthop":"192.168.0.1" } ]``
+ - destination: The destination for a static route (optional)
+ - nexthop: The next hop for the destination (optional)
+ - ipv6\_ra\_mode: Valid values: "dhcpv6-stateful",
+ "dhcpv6-stateless", and "slaac" (optional)
+ - ipv6\_address\_mode: Valid values: "dhcpv6-stateful",
+ "dhcpv6-stateless", and "slaac" (optional)
+
+ - security_groups:
+
+ - security_group:
+
+ - os\_creds\_name: the connection name (default = 'default'
+ required or use "os\_user" below instead)
+ - os\_user: the connection from a new user defined in template
+ (required or use "os\_creds\_name" above
+
+ - name: the user's name (required)
+ - project\_name: the project name to use
+
+ - name: The name of the security group to be created (required)
+ - description: The security group's description (optional)
+ - project\_name: Name of the project who owns the security group (optional)
+ - rule\_settings: List of rules to place onto security group (optional)
+
+ - description: the rule's description (optional)
+ - protocol: rule's protcol ('icmp' or 'tcp' or 'udp' or 'null')
+ - ethertype: rule's ethertype ('4' or '6')
+ - port\_range\_min: The minimum port number in the range that is
+ matched by the security group rule. When the protocol is 'tcp'
+ or 'udp', this value must be <= 'port_range_max' (optional)
+ - port\_range\_max: The maximum port number in the range that is
+ matched by the security group rule. When the protocol is 'tcp'
+ or 'udp', this value must be <= 'port_range_max' (optional)
+ - remote\_ip\_prefix: The remote IP prefix to associate with this
+ metering rule packet (optional)
- routers:
- router:
- - name: The name of the router to be created. If one already
- exists, a new one will not be created (required)
- - project\_name: Name of the project who owns the network. Note:
- only administrative users can specify projects other than their
- own (optional)
- - internal\_subnets: A list of subnet names on which the router
- will be placed (optional)
- - external\_gateway: A dictionary containing the external gateway
- parameters: "network\_id", "enable\_snat",
- "external\_fixed\_ips" (optional)
- - interfaces: A list of port interfaces to create to other
- subnets (optional)
-
- - port (Leverages the same class/structure as port objects on
- VM instances. See port definition below for a
- full accounting of the port attributes. The ones listed
- below are generally used for routers)
-
- - name: The name given to the new port (must be unique for
- project) (required)
- - network\_name: The name of the new port's network
- (required)
- - ip\_addrs: A list of k/v pairs (optional)
- - subnet\_name: the name of a subnet that is on the port's
- network
- - ip: An IP address of the associated subnet to assign to
- the new port (optional but generally required for router
- interfaces)
+
+ - os\_creds\_name: the connection name (default = 'default'
+ required or use "os\_user" below instead)
+ - os\_user: the connection from a new user defined in template
+ (required or use "os\_creds\_name" above
+
+ - name: the user's name (required)
+ - project\_name: the project name to use
+
+ - name: The name of the router to be created (required)
+ - project\_name: Name of the project who owns the network (optional)
+ - external\_gateway: Name of the external network to which to route
+ (optional)
+ - admin\_state\_up: T\|F (default True)
+ - external\_fixed\_ids: Dictionary containing the IP address
+ parameters (optional)
+ - internal\_subnets: List of subnet names to which to connect this
+ router (optional)
+
+ - port_settings (Leverages the same class/structure as port objects on
+ VM instances. See port definition below for a
+ full accounting of the port attributes. The ones listed
+ below are generally used for routers)
+
+ - name: The name given to the new port (required and must be
+ unique for project)
+ - network\_name: The name of the network on which to create
+ the port (optional)
+ - admin\_state\_up: T\|F (default True)
+ - project\_name: Name of the project who owns the network (optional)
+ - mac\_address: The port's MAC address (optional)
+ - ip\_addrs: A list of k/v pairs (optional)
+ - security\_groups: a list of names of the the security groups
+ to apply to the port
+ - opt\_value: The extra DHCP option value (optional)
+ - opt\_name: The extra DHCP option name (optional)
- keypairs:
- keypair:
- - name: The name of the keypair to be created. If one already
- exists, a new one will not be created but simply loaded from
- its configured file location (required)
- - public\_filepath: The path to where the generated public key
- will be stored if it does not exist (optional but really
- required for provisioning purposes)
- - private\_filepath: The path to where the generated private key
- will be stored if it does not exist (optional but really
- required for provisioning purposes)
+
+ - os\_creds\_name: the connection name (default = 'default'
+ required or use "os\_user" below instead)
+ - os\_user: the connection from a new user defined in template
+ (required or use "os\_creds\_name" above
+
+ - name: the user's name (required)
+ - project\_name: the project name to use
+
+ - name: The name of the keypair to be created. If one already
+ exists, a new one will not be created but simply loaded from
+ its configured file location (required)
+ - public\_filepath: The path to where the generated public key
+ will be stored if it does not exist (optional but really
+ required for provisioning purposes)
+ - private\_filepath: The path to where the generated private key
+ will be stored if it does not exist (optional but really
+ required for provisioning purposes)
- instances:
- instance:
- - name: The unique instance name for project. (required)
- - flavor: Must be one of the preconfigured flavors (required)
- - imageName: The name of the image to be used for deployment
- (required)
- - keypair\_name: The name of the keypair to attach to instance
- (optional but required for NIC configuration and Ansible
- provisioning)
- - sudo\_user: The name of a sudo\_user that is attached to the
- keypair (optional but required for NIC configuration and
- Ansible provisioning)
- - vm\_boot\_timeout: The number of seconds to block waiting for
- an instance to deploy and boot (default 900)
- - vm\_delete\_timeout: The number of seconds to block waiting for
- an instance to be deleted (default 300)
- - ssh\_connect\_timeout: The number of seconds to block waiting
- for an instance to achieve an SSH connection (default 120)
- - ports: A list of port configurations (should contain at least
- one)
- - port: Denotes the configuration of a NIC
-
- - name: The unique port name for project (required)
- - network\_name: The name of the network to which the port is
- attached (required)
- - ip\_addrs: Static IP addresses to be added to the port by
- subnet (optional)
- - subnet\_name: The name of the subnet
- - ip: The assigned IP address (when null, OpenStack will
- assign an IP to the port)
- - admin\_state\_up: T\|F (default True)
- - project\_name: The name of the project who owns the network.
- Only administrative users can specify a the project ID other
- than their own (optional)
- - mac\_address: The desired MAC for the port (optional)
- - fixed\_ips: A dictionary that allows one to specify only a
- subnet ID, OpenStack Networking allocates an available IP
- from that subnet to the port. If you specify both a subnet
- ID and an IP address, OpenStack Networking tries to allocate
- the specified address to the port. (optional)
- - seurity\_groups: A list of security group IDs (optional)
- - allowed\_address\_pairs: A dictionary containing a set of
- zero or more allowed address pairs. An address pair contains
- an IP address and MAC address. (optional)
- - opt\_value: The extra DHCP option value (optional)
- - opt\_name: The extra DHCP option name (optional)
- - device\_owner: The ID of the entity that uses this port. For
- example, a DHCP agent (optional)
- - device\_id: The ID of the device that uses this port. For
- example, a virtual server (optional)
-
- - floating\_ips: list of floating\_ip configurations (optional)
-
- - floating\_ip:
- - name: Must be unique for VM instance (required)
- - port\_name: The name of the port requiring access to the
- external network (required)
- - subnet\_name: The name of the subnet contains the IP address on
- the port on which to create the floating IP (optional)
- - router\_name: The name of the router connected to an external
- network used to attach the floating IP (required)
- - provisioning: (True\|False) Denotes whether or not this IP can
- be used for Ansible provisioning (default True)
+
+ - os\_creds\_name: the connection name (default = 'default'
+ required or use "os\_user" below instead)
+ - os\_user: the connection from a new user defined in template
+ (required or use "os\_creds\_name" above
+
+ - name: the user's name (required)
+ - project\_name: the project name to use
+
+ - name: The unique instance name for project. (required)
+ - flavor: Must be one of the preconfigured flavors (required)
+ - imageName: The name of the image to be used for deployment
+ (required)
+ - keypair\_name: The name of the keypair to attach to instance
+ (optional but required for NIC configuration and Ansible
+ provisioning)
+ - sudo\_user: The name of a sudo\_user that is attached to the
+ keypair (optional but required for NIC configuration and
+ Ansible provisioning)
+ - vm\_boot\_timeout: The number of seconds to block waiting for
+ an instance to deploy and boot (default 900)
+ - vm\_delete\_timeout: The number of seconds to block waiting for
+ an instance to be deleted (default 300)
+ - ssh\_connect\_timeout: The number of seconds to block waiting
+ for an instance to achieve an SSH connection (default 120)
+ - ports: A list of port configurations (should contain at least
+ one)
+ - port: Denotes the configuration of a NIC
+
+ - name: The unique port name for project (required)
+ - network\_name: The name of the network to which the port is
+ attached (required)
+ - ip\_addrs: Static IP addresses to be added to the port by
+ subnet (optional)
+ - subnet\_name: The name of the subnet
+ - ip: The assigned IP address (when null, OpenStack will
+ assign an IP to the port)
+ - admin\_state\_up: T\|F (default True)
+ - project\_name: The name of the project who owns the network.
+ Only administrative users can specify a the project ID other
+ than their own (optional)
+ - mac\_address: The desired MAC for the port (optional)
+ - fixed\_ips: A dictionary that allows one to specify only a
+ subnet ID, OpenStack Networking allocates an available IP
+ from that subnet to the port. If you specify both a subnet
+ ID and an IP address, OpenStack Networking tries to allocate
+ the specified address to the port. (optional)
+ - seurity\_groups: A list of security group IDs (optional)
+ - allowed\_address\_pairs: A dictionary containing a set of
+ zero or more allowed address pairs. An address pair contains
+ an IP address and MAC address. (optional)
+ - opt\_value: The extra DHCP option value (optional)
+ - opt\_name: The extra DHCP option name (optional)
+ - device\_owner: The ID of the entity that uses this port. For
+ example, a DHCP agent (optional)
+ - device\_id: The ID of the device that uses this port. For
+ example, a virtual server (optional)
+
+ - floating\_ips: list of floating\_ip configurations (optional)
+
+ - floating\_ip:
+ - name: Must be unique for VM instance (required)
+ - port\_name: The name of the port requiring access to the
+ external network (required)
+ - subnet\_name: The name of the subnet contains the IP address on
+ the port on which to create the floating IP (optional)
+ - router\_name: The name of the router connected to an external
+ network used to attach the floating IP (required)
+ - provisioning: (True\|False) Denotes whether or not this IP can
+ be used for Ansible provisioning (default True)
- ansible: Each set of attributes below are contained in a list
import logging
+
+from snaps.config.vm_inst import VmInstanceConfig
+
logging.basicConfig(level=logging.INFO)
# Credentials
from snaps.openstack.os_credentials import OSCreds, ProxySettings
-proxy_settings = ProxySettings(host='10.197.123.27', port='3128',
- ssh_proxy_cmd='/usr/local/bin/corkscrew 10.197.123.27 3128 %h %p')
+proxy_settings = ProxySettings(
+ host='10.197.123.27', port='3128',
+ ssh_proxy_cmd='/usr/local/bin/corkscrew 10.197.123.27 3128 %h %p')
-os_creds = OSCreds(username='admin', password='cable123', auth_url='http://192.168.67.10:5000/v2.0/',
- project_name='admin', proxy_settings=proxy_settings)
+os_creds = OSCreds(
+ username='admin', password='cable123',
+ auth_url='http://192.168.67.10:5000/v2.0/', project_name='admin',
+ proxy_settings=proxy_settings)
# Images
-from snaps.openstack.create_image import ImageSettings, OpenStackImage
+from snaps.openstack.create_image import OpenStackImage
+from snaps.config.image import ImageConfig
-image_settings = ImageSettings(name='cirros-test', image_user='cirros', img_format='qcow2',
- url='http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-x86_64-disk.img')
+image_settings = ImageConfig(name='cirros-test', image_user='cirros', img_format='qcow2',
+ url='http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-x86_64-disk.img')
image = OpenStackImage(os_creds, image_settings)
image.create()
# Network
-from snaps.openstack.create_network import NetworkSettings, SubnetSettings, OpenStackNetwork
+from snaps.config.network import NetworkConfig, SubnetConfig
+from snaps.openstack.create_network import OpenStackNetwork
-subnet_settings = SubnetSettings(name='test-subnet', cidr='10.0.0.1/24')
-network_settings = NetworkSettings(name='test-net', subnet_settings=[subnet_settings])
+subnet_settings = SubnetConfig(name='test-subnet', cidr='10.0.0.1/24')
+network_settings = NetworkConfig(
+ name='test-net', subnet_settings=[subnet_settings])
network = OpenStackNetwork(os_creds, network_settings)
network.create()
# Flavors
-from snaps.openstack.create_flavor import FlavorSettings, OpenStackFlavor
+from snaps.config.flavor import FlavorConfig
+from snaps.openstack.create_flavor import OpenStackFlavor
-flavor_settings = FlavorSettings(name='test-flavor', ram=256, disk=10, vcpus=2)
+flavor_settings = FlavorConfig(name='test-flavor', ram=256, disk=10, vcpus=2)
flavor = OpenStackFlavor(os_creds, flavor_settings)
flavor.create()
# Instances
-from snaps.openstack.create_network import PortSettings
-from snaps.openstack.create_instance import VmInstanceSettings, OpenStackVmInstance
-
-port_settings = PortSettings(name='test-port', network_name=network_settings.name)
-instance_settings = VmInstanceSettings(name='test-inst', flavor=flavor_settings.name, port_settings=[port_settings])
+from snaps.config.network import PortConfig
+from snaps.openstack.create_instance import OpenStackVmInstance
+
+port_settings = PortConfig(
+ name='test-port', network_name=network_settings.name)
+instance_settings = VmInstanceConfig(
+ name='test-inst', flavor=flavor_settings.name,
+ port_settings=[port_settings])
vm_inst = OpenStackVmInstance(os_creds, instance_settings, image_settings)
vm_inst.create(block=True)
--- /dev/null
+---
+admin_user: admin
+admin_pass: ChangeMe
+admin_proj: admin
+auth_url: http://10.197.103.31:5000/
+proxy_host:
+proxy_port:
+id_api_version: 3
+
+ext_net: public1
+
+username: test_user
+pass: test_user
+proj: example-volume
+
+flavor_name: example.m1.small
+flavor_ram: 2048
+flavor_disk: 10
+flavor_cpus: 1
+
+qos_name: example_qos
+vol_type_name: example-vol-type
+vol_type_encryption_name: example_vol-type-encryption
+volume_name: example-volume
+
+image_name: example-image
+image_format: qcow2
+image_url: http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-x86_64-disk.img
+image_file:
+image_user: cirros
+
+net_name: example-net
+subnet_name: example-subnet
+cidr: 10.0.8.0/24
+router_name: example-router
+
+kp_name: example-kp
+kp_pub_path: ~/tmp/example-kp.pub
+kp_priv_path: ~/tmp/example-kp
+
+sg_name: example-sg
+
+port_name: example-port
+inst_name: example-inst
--- /dev/null
+# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs")
+# and others. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+---
+openstack:
+ connections:
+ # Note - when http_proxy is set, you must also configure ssh for proxy tunneling on your host.
+ - connection:
+ name: admin-creds
+ username: {{ admin_user }}
+ project_name: {{ admin_proj }}
+ password: {{ admin_pass }}
+ auth_url: {{ auth_url }}
+ identity_api_version: {{ id_api_version }}
+ projects:
+ - project:
+ os_creds_name: admin-creds
+ name: {{ proj }}
+ description: Project for Orchestrators
+ users:
+ - {{ username }}
+ - {{ admin_user }}
+ users:
+ - user:
+ os_creds_name: admin-creds
+ name: {{ username }}
+ password: {{ pass }}
+ project_name: {{ proj }}
+ roles: {admin: {{ proj }}}
+ flavors:
+ - flavor:
+ os_creds_name: admin-creds
+ name: {{ flavor_name }}
+ ram: {{ flavor_ram }}
+ disk: {{ flavor_disk }}
+ vcpus: {{ flavor_cpus }}
+ qos_specs:
+ - qos_spec:
+ os_creds_name: admin-creds
+ name: {{ qos_name }}
+ consumer: both
+ volume_types:
+ - volume_type:
+ os_creds_name: admin-creds
+ name: {{ vol_type_name }}
+ encryption:
+ name: {{ vol_type_encryption_name }}
+ provider_class: LuksEncryptor
+ control_location: front-end
+ volumes:
+ - volume:
+ os_user:
+ name: {{ username }}
+ project_name: {{ proj }}
+ name: {{ volume_name }}
+ size: 10
+ images:
+ - image:
+ os_creds_name: admin-creds
+ name: {{ image_name }}
+ format: {{ image_format }}
+ image_user: {{ image_user }}
+ download_url: {{ image_url }}
+ image_file: {{ image_file }}
+ public: True
+ networks:
+ - network:
+ os_user:
+ name: {{ username }}
+ project_name: {{ proj }}
+ name: {{ net_name }}
+ project_name: {{ proj }}
+ subnets:
+ - subnet:
+ name: {{ subnet_name }}
+ project_name: {{ proj }}
+ cidr: {{ cidr }}
+ dns_nameservers: [8.8.8.8]
+ routers:
+ - router:
+ os_user:
+ name: {{ username }}
+ project_name: {{ proj }}
+ name: {{ router_name }}
+ external_gateway: {{ ext_net }}
+ internal_subnets:
+ - {{ subnet_name }}
+ keypairs:
+ - keypair:
+ os_user:
+ name: {{ username }}
+ project_name: {{ proj }}
+ name: {{ kp_name }}
+ public_filepath: {{ kp_pub_path }}
+ private_filepath: {{ kp_priv_path }}
+ delete_on_clean: True
+ security_groups:
+ - security_group:
+ os_user:
+ name: {{ username }}
+ project_name: {{ proj }}
+ name: {{ sg_name }}
+ rules:
+ - direction: ingress
+ protocol: icmp
+ - direction: ingress
+ protocol: tcp
+ port_range_min: 22
+ port_range_max: 22
+ instances:
+ - instance:
+ os_user:
+ name: {{ username }}
+ project_name: {{ proj }}
+ name: {{ inst_name }}
+ flavor: {{ flavor_name }}
+ imageName: {{ image_name }}
+ keypair_name: {{ kp_name }}
+ security_group_names: [{{ sg_name }}]
+ volume_names:
+ - {{ volume_name }}
+ ports:
+ - port:
+ name: {{ port_name_prfx }}-1a
+ network_name: {{ net_name }}
+ floating_ips:
+ - floating_ip:
+ name: fip1
+ port_name: {{ port_name }}
+ router_name: {{ router_name }}
# This script is responsible for deploying virtual environments
import argparse
import logging
-import re
-import time
from jinja2 import Environment, FileSystemLoader
import os
import yaml
from snaps import file_utils
-from snaps.openstack.create_flavor import FlavorSettings, OpenStackFlavor
-from snaps.openstack.create_image import ImageSettings, OpenStackImage
-from snaps.openstack.create_instance import VmInstanceSettings
-from snaps.openstack.create_keypairs import KeypairSettings, OpenStackKeypair
-from snaps.openstack.create_network import (
- PortSettings, NetworkSettings, OpenStackNetwork)
-from snaps.openstack.create_project import OpenStackProject, ProjectSettings
-from snaps.openstack.create_router import RouterSettings, OpenStackRouter
-from snaps.openstack.create_security_group import (
- OpenStackSecurityGroup, SecurityGroupSettings)
-from snaps.openstack.create_user import OpenStackUser, UserSettings
-from snaps.openstack.os_credentials import OSCreds, ProxySettings
-from snaps.openstack.utils import deploy_utils
-from snaps.provisioning import ansible_utils
+from snaps.openstack.utils import launch_utils
__author__ = 'spisarski'
logger = logging.getLogger('snaps_launcher')
ARG_NOT_SET = "argument not set"
-DEFAULT_CREDS_KEY = 'admin'
-
-
-def __get_creds_dict(os_conn_config):
- """
- Returns a dict of OSCreds where the key is the creds name.
- For backwards compatibility, credentials not contained in a list (only
- one) will be returned with the key of None
- :param os_conn_config: the credential configuration
- :return: a dict of OSCreds objects
- """
- if 'connection' in os_conn_config:
- return {DEFAULT_CREDS_KEY: __get_os_credentials(os_conn_config)}
- elif 'connections' in os_conn_config:
- out = dict()
- for os_conn_dict in os_conn_config['connections']:
- config = os_conn_dict.get('connection')
- if not config:
- raise Exception('Invalid connection format')
-
- name = config.get('name')
- if not name:
- raise Exception('Connection config requires a name field')
-
- out[name] = __get_os_credentials(os_conn_dict)
- return out
-
-
-def __get_creds(os_creds_dict, os_user_dict, inst_config):
- """
- Returns the appropriate credentials
- :param os_creds_dict: a dictionary of OSCreds objects where the name is the
- key
- :param os_user_dict: a dictionary of OpenStackUser objects where the name
- is the key
- :param inst_config:
- :return: an OSCreds instance or None
- """
- os_creds = os_creds_dict.get(DEFAULT_CREDS_KEY)
- if 'os_user' in inst_config:
- os_user_conf = inst_config['os_user']
- if 'name' in os_user_conf:
- user_creator = os_user_dict.get(os_user_conf['name'])
- if user_creator:
- return user_creator.get_os_creds(
- project_name=os_user_conf.get('project_name'))
- elif 'os_creds_name' in inst_config:
- if 'os_creds_name' in inst_config:
- os_creds = os_creds_dict[inst_config['os_creds_name']]
- return os_creds
-
-
-def __get_os_credentials(os_conn_config):
- """
- Returns an object containing all of the information required to access
- OpenStack APIs
- :param os_conn_config: The configuration holding the credentials
- :return: an OSCreds instance
- """
- config = os_conn_config.get('connection')
- if not config:
- raise Exception('Invalid connection configuration')
-
- proxy_settings = None
- http_proxy = config.get('http_proxy')
- if http_proxy:
- tokens = re.split(':', http_proxy)
- ssh_proxy_cmd = config.get('ssh_proxy_cmd')
- proxy_settings = ProxySettings(host=tokens[0], port=tokens[1],
- ssh_proxy_cmd=ssh_proxy_cmd)
- else:
- if 'proxy_settings' in config:
- host = config['proxy_settings'].get('host')
- port = config['proxy_settings'].get('port')
- if host and host != 'None' and port and port != 'None':
- proxy_settings = ProxySettings(**config['proxy_settings'])
-
- if proxy_settings:
- config['proxy_settings'] = proxy_settings
- else:
- del config['proxy_settings']
-
- return OSCreds(**config)
-
-
-def __parse_ports_config(config):
- """
- Parses the "ports" configuration
- :param config: The dictionary to parse
- :return: a list of PortConfig objects
- """
- out = list()
- for port_config in config:
- out.append(PortSettings(**port_config.get('port')))
- return out
-
-
-def __create_instances(os_creds_dict, creator_class, config_class, config,
- config_key, cleanup=False, os_users_dict=None):
- """
- Returns a dictionary of SNAPS creator objects where the key is the name
- :param os_creds_dict: Dictionary of OSCreds objects where the key is the
- name
- :param config: The list of configurations for the same type
- :param config_key: The list of configurations for the same type
- :param cleanup: Denotes whether or not this is being called for cleanup
- :return: dictionary
- """
- out = {}
-
- if config:
- try:
- for config_dict in config:
- inst_config = config_dict.get(config_key)
- if inst_config:
- creator = creator_class(
- __get_creds(os_creds_dict, os_users_dict, inst_config),
- config_class(**inst_config))
-
- if cleanup:
- creator.initialize()
- else:
- creator.create()
- out[inst_config['name']] = creator
- logger.info('Created configured %s', config_key)
- except Exception as e:
- logger.error('Unexpected error instantiating creator [%s] '
- 'with exception %s', creator_class, e)
-
- return out
-
-
-def __create_vm_instances(os_creds_dict, os_users_dict, instances_config,
- image_dict, keypairs_dict, cleanup=False):
- """
- Returns a dictionary of OpenStackVmInstance objects where the key is the
- instance name
- :param os_creds_dict: Dictionary of OSCreds objects where the key is the
- name
- :param os_users_dict: Dictionary of OpenStackUser objects where the key is
- the username
- :param instances_config: The list of VM instance configurations
- :param image_dict: A dictionary of images that will probably be used to
- instantiate the VM instance
- :param keypairs_dict: A dictionary of keypairs that will probably be used
- to instantiate the VM instance
- :param cleanup: Denotes whether or not this is being called for cleanup
- :return: dictionary
- """
- vm_dict = {}
-
- if instances_config:
- try:
- for instance_config in instances_config:
- conf = instance_config.get('instance')
- if conf:
- if image_dict:
- image_creator = image_dict.get(conf.get('imageName'))
- if image_creator:
- instance_settings = VmInstanceSettings(
- **instance_config['instance'])
- kp_name = conf.get('keypair_name')
- vm_dict[conf[
- 'name']] = deploy_utils.create_vm_instance(
- __get_creds(
- os_creds_dict, os_users_dict, conf),
- instance_settings,
- image_creator.image_settings,
- keypair_creator=keypairs_dict[kp_name],
- init_only=cleanup)
- else:
- raise Exception('Image creator instance not found.'
- ' Cannot instantiate')
- else:
- raise Exception('Image dictionary is None. Cannot '
- 'instantiate')
- else:
- raise Exception('Instance configuration is None. Cannot '
- 'instantiate')
- logger.info('Created configured instances')
- except Exception as e:
- logger.error('Unexpected error creating VM instances - %s', e)
- return vm_dict
-
-
-def __apply_ansible_playbooks(ansible_configs, os_creds_dict, vm_dict,
- image_dict, flavor_dict, env_file):
- """
- Applies ansible playbooks to running VMs with floating IPs
- :param ansible_configs: a list of Ansible configurations
- :param os_creds_dict: Dictionary of OSCreds objects where the key is the
- name
- :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
- name is the key
- :param flavor_dict: the dictionary of newly instantiated flavors where the
- name is the key
- :param env_file: the path of the environment for setting the CWD so
- playbook location is relative to the deployment file
- :return: t/f - true if successful
- """
- logger.info("Applying Ansible Playbooks")
- if ansible_configs:
- # Ensure all hosts are accepting SSH session requests
- for vm_inst in list(vm_dict.values()):
- if not vm_inst.vm_ssh_active(block=True):
- logger.warning(
- "Timeout waiting for instance to respond to SSH requests")
- return False
-
- # Set CWD so the deployment file's playbook location can leverage
- # relative paths
- orig_cwd = os.getcwd()
- env_dir = os.path.dirname(env_file)
- os.chdir(env_dir)
-
- # Apply playbooks
- for ansible_config in ansible_configs:
- if 'pre_sleep_time' in ansible_config:
- try:
- sleep_time = int(ansible_config['pre_sleep_time'])
- logger.info('Waiting %s seconds to apply playbooks',
- sleep_time)
- time.sleep(sleep_time)
- except:
- pass
-
- os_creds = os_creds_dict.get(None, 'admin')
- __apply_ansible_playbook(ansible_config, os_creds, vm_dict,
- image_dict, flavor_dict)
-
- # Return to original directory
- os.chdir(orig_cwd)
-
- return True
-
-
-def __apply_ansible_playbook(ansible_config, os_creds, vm_dict, image_dict,
- flavor_dict):
- """
- Applies an Ansible configuration setting
- :param ansible_config: the configuration settings
- :param os_creds: the OpenStack credentials object
- :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
- name is the key
- :param flavor_dict: the dictionary of newly instantiated flavors where the
- name is the key
- """
- if ansible_config:
- (remote_user, floating_ips, private_key_filepath,
- proxy_settings) = __get_connection_info(
- ansible_config, vm_dict)
- if floating_ips:
- retval = ansible_utils.apply_playbook(
- ansible_config['playbook_location'], floating_ips, remote_user,
- private_key_filepath,
- variables=__get_variables(ansible_config.get('variables'),
- os_creds, vm_dict, image_dict,
- flavor_dict),
- proxy_setting=proxy_settings)
- if retval != 0:
- # Not a fatal type of event
- logger.warning(
- 'Unable to apply playbook found at location - %s',
- ansible_config.get('playbook_location'))
-
-
-def __get_connection_info(ansible_config, vm_dict):
- """
- Returns a tuple of data required for connecting to the running VMs
- (remote_user, [floating_ips], private_key_filepath, proxy_settings)
- :param ansible_config: the configuration settings
- :param vm_dict: the dictionary of VMs where the VM name is the key
- :return: tuple where the first element is the user and the second is a list
- of floating IPs and the third is the
- private key file location and the fourth is an instance of the
- snaps.ProxySettings class
- (note: in order to work, each of the hosts need to have the same sudo_user
- and private key file location values)
- """
- if ansible_config.get('hosts'):
- hosts = ansible_config['hosts']
- if len(hosts) > 0:
- floating_ips = list()
- remote_user = None
- pk_file = None
- proxy_settings = None
- for host in hosts:
- vm = vm_dict.get(host)
- if vm:
- fip = vm.get_floating_ip()
- if fip:
- remote_user = vm.get_image_user()
-
- if fip:
- floating_ips.append(fip.ip)
- else:
- raise Exception(
- 'Could not find floating IP for VM - ' +
- vm.name)
-
- pk_file = vm.keypair_settings.private_filepath
- proxy_settings = vm.get_os_creds().proxy_settings
- else:
- logger.error('Could not locate VM with name - ' + host)
-
- return remote_user, floating_ips, pk_file, proxy_settings
- return None
-
-
-def __get_variables(var_config, os_creds, vm_dict, image_dict, flavor_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 credentials object
- :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
- name is the key
- :param flavor_dict: the dictionary of newly instantiated flavors where the
- name is the key
- :return: dictionary or None
- """
- if var_config and vm_dict and len(vm_dict) > 0:
- variables = dict()
- for key, value in var_config.items():
- value = __get_variable_value(value, os_creds, vm_dict, image_dict,
- flavor_dict)
- if key and value:
- variables[key] = value
- logger.info(
- "Set Jinga2 variable with key [%s] the value [%s]",
- key, value)
- else:
- logger.warning('Key [%s] or Value [%s] must not be None',
- str(key), str(value))
- return variables
- return None
-
-
-def __get_variable_value(var_config_values, os_creds, vm_dict, image_dict,
- flavor_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 credentials object
- :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
- name is the key
- :param flavor_dict: the dictionary of newly instantiated flavors where the
- name is the key
- :return:
- """
- if var_config_values['type'] == 'string':
- return __get_string_variable_value(var_config_values)
- 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)
- if var_config_values['type'] == 'port':
- return __get_vm_port_variable_value(var_config_values, vm_dict)
- if var_config_values['type'] == 'floating_ip':
- return __get_vm_fip_variable_value(var_config_values, vm_dict)
- if var_config_values['type'] == 'image':
- 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)
- return None
-
-
-def __get_string_variable_value(var_config_values):
- """
- Returns the associated string value
- :param var_config_values: the configuration dictionary
- :return: the value contained in the dictionary with the key 'value'
- """
- return var_config_values['value']
-
-
-def __get_vm_attr_variable_value(var_config_values, vm_dict):
- """
- Returns the associated value contained on a VM instance
- :param var_config_values: the configuration dictionary
- :param vm_dict: the dictionary containing all VMs where the key is the VM's
- name
- :return: the value
- """
- vm = vm_dict.get(var_config_values['vm_name'])
- if vm:
- if var_config_values['value'] == 'floating_ip':
- return vm.get_floating_ip().ip
- if var_config_values['value'] == 'image_user':
- return vm.get_image_user()
-
-
-def __get_os_creds_variable_value(var_config_values, os_creds):
- """
- Returns the associated OS credentials value
- :param var_config_values: the configuration dictionary
- :param os_creds: the credentials
- :return: the value
- """
- logger.info("Retrieving OS Credentials")
- if os_creds:
- if var_config_values['value'] == 'username':
- logger.info("Returning OS username")
- return os_creds.username
- elif var_config_values['value'] == 'password':
- logger.info("Returning OS password")
- return os_creds.password
- elif var_config_values['value'] == 'auth_url':
- logger.info("Returning OS auth_url")
- return os_creds.auth_url
- elif var_config_values['value'] == 'project_name':
- logger.info("Returning OS project_name")
- return os_creds.project_name
-
- logger.info("Returning none")
- return None
-
-
-def __get_vm_port_variable_value(var_config_values, vm_dict):
- """
- Returns the associated OS credentials value
- :param var_config_values: the configuration dictionary
- :param vm_dict: the dictionary containing all VMs where the key is the VM's
- name
- :return: the value
- """
- port_name = var_config_values.get('port_name')
- vm_name = var_config_values.get('vm_name')
-
- if port_name and vm_name:
- vm = vm_dict.get(vm_name)
- if vm:
- port_value_id = var_config_values.get('port_value')
- if port_value_id:
- if port_value_id == 'mac_address':
- return vm.get_port_mac(port_name)
- if port_value_id == 'ip_address':
- return vm.get_port_ip(port_name)
-
-
-def __get_vm_fip_variable_value(var_config_values, vm_dict):
- """
- Returns the floating IP value if found
- :param var_config_values: the configuration dictionary
- :param vm_dict: the dictionary containing all VMs where the key is the VM's
- name
- :return: the floating IP string value or None
- """
- fip_name = var_config_values.get('fip_name')
- vm_name = var_config_values.get('vm_name')
-
- if vm_name:
- vm = vm_dict.get(vm_name)
- if vm:
- fip = vm.get_floating_ip(fip_name)
- if fip:
- return fip.ip
-
-
-def __get_image_variable_value(var_config_values, image_dict):
- """
- Returns the associated image value
- :param var_config_values: the configuration dictionary
- :param image_dict: the dictionary containing all images where the key is
- the name
- :return: the value
- """
- logger.info("Retrieving image values")
-
- if image_dict:
- if var_config_values.get('image_name'):
- image_creator = image_dict.get(var_config_values['image_name'])
- if image_creator:
- if var_config_values.get('value') and \
- var_config_values['value'] == 'id':
- return image_creator.get_image().id
- if var_config_values.get('value') and \
- var_config_values['value'] == 'user':
- return image_creator.image_settings.image_user
-
- logger.info("Returning none")
- return None
-
-
-def __get_flavor_variable_value(var_config_values, flavor_dict):
- """
- Returns the associated flavor value
- :param var_config_values: the configuration dictionary
- :param flavor_dict: the dictionary containing all flavor creators where the
- key is the name
- :return: the value or None
- """
- logger.info("Retrieving flavor values")
-
- if flavor_dict:
- if var_config_values.get('flavor_name'):
- flavor_creator = flavor_dict.get(var_config_values['flavor_name'])
- if flavor_creator:
- if var_config_values.get('value') and \
- var_config_values['value'] == 'id':
- return flavor_creator.get_flavor().id
def main(arguments):
config = yaml.load(output)
if config:
- os_config = config.get('openstack')
-
- creators = list()
- vm_dict = dict()
- images_dict = dict()
- flavors_dict = dict()
- os_creds_dict = dict()
clean = arguments.clean is not ARG_NOT_SET
-
- if os_config:
- os_creds_dict = __get_creds_dict(os_config)
-
- try:
- # Create projects
- projects_dict = __create_instances(
- os_creds_dict, OpenStackProject, ProjectSettings,
- os_config.get('projects'), 'project', clean)
- creators.append(projects_dict)
-
- # Create users
- users_dict = __create_instances(
- os_creds_dict, OpenStackUser, UserSettings,
- os_config.get('users'), 'user', clean)
- creators.append(users_dict)
-
- # Associate new users to projects
- if not clean:
- for project_creator in projects_dict.values():
- users = project_creator.project_settings.users
- for user_name in users:
- user_creator = users_dict.get(user_name)
- if user_creator:
- project_creator.assoc_user(
- user_creator.get_user())
-
- # Create flavors
- flavors_dict = __create_instances(
- os_creds_dict, OpenStackFlavor, FlavorSettings,
- os_config.get('flavors'), 'flavor', clean, users_dict)
- creators.append(flavors_dict)
-
- # Create images
- images_dict = __create_instances(
- os_creds_dict, OpenStackImage, ImageSettings,
- os_config.get('images'), 'image', clean, users_dict)
- creators.append(images_dict)
-
- # Create networks
- creators.append(__create_instances(
- os_creds_dict, OpenStackNetwork, NetworkSettings,
- os_config.get('networks'), 'network', clean, users_dict))
-
- # Create routers
- creators.append(__create_instances(
- os_creds_dict, OpenStackRouter, RouterSettings,
- os_config.get('routers'), 'router', clean, users_dict))
-
- # Create keypairs
- keypairs_dict = __create_instances(
- os_creds_dict, OpenStackKeypair, KeypairSettings,
- os_config.get('keypairs'), 'keypair', clean, users_dict)
- creators.append(keypairs_dict)
-
- # Create security groups
- creators.append(__create_instances(
- os_creds_dict, OpenStackSecurityGroup,
- SecurityGroupSettings,
- os_config.get('security_groups'), 'security_group', clean,
- users_dict))
-
- # Create instance
- vm_dict = __create_vm_instances(
- os_creds_dict, users_dict, os_config.get('instances'),
- images_dict, keypairs_dict,
- arguments.clean is not ARG_NOT_SET)
- creators.append(vm_dict)
- logger.info(
- 'Completed creating/retrieving all configured instances')
- except Exception as e:
- logger.error(
- 'Unexpected error deploying environment. Rolling back due'
- ' to - ' + str(e))
- raise
-
- # Must enter either block
- if arguments.clean is not ARG_NOT_SET:
- # Cleanup Environment
- __cleanup(creators, arguments.clean_image is not ARG_NOT_SET)
- elif arguments.deploy is not ARG_NOT_SET:
- logger.info('Configuring NICs where required')
- for vm in vm_dict.values():
- vm.config_nics()
- logger.info('Completed NIC configuration')
-
- # Provision VMs
- ansible_config = config.get('ansible')
- if ansible_config and vm_dict:
- if not __apply_ansible_playbooks(ansible_config,
- os_creds_dict, vm_dict,
- images_dict, flavors_dict,
- arguments.tmplt_file):
- logger.error("Problem applying ansible playbooks")
+ clean_image = arguments.clean_image is not ARG_NOT_SET
+ deploy = arguments.deploy is not ARG_NOT_SET
+ launch_utils.launch_config(
+ config, arguments.tmplt_file, deploy, clean, clean_image)
else:
logger.error(
'Unable to read configuration file - ' + arguments.tmplt_file)
exit(0)
-def __cleanup(creators, clean_image=False):
- for creator_dict in reversed(creators):
- for key, creator in creator_dict.items():
- if ((isinstance(creator, OpenStackImage) and clean_image)
- or not isinstance(creator, OpenStackImage)):
- try:
- creator.clean()
- except Exception as e:
- logger.warning('Error cleaning component - %s', e)
-
-
if __name__ == '__main__':
# To ensure any files referenced via a relative path will begin from the
# directory in which this file resides
-python-novaclient!=7.0.0,>=6.0.0 # Apache-2.0
-python-neutronclient>=5.1.0 # Apache-2.0
+# 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-keystoneclient>=3.8.0 # Apache-2.0
-python-glanceclient>=2.5.0 # Apache-2.0
+python-glanceclient>=2.8.0 # Apache-2.0
python-heatclient>=1.6.1 # Apache-2.0
-python-cinderclient
-ansible>=2.1.0,<2.4
+python-cinderclient>=3.1.0 # Apache-2.0
+python-magnumclient>=2.0.0 # Apache-2.0
+ansible<2.4,>=2.1.0
wrapt>=1.7.0 # BSD License
scp
-cryptography!=1.3.0,>=1.0 # BSD/Apache-2.0
+cryptography!=2.0,>=1.6 # BSD/Apache-2.0
-#!/usr/bin/env python
-
-# Copyright (c) 2016 Cable Television Laboratories and others.
+# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
+#
+# 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
#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available 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 setuptools import setup
+# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
+import setuptools
-__author__ = 'spisarski'
+# In python < 2.7.4, a lazy loading of package `pbr` will break
+# setuptools if some other modules registered functions in `atexit`.
+# solution from: http://bugs.python.org/issue15881#msg170215
+try:
+ import multiprocessing # noqa
+except ImportError:
+ pass
-setup(
- setup_requires=['pbr>=1.9', 'setuptools>=17.1'],
- pbr=True,
-)
+setuptools.setup(
+ setup_requires=['pbr>=2.0.0'],
+ pbr=True)
-# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs")
+# Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
# and others. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# 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.
----
-- name: Configure NIC
- hosts: all
- become: yes
- become_method: sudo
- become_user: root
-
- tasks:
- - name: Setup /etc/network/interfaces.d/{{nic_name}}.cfg file
- action: template owner=root group=root mode=644 src=../templates/ethN.cfg dest=/etc/network/interfaces.d/{{nic_name}}.cfg
- - name : Restart Network
- command: service networking restart
\ No newline at end of file
+__author__ = 'spisarski'
--- /dev/null
+# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs")
+# and others. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import enum
+from neutronclient.common.utils import str2bool
+
+
+class ServerType(enum.Enum):
+ """
+ The cluter server types supported
+ """
+ vm = 'vm'
+ baremetal = 'baremetal'
+
+
+class ContainerOrchestrationEngine(enum.Enum):
+ """
+ The types of supported COEs
+ """
+ kubernetes = 'kubernetes'
+ swarm = 'swarm'
+ mesos = 'mesos'
+
+
+class DockerStorageDriver(enum.Enum):
+ """
+ Drivers for managing storage for the images in the container's writable
+ layer
+ """
+ devicemapper = 'devicemapper'
+ overlay = 'overlay'
+
+
+class ClusterTemplateConfig(object):
+ """
+ Configuration settings for OpenStack cluster template creation
+ """
+
+ def __init__(self, **kwargs):
+ """
+ Constructor
+ :param name: the cluster template's name (required)
+ :param image: name or ID of the base image in Glance used to boot the
+ cluster's servers. The image must have the attribute
+ 'os-distro' defined as appropriate for the cluster
+ driver (required)
+ :param keypair: name or ID of the keypair to gain cluster machine
+ access (required)
+ :param network_driver: The name of a network driver for providing the
+ networks for the containers. Note that this is
+ different and separate from the Neutron network
+ for the bay/cluster. The operation and
+ networking model are specific to the particular
+ driver (optional)
+ :param external_net: name or IDof the external Neutron network to
+ provide connectivity to the cluster (required)
+ :param floating_ip_enabled: Whether enable or not using the floating IP
+ of cloud provider. Some cloud providers
+ used floating IP, some used public IP,
+ thus Magnum provide this option for
+ specifying the choice of using floating IP
+ (default - True)
+ :param docker_volume_size: The size in GB for the local storage on each
+ server for the Docker daemon to cache the
+ images and host the containers. Cinder
+ volumes provide the storage. The default is
+ 25 GB. For the devicemapper storage driver,
+ the minimum value is 3GB. For the overlay
+ storage driver, the minimum value is 1GB.
+ (default - 3)
+ :param server_type: ServerType enumeration (default - vm)
+ :param flavor: name or ID of the nova flavor for booting the node
+ servers (default - m1.small)
+ :param master_flavor: name or ID of the nova flavor of the master node
+ for this cluster (optional)
+ :param coe: ContainerOrchestrationEngine enum instance
+ (default - kubernetes)
+ :param fixed_net: name of a Neutron network to provide connectivity
+ to the internal network for the cluster
+ (optional)
+ :param fixed_subnet: Fixed subnet that are using to allocate network
+ address for nodes in bay/cluster (optional)
+ :param registry_enabled: Docker images by default are pulled from the
+ public Docker registry, but in some cases,
+ users may want to use a private registry.
+ This option provides an alternative registry
+ based on the Registry V2: Magnum will create a
+ local registry in the bay/cluster backed by
+ swift to host the images (default - True)
+ :param insecure_registry: The URL pointing to the user's own private
+ insecure docker registry to deploy and run
+ docker containers (optional)
+ :param docker_storage_driver: DockerStorageDriver enum instance to
+ manage storage for the images and
+ container's writable layer
+ (default - devicemapper)
+ :param dns_nameserver: The DNS nameserver for the servers and
+ containers in the bay/cluster to use.
+ This is configured in the private Neutron
+ network for the bay/cluster.
+ (default provided by Magnum - 8.8.8.8)
+ :param public: denotes whether or not the cluster template is public
+ (default False)
+ :param tls_disabled: denotes whether or not TLS should be enabled
+ (default False)
+ :param http_proxy: host:port for a proxy to use when direct HTTP
+ access from the servers to sites on the external
+ internet is blocked (optional)
+ :param https_proxy: host:port for a proxy to use when direct HTTPS
+ access from the servers to sites on the external
+ internet is blocked (optional)
+ :param no_proxy: comma separated list of IPs that should not be
+ redirected through the proxy (optional)
+ :param volume_driver: The name of a volume driver for managing the
+ persistent storage for the containers. The
+ functionality supported are specific to the
+ driver (optional)
+ :param master_lb_enabled: Since multiple masters may exist in a
+ bay/cluster, a Neutron load balancer is
+ created to provide the API endpoint for the
+ bay/cluster and to direct requests to the
+ masters. In some cases, such as when the
+ LBaaS service is not available, this option
+ can be set to false to create a bay/cluster
+ without the load balancer. In this case, one
+ of the masters will serve as the API endpoint
+ (default - True)
+ :param labels: Arbitrary labels in the form of a dict. The accepted
+ keys and valid values are defined in the bay/cluster
+ drivers. They are used as a way to pass additional
+ parameters that are specific to a bay/cluster driver.
+ (optional)
+ """
+ self.name = kwargs.get('name')
+ self.image = kwargs.get('image')
+ self.keypair = kwargs.get('keypair')
+ self.network_driver = kwargs.get('network_driver')
+ self.external_net = kwargs.get('external_net')
+ self.floating_ip_enabled = str2bool(
+ str(kwargs.get('floating_ip_enabled', True)))
+ self.docker_volume_size = int(kwargs.get('docker_volume_size', 3))
+ self.server_type = map_server_type(
+ kwargs.get('server_type', ServerType.vm))
+ self.flavor = kwargs.get('flavor')
+ self.master_flavor = kwargs.get('master_flavor')
+ self.coe = map_coe(
+ kwargs.get('coe', ContainerOrchestrationEngine.kubernetes))
+ self.fixed_net = kwargs.get('fixed_net')
+ self.fixed_subnet = kwargs.get('fixed_subnet')
+ self.registry_enabled = str2bool(
+ str(kwargs.get('registry_enabled', True)))
+ self.insecure_registry = kwargs.get('insecure_registry')
+ self.docker_storage_driver = map_docker_storage_driver(
+ kwargs.get('docker_storage_driver',
+ DockerStorageDriver.devicemapper))
+ self.dns_nameserver = kwargs.get('dns_nameserver')
+ self.public = str2bool(str(kwargs.get('public', False)))
+ self.tls_disabled = str2bool(str(kwargs.get('tls_disabled', False)))
+ self.http_proxy = kwargs.get('http_proxy')
+ self.https_proxy = kwargs.get('https_proxy')
+ self.no_proxy = kwargs.get('no_proxy')
+ self.volume_driver = kwargs.get('volume_driver')
+ self.master_lb_enabled = str2bool(
+ str(kwargs.get('master_lb_enabled', True)))
+ self.labels = kwargs.get('labels')
+
+ if (not self.name or not self.image or not self.keypair
+ or not self.external_net):
+ raise ClusterTemplateConfigError(
+ 'The attributes name, image, keypair, and '
+ 'external_net are required for ClusterTemplateConfig')
+
+ def magnum_dict(self):
+ """
+ Returns a dictionary object representing this object.
+ This is meant to be sent into as kwargs into the Magnum client
+
+ :return: the dictionary object
+ """
+ out = dict()
+
+ if self.name:
+ out['name'] = self.name
+ if self.image:
+ out['image_id'] = self.image
+ if self.keypair:
+ out['keypair_id'] = self.keypair
+ if self.network_driver:
+ out['network_driver'] = self.network_driver
+ if self.external_net:
+ out['external_network_id'] = self.external_net
+ if self.floating_ip_enabled:
+ out['floating_ip_enabled'] = self.floating_ip_enabled
+ if self.docker_volume_size:
+ out['docker_volume_size'] = self.docker_volume_size
+ if self.server_type:
+ out['server_type'] = self.server_type.value
+ if self.flavor:
+ out['flavor_id'] = self.flavor
+ if self.master_flavor:
+ out['master_flavor_id'] = self.master_flavor
+ if self.coe:
+ out['coe'] = self.coe.value
+ if self.fixed_net:
+ out['fixed_network'] = self.fixed_net
+ if self.fixed_subnet:
+ out['fixed_subnet'] = self.fixed_subnet
+ if self.registry_enabled:
+ out['registry_enabled'] = self.registry_enabled
+ if self.insecure_registry:
+ out['insecure_registry'] = self.insecure_registry
+ if self.docker_storage_driver:
+ out['docker_storage_driver'] = self.docker_storage_driver.value
+ if self.dns_nameserver:
+ out['dns_nameserver'] = self.dns_nameserver
+ if self.public:
+ out['public'] = self.public
+ if self.tls_disabled:
+ out['tls_disabled'] = self.tls_disabled
+ if self.http_proxy:
+ out['http_proxy'] = self.http_proxy
+ if self.https_proxy:
+ out['https_proxy'] = self.https_proxy
+ if self.no_proxy:
+ out['no_proxy'] = self.no_proxy
+ if self.volume_driver:
+ out['volume_driver'] = self.volume_driver
+ if self.master_lb_enabled:
+ out['master_lb_enabled'] = self.master_lb_enabled
+ if self.labels:
+ out['labels'] = self.labels
+ return out
+
+
+class ClusterTemplateConfigError(Exception):
+ """
+ Exception to be thrown when a cluster template configuration is incorrect
+ """
+
+
+def map_server_type(server_type):
+ """
+ Takes a the server_type value maps it to the ServerType enum. When None
+ return None
+ :param server_type: the server_type value to map
+ :return: the ServerType enum object
+ :raise: ClusterTemplateConfigError if value is invalid
+ """
+ if not server_type:
+ return None
+ if isinstance(server_type, ServerType):
+ return server_type
+ elif isinstance(server_type, str):
+ for this_type in ServerType:
+ if this_type.value == server_type:
+ return this_type
+ raise ClusterTemplateConfigError(
+ 'Invalid server type - ' + server_type)
+
+
+def map_coe(coe):
+ """
+ Takes a the coe value maps it to the ContainerOrchestrationEngine enum.
+ When None return None
+ :param coe: the COE value to map
+ :return: the ContainerOrchestrationEngine enum object
+ :raise: ClusterTemplateConfigError if value is invalid
+ """
+ if not coe:
+ return None
+ if isinstance(coe, ContainerOrchestrationEngine):
+ return coe
+ elif isinstance(coe, str):
+ for this_type in ContainerOrchestrationEngine:
+ if this_type.value == coe:
+ return this_type
+ raise ClusterTemplateConfigError('Invalid COE - ' + coe)
+
+
+def map_docker_storage_driver(driver):
+ """
+ Takes a the coe value maps it to the ContainerOrchestrationEngine enum.
+ When None return None
+ :param driver: the docker storage driver value to map
+ :return: the DockerStorageDriver enum object
+ :raise: ClusterTemplateConfigError if value is invalid
+ """
+ if not driver:
+ return None
+ if isinstance(driver, DockerStorageDriver):
+ return driver
+ elif isinstance(driver, str):
+ for this_type in DockerStorageDriver:
+ if this_type.value == driver:
+ return this_type
+ raise ClusterTemplateConfigError(
+ 'Invalid DockerStorageDriver - ' + driver)
--- /dev/null
+# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs")
+# and others. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+class FlavorConfig(object):
+ """
+ Configuration settings for OpenStack flavor creation
+ """
+
+ def __init__(self, **kwargs):
+ """
+ Constructor
+ :param name: the flavor's name (required)
+ :param flavor_id: the string ID (default 'auto')
+ :param ram: the required RAM in MB (required)
+ :param disk: the size of the root disk in GB (required)
+ :param vcpus: the number of virtual CPUs (required)
+ :param ephemeral: the size of the ephemeral disk in GB (default 0)
+ :param swap: the size of the dedicated 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: denotes whether or not the flavor is public
+ (default True)
+ :param metadata: freeform dict() for special metadata
+ """
+ self.name = kwargs.get('name')
+
+ if kwargs.get('flavor_id'):
+ self.flavor_id = kwargs['flavor_id']
+ else:
+ self.flavor_id = 'auto'
+
+ self.ram = kwargs.get('ram')
+ self.disk = kwargs.get('disk')
+ self.vcpus = kwargs.get('vcpus')
+
+ if kwargs.get('ephemeral'):
+ self.ephemeral = kwargs['ephemeral']
+ else:
+ self.ephemeral = 0
+
+ if kwargs.get('swap'):
+ self.swap = kwargs['swap']
+ else:
+ self.swap = 0
+
+ if kwargs.get('rxtx_factor'):
+ self.rxtx_factor = kwargs['rxtx_factor']
+ else:
+ self.rxtx_factor = 1.0
+
+ if kwargs.get('is_public') is not None:
+ self.is_public = kwargs['is_public']
+ else:
+ self.is_public = True
+
+ if kwargs.get('metadata'):
+ self.metadata = kwargs['metadata']
+ else:
+ self.metadata = None
+
+ if not self.name or not self.ram or not self.disk or not self.vcpus:
+ raise FlavorConfigError(
+ 'The attributes name, ram, disk, and vcpus are required for'
+ 'FlavorConfig')
+
+ if not isinstance(self.ram, int):
+ raise FlavorConfigError('The ram attribute must be a integer')
+
+ if not isinstance(self.disk, int):
+ raise FlavorConfigError('The ram attribute must be a integer')
+
+ if not isinstance(self.vcpus, int):
+ raise FlavorConfigError('The vcpus attribute must be a integer')
+
+ if self.ephemeral and not isinstance(self.ephemeral, int):
+ raise FlavorConfigError(
+ 'The ephemeral attribute must be an integer')
+
+ if self.swap and not isinstance(self.swap, int):
+ raise FlavorConfigError('The swap attribute must be an integer')
+
+ if self.rxtx_factor and not isinstance(self.rxtx_factor, (int, float)):
+ raise FlavorConfigError(
+ 'The is_public attribute must be an integer or float')
+
+ if self.is_public and not isinstance(self.is_public, bool):
+ raise FlavorConfigError(
+ 'The is_public attribute must be a boolean')
+
+
+class FlavorConfigError(Exception):
+ """
+ Exception to be thrown when a flavor configuration is incorrect
+ """
--- /dev/null
+# 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.
+
+
+class ImageConfig(object):
+ def __init__(self, **kwargs):
+ """
+ Constructor
+ :param name: the image's name (required)
+ :param image_user: the image's default sudo user (required)
+ :param format or img_format: the image format type (required)
+ :param url or download_url: the image download location (requires url
+ or img_file)
+ :param image_file: the image file location (requires url or img_file)
+ :param extra_properties: dict() object containing extra parameters to
+ pass when loading the image;
+ can be ids of kernel and initramfs images for
+ a 3-part image
+ :param nic_config_pb_loc: the file location to the Ansible Playbook
+ that can configure multiple NICs
+ :param kernel_image_settings: the settings for a kernel image
+ :param ramdisk_image_settings: the settings for a ramdisk image
+ :param exists: When True, an image with the given name must exist
+ :param public: When True, an image will be created with public
+ visibility
+ """
+
+ self.name = kwargs.get('name')
+ self.image_user = kwargs.get('image_user')
+ self.format = kwargs.get('format')
+ if not self.format:
+ self.format = kwargs.get('img_format')
+
+ self.url = kwargs.get('url')
+ if not self.url:
+ self.url = kwargs.get('download_url')
+ if self.url == 'None':
+ self.url = None
+
+ self.image_file = kwargs.get('image_file')
+ if self.image_file == 'None':
+ self.image_file = None
+
+ self.extra_properties = kwargs.get('extra_properties')
+ self.nic_config_pb_loc = kwargs.get('nic_config_pb_loc')
+
+ kernel_image_settings = kwargs.get('kernel_image_settings')
+ if kernel_image_settings:
+ if isinstance(kernel_image_settings, dict):
+ self.kernel_image_settings = ImageConfig(
+ **kernel_image_settings)
+ else:
+ self.kernel_image_settings = kernel_image_settings
+ else:
+ self.kernel_image_settings = None
+
+ ramdisk_image_settings = kwargs.get('ramdisk_image_settings')
+ if ramdisk_image_settings:
+ if isinstance(ramdisk_image_settings, dict):
+ self.ramdisk_image_settings = ImageConfig(
+ **ramdisk_image_settings)
+ else:
+ self.ramdisk_image_settings = ramdisk_image_settings
+ else:
+ self.ramdisk_image_settings = None
+
+ if 'exists' in kwargs and kwargs['exists'] is True:
+ self.exists = True
+ else:
+ self.exists = False
+
+ if 'public' in kwargs and kwargs['public'] is True:
+ self.public = True
+ else:
+ self.public = False
+
+ if not self.name:
+ raise ImageConfigError("The attribute name is required")
+
+ if not (self.url or self.image_file) and not self.exists:
+ raise ImageConfigError(
+ 'URL or image file must be set or image must already exist')
+
+ if not self.image_user:
+ raise ImageConfigError('Image user is required')
+
+ if not self.format and not self.exists:
+ raise ImageConfigError(
+ 'Format is required when the image should not already exist')
+
+
+class ImageConfigError(Exception):
+ """
+ Exception to be thrown when an image settings are incorrect
+ """
+
+ def __init__(self, message):
+ Exception.__init__(self, message)
--- /dev/null
+# 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 neutronclient.common.utils import str2bool
+
+
+class KeypairConfig(object):
+ """
+ Class representing a keypair configuration
+ """
+
+ def __init__(self, **kwargs):
+ """
+ Constructor - all parameters are optional
+ :param name: The keypair name.
+ :param public_filepath: The path to/from the filesystem where the
+ public key file is or will be stored
+ :param private_filepath: The path where the generated private key file
+ will be stored
+ :param key_size: The number of bytes for the key size when it needs to
+ be generated (Must be >=512 default 1024)
+ :param delete_on_clean: when True, the key files will be deleted when
+ OpenStackKeypair#clean() is called
+ :return:
+ """
+
+ self.name = kwargs.get('name')
+ self.public_filepath = kwargs.get('public_filepath')
+ self.private_filepath = kwargs.get('private_filepath')
+ self.key_size = int(kwargs.get('key_size', 1024))
+
+ if kwargs.get('delete_on_clean') is not None:
+ if isinstance(kwargs.get('delete_on_clean'), bool):
+ self.delete_on_clean = kwargs.get('delete_on_clean')
+ else:
+ self.delete_on_clean = str2bool(kwargs.get('delete_on_clean'))
+ else:
+ self.delete_on_clean = None
+
+ if not self.name:
+ raise KeypairConfigError('Name is a required attribute')
+
+ if self.key_size < 512:
+ raise KeypairConfigError('key_size must be >=512')
+
+
+class KeypairConfigError(Exception):
+ """
+ Exception to be thrown when keypair settings are incorrect
+ """
--- /dev/null
+# 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.
+import enum
+from neutronclient.common.utils import str2bool
+
+from snaps.openstack.utils import keystone_utils, neutron_utils
+
+
+class NetworkConfig(object):
+ """
+ Class representing a network configuration
+ """
+
+ def __init__(self, **kwargs):
+ """
+ Constructor - all parameters are optional
+ :param name: The network name.
+ :param admin_state_up: The administrative status of the network.
+ True = up / False = down (default True)
+ :param shared: Boolean value indicating whether this network is shared
+ across all projects/tenants. By default, only
+ administrative users can change this value.
+ :param project_name: Admin-only. The name of the project that will own
+ the network. This project can be different from
+ the project that makes the create network request.
+ However, only administrative users can specify a
+ project ID other than their own. You cannot change
+ this value through authorization policies.
+ :param external: when true, will setup an external network
+ (default False).
+ :param network_type: the type of network (i.e. vlan|flat).
+ :param physical_network: the name of the physical network
+ (required when network_type is 'flat')
+ :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.
+ :return:
+ """
+
+ self.project_id = None
+
+ self.name = kwargs.get('name')
+ if kwargs.get('admin_state_up') is not None:
+ self.admin_state_up = str2bool(str(kwargs['admin_state_up']))
+ else:
+ self.admin_state_up = True
+
+ if kwargs.get('shared') is not None:
+ self.shared = str2bool(str(kwargs['shared']))
+ else:
+ self.shared = None
+
+ self.project_name = kwargs.get('project_name')
+
+ if kwargs.get('external') is not None:
+ self.external = str2bool(str(kwargs.get('external')))
+ else:
+ self.external = False
+
+ self.network_type = kwargs.get('network_type')
+ self.physical_network = kwargs.get('physical_network')
+ self.segmentation_id = kwargs.get('segmentation_id')
+
+ self.subnet_settings = list()
+ subnet_settings = kwargs.get('subnets')
+ if not subnet_settings:
+ subnet_settings = kwargs.get('subnet_settings', list())
+ if subnet_settings:
+ for subnet_config in subnet_settings:
+ if isinstance(subnet_config, SubnetConfig):
+ self.subnet_settings.append(subnet_config)
+ else:
+ self.subnet_settings.append(
+ SubnetConfig(**subnet_config['subnet']))
+
+ if not self.name or len(self.name) < 1:
+ raise NetworkConfigError('Name required for networks')
+
+ def get_project_id(self, os_creds):
+ """
+ Returns the project ID for a given project_name or None
+ :param os_creds: the credentials required for keystone client retrieval
+ :return: the ID or None
+ """
+ if self.project_id:
+ 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
+
+ return None
+
+ def dict_for_neutron(self, os_creds):
+ """
+ Returns a dictionary object representing this object.
+ This is meant to be converted into JSON designed for use by the Neutron
+ API
+ TODO - expand automated testing to exercise all parameters
+
+ :param os_creds: the OpenStack credentials
+ :return: the dictionary object
+ """
+ out = dict()
+
+ if self.name:
+ out['name'] = self.name
+ if self.admin_state_up is not None:
+ out['admin_state_up'] = self.admin_state_up
+ if self.shared:
+ out['shared'] = self.shared
+ if self.project_name:
+ project_id = self.get_project_id(os_creds)
+ if project_id:
+ out['tenant_id'] = project_id
+ else:
+ raise NetworkConfigError(
+ 'Could not find project ID for project named - ' +
+ self.project_name)
+ if self.network_type:
+ out['provider:network_type'] = self.network_type
+ if self.physical_network:
+ out['provider:physical_network'] = self.physical_network
+ if self.segmentation_id:
+ out['provider:segmentation_id'] = self.segmentation_id
+ if self.external:
+ out['router:external'] = self.external
+ return {'network': out}
+
+
+class NetworkConfigError(Exception):
+ """
+ Exception to be thrown when networks settings attributes are incorrect
+ """
+
+
+class IPv6Mode(enum.Enum):
+ """
+ A rule's direction
+ """
+ slaac = 'slaac'
+ stateful = 'dhcpv6-stateful'
+ stateless = 'dhcpv6-stateless'
+
+
+class SubnetConfig(object):
+ """
+ Class representing a subnet configuration
+ """
+
+ def __init__(self, **kwargs):
+ """
+ Constructor - all parameters are optional except cidr (subnet mask)
+ :param name: The subnet name (required)
+ :param cidr: The CIDR (required)
+ :param ip_version: The IP version, which is 4 or 6 (required)
+ :param project_name: The name of the project who owns the network.
+ Only administrative users can specify a project ID
+ other than their own. You cannot change this value
+ 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 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')
+ :param host_routes: A list of host route dictionaries for the subnet.
+ For example:
+ "host_routes":[
+ {
+ "destination":"0.0.0.0/0",
+ "nexthop":"123.456.78.9"
+ },
+ {
+ "destination":"192.168.0.0/24",
+ "nexthop":"192.168.0.1"
+ }
+ ]
+ :param destination: The destination for static route (optional)
+ :param nexthop: The next hop for the destination (optional)
+ :param ipv6_ra_mode: an instance of the IPv6Mode enum
+ (optional when enable_dhcp is True)
+ :param ipv6_address_mode: an instance of the IPv6Mode enum
+ (optional when enable_dhcp is True)
+ :raise: SubnetConfigError when config does not have or cidr values
+ are None
+ """
+ self.cidr = kwargs.get('cidr')
+ if kwargs.get('ip_version'):
+ self.ip_version = kwargs['ip_version']
+ else:
+ self.ip_version = 4
+
+ # Optional attributes that can be set after instantiation
+ self.name = kwargs.get('name')
+ self.project_name = kwargs.get('project_name')
+ self.start = kwargs.get('start')
+ self.end = kwargs.get('end')
+ self.gateway_ip = kwargs.get('gateway_ip')
+ self.enable_dhcp = kwargs.get('enable_dhcp')
+
+ 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.host_routes = kwargs.get('host_routes')
+ self.destination = kwargs.get('destination')
+ self.nexthop = kwargs.get('nexthop')
+ self.ipv6_ra_mode = map_mode(kwargs.get('ipv6_ra_mode'))
+ self.ipv6_address_mode = map_mode(kwargs.get('ipv6_address_mode'))
+
+ if not self.name or not self.cidr:
+ raise SubnetConfigError('Name and cidr required for subnets')
+
+ def dict_for_neutron(self, os_creds, network=None):
+ """
+ Returns a dictionary object representing this object.
+ This is meant to be converted into JSON designed for use by the Neutron
+ API
+ :param os_creds: the OpenStack credentials
+ :param network: The network object on which the subnet will be created
+ (optional)
+ :return: the dictionary object
+ """
+ out = {
+ 'cidr': self.cidr,
+ 'ip_version': self.ip_version,
+ }
+
+ if network:
+ out['network_id'] = network.id
+ 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 SubnetConfigError(
+ 'Could not find project ID for project named - ' +
+ self.project_name)
+ 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.enable_dhcp is not None:
+ out['enable_dhcp'] = self.enable_dhcp
+ if self.dns_nameservers and len(self.dns_nameservers) > 0:
+ out['dns_nameservers'] = self.dns_nameservers
+ if self.host_routes and len(self.host_routes) > 0:
+ out['host_routes'] = self.host_routes
+ if self.destination:
+ out['destination'] = self.destination
+ if self.nexthop:
+ out['nexthop'] = self.nexthop
+ if self.ipv6_ra_mode:
+ out['ipv6_ra_mode'] = self.ipv6_ra_mode.value
+ if self.ipv6_address_mode:
+ out['ipv6_address_mode'] = self.ipv6_address_mode.value
+ return out
+
+
+def map_mode(mode):
+ """
+ Takes a the direction value maps it to the Direction enum. When None return
+ None
+ :param mode: the mode value
+ :return: the IPv6Mode enum object
+ :raise: SubnetConfigError if value is invalid
+ """
+ if not mode:
+ return None
+ if isinstance(mode, IPv6Mode):
+ return mode
+ elif isinstance(mode, str):
+ mode_str = str(mode)
+ if mode_str == 'slaac':
+ return IPv6Mode.slaac
+ elif mode_str == 'dhcpv6-stateful':
+ return IPv6Mode.stateful
+ elif mode_str == 'stateful':
+ return IPv6Mode.stateful
+ elif mode_str == 'dhcpv6-stateless':
+ return IPv6Mode.stateless
+ elif mode_str == 'stateless':
+ return IPv6Mode.stateless
+ else:
+ raise SubnetConfigError('Invalid mode - ' + mode_str)
+ else:
+ return map_mode(mode.value)
+
+
+class SubnetConfigError(Exception):
+ """
+ Exception to be thrown when subnet settings attributes are incorrect
+ """
+
+
+class PortConfig(object):
+ """
+ Class representing a port configuration
+ """
+
+ def __init__(self, **kwargs):
+ """
+ Constructor
+ :param name: A symbolic name for the port (optional).
+ :param network_name: The name of the network on which to create the
+ port (required).
+ :param admin_state_up: A boolean value denoting the administrative
+ status of the port (default = True)
+ :param project_name: The name of the project who owns the network.
+ Only administrative users can specify a project ID
+ other than their own. You cannot change this value
+ through authorization policies (optional)
+ :param mac_address: The MAC address. If you specify an address that is
+ not valid, a Bad Request (400) status code is
+ returned. If you do not specify a MAC address,
+ OpenStack Networking tries to allocate one. If a
+ failure occurs, a Service Unavailable (503) status
+ code is returned (optional)
+ :param ip_addrs: A list of dict objects where each contains two keys
+ 'subnet_name' and 'ip' values which will get mapped to
+ self.fixed_ips. These values will be directly
+ translated into the fixed_ips dict (optional)
+ :param security_groups: One or more security group IDs.
+ :param port_security_enabled: When True, security groups will be
+ applied to the port else not
+ (default - True)
+ :param allowed_address_pairs: A dictionary containing a set of zero or
+ more allowed address pairs. An address
+ pair contains an IP address and MAC
+ address (optional)
+ :param opt_value: The extra DHCP option value (optional)
+ :param opt_name: The extra DHCP option name (optional)
+ :param device_owner: The ID of the entity that uses this port.
+ For example, a DHCP agent (optional)
+ :param device_id: The ID of the device that uses this port.
+ For example, a virtual server (optional)
+ :param extra_dhcp_opts: k/v of options to use with your DHCP (optional)
+ :return:
+ """
+ if 'port' in kwargs:
+ kwargs = kwargs['port']
+
+ self.name = kwargs.get('name')
+ self.network_name = kwargs.get('network_name')
+
+ if kwargs.get('admin_state_up') is not None:
+ self.admin_state_up = str2bool(str(kwargs['admin_state_up']))
+ else:
+ self.admin_state_up = True
+
+ self.project_name = kwargs.get('project_name')
+ self.mac_address = kwargs.get('mac_address')
+ self.ip_addrs = kwargs.get('ip_addrs')
+ self.security_groups = kwargs.get('security_groups')
+
+ if kwargs.get('port_security_enabled') is not None:
+ self.port_security_enabled = str2bool(
+ str(kwargs['port_security_enabled']))
+ else:
+ self.port_security_enabled = None
+
+ self.allowed_address_pairs = kwargs.get('allowed_address_pairs')
+ self.opt_value = kwargs.get('opt_value')
+ self.opt_name = kwargs.get('opt_name')
+ self.device_owner = kwargs.get('device_owner')
+ self.device_id = kwargs.get('device_id')
+ self.extra_dhcp_opts = kwargs.get('extra_dhcp_opts')
+
+ if not self.network_name:
+ raise PortConfigError(
+ 'The attribute network_name is required')
+
+ def __get_fixed_ips(self, neutron):
+ """
+ Sets the self.fixed_ips value
+ :param neutron: the Neutron client
+ :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})
+ else:
+ raise PortConfigError(
+ 'Invalid port configuration, subnet does not exist '
+ 'with name - ' + ip_addr_dict['subnet_name'])
+
+ return fixed_ips
+
+ def dict_for_neutron(self, neutron, os_creds):
+ """
+ Returns a dictionary object representing this object.
+ This is meant to be converted into JSON designed for use by the Neutron
+ API
+
+ TODO - expand automated testing to exercise all parameters
+ :param neutron: the Neutron client
+ :param os_creds: the OpenStack credentials
+ :return: the dictionary object
+ """
+
+ out = dict()
+
+ project_id = None
+ 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
+
+ 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)
+
+ out['network_id'] = network.id
+
+ if self.admin_state_up is not None:
+ out['admin_state_up'] = self.admin_state_up
+ if self.name:
+ out['name'] = self.name
+ if self.project_name:
+ if project_id:
+ out['tenant_id'] = project_id
+ else:
+ raise PortConfigError(
+ 'Could not find project ID for project named - ' +
+ self.project_name)
+ if self.mac_address:
+ out['mac_address'] = self.mac_address
+
+ fixed_ips = self.__get_fixed_ips(neutron)
+ if fixed_ips and len(fixed_ips) > 0:
+ out['fixed_ips'] = fixed_ips
+
+ if self.security_groups:
+ 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)
+ if sec_grp:
+ sec_grp_ids.append(sec_grp.id)
+ out['security_groups'] = sec_grp_ids
+ if self.port_security_enabled is not None:
+ out['port_security_enabled'] = self.port_security_enabled
+ if self.allowed_address_pairs and len(self.allowed_address_pairs) > 0:
+ out['allowed_address_pairs'] = self.allowed_address_pairs
+ if self.opt_value:
+ out['opt_value'] = self.opt_value
+ if self.opt_name:
+ out['opt_name'] = self.opt_name
+ if self.device_owner:
+ out['device_owner'] = self.device_owner
+ if self.device_id:
+ out['device_id'] = self.device_id
+ if self.extra_dhcp_opts:
+ out['extra_dhcp_opts'] = self.extra_dhcp_opts
+ return {'port': out}
+
+ def __eq__(self, other):
+ return (self.name == other.name and
+ self.network_name == other.network_name and
+ self.admin_state_up == other.admin_state_up and
+ self.project_name == other.project_name and
+ self.mac_address == other.mac_address and
+ self.ip_addrs == other.ip_addrs and
+ # self.fixed_ips == other.fixed_ips and
+ self.security_groups == other.security_groups and
+ self.allowed_address_pairs == other.allowed_address_pairs and
+ self.opt_value == other.opt_value and
+ self.opt_name == other.opt_name and
+ self.device_owner == other.device_owner and
+ self.device_id == other.device_id)
+
+
+class PortConfigError(Exception):
+ """
+ Exception to be thrown when port settings attributes are incorrect
+ """
--- /dev/null
+# 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.
+
+
+class ProjectConfig(object):
+ """
+ Class to hold the configuration settings required for creating OpenStack
+ project objects
+ """
+
+ def __init__(self, **kwargs):
+
+ """
+ Constructor
+ :param name: the project's name (required)
+ :param domain or domain_name: the project's domain name
+ (default = 'Default').
+ Field is used for v3 clients
+ :param description: the description (optional)
+ :param users: list of users to associate project to (optional)
+ :param enabled: denotes whether or not the project is enabled
+ (default True)
+ """
+
+ self.name = kwargs.get('name')
+ self.domain_name = kwargs.get(
+ 'domain', kwargs.get('domain', 'Default'))
+
+ self.description = kwargs.get('description')
+ if kwargs.get('enabled') is not None:
+ self.enabled = kwargs['enabled']
+ else:
+ self.enabled = True
+
+ self.users = kwargs.get('users', list())
+
+ if not self.name:
+ raise ProjectConfigError(
+ "The attribute name is required for ProjectConfig")
+
+
+class ProjectConfigError(Exception):
+ """
+ Exception to be thrown when project settings attributes are incorrect
+ """
--- /dev/null
+# 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.
+import enum
+
+
+class Consumer(enum.Enum):
+ """
+ QoS Specification consumer types
+ """
+ front_end = 'front-end'
+ back_end = 'back-end'
+ both = 'both'
+
+
+class QoSConfig(object):
+ def __init__(self, **kwargs):
+ """
+ Constructor
+ :param name: the qos's name (required)
+ :param consumer: the qos's consumer type of the enum type Consumer
+ (required)
+ :param specs: dict of key/values
+ """
+
+ self.name = kwargs.get('name')
+
+ if kwargs.get('consumer'):
+ self.consumer = map_consumer(kwargs['consumer'])
+ else:
+ self.consumer = None
+
+ self.specs = kwargs.get('specs')
+ if not self.specs:
+ self.specs = dict()
+
+ if not self.name or not self.consumer:
+ raise QoSConfigError(
+ "The attributes name and consumer are required")
+
+
+def map_consumer(consumer):
+ """
+ Takes a the protocol value maps it to the Consumer enum. When None return
+ None
+ :param consumer: the value to map to the Enum
+ :return: the Protocol enum object
+ :raise: Exception if value is invalid
+ """
+ if not consumer:
+ return None
+ elif isinstance(consumer, Consumer):
+ return consumer
+ elif isinstance(consumer, str):
+ proto_str = str(consumer)
+ if proto_str == 'front-end':
+ return Consumer.front_end
+ elif proto_str == 'back-end':
+ return Consumer.back_end
+ elif proto_str == 'both':
+ return Consumer.both
+ else:
+ raise QoSConfigError('Invalid Consumer - ' + proto_str)
+ else:
+ if consumer.value == 'front-end':
+ return Consumer.front_end
+ elif consumer.value == 'back-end':
+ return Consumer.back_end
+ elif consumer.value == 'both':
+ return Consumer.both
+ else:
+ raise QoSConfigError('Invalid Consumer - ' + consumer.value)
+
+
+class QoSConfigError(Exception):
+ """
+ Exception to be thrown when an qos settings are incorrect
+ """
+
+ def __init__(self, message):
+ Exception.__init__(self, message)
--- /dev/null
+# 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 snaps.config.network import PortConfig
+from snaps.openstack.utils import neutron_utils, keystone_utils
+
+
+class RouterConfig(object):
+ """
+ Class representing a router configuration
+ """
+
+ def __init__(self, **kwargs):
+ """
+ Constructor - all parameters are optional
+ :param name: The router name.
+ :param project_name: The name of the project who owns the network. Only
+ administrative users can specify a project ID
+ other than their own. You cannot change this value
+ through authorization policies.
+ :param external_gateway: Name of the external network to which to route
+ :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
+ :param port_settings: List of PortConfig objects
+ :return:
+ """
+ self.name = kwargs.get('name')
+ self.project_name = kwargs.get('project_name')
+ self.external_gateway = kwargs.get('external_gateway')
+
+ self.admin_state_up = kwargs.get('admin_state_up', True)
+ self.enable_snat = kwargs.get('enable_snat')
+ if kwargs.get('internal_subnets'):
+ self.internal_subnets = kwargs['internal_subnets']
+ else:
+ self.internal_subnets = list()
+
+ self.port_settings = list()
+ if kwargs.get('interfaces', kwargs.get('port_settings')):
+ interfaces = kwargs.get('interfaces', kwargs.get('port_settings'))
+ for interface in interfaces:
+ if isinstance(interface, PortConfig):
+ self.port_settings.append(interface)
+ else:
+ self.port_settings.append(
+ PortConfig(**interface['port']))
+
+ if not self.name:
+ raise RouterConfigError('Name is required')
+
+ def dict_for_neutron(self, neutron, os_creds):
+ """
+ Returns a dictionary object representing this object.
+ This is meant to be converted into JSON designed for use by the Neutron
+ API
+
+ 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
+ :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)
+
+ return {'router': out}
+
+
+class RouterConfigError(Exception):
+ """
+ Exception to be thrown when router settings attributes are incorrect
+ """
--- /dev/null
+# 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.
+import enum
+
+from snaps.openstack.utils import keystone_utils, neutron_utils
+
+
+class SecurityGroupConfig(object):
+ """
+ Class representing a keypair configuration
+ """
+
+ def __init__(self, **kwargs):
+ """
+ Constructor
+ :param name: The security group's name (required)
+ :param description: The security group's description (optional)
+ :param project_name: The name of the project under which the security
+ group will be created
+ :param rule_settings: a list of SecurityGroupRuleConfig objects
+ :return:
+ """
+ self.name = kwargs.get('name')
+ self.description = kwargs.get('description')
+ self.project_name = kwargs.get('project_name')
+ self.rule_settings = list()
+
+ rule_settings = kwargs.get('rules')
+ if not rule_settings:
+ rule_settings = kwargs.get('rule_settings')
+
+ if rule_settings:
+ for rule_setting in rule_settings:
+ if isinstance(rule_setting, SecurityGroupRuleConfig):
+ self.rule_settings.append(rule_setting)
+ else:
+ rule_setting['sec_grp_name'] = self.name
+ self.rule_settings.append(SecurityGroupRuleConfig(
+ **rule_setting))
+
+ if not self.name:
+ raise SecurityGroupConfigError('The attribute name is required')
+
+ for rule_setting in self.rule_settings:
+ if rule_setting.sec_grp_name is not self.name:
+ raise SecurityGroupConfigError(
+ 'Rule settings must correspond with the name of this '
+ 'security group')
+
+ def dict_for_neutron(self, keystone):
+ """
+ Returns a dictionary object representing this object.
+ This is meant to be converted into JSON designed for use by the Neutron
+ API
+
+ TODO - expand automated testing to exercise all parameters
+ :param keystone: the Keystone client
+ :return: the dictionary object
+ """
+ out = dict()
+
+ if self.name:
+ out['name'] = self.name
+ if self.description:
+ out['description'] = self.description
+ 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:
+ raise SecurityGroupConfigError(
+ 'Could not find project ID for project named - ' +
+ self.project_name)
+
+ return {'security_group': out}
+
+
+class Direction(enum.Enum):
+ """
+ A rule's direction
+ """
+ ingress = 'ingress'
+ egress = 'egress'
+
+
+class Protocol(enum.Enum):
+ """
+ A rule's protocol
+ """
+ ah = 51
+ dccp = 33
+ egp = 8
+ esp = 50
+ gre = 47
+ icmp = 1
+ icmpv6 = 58
+ igmp = 2
+ ipv6_encap = 41
+ ipv6_frag = 44
+ ipv6_icmp = 58
+ ipv6_nonxt = 59
+ ipv6_opts = 60
+ ipv6_route = 43
+ ospf = 89
+ pgm = 113
+ rsvp = 46
+ sctp = 132
+ tcp = 6
+ udp = 17
+ udplite = 136
+ vrrp = 112
+ any = 'any'
+ null = 'null'
+
+
+class Ethertype(enum.Enum):
+ """
+ A rule's ethertype
+ """
+ IPv4 = 4
+ IPv6 = 6
+
+
+class SecurityGroupConfigError(Exception):
+ """
+ Exception to be thrown when security group settings attributes are
+ invalid
+ """
+
+
+class SecurityGroupRuleConfig(object):
+ """
+ Class representing a keypair configuration
+ """
+
+ def __init__(self, **kwargs):
+ """
+ Constructor - all parameters are optional
+ :param sec_grp_name: The security group's name on which to add the
+ rule. (required)
+ :param description: The rule's description
+ :param direction: An enumeration of type
+ create_security_group.RULE_DIRECTION (required)
+ :param remote_group_id: The group ID to associate with this rule
+ (this should be changed to group name once
+ snaps support Groups) (optional)
+ :param protocol: An enumeration of type
+ create_security_group.RULE_PROTOCOL or a string value
+ that will be mapped accordingly (optional)
+ :param ethertype: An enumeration of type
+ create_security_group.RULE_ETHERTYPE (optional)
+ :param port_range_min: The minimum port number in the range that is
+ matched by the security group rule. When the
+ protocol is TCP or UDP, this value must be <=
+ port_range_max.
+ :param port_range_max: The maximum port number in the range that is
+ matched by the security group rule. When the
+ protocol is TCP or UDP, this value must be <=
+ port_range_max.
+ :param remote_ip_prefix: The remote IP prefix to associate with this
+ metering rule packet (optional)
+
+ TODO - Need to support the tenant...
+ """
+
+ self.description = kwargs.get('description')
+ self.sec_grp_name = kwargs.get('sec_grp_name')
+ self.remote_group_id = kwargs.get('remote_group_id')
+ self.direction = None
+ if kwargs.get('direction'):
+ self.direction = map_direction(kwargs['direction'])
+
+ self.protocol = None
+ if kwargs.get('protocol'):
+ self.protocol = map_protocol(kwargs['protocol'])
+ else:
+ self.protocol = Protocol.null
+
+ self.ethertype = None
+ if kwargs.get('ethertype'):
+ self.ethertype = map_ethertype(kwargs['ethertype'])
+
+ self.port_range_min = kwargs.get('port_range_min')
+ self.port_range_max = kwargs.get('port_range_max')
+ self.remote_ip_prefix = kwargs.get('remote_ip_prefix')
+
+ if not self.direction or not self.sec_grp_name:
+ raise SecurityGroupRuleConfigError(
+ 'direction and sec_grp_name are required')
+
+ def dict_for_neutron(self, neutron):
+ """
+ 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
+ :return: the dictionary object
+ """
+ out = dict()
+
+ if self.description:
+ out['description'] = self.description
+ if self.direction:
+ out['direction'] = self.direction.name
+ if self.port_range_min:
+ out['port_range_min'] = self.port_range_min
+ if self.port_range_max:
+ out['port_range_max'] = self.port_range_max
+ if self.ethertype:
+ out['ethertype'] = self.ethertype.name
+ if self.protocol and self.protocol.value != 'null':
+ 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)
+ if sec_grp:
+ out['security_group_id'] = sec_grp.id
+ else:
+ raise SecurityGroupRuleConfigError(
+ 'Cannot locate security group with name - ' +
+ self.sec_grp_name)
+ if self.remote_group_id:
+ out['remote_group_id'] = self.remote_group_id
+ if self.remote_ip_prefix:
+ out['remote_ip_prefix'] = self.remote_ip_prefix
+
+ return {'security_group_rule': out}
+
+ def rule_eq(self, rule):
+ """
+ Returns True if this setting created the rule
+ :param rule: the rule to evaluate
+ :return: T/F
+ """
+ if self.description is not None:
+ if rule.description is not None and rule.description != '':
+ return False
+ elif self.description != rule.description:
+ if rule.description != '':
+ return False
+
+ if self.direction.name != rule.direction:
+ return False
+
+ if self.ethertype and rule.ethertype:
+ if self.ethertype.name != rule.ethertype:
+ return False
+
+ if self.port_range_min and rule.port_range_min:
+ if self.port_range_min != rule.port_range_min:
+ return False
+
+ if self.port_range_max and rule.port_range_max:
+ if self.port_range_max != rule.port_range_max:
+ return False
+
+ if self.protocol and rule.protocol:
+ if self.protocol.name != rule.protocol:
+ return False
+
+ if self.remote_group_id and rule.remote_group_id:
+ if self.remote_group_id != rule.remote_group_id:
+ return False
+
+ if self.remote_ip_prefix and rule.remote_ip_prefix:
+ if self.remote_ip_prefix != rule.remote_ip_prefix:
+ return False
+
+ return True
+
+ def __eq__(self, other):
+ return (
+ self.description == other.description and
+ self.direction == other.direction and
+ self.port_range_min == other.port_range_min and
+ self.port_range_max == other.port_range_max and
+ self.ethertype == other.ethertype and
+ self.protocol == other.protocol and
+ self.sec_grp_name == other.sec_grp_name and
+ self.remote_group_id == other.remote_group_id and
+ self.remote_ip_prefix == other.remote_ip_prefix)
+
+ def __hash__(self):
+ return hash((self.sec_grp_name, self.description, self.direction,
+ self.remote_group_id,
+ self.protocol, self.ethertype, self.port_range_min,
+ self.port_range_max, self.remote_ip_prefix))
+
+
+def map_direction(direction):
+ """
+ Takes a the direction value maps it to the Direction enum. When None return
+ None
+ :param direction: the direction value
+ :return: the Direction enum object
+ :raise: Exception if value is invalid
+ """
+ if not direction:
+ return None
+ if isinstance(direction, Direction):
+ return direction
+ elif (isinstance(direction, str) or isinstance(direction, unicode)
+ or isinstance(direction, unicode)):
+ dir_str = str(direction)
+ if dir_str == 'egress':
+ return Direction.egress
+ elif dir_str == 'ingress':
+ return Direction.ingress
+ else:
+ raise SecurityGroupRuleConfigError(
+ 'Invalid Direction - ' + dir_str)
+ else:
+ return map_direction(direction.value)
+
+
+def map_protocol(protocol):
+ """
+ Takes a the protocol value maps it to the Protocol enum. When None return
+ None
+ :param protocol: the protocol value
+ :return: the Protocol enum object
+ :raise: Exception if value is invalid
+ """
+ if not protocol:
+ return None
+ elif isinstance(protocol, Protocol):
+ return protocol
+ elif (isinstance(protocol, str) or isinstance(protocol, unicode)
+ 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)
+ else:
+ return map_protocol(protocol.value)
+
+
+def map_ethertype(ethertype):
+ """
+ Takes a the ethertype value maps it to the Ethertype enum. When None return
+ None
+ :param ethertype: the ethertype value
+ :return: the Ethertype enum object
+ :raise: Exception if value is invalid
+ """
+ if not ethertype:
+ return None
+ elif isinstance(ethertype, Ethertype):
+ return ethertype
+ elif (isinstance(ethertype, str) or isinstance(ethertype, unicode)
+ or isinstance(ethertype, int)):
+ eth_str = str(ethertype)
+ if eth_str == 'IPv6' or eth_str == '6':
+ return Ethertype.IPv6
+ elif eth_str == 'IPv4' or eth_str == '4':
+ return Ethertype.IPv4
+ else:
+ raise SecurityGroupRuleConfigError(
+ 'Invalid Ethertype - ' + eth_str)
+ else:
+ return map_ethertype(ethertype.value)
+
+
+class SecurityGroupRuleConfigError(Exception):
+ """
+ Exception to be thrown when security group rule settings attributes are
+ invalid
+ """
--- /dev/null
+# 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.
+
+STACK_DELETE_TIMEOUT = 1200
+STACK_COMPLETE_TIMEOUT = 1200
+POLL_INTERVAL = 3
+STATUS_CREATE_FAILED = 'CREATE_FAILED'
+STATUS_CREATE_COMPLETE = 'CREATE_COMPLETE'
+STATUS_DELETE_COMPLETE = 'DELETE_COMPLETE'
+STATUS_DELETE_FAILED = 'DELETE_FAILED'
+
+
+class StackConfig(object):
+ """
+ Configuration for Heat stack
+ """
+
+ def __init__(self, **kwargs):
+ """
+ Constructor
+ :param name: the stack's name (required)
+ :param template: the heat template in dict() format (required if
+ template_path attribute is None)
+ :param template_path: the location of the heat template file (required
+ if template attribute is None)
+ :param resource_files: List of file paths to the resources referred to
+ by the template
+ :param env_values: dict() of strings for substitution of template
+ default values (optional)
+ """
+
+ self.name = kwargs.get('name')
+ self.template = kwargs.get('template')
+ self.template_path = kwargs.get('template_path')
+ self.resource_files = kwargs.get('resource_files')
+ self.env_values = kwargs.get('env_values')
+
+ if 'stack_create_timeout' in kwargs:
+ self.stack_create_timeout = kwargs['stack_create_timeout']
+ else:
+ self.stack_create_timeout = STACK_COMPLETE_TIMEOUT
+
+ if not self.name:
+ raise StackConfigError('name is required')
+
+ if not self.template and not self.template_path:
+ raise StackConfigError('A Heat template is required')
+
+ if self.resource_files and not isinstance(self.resource_files, list):
+ raise StackConfigError(
+ 'resource_files must be a list when not None')
+
+ def __eq__(self, other):
+ return (self.name == other.name and
+ self.template == other.template and
+ self.template_path == other.template_path and
+ self.env_values == other.env_values and
+ self.stack_create_timeout == other.stack_create_timeout)
+
+
+class StackConfigError(Exception):
+ """
+ Exception to be thrown when an stack configuration are incorrect
+ """
--- /dev/null
+# 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.
+__author__ = 'spisarski'
--- /dev/null
+# 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.
+import unittest
+
+from snaps.config.cluster_template import (
+ ClusterTemplateConfig, ClusterTemplateConfigError, ServerType,
+ DockerStorageDriver, ContainerOrchestrationEngine)
+
+
+class ClusterTemplateConfigUnitTests(unittest.TestCase):
+ """
+ Tests the construction of the ClusterTemplateConfig class
+ """
+
+ def test_no_params(self):
+ with self.assertRaises(ClusterTemplateConfigError):
+ ClusterTemplateConfig()
+
+ def test_empty_config(self):
+ with self.assertRaises(ClusterTemplateConfigError):
+ ClusterTemplateConfig(config=dict())
+
+ def test_name_only(self):
+ with self.assertRaises(ClusterTemplateConfigError):
+ ClusterTemplateConfig(name='foo')
+
+ def test_minimal_named(self):
+ config = ClusterTemplateConfig(
+ name='foo', image='bar', keypair='keys', external_net='external')
+ self.assertIsNotNone(config)
+ self.assertEqual('foo', config.name)
+ self.assertEqual('bar', config.image)
+ self.assertEqual('keys', config.keypair)
+ self.assertIsNone(config.network_driver)
+ self.assertEqual('external', config.external_net)
+ self.assertTrue(config.floating_ip_enabled)
+ self.assertEqual(3, config.docker_volume_size)
+ self.assertEqual(ServerType.vm, config.server_type)
+ self.assertIsNone(config.flavor)
+ self.assertIsNone(config.master_flavor)
+ self.assertEqual(ContainerOrchestrationEngine.kubernetes, config.coe)
+ self.assertIsNone(config.fixed_net)
+ self.assertIsNone(config.fixed_subnet)
+ self.assertTrue(config.registry_enabled)
+ self.assertIsNone(config.insecure_registry)
+ self.assertEqual(DockerStorageDriver.devicemapper,
+ config.docker_storage_driver)
+ self.assertIsNone(config.dns_nameserver)
+ self.assertFalse(config.public)
+ self.assertFalse(config.tls_disabled)
+ self.assertIsNone(config.http_proxy)
+ self.assertIsNone(config.https_proxy)
+ self.assertIsNone(config.no_proxy)
+ self.assertIsNone(config.volume_driver)
+ self.assertTrue(config.master_lb_enabled)
+ self.assertIsNone(config.labels)
+
+ def test_minimal_config(self):
+ config = ClusterTemplateConfig(
+ **{'name': 'foo', 'image': 'bar', 'keypair': 'keys',
+ 'external_net': 'external'})
+ self.assertIsNotNone(config)
+ self.assertEqual('foo', config.name)
+ self.assertEqual('bar', config.image)
+ self.assertEqual('keys', config.keypair)
+ self.assertIsNone(config.network_driver)
+ self.assertEqual('external', config.external_net)
+ self.assertTrue(config.floating_ip_enabled)
+ self.assertEqual(3, config.docker_volume_size)
+ self.assertEqual(ServerType.vm, config.server_type)
+ self.assertIsNone(config.flavor)
+ self.assertIsNone(config.master_flavor)
+ self.assertEqual(ContainerOrchestrationEngine.kubernetes, config.coe)
+ self.assertIsNone(config.fixed_net)
+ self.assertIsNone(config.fixed_subnet)
+ self.assertTrue(config.registry_enabled)
+ self.assertIsNone(config.insecure_registry)
+ self.assertEqual(DockerStorageDriver.devicemapper,
+ config.docker_storage_driver)
+ self.assertIsNone(config.dns_nameserver)
+ self.assertFalse(config.public)
+ self.assertFalse(config.tls_disabled)
+ self.assertIsNone(config.http_proxy)
+ self.assertIsNone(config.https_proxy)
+ self.assertIsNone(config.no_proxy)
+ self.assertIsNone(config.volume_driver)
+ self.assertTrue(config.master_lb_enabled)
+ self.assertIsNone(config.labels)
+
+ def test_all_named(self):
+ labels = {'foo': 'bar'}
+ config = ClusterTemplateConfig(
+ name='foo', image='bar', keypair='keys', network_driver='driver',
+ external_net='external', docker_volume_size=99,
+ server_type=ServerType.baremetal, flavor='testFlavor',
+ master_flavor='masterFlavor',
+ coe=ContainerOrchestrationEngine.kubernetes, fixed_net='fixedNet',
+ fixed_subnet='fixedSubnet', registry_enabled=False,
+ docker_storage_driver=DockerStorageDriver.overlay,
+ dns_nameserver='8.8.4.4', public=True, tls=False,
+ http_proxy='http://foo:8080', https_proxy='https://foo:443',
+ no_proxy='foo,bar', volume_driver='volDriver',
+ master_lb_enabled=False, labels=labels)
+ self.assertIsNotNone(config)
+ self.assertEqual('foo', config.name)
+ self.assertEqual('bar', config.image)
+ self.assertEqual('keys', config.keypair)
+ self.assertEqual('driver', config.network_driver)
+ self.assertEqual('external', config.external_net)
+ self.assertEqual(99, config.docker_volume_size)
+ self.assertEqual(ServerType.baremetal, config.server_type)
+ self.assertEqual('testFlavor', config.flavor)
+ self.assertEqual('masterFlavor', config.master_flavor)
+ self.assertEqual(ContainerOrchestrationEngine.kubernetes, config.coe)
+ self.assertEqual('fixedNet', config.fixed_net)
+ self.assertEqual('fixedSubnet', config.fixed_subnet)
+ self.assertFalse(config.registry_enabled)
+ self.assertEqual(DockerStorageDriver.overlay,
+ config.docker_storage_driver)
+ self.assertEqual('8.8.4.4', config.dns_nameserver)
+ self.assertTrue(config.public)
+ self.assertFalse(config.tls_disabled)
+ self.assertEqual('http://foo:8080', config.http_proxy)
+ self.assertEqual('https://foo:443', config.https_proxy)
+ self.assertEqual('foo,bar', config.no_proxy)
+ self.assertEqual('volDriver', config.volume_driver)
+ self.assertFalse(config.master_lb_enabled)
+ self.assertEqual(labels, config.labels)
+
+ def test_all_config(self):
+ labels = {'foo': 'bar'}
+ config = ClusterTemplateConfig(**{
+ 'name': 'foo', 'image': 'bar', 'keypair': 'keys',
+ 'network_driver': 'driver', 'external_net': 'external',
+ 'docker_volume_size': '99', 'server_type': 'baremetal',
+ 'flavor': 'testFlavor', 'master_flavor': 'masterFlavor',
+ 'coe': 'kubernetes', 'fixed_net': 'fixedNet',
+ 'fixed_subnet': 'fixedSubnet', 'registry_enabled': 'false',
+ 'docker_storage_driver': 'overlay', 'dns_nameserver': '8.8.4.4',
+ 'public': 'true', 'tls': 'false', 'http_proxy': 'http://foo:8080',
+ 'https_proxy': 'https://foo:443', 'no_proxy': 'foo,bar',
+ 'volume_driver': 'volDriver', 'master_lb_enabled': 'false',
+ 'labels': labels})
+ self.assertIsNotNone(config)
+ self.assertEqual('foo', config.name)
+ self.assertEqual('bar', config.image)
+ self.assertEqual('keys', config.keypair)
+ self.assertEqual('driver', config.network_driver)
+ self.assertEqual('external', config.external_net)
+ self.assertEqual(99, config.docker_volume_size)
+ self.assertEqual(ServerType.baremetal, config.server_type)
+ self.assertEqual('testFlavor', config.flavor)
+ self.assertEqual('masterFlavor', config.master_flavor)
+ self.assertEqual(ContainerOrchestrationEngine.kubernetes, config.coe)
+ self.assertEqual('fixedNet', config.fixed_net)
+ self.assertEqual('fixedSubnet', config.fixed_subnet)
+ self.assertFalse(config.registry_enabled)
+ self.assertEqual(DockerStorageDriver.overlay,
+ config.docker_storage_driver)
+ self.assertEqual('8.8.4.4', config.dns_nameserver)
+ self.assertTrue(config.public)
+ self.assertFalse(config.tls_disabled)
+ self.assertEqual('http://foo:8080', config.http_proxy)
+ self.assertEqual('https://foo:443', config.https_proxy)
+ self.assertEqual('foo,bar', config.no_proxy)
+ self.assertEqual('volDriver', config.volume_driver)
+ self.assertFalse(config.master_lb_enabled)
+ self.assertEqual(labels, config.labels)
--- /dev/null
+# 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.
+import unittest
+
+from snaps.config.flavor import FlavorConfig, FlavorConfigError
+
+
+class FlavorConfigUnitTests(unittest.TestCase):
+ """
+ Tests the construction of the FlavorConfig class
+ """
+
+ def test_no_params(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig()
+
+ def test_empty_config(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(config=dict())
+
+ def test_name_only(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(name='foo')
+
+ def test_config_with_name_only(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(config={'name': 'foo'})
+
+ def test_name_ram_only(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(name='foo', ram=1)
+
+ def test_config_with_name_ram_only(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(config={'name': 'foo', 'ram': 1})
+
+ def test_name_ram_disk_only(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(name='foo', ram=1, disk=1)
+
+ def test_config_with_name_ram_disk_only(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(config={'name': 'foo', 'ram': 1, 'disk': 1})
+
+ def test_ram_string(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(name='foo', ram='bar', disk=2, vcpus=3, ephemeral=4,
+ swap=5, rxtx_factor=6.0,
+ is_public=False)
+
+ def test_config_ram_string(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(
+ config={'name': 'foo', 'ram': 'bar', 'disk': 2, 'vcpus': 3,
+ 'ephemeral': 4, 'swap': 5,
+ 'rxtx_factor': 6.0, 'is_public': False})
+
+ def test_ram_float(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(name='foo', ram=1.5, disk=2, vcpus=3, ephemeral=4,
+ swap=5, rxtx_factor=6.0, is_public=False)
+
+ def test_config_ram_float(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(
+ config={'name': 'foo', 'ram': 1.5, 'disk': 2, 'vcpus': 3,
+ 'ephemeral': 4, 'swap': 5,
+ 'rxtx_factor': 6.0, 'is_public': False})
+
+ def test_disk_string(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(name='foo', ram=1, disk='bar', vcpus=3, ephemeral=4,
+ swap=5, rxtx_factor=6.0,
+ is_public=False)
+
+ def test_config_disk_string(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(
+ config={'name': 'foo', 'ram': 1, 'disk': 'bar', 'vcpus': 3,
+ 'ephemeral': 4, 'swap': 5,
+ 'rxtx_factor': 6.0, 'is_public': False})
+
+ def test_disk_float(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(name='foo', ram=1, disk=2.5, vcpus=3, ephemeral=4,
+ swap=5, rxtx_factor=6.0, is_public=False)
+
+ def test_config_disk_float(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(
+ config={'name': 'foo', 'ram': 1, 'disk': 2.5, 'vcpus': 3,
+ 'ephemeral': 4, 'swap': 5,
+ 'rxtx_factor': 6.0, 'is_public': False})
+
+ def test_vcpus_string(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(name='foo', ram=1, disk=2, vcpus='bar', ephemeral=4,
+ swap=5, rxtx_factor=6.0,
+ is_public=False)
+
+ def test_config_vcpus_string(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(
+ config={'name': 'foo', 'ram': 1, 'disk': 2, 'vcpus': 'bar',
+ 'ephemeral': 4, 'swap': 5,
+ 'rxtx_factor': 6.0, 'is_public': False})
+
+ def test_ephemeral_string(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(name='foo', ram=1, disk=2, vcpus=3, ephemeral='bar',
+ swap=5, rxtx_factor=6.0,
+ is_public=False)
+
+ def test_config_ephemeral_string(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(
+ config={'name': 'foo', 'ram': 1, 'disk': 2, 'vcpus': 3,
+ 'ephemeral': 'bar', 'swap': 5,
+ 'rxtx_factor': 6.0, 'is_public': False})
+
+ def test_ephemeral_float(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(name='foo', ram=1, disk=2, vcpus=3, ephemeral=4.5,
+ swap=5, rxtx_factor=6.0, is_public=False)
+
+ def test_config_ephemeral_float(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(
+ config={'name': 'foo', 'ram': 1, 'disk': 2, 'vcpus': 3,
+ 'ephemeral': 4.5, 'swap': 5,
+ 'rxtx_factor': 6.0, 'is_public': False})
+
+ def test_swap_string(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(name='foo', ram=1, disk=2, vcpus=3, ephemeral=4,
+ swap='bar', rxtx_factor=6.0,
+ is_public=False)
+
+ def test_config_swap_string(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(
+ config={'name': 'foo', 'ram': 1, 'disk': 2, 'vcpus': 3,
+ 'ephemeral': 4, 'swap': 'bar',
+ 'rxtx_factor': 6.0, 'is_public': False})
+
+ def test_swap_float(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(name='foo', ram=1, disk=2, vcpus=3, ephemeral=4,
+ swap=5.5, rxtx_factor=6.0, is_public=False)
+
+ def test_config_swap_float(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(
+ config={'name': 'foo', 'ram': 1, 'disk': 2, 'vcpus': 3,
+ 'ephemeral': 4, 'swap': 5.5,
+ 'rxtx_factor': 6.0, 'is_public': False})
+
+ def test_rxtx_string(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(name='foo', ram=1, disk=2, vcpus=3, ephemeral=4,
+ swap=5, rxtx_factor='bar', is_public=False)
+
+ def test_config_rxtx_string(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(
+ config={'name': 'foo', 'ram': 1, 'disk': 2, 'vcpus': 3,
+ 'ephemeral': 4, 'swap': 5,
+ 'rxtx_factor': 'bar', 'is_public': False})
+
+ def test_is_pub_string(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(name='foo', ram=1, disk=2, vcpus=3, ephemeral=4,
+ swap=5, rxtx_factor=6.0, is_public='bar')
+
+ def test_config_is_pub_string(self):
+ with self.assertRaises(FlavorConfigError):
+ FlavorConfig(
+ config={'name': 'foo', 'ram': 1, 'disk': 2, 'vcpus': 3,
+ 'ephemeral': 4, 'swap': 5,
+ 'rxtx_factor': 6.0, 'is_public': 'bar'})
+
+ def test_name_ram_disk_vcpus_only(self):
+ settings = FlavorConfig(name='foo', ram=1, disk=2, vcpus=3)
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('auto', settings.flavor_id)
+ self.assertEqual(1, settings.ram)
+ self.assertEqual(2, settings.disk)
+ self.assertEqual(3, settings.vcpus)
+ self.assertEqual(0, settings.ephemeral)
+ self.assertEqual(0, settings.swap)
+ self.assertEqual(1.0, settings.rxtx_factor)
+ self.assertEqual(True, settings.is_public)
+ self.assertEqual(None, settings.metadata)
+
+ def test_config_with_name_ram_disk_vcpus_only(self):
+ settings = FlavorConfig(
+ **{'name': 'foo', 'ram': 1, 'disk': 2, 'vcpus': 3})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('auto', settings.flavor_id)
+ self.assertEqual(1, settings.ram)
+ self.assertEqual(2, settings.disk)
+ self.assertEqual(3, settings.vcpus)
+ self.assertEqual(0, settings.ephemeral)
+ self.assertEqual(0, settings.swap)
+ self.assertEqual(1.0, settings.rxtx_factor)
+ self.assertEqual(True, settings.is_public)
+ self.assertEqual(None, settings.metadata)
+
+ def test_all(self):
+ metadata = {'foo': 'bar'}
+ settings = FlavorConfig(
+ name='foo', flavor_id='bar', ram=1, disk=2, vcpus=3, ephemeral=4,
+ swap=5, rxtx_factor=6.0, is_public=False, metadata=metadata)
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('bar', settings.flavor_id)
+ self.assertEqual(1, settings.ram)
+ self.assertEqual(2, settings.disk)
+ self.assertEqual(3, settings.vcpus)
+ self.assertEqual(4, settings.ephemeral)
+ self.assertEqual(5, settings.swap)
+ self.assertEqual(6.0, settings.rxtx_factor)
+ self.assertEqual(False, settings.is_public)
+ self.assertEqual(metadata, settings.metadata)
+
+ def test_config_all(self):
+ metadata = {'foo': 'bar'}
+ settings = FlavorConfig(
+ **{'name': 'foo', 'flavor_id': 'bar', 'ram': 1, 'disk': 2,
+ 'vcpus': 3,
+ 'ephemeral': 4, 'swap': 5, 'rxtx_factor': 6.0,
+ 'is_public': False,
+ 'metadata': metadata})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('bar', settings.flavor_id)
+ self.assertEqual(1, settings.ram)
+ self.assertEqual(2, settings.disk)
+ self.assertEqual(3, settings.vcpus)
+ self.assertEqual(4, settings.ephemeral)
+ self.assertEqual(5, settings.swap)
+ self.assertEqual(6.0, settings.rxtx_factor)
+ self.assertEqual(False, settings.is_public)
+ self.assertEqual(metadata, settings.metadata)
--- /dev/null
+# 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.
+import unittest
+
+from snaps.config.image import ImageConfigError, ImageConfig
+
+
+class ImageConfigUnitTests(unittest.TestCase):
+ """
+ Tests the construction of the ImageConfig class
+ """
+
+ def test_no_params(self):
+ with self.assertRaises(ImageConfigError):
+ ImageConfig()
+
+ def test_empty_config(self):
+ with self.assertRaises(ImageConfigError):
+ ImageConfig(**dict())
+
+ def test_name_only(self):
+ with self.assertRaises(ImageConfigError):
+ ImageConfig(name='foo')
+
+ def test_config_with_name_only(self):
+ with self.assertRaises(ImageConfigError):
+ ImageConfig(**{'name': 'foo'})
+
+ def test_name_user_only(self):
+ with self.assertRaises(ImageConfigError):
+ ImageConfig(name='foo', image_user='bar')
+
+ def test_config_with_name_user_only(self):
+ with self.assertRaises(ImageConfigError):
+ ImageConfig(**{'name': 'foo', 'image_user': 'bar'})
+
+ def test_name_user_format_only(self):
+ with self.assertRaises(ImageConfigError):
+ ImageConfig(name='foo', image_user='bar', img_format='qcow2')
+
+ def test_config_with_name_user_format_only(self):
+ with self.assertRaises(ImageConfigError):
+ ImageConfig(
+ **{'name': 'foo', 'image_user': 'bar', 'format': 'qcow2'})
+
+ def test_name_user_format_url_only(self):
+ settings = ImageConfig(name='foo', image_user='bar',
+ img_format='qcow2', url='http://foo.com')
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('bar', settings.image_user)
+ self.assertEqual('qcow2', settings.format)
+ self.assertEqual('http://foo.com', settings.url)
+ self.assertIsNone(settings.image_file)
+ self.assertFalse(settings.exists)
+ self.assertFalse(settings.public)
+ self.assertIsNone(settings.nic_config_pb_loc)
+
+ def test_name_user_format_url_only_properties(self):
+ properties = {'hw_video_model': 'vga'}
+ settings = ImageConfig(name='foo', image_user='bar',
+ img_format='qcow2', url='http://foo.com',
+ extra_properties=properties)
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('bar', settings.image_user)
+ self.assertEqual('qcow2', settings.format)
+ self.assertEqual('http://foo.com', settings.url)
+ self.assertEqual(properties, settings.extra_properties)
+ self.assertIsNone(settings.image_file)
+ self.assertFalse(settings.exists)
+ self.assertFalse(settings.public)
+ self.assertIsNone(settings.nic_config_pb_loc)
+
+ def test_config_with_name_user_format_url_only(self):
+ settings = ImageConfig(
+ **{'name': 'foo', 'image_user': 'bar', 'format': 'qcow2',
+ 'download_url': 'http://foo.com'})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('bar', settings.image_user)
+ self.assertEqual('qcow2', settings.format)
+ self.assertEqual('http://foo.com', settings.url)
+ self.assertIsNone(settings.image_file)
+ self.assertFalse(settings.exists)
+ self.assertFalse(settings.public)
+ self.assertIsNone(settings.nic_config_pb_loc)
+
+ def test_name_user_format_file_only(self):
+ settings = ImageConfig(name='foo', image_user='bar',
+ img_format='qcow2',
+ image_file='/foo/bar.qcow')
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('bar', settings.image_user)
+ self.assertEqual('qcow2', settings.format)
+ self.assertIsNone(settings.url)
+ self.assertEqual('/foo/bar.qcow', settings.image_file)
+ self.assertFalse(settings.exists)
+ self.assertFalse(settings.public)
+ self.assertIsNone(settings.nic_config_pb_loc)
+
+ def test_config_with_name_user_format_file_only(self):
+ settings = ImageConfig(
+ **{'name': 'foo', 'image_user': 'bar', 'format': 'qcow2',
+ 'image_file': '/foo/bar.qcow'})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('bar', settings.image_user)
+ self.assertEqual('qcow2', settings.format)
+ self.assertIsNone(settings.url)
+ self.assertEqual('/foo/bar.qcow', settings.image_file)
+ self.assertFalse(settings.exists)
+ self.assertFalse(settings.public)
+ self.assertIsNone(settings.nic_config_pb_loc)
+
+ def test_all_url(self):
+ properties = {'hw_video_model': 'vga'}
+ kernel_settings = ImageConfig(name='kernel', url='http://kernel.com',
+ image_user='bar', img_format='qcow2')
+ ramdisk_settings = ImageConfig(name='ramdisk',
+ url='http://ramdisk.com',
+ image_user='bar', img_format='qcow2')
+ settings = ImageConfig(name='foo', image_user='bar',
+ img_format='qcow2', url='http://foo.com',
+ extra_properties=properties,
+ nic_config_pb_loc='/foo/bar',
+ kernel_image_settings=kernel_settings,
+ ramdisk_image_settings=ramdisk_settings,
+ exists=True, public=True)
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('bar', settings.image_user)
+ self.assertEqual('qcow2', settings.format)
+ self.assertEqual('http://foo.com', settings.url)
+ self.assertEqual(properties, settings.extra_properties)
+ self.assertIsNone(settings.image_file)
+ self.assertEqual('/foo/bar', settings.nic_config_pb_loc)
+ self.assertEqual('kernel', settings.kernel_image_settings.name)
+ self.assertEqual('http://kernel.com',
+ settings.kernel_image_settings.url)
+ self.assertEqual('bar', settings.kernel_image_settings.image_user)
+ self.assertEqual('qcow2', settings.kernel_image_settings.format)
+ self.assertEqual('ramdisk', settings.ramdisk_image_settings.name)
+ self.assertEqual('http://ramdisk.com',
+ settings.ramdisk_image_settings.url)
+ self.assertEqual('bar', settings.ramdisk_image_settings.image_user)
+ self.assertEqual('qcow2', settings.ramdisk_image_settings.format)
+ self.assertTrue(settings.exists)
+ self.assertTrue(settings.public)
+
+ def test_config_all_url(self):
+ settings = ImageConfig(
+ **{'name': 'foo', 'image_user': 'bar', 'format': 'qcow2',
+ 'download_url': 'http://foo.com',
+ 'extra_properties': '{\'hw_video_model\': \'vga\'}',
+ 'nic_config_pb_loc': '/foo/bar',
+ 'kernel_image_settings': {
+ 'name': 'kernel',
+ 'download_url': 'http://kernel.com',
+ 'image_user': 'bar',
+ 'format': 'qcow2'},
+ 'ramdisk_image_settings': {
+ 'name': 'ramdisk',
+ 'download_url': 'http://ramdisk.com',
+ 'image_user': 'bar',
+ 'format': 'qcow2'},
+ 'exists': True, 'public': True})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('bar', settings.image_user)
+ self.assertEqual('qcow2', settings.format)
+ self.assertEqual('http://foo.com', settings.url)
+ self.assertEqual('{\'hw_video_model\': \'vga\'}',
+ settings.extra_properties)
+ self.assertIsNone(settings.image_file)
+ self.assertEqual('/foo/bar', settings.nic_config_pb_loc)
+ self.assertEqual('kernel', settings.kernel_image_settings.name)
+ self.assertEqual('http://kernel.com',
+ settings.kernel_image_settings.url)
+ self.assertEqual('ramdisk', settings.ramdisk_image_settings.name)
+ self.assertEqual('http://ramdisk.com',
+ settings.ramdisk_image_settings.url)
+ self.assertTrue(settings.exists)
+ self.assertTrue(settings.public)
+
+ def test_all_file(self):
+ properties = {'hw_video_model': 'vga'}
+ settings = ImageConfig(name='foo', image_user='bar',
+ img_format='qcow2',
+ image_file='/foo/bar.qcow',
+ extra_properties=properties,
+ nic_config_pb_loc='/foo/bar', exists=True,
+ public=True)
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('bar', settings.image_user)
+ self.assertEqual('qcow2', settings.format)
+ self.assertIsNone(settings.url)
+ self.assertEqual('/foo/bar.qcow', settings.image_file)
+ self.assertEqual(properties, settings.extra_properties)
+ self.assertEqual('/foo/bar', settings.nic_config_pb_loc)
+ self.assertTrue(settings.exists)
+ self.assertTrue(settings.public)
+
+ def test_config_all_file(self):
+ settings = ImageConfig(
+ **{'name': 'foo', 'image_user': 'bar', 'format': 'qcow2',
+ 'image_file': '/foo/bar.qcow',
+ 'extra_properties': '{\'hw_video_model\' : \'vga\'}',
+ 'nic_config_pb_loc': '/foo/bar', 'exists': True,
+ 'public': True})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('bar', settings.image_user)
+ self.assertEqual('qcow2', settings.format)
+ self.assertIsNone(settings.url)
+ self.assertEqual('/foo/bar.qcow', settings.image_file)
+ self.assertEqual('{\'hw_video_model\' : \'vga\'}',
+ settings.extra_properties)
+ self.assertEqual('/foo/bar', settings.nic_config_pb_loc)
+ self.assertTrue(settings.exists)
+ self.assertTrue(settings.public)
--- /dev/null
+# 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.
+import unittest
+
+from snaps.config.keypair import KeypairConfigError, KeypairConfig
+
+
+class KeypairConfigUnitTests(unittest.TestCase):
+ """
+ Tests the construction of the KeypairConfig class
+ """
+
+ def test_no_params(self):
+ with self.assertRaises(KeypairConfigError):
+ KeypairConfig()
+
+ def test_empty_config(self):
+ with self.assertRaises(KeypairConfigError):
+ KeypairConfig(**dict())
+
+ def test_small_key_size(self):
+ with self.assertRaises(KeypairConfigError):
+ KeypairConfig(name='foo', key_size=511)
+
+ def test_name_only(self):
+ settings = KeypairConfig(name='foo')
+ self.assertEqual('foo', settings.name)
+ self.assertEqual(1024, settings.key_size)
+ self.assertIsNone(settings.public_filepath)
+ self.assertIsNone(settings.private_filepath)
+ self.assertIsNone(settings.delete_on_clean)
+
+ def test_config_with_name_only(self):
+ settings = KeypairConfig(**{'name': 'foo'})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual(1024, settings.key_size)
+ self.assertIsNone(settings.public_filepath)
+ self.assertIsNone(settings.private_filepath)
+ self.assertIsNone(settings.delete_on_clean)
+
+ def test_name_pub_only(self):
+ settings = KeypairConfig(name='foo', public_filepath='/foo/bar.pub')
+ self.assertEqual('foo', settings.name)
+ self.assertEqual(1024, settings.key_size)
+ self.assertEqual('/foo/bar.pub', settings.public_filepath)
+ self.assertIsNone(settings.private_filepath)
+ self.assertIsNone(settings.delete_on_clean)
+
+ def test_config_with_name_pub_only(self):
+ settings = KeypairConfig(
+ **{'name': 'foo', 'public_filepath': '/foo/bar.pub'})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual(1024, settings.key_size)
+ self.assertEqual('/foo/bar.pub', settings.public_filepath)
+ self.assertIsNone(settings.private_filepath)
+ self.assertIsNone(settings.delete_on_clean)
+
+ def test_name_priv_only(self):
+ settings = KeypairConfig(name='foo', private_filepath='/foo/bar')
+ self.assertEqual('foo', settings.name)
+ self.assertEqual(1024, settings.key_size)
+ self.assertIsNone(settings.public_filepath)
+ self.assertEqual('/foo/bar', settings.private_filepath)
+ self.assertIsNone(settings.delete_on_clean)
+
+ def test_config_with_name_priv_only(self):
+ settings = KeypairConfig(
+ **{'name': 'foo', 'private_filepath': '/foo/bar'})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual(1024, settings.key_size)
+ self.assertIsNone(settings.public_filepath)
+ self.assertEqual('/foo/bar', settings.private_filepath)
+ self.assertIsNone(settings.delete_on_clean)
+
+ def test_all_delete_bool(self):
+ settings = KeypairConfig(
+ name='foo', public_filepath='/foo/bar.pub',
+ private_filepath='/foo/bar', delete_on_clean=True,
+ key_size=999)
+ self.assertEqual('foo', settings.name)
+ self.assertEqual(999, settings.key_size)
+ self.assertEqual('/foo/bar.pub', settings.public_filepath)
+ self.assertEqual('/foo/bar', settings.private_filepath)
+ self.assertTrue(settings.delete_on_clean)
+
+ def test_all_delete_str_true_cap(self):
+ settings = KeypairConfig(
+ name='foo', public_filepath='/foo/bar.pub',
+ private_filepath='/foo/bar', delete_on_clean='True')
+ self.assertEqual('foo', settings.name)
+ self.assertEqual(1024, settings.key_size)
+ self.assertEqual('/foo/bar.pub', settings.public_filepath)
+ self.assertEqual('/foo/bar', settings.private_filepath)
+ self.assertTrue(settings.delete_on_clean)
+
+ def test_all_delete_str_true_lc(self):
+ settings = KeypairConfig(
+ name='foo', public_filepath='/foo/bar.pub',
+ private_filepath='/foo/bar', delete_on_clean='true')
+ self.assertEqual('foo', settings.name)
+ self.assertEqual(1024, settings.key_size)
+ self.assertEqual('/foo/bar.pub', settings.public_filepath)
+ self.assertEqual('/foo/bar', settings.private_filepath)
+ self.assertTrue(settings.delete_on_clean)
+
+ def test_all_delete_str_false_cap(self):
+ settings = KeypairConfig(
+ name='foo', public_filepath='/foo/bar.pub',
+ private_filepath='/foo/bar', delete_on_clean='False')
+ self.assertEqual('foo', settings.name)
+ self.assertEqual(1024, settings.key_size)
+ self.assertEqual('/foo/bar.pub', settings.public_filepath)
+ self.assertEqual('/foo/bar', settings.private_filepath)
+ self.assertFalse(settings.delete_on_clean)
+
+ def test_all_delete_str_false_lc(self):
+ settings = KeypairConfig(
+ name='foo', public_filepath='/foo/bar.pub',
+ private_filepath='/foo/bar', delete_on_clean='false',
+ key_size='999')
+ self.assertEqual('foo', settings.name)
+ self.assertEqual(999, settings.key_size)
+ self.assertEqual('/foo/bar.pub', settings.public_filepath)
+ self.assertEqual('/foo/bar', settings.private_filepath)
+ self.assertFalse(settings.delete_on_clean)
+
+ def test_config_all_delete_false_bool(self):
+ settings = KeypairConfig(
+ **{'name': 'foo', 'public_filepath': '/foo/bar.pub',
+ 'private_filepath': '/foo/bar', 'delete_on_clean': False,
+ 'key_size': 999})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual(999, settings.key_size)
+ self.assertEqual('/foo/bar.pub', settings.public_filepath)
+ self.assertEqual('/foo/bar', settings.private_filepath)
+ self.assertFalse(settings.delete_on_clean)
+
+ def test_config_all_delete_false_str_cap(self):
+ settings = KeypairConfig(
+ **{'name': 'foo', 'public_filepath': '/foo/bar.pub',
+ 'private_filepath': '/foo/bar', 'delete_on_clean': 'False'})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual(1024, settings.key_size)
+ self.assertEqual('/foo/bar.pub', settings.public_filepath)
+ self.assertEqual('/foo/bar', settings.private_filepath)
+ self.assertFalse(settings.delete_on_clean)
+
+ def test_config_all_delete_false_str_lc(self):
+ settings = KeypairConfig(
+ **{'name': 'foo', 'public_filepath': '/foo/bar.pub',
+ 'private_filepath': '/foo/bar', 'delete_on_clean': 'false'})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual(1024, settings.key_size)
+ self.assertEqual('/foo/bar.pub', settings.public_filepath)
+ self.assertEqual('/foo/bar', settings.private_filepath)
+ self.assertFalse(settings.delete_on_clean)
+
+ def test_config_all_delete_false_str_foo(self):
+ settings = KeypairConfig(
+ **{'name': 'foo', 'public_filepath': '/foo/bar.pub',
+ 'private_filepath': '/foo/bar', 'delete_on_clean': 'foo',
+ 'key_size': '999'})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual(999, settings.key_size)
+ self.assertEqual('/foo/bar.pub', settings.public_filepath)
+ self.assertEqual('/foo/bar', settings.private_filepath)
+ self.assertFalse(settings.delete_on_clean)
--- /dev/null
+# 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.
+import unittest
+
+from snaps.config.network import (
+ NetworkConfigError, NetworkConfig, SubnetConfig, SubnetConfigError,
+ IPv6Mode, PortConfig, PortConfigError)
+
+
+class NetworkConfigUnitTests(unittest.TestCase):
+ """
+ Tests the construction of the NetworkConfig class
+ """
+
+ def test_no_params(self):
+ with self.assertRaises(NetworkConfigError):
+ NetworkConfig()
+
+ def test_empty_config(self):
+ with self.assertRaises(NetworkConfigError):
+ NetworkConfig(**dict())
+
+ def test_name_only(self):
+ settings = NetworkConfig(name='foo')
+ self.assertEqual('foo', settings.name)
+ self.assertTrue(settings.admin_state_up)
+ self.assertIsNone(settings.shared)
+ self.assertIsNone(settings.project_name)
+ self.assertFalse(settings.external)
+ self.assertIsNone(settings.network_type)
+ self.assertIsNone(settings.segmentation_id)
+ self.assertEqual(0, len(settings.subnet_settings))
+
+ def test_config_with_name_only(self):
+ settings = NetworkConfig(**{'name': 'foo'})
+ self.assertEqual('foo', settings.name)
+ self.assertTrue(settings.admin_state_up)
+ self.assertIsNone(settings.shared)
+ self.assertIsNone(settings.project_name)
+ self.assertFalse(settings.external)
+ self.assertIsNone(settings.network_type)
+ self.assertIsNone(settings.segmentation_id)
+ self.assertEqual(0, len(settings.subnet_settings))
+
+ 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])
+ self.assertEqual('foo', settings.name)
+ self.assertFalse(settings.admin_state_up)
+ self.assertTrue(settings.shared)
+ self.assertEqual('bar', settings.project_name)
+ self.assertTrue(settings.external)
+ self.assertEqual('vlan', settings.network_type)
+ self.assertEqual('phy', settings.physical_network)
+ self.assertEqual(2366, settings.segmentation_id)
+ self.assertEqual(1, len(settings.subnet_settings))
+ self.assertEqual('foo-subnet', settings.subnet_settings[0].name)
+
+ def test_config_all(self):
+ settings = NetworkConfig(
+ **{'name': 'foo', 'admin_state_up': False, 'shared': True,
+ 'project_name': 'bar', 'external': True, 'network_type': 'vlan',
+ 'physical_network': 'phy',
+ 'segmentation_id': 2366,
+ 'subnets':
+ [{'subnet': {'name': 'foo-subnet',
+ 'cidr': '10.0.0.0/24'}}]})
+ self.assertEqual('foo', settings.name)
+ self.assertFalse(settings.admin_state_up)
+ self.assertTrue(settings.shared)
+ self.assertEqual('bar', settings.project_name)
+ self.assertTrue(settings.external)
+ self.assertEqual('vlan', settings.network_type)
+ self.assertEqual('phy', settings.physical_network)
+ self.assertEqual(2366, settings.segmentation_id)
+ self.assertEqual(1, len(settings.subnet_settings))
+ self.assertEqual('foo-subnet', settings.subnet_settings[0].name)
+
+
+class SubnetConfigUnitTests(unittest.TestCase):
+ """
+ Tests the construction of the SubnetConfig class
+ """
+
+ def test_no_params(self):
+ with self.assertRaises(SubnetConfigError):
+ SubnetConfig()
+
+ def test_empty_config(self):
+ with self.assertRaises(SubnetConfigError):
+ SubnetConfig(**dict())
+
+ def test_name_only(self):
+ with self.assertRaises(SubnetConfigError):
+ SubnetConfig(name='foo')
+
+ def test_config_with_name_only(self):
+ with self.assertRaises(SubnetConfigError):
+ SubnetConfig(**{'name': 'foo'})
+
+ def test_name_cidr_only(self):
+ settings = SubnetConfig(name='foo', cidr='10.0.0.0/24')
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('10.0.0.0/24', settings.cidr)
+ self.assertEqual(4, settings.ip_version)
+ self.assertIsNone(settings.project_name)
+ 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.assertIsNone(settings.host_routes)
+ self.assertIsNone(settings.destination)
+ self.assertIsNone(settings.nexthop)
+ self.assertIsNone(settings.ipv6_ra_mode)
+ self.assertIsNone(settings.ipv6_address_mode)
+
+ def test_config_with_name_cidr_only(self):
+ settings = SubnetConfig(**{'name': 'foo', 'cidr': '10.0.0.0/24'})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('10.0.0.0/24', settings.cidr)
+ self.assertEqual(4, settings.ip_version)
+ self.assertIsNone(settings.project_name)
+ self.assertIsNone(settings.start)
+ self.assertIsNone(settings.end)
+ self.assertIsNone(settings.gateway_ip)
+ self.assertIsNone(settings.enable_dhcp)
+ self.assertEqual(1, len(settings.dns_nameservers))
+ self.assertEqual('8.8.8.8', settings.dns_nameservers[0])
+ self.assertIsNone(settings.host_routes)
+ self.assertIsNone(settings.destination)
+ self.assertIsNone(settings.nexthop)
+ self.assertIsNone(settings.ipv6_ra_mode)
+ self.assertIsNone(settings.ipv6_address_mode)
+
+ def test_all_string_enums(self):
+ host_routes = {'destination': '0.0.0.0/0', 'nexthop': '123.456.78.9'}
+ settings = SubnetConfig(
+ name='foo', cidr='10.0.0.0/24', ip_version=6,
+ project_name='bar-project', start='10.0.0.2', end='10.0.0.101',
+ gateway_ip='10.0.0.1', enable_dhcp=False,
+ dns_nameservers=['8.8.8.8'], host_routes=[host_routes],
+ destination='dest', nexthop='hop', ipv6_ra_mode='dhcpv6-stateful',
+ ipv6_address_mode='slaac')
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('10.0.0.0/24', settings.cidr)
+ self.assertEqual(6, settings.ip_version)
+ self.assertEqual('bar-project', settings.project_name)
+ self.assertEqual('10.0.0.2', settings.start)
+ self.assertEqual('10.0.0.101', settings.end)
+ self.assertEqual('10.0.0.1', settings.gateway_ip)
+ self.assertEqual(False, settings.enable_dhcp)
+ self.assertEqual(1, len(settings.dns_nameservers))
+ self.assertEqual('8.8.8.8', settings.dns_nameservers[0])
+ self.assertEqual(1, len(settings.host_routes))
+ self.assertEqual(host_routes, settings.host_routes[0])
+ self.assertEqual('dest', settings.destination)
+ self.assertEqual('hop', settings.nexthop)
+ self.assertEqual(IPv6Mode.stateful, settings.ipv6_ra_mode)
+ self.assertEqual(IPv6Mode.slaac, settings.ipv6_address_mode)
+
+ def test_all_type_enums(self):
+ host_routes = {'destination': '0.0.0.0/0', 'nexthop': '123.456.78.9'}
+ settings = SubnetConfig(
+ name='foo', cidr='10.0.0.0/24', ip_version=6,
+ project_name='bar-project', start='10.0.0.2', end='10.0.0.101',
+ gateway_ip='10.0.0.1', enable_dhcp=False,
+ dns_nameservers=['8.8.8.8'], host_routes=[host_routes],
+ destination='dest', nexthop='hop', ipv6_ra_mode=IPv6Mode.stateful,
+ ipv6_address_mode=IPv6Mode.slaac)
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('10.0.0.0/24', settings.cidr)
+ self.assertEqual(6, settings.ip_version)
+ self.assertEqual('bar-project', settings.project_name)
+ self.assertEqual('10.0.0.2', settings.start)
+ self.assertEqual('10.0.0.101', settings.end)
+ self.assertEqual('10.0.0.1', settings.gateway_ip)
+ self.assertEqual(False, settings.enable_dhcp)
+ self.assertEqual(1, len(settings.dns_nameservers))
+ self.assertEqual('8.8.8.8', settings.dns_nameservers[0])
+ self.assertEqual(1, len(settings.host_routes))
+ self.assertEqual(host_routes, settings.host_routes[0])
+ self.assertEqual('dest', settings.destination)
+ self.assertEqual('hop', settings.nexthop)
+ self.assertEqual(IPv6Mode.stateful, settings.ipv6_ra_mode)
+ self.assertEqual(IPv6Mode.slaac, settings.ipv6_address_mode)
+
+ def test_config_all(self):
+ host_routes = {'destination': '0.0.0.0/0', 'nexthop': '123.456.78.9'}
+ settings = SubnetConfig(
+ **{'name': 'foo', 'cidr': '10.0.0.0/24', 'ip_version': 6,
+ 'project_name': 'bar-project', 'start': '10.0.0.2',
+ 'end': '10.0.0.101',
+ 'gateway_ip': '10.0.0.1', 'enable_dhcp': False,
+ 'dns_nameservers': ['8.8.8.8'], 'host_routes': [host_routes],
+ 'destination': 'dest', 'nexthop': 'hop',
+ 'ipv6_ra_mode': 'dhcpv6-stateless',
+ 'ipv6_address_mode': 'slaac'})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('10.0.0.0/24', settings.cidr)
+ self.assertEqual(6, settings.ip_version)
+ self.assertEqual('bar-project', settings.project_name)
+ self.assertEqual('10.0.0.2', settings.start)
+ self.assertEqual('10.0.0.101', settings.end)
+ self.assertEqual('10.0.0.1', settings.gateway_ip)
+ self.assertEqual(False, settings.enable_dhcp)
+ self.assertEqual(1, len(settings.dns_nameservers))
+ self.assertEqual('8.8.8.8', settings.dns_nameservers[0])
+ self.assertEqual(1, len(settings.host_routes))
+ self.assertEqual(host_routes, settings.host_routes[0])
+ self.assertEqual('dest', settings.destination)
+ self.assertEqual('hop', settings.nexthop)
+ self.assertEqual(IPv6Mode.stateless, settings.ipv6_ra_mode)
+ self.assertEqual(IPv6Mode.slaac, settings.ipv6_address_mode)
+
+
+class PortConfigUnitTests(unittest.TestCase):
+ """
+ Tests the construction of the PortConfig class
+ """
+
+ def test_no_params(self):
+ with self.assertRaises(PortConfigError):
+ PortConfig()
+
+ def test_empty_config(self):
+ with self.assertRaises(PortConfigError):
+ PortConfig(**dict())
+
+ def test_name_only(self):
+ with self.assertRaises(PortConfigError):
+ PortConfig(name='foo')
+
+ def test_config_name_only(self):
+ with self.assertRaises(PortConfigError):
+ PortConfig(**{'name': 'foo'})
+
+ def test_name_netname_only(self):
+ settings = PortConfig(name='foo', network_name='bar')
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('bar', settings.network_name)
+ self.assertTrue(settings.admin_state_up)
+ self.assertIsNone(settings.project_name)
+ self.assertIsNone(settings.mac_address)
+ self.assertIsNone(settings.ip_addrs)
+ self.assertIsNone(settings.security_groups)
+ self.assertIsNone(settings.allowed_address_pairs)
+ self.assertIsNone(settings.opt_value)
+ self.assertIsNone(settings.opt_name)
+ self.assertIsNone(settings.device_owner)
+ self.assertIsNone(settings.device_id)
+
+ def test_config_with_name_netname_only(self):
+ settings = PortConfig(**{'name': 'foo', 'network_name': 'bar'})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('bar', settings.network_name)
+ self.assertTrue(settings.admin_state_up)
+ self.assertIsNone(settings.project_name)
+ self.assertIsNone(settings.mac_address)
+ self.assertIsNone(settings.ip_addrs)
+ self.assertIsNone(settings.security_groups)
+ self.assertIsNone(settings.port_security_enabled)
+ self.assertIsNone(settings.allowed_address_pairs)
+ self.assertIsNone(settings.opt_value)
+ self.assertIsNone(settings.opt_name)
+ self.assertIsNone(settings.device_owner)
+ self.assertIsNone(settings.device_id)
+
+ def test_all(self):
+ ip_addrs = [{'subnet_name', 'foo-sub', 'ip', '10.0.0.10'}]
+ allowed_address_pairs = {'10.0.0.101', '1234.5678'}
+
+ settings = PortConfig(
+ name='foo', network_name='bar', admin_state_up=False,
+ project_name='foo-project', mac_address='1234', ip_addrs=ip_addrs,
+ security_groups=['foo_grp_id'], port_security_enabled=False,
+ allowed_address_pairs=allowed_address_pairs, opt_value='opt value',
+ opt_name='opt name', device_owner='owner',
+ device_id='device number')
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('bar', settings.network_name)
+ self.assertFalse(settings.admin_state_up)
+ self.assertEqual('foo-project', settings.project_name)
+ self.assertEqual('1234', settings.mac_address)
+ self.assertEqual(ip_addrs, settings.ip_addrs)
+ self.assertEqual(1, len(settings.security_groups))
+ self.assertFalse(settings.port_security_enabled)
+ self.assertEqual('foo_grp_id', settings.security_groups[0])
+ self.assertFalse(settings.port_security_enabled)
+ self.assertEqual(allowed_address_pairs, settings.allowed_address_pairs)
+ self.assertEqual('opt value', settings.opt_value)
+ self.assertEqual('opt name', settings.opt_name)
+ self.assertEqual('owner', settings.device_owner)
+ self.assertEqual('device number', settings.device_id)
+
+ def test_config_all(self):
+ ip_addrs = [{'subnet_name', 'foo-sub', 'ip', '10.0.0.10'}]
+ allowed_address_pairs = {'10.0.0.101', '1234.5678'}
+
+ settings = PortConfig(
+ **{'name': 'foo', 'network_name': 'bar', 'admin_state_up': False,
+ 'project_name': 'foo-project', 'mac_address': '1234',
+ 'ip_addrs': ip_addrs, 'security_groups': ['foo_grp_id'],
+ 'port_security_enabled': 'false',
+ 'allowed_address_pairs': allowed_address_pairs,
+ 'opt_value': 'opt value', 'opt_name': 'opt name',
+ 'device_owner': 'owner', 'device_id': 'device number'})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('bar', settings.network_name)
+ self.assertFalse(settings.admin_state_up)
+ self.assertEqual('foo-project', settings.project_name)
+ self.assertEqual('1234', settings.mac_address)
+ self.assertEqual(ip_addrs, settings.ip_addrs)
+ self.assertEqual(1, len(settings.security_groups))
+ self.assertFalse(settings.port_security_enabled)
+ self.assertEqual('foo_grp_id', settings.security_groups[0])
+ self.assertEqual(allowed_address_pairs, settings.allowed_address_pairs)
+ self.assertEqual('opt value', settings.opt_value)
+ self.assertEqual('opt name', settings.opt_name)
+ self.assertEqual('owner', settings.device_owner)
+ self.assertEqual('device number', settings.device_id)
--- /dev/null
+# 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.
+import unittest
+
+from snaps.config.project import ProjectConfig, ProjectConfigError
+
+
+class ProjectConfigUnitTests(unittest.TestCase):
+ """
+ Tests the construction of the ProjectConfig class
+ """
+
+ def test_no_params(self):
+ with self.assertRaises(ProjectConfigError):
+ ProjectConfig()
+
+ def test_empty_config(self):
+ with self.assertRaises(ProjectConfigError):
+ ProjectConfig(**dict())
+
+ def test_name_only(self):
+ settings = ProjectConfig(name='foo')
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('Default', settings.domain_name)
+ self.assertIsNone(settings.description)
+ self.assertTrue(settings.enabled)
+ self.assertEqual(list(), settings.users)
+
+ def test_config_with_name_only(self):
+ settings = ProjectConfig(**{'name': 'foo'})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('Default', settings.domain_name)
+ self.assertIsNone(settings.description)
+ self.assertTrue(settings.enabled)
+ self.assertEqual(list(), settings.users)
+
+ def test_all(self):
+ users = ['test1', 'test2']
+ settings = ProjectConfig(
+ name='foo', domain='bar', description='foobar', enabled=False,
+ users=users)
+ 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)
+
+ def test_config_all(self):
+ users = ['test1', 'test2']
+ settings = ProjectConfig(
+ **{'name': 'foo', 'domain': 'bar', 'description': 'foobar',
+ 'enabled': False, 'users': users})
+ 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)
--- /dev/null
+# 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.
+import unittest
+
+from snaps.config.qos import QoSConfig, QoSConfigError, Consumer
+
+
+class QoSConfigUnitTests(unittest.TestCase):
+ """
+ Tests the construction of the QoSConfig class
+ """
+
+ def test_no_params(self):
+ with self.assertRaises(QoSConfigError):
+ QoSConfig()
+
+ def test_empty_config(self):
+ with self.assertRaises(QoSConfigError):
+ QoSConfig(**dict())
+
+ def test_name_only(self):
+ with self.assertRaises(QoSConfigError):
+ QoSConfig(name='foo')
+
+ def test_config_with_name_only(self):
+ with self.assertRaises(QoSConfigError):
+ QoSConfig(**{'name': 'foo'})
+
+ def test_invalid_consumer(self):
+ with self.assertRaises(QoSConfigError):
+ QoSConfig(name='foo', consumer='bar')
+
+ def test_config_with_invalid_consumer(self):
+ with self.assertRaises(QoSConfigError):
+ QoSConfig(**{'name': 'foo', 'consumer': 'bar'})
+
+ def test_name_consumer(self):
+ settings = QoSConfig(name='foo', consumer=Consumer.front_end)
+
+ self.assertEqual('foo', settings.name)
+ self.assertEqual(Consumer.front_end, settings.consumer)
+ self.assertEqual(dict(), settings.specs)
+
+ def test_name_consumer_front_end_strings(self):
+ settings = QoSConfig(name='foo', consumer='front-end')
+
+ self.assertEqual('foo', settings.name)
+ self.assertEqual(Consumer.front_end, settings.consumer)
+ self.assertEqual(dict(), settings.specs)
+
+ def test_name_consumer_back_end_strings(self):
+ settings = QoSConfig(name='foo', consumer='back-end')
+
+ self.assertEqual('foo', settings.name)
+ self.assertEqual(Consumer.back_end, settings.consumer)
+ self.assertEqual(dict(), settings.specs)
+
+ def test_name_consumer_both_strings(self):
+ settings = QoSConfig(name='foo', consumer='both')
+
+ self.assertEqual('foo', settings.name)
+ self.assertEqual(Consumer.both, settings.consumer)
+ self.assertEqual(dict(), settings.specs)
+
+ def test_all(self):
+ specs = {'spec1': 'val1', 'spec2': 'val2'}
+ settings = QoSConfig(name='foo', consumer=Consumer.both, specs=specs)
+
+ self.assertEqual('foo', settings.name)
+ self.assertEqual(Consumer.both, settings.consumer)
+ self.assertEqual(specs, settings.specs)
+
+ def test_config_all(self):
+ settings = QoSConfig(
+ **{'name': 'foo', 'consumer': 'both', 'specs': {'spec1': 'val1'}})
+
+ self.assertEqual('foo', settings.name)
+ self.assertEqual(Consumer.both, settings.consumer)
+ self.assertEqual({'spec1': 'val1'}, settings.specs)
--- /dev/null
+# 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.
+import unittest
+
+from snaps.config.network import PortConfig
+from snaps.config.router import RouterConfig, RouterConfigError
+
+
+class RouterConfigUnitTests(unittest.TestCase):
+ """
+ Class for testing the RouterConfig class
+ """
+
+ def test_no_params(self):
+ with self.assertRaises(RouterConfigError):
+ RouterConfig()
+
+ def test_empty_config(self):
+ with self.assertRaises(RouterConfigError):
+ RouterConfig(**dict())
+
+ def test_name_only(self):
+ settings = RouterConfig(name='foo')
+ self.assertEqual('foo', settings.name)
+ self.assertIsNone(settings.project_name)
+ self.assertIsNone(settings.external_gateway)
+ self.assertTrue(settings.admin_state_up)
+ self.assertIsNone(settings.enable_snat)
+ self.assertIsNotNone(settings.internal_subnets)
+ self.assertTrue(isinstance(settings.internal_subnets, list))
+ self.assertEqual(0, len(settings.internal_subnets))
+ self.assertIsNotNone(settings.port_settings)
+ self.assertTrue(isinstance(settings.port_settings, list))
+ self.assertEqual(0, len(settings.port_settings))
+
+ def test_config_with_name_only(self):
+ settings = RouterConfig(**{'name': 'foo'})
+ self.assertEqual('foo', settings.name)
+ self.assertIsNone(settings.project_name)
+ self.assertIsNone(settings.external_gateway)
+ self.assertTrue(settings.admin_state_up)
+ self.assertIsNone(settings.enable_snat)
+ self.assertIsNotNone(settings.internal_subnets)
+ self.assertTrue(isinstance(settings.internal_subnets, list))
+ self.assertEqual(0, len(settings.internal_subnets))
+ self.assertIsNotNone(settings.port_settings)
+ self.assertTrue(isinstance(settings.port_settings, list))
+ self.assertEqual(0, len(settings.port_settings))
+
+ def test_all(self):
+ port_settings = PortConfig(name='foo', network_name='bar')
+ 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'], 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, list))
+ self.assertEqual(1, len(settings.internal_subnets))
+ self.assertEqual(['10.0.0.1/24'], settings.internal_subnets)
+ self.assertEqual([port_settings], settings.port_settings)
+
+ def test_config_all(self):
+ 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'],
+ 'interfaces':
+ [{'port': {'name': 'foo-port',
+ 'network_name': 'bar-net'}}]})
+ 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, list))
+ self.assertEqual(1, len(settings.internal_subnets))
+ self.assertEqual(['10.0.0.1/24'], settings.internal_subnets)
+ self.assertEqual([PortConfig(**{'name': 'foo-port',
+ 'network_name': 'bar-net'})],
+ settings.port_settings)
--- /dev/null
+# 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.
+import unittest
+
+from snaps.config.security_group import (
+ Direction, SecurityGroupConfig, SecurityGroupRuleConfig,
+ SecurityGroupConfigError, Protocol, Ethertype,
+ SecurityGroupRuleConfigError)
+
+
+class SecurityGroupRuleConfigUnitTests(unittest.TestCase):
+ """
+ Tests the construction of the SecurityGroupRuleConfig class
+ """
+
+ def test_no_params(self):
+ with self.assertRaises(SecurityGroupRuleConfigError):
+ SecurityGroupRuleConfig()
+
+ def test_empty_config(self):
+ with self.assertRaises(SecurityGroupRuleConfigError):
+ SecurityGroupRuleConfig(**dict())
+
+ def test_name_only(self):
+ with self.assertRaises(SecurityGroupRuleConfigError):
+ SecurityGroupRuleConfig(sec_grp_name='foo')
+
+ def test_config_with_name_only(self):
+ with self.assertRaises(SecurityGroupRuleConfigError):
+ SecurityGroupRuleConfig(**{'sec_grp_name': 'foo'})
+
+ def test_name_and_direction(self):
+ settings = SecurityGroupRuleConfig(sec_grp_name='foo',
+ direction=Direction.ingress)
+ self.assertEqual('foo', settings.sec_grp_name)
+ self.assertEqual(Direction.ingress, settings.direction)
+
+ def test_config_name_and_direction(self):
+ settings = SecurityGroupRuleConfig(
+ **{'sec_grp_name': 'foo', 'direction': 'ingress'})
+ self.assertEqual('foo', settings.sec_grp_name)
+ self.assertEqual(Direction.ingress, settings.direction)
+
+ def test_proto_ah_str(self):
+ settings = SecurityGroupRuleConfig(
+ **{'sec_grp_name': 'foo', 'direction': 'ingress',
+ 'protocol': 'ah'})
+ self.assertEqual('foo', settings.sec_grp_name)
+ self.assertEqual(Direction.ingress, settings.direction)
+ self.assertEqual(Protocol.ah, settings.protocol)
+
+ def test_proto_ah_value(self):
+ settings = SecurityGroupRuleConfig(
+ **{'sec_grp_name': 'foo', 'direction': 'ingress',
+ 'protocol': 51})
+ self.assertEqual('foo', settings.sec_grp_name)
+ self.assertEqual(Direction.ingress, settings.direction)
+ self.assertEqual(Protocol.ah, settings.protocol)
+
+ def test_proto_any(self):
+ settings = SecurityGroupRuleConfig(
+ **{'sec_grp_name': 'foo', 'direction': 'ingress',
+ 'protocol': 'any'})
+ self.assertEqual('foo', settings.sec_grp_name)
+ self.assertEqual(Direction.ingress, settings.direction)
+ self.assertEqual(Protocol.null, settings.protocol)
+
+ def test_proto_null(self):
+ settings = SecurityGroupRuleConfig(
+ **{'sec_grp_name': 'foo', 'direction': 'ingress',
+ 'protocol': 'null'})
+ self.assertEqual('foo', settings.sec_grp_name)
+ self.assertEqual(Direction.ingress, settings.direction)
+ self.assertEqual(Protocol.null, settings.protocol)
+
+ def test_all(self):
+ settings = SecurityGroupRuleConfig(
+ sec_grp_name='foo', description='fubar',
+ direction=Direction.egress, remote_group_id='rgi',
+ protocol=Protocol.icmp, ethertype=Ethertype.IPv6, port_range_min=1,
+ port_range_max=2,
+ remote_ip_prefix='prfx')
+ self.assertEqual('foo', settings.sec_grp_name)
+ self.assertEqual('fubar', settings.description)
+ self.assertEqual(Direction.egress, settings.direction)
+ self.assertEqual('rgi', settings.remote_group_id)
+ self.assertEqual(Protocol.icmp, settings.protocol)
+ self.assertEqual(Ethertype.IPv6, settings.ethertype)
+ self.assertEqual(1, settings.port_range_min)
+ self.assertEqual(2, settings.port_range_max)
+ self.assertEqual('prfx', settings.remote_ip_prefix)
+
+ def test_config_all(self):
+ settings = SecurityGroupRuleConfig(
+ **{'sec_grp_name': 'foo',
+ 'description': 'fubar',
+ 'direction': 'egress',
+ 'remote_group_id': 'rgi',
+ 'protocol': 'tcp',
+ 'ethertype': 'IPv6',
+ 'port_range_min': 1,
+ 'port_range_max': 2,
+ 'remote_ip_prefix': 'prfx'})
+ self.assertEqual('foo', settings.sec_grp_name)
+ self.assertEqual('fubar', settings.description)
+ self.assertEqual(Direction.egress, settings.direction)
+ self.assertEqual('rgi', settings.remote_group_id)
+ self.assertEqual(Protocol.tcp, settings.protocol)
+ self.assertEqual(Ethertype.IPv6, settings.ethertype)
+ self.assertEqual(1, settings.port_range_min)
+ self.assertEqual(2, settings.port_range_max)
+ self.assertEqual('prfx', settings.remote_ip_prefix)
+
+
+class SecurityGroupConfigUnitTests(unittest.TestCase):
+ """
+ Tests the construction of the SecurityGroupConfig class
+ """
+
+ def test_no_params(self):
+ with self.assertRaises(SecurityGroupConfigError):
+ SecurityGroupConfig()
+
+ def test_empty_config(self):
+ with self.assertRaises(SecurityGroupConfigError):
+ SecurityGroupConfig(**dict())
+
+ def test_name_only(self):
+ settings = SecurityGroupConfig(name='foo')
+ self.assertEqual('foo', settings.name)
+
+ def test_config_with_name_only(self):
+ settings = SecurityGroupConfig(**{'name': 'foo'})
+ self.assertEqual('foo', settings.name)
+
+ def test_invalid_rule(self):
+ rule_setting = SecurityGroupRuleConfig(
+ sec_grp_name='bar', direction=Direction.ingress,
+ description='test_rule_1')
+ with self.assertRaises(SecurityGroupConfigError):
+ SecurityGroupConfig(name='foo', rule_settings=[rule_setting])
+
+ def test_all(self):
+ rule_settings = list()
+ rule_settings.append(SecurityGroupRuleConfig(
+ sec_grp_name='bar', direction=Direction.egress,
+ description='test_rule_1'))
+ rule_settings.append(SecurityGroupRuleConfig(
+ sec_grp_name='bar', direction=Direction.ingress,
+ description='test_rule_2'))
+ settings = SecurityGroupConfig(
+ name='bar', description='fubar', project_name='foo',
+ rule_settings=rule_settings)
+
+ self.assertEqual('bar', settings.name)
+ self.assertEqual('fubar', settings.description)
+ self.assertEqual('foo', settings.project_name)
+ self.assertEqual(rule_settings[0], settings.rule_settings[0])
+ self.assertEqual(rule_settings[1], settings.rule_settings[1])
+
+ def test_config_all(self):
+ settings = SecurityGroupConfig(
+ **{'name': 'bar',
+ 'description': 'fubar',
+ 'project_name': 'foo',
+ 'rules': [
+ {'sec_grp_name': 'bar', 'direction': 'ingress'}]})
+
+ self.assertEqual('bar', settings.name)
+ self.assertEqual('fubar', settings.description)
+ self.assertEqual('foo', settings.project_name)
+ self.assertEqual(1, len(settings.rule_settings))
+ self.assertEqual('bar', settings.rule_settings[0].sec_grp_name)
+ self.assertEqual(Direction.ingress,
+ settings.rule_settings[0].direction)
--- /dev/null
+# 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.
+import unittest
+
+import snaps
+from snaps.config.stack import StackConfigError, StackConfig
+
+
+class StackConfigUnitTests(unittest.TestCase):
+ """
+ Tests the construction of the StackConfig class
+ """
+
+ def test_no_params(self):
+ with self.assertRaises(StackConfigError):
+ StackConfig()
+
+ def test_empty_config(self):
+ with self.assertRaises(StackConfigError):
+ StackConfig(**dict())
+
+ def test_name_only(self):
+ with self.assertRaises(StackConfigError):
+ StackConfig(name='foo')
+
+ def test_config_with_name_only(self):
+ with self.assertRaises(StackConfigError):
+ StackConfig(**{'name': 'foo'})
+
+ def test_resource_not_list(self):
+ with self.assertRaises(StackConfigError):
+ StackConfig(**{'name': 'foo', 'resource_files': 'bar'})
+
+ def test_config_minimum_template(self):
+ settings = StackConfig(**{'name': 'stack', 'template': 'foo'})
+ self.assertEqual('stack', settings.name)
+ self.assertEqual('foo', settings.template)
+ self.assertIsNone(settings.template_path)
+ self.assertIsNone(settings.resource_files)
+ self.assertIsNone(settings.env_values)
+ self.assertEqual(snaps.config.stack.STACK_COMPLETE_TIMEOUT,
+ settings.stack_create_timeout)
+
+ def test_config_minimum_template_path(self):
+ settings = StackConfig(**{'name': 'stack', 'template_path': 'foo'})
+ self.assertEqual('stack', settings.name)
+ self.assertIsNone(settings.template)
+ self.assertEqual('foo', settings.template_path)
+ self.assertIsNone(settings.resource_files)
+ self.assertIsNone(settings.env_values)
+ self.assertEqual(snaps.config.stack.STACK_COMPLETE_TIMEOUT,
+ settings.stack_create_timeout)
+
+ def test_minimum_template(self):
+ settings = StackConfig(name='stack', template='foo')
+ self.assertEqual('stack', settings.name)
+ self.assertEqual('foo', settings.template)
+ self.assertIsNone(settings.template_path)
+ self.assertIsNone(settings.resource_files)
+ self.assertIsNone(settings.env_values)
+ self.assertEqual(snaps.config.stack.STACK_COMPLETE_TIMEOUT,
+ settings.stack_create_timeout)
+
+ def test_minimum_template_path(self):
+ settings = StackConfig(name='stack', template_path='foo')
+ self.assertEqual('stack', settings.name)
+ self.assertEqual('foo', settings.template_path)
+ self.assertIsNone(settings.template)
+ self.assertIsNone(settings.resource_files)
+ self.assertIsNone(settings.env_values)
+ self.assertEqual(snaps.config.stack.STACK_COMPLETE_TIMEOUT,
+ settings.stack_create_timeout)
+
+ def test_resource(self):
+ settings = StackConfig(
+ name='stack', template_path='foo', resource_files=['foo', 'bar'])
+ self.assertEqual('stack', settings.name)
+ self.assertEqual('foo', settings.template_path)
+ self.assertIsNone(settings.template)
+ self.assertEqual(['foo', 'bar'], settings.resource_files)
+ self.assertIsNone(settings.env_values)
+ self.assertEqual(snaps.config.stack.STACK_COMPLETE_TIMEOUT,
+ settings.stack_create_timeout)
+
+ def test_all(self):
+ env_values = {'foo': 'bar'}
+ settings = StackConfig(
+ name='stack', template='bar', template_path='foo',
+ env_values=env_values, stack_create_timeout=999)
+ self.assertEqual('stack', settings.name)
+ self.assertEqual('bar', settings.template)
+ self.assertEqual('foo', settings.template_path)
+ self.assertEqual(env_values, settings.env_values)
+ self.assertEqual(999, settings.stack_create_timeout)
+
+ def test_config_all(self):
+ env_values = {'foo': 'bar'}
+ settings = StackConfig(
+ **{'name': 'stack', 'template': 'bar', 'template_path': 'foo',
+ 'env_values': env_values, 'stack_create_timeout': 999})
+ self.assertEqual('stack', settings.name)
+ self.assertEqual('bar', settings.template)
+ self.assertEqual('foo', settings.template_path)
+ self.assertEqual(env_values, settings.env_values)
+ self.assertEqual(999, settings.stack_create_timeout)
--- /dev/null
+# 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.
+import unittest
+
+from snaps.config.user import UserConfig
+
+
+class UserConfigUnitTests(unittest.TestCase):
+ """
+ Tests the construction of the UserConfig class
+ """
+
+ def test_no_params(self):
+ with self.assertRaises(Exception):
+ UserConfig()
+
+ def test_empty_config(self):
+ with self.assertRaises(Exception):
+ UserConfig(**dict())
+
+ def test_name_only(self):
+ with self.assertRaises(Exception):
+ UserConfig(name='foo')
+
+ def test_config_with_name_only(self):
+ with self.assertRaises(Exception):
+ UserConfig(**{'name': 'foo'})
+
+ def test_name_pass_enabled_str(self):
+ with self.assertRaises(Exception):
+ UserConfig(name='foo', password='bar', enabled='true')
+
+ def test_config_with_name_pass_enabled_str(self):
+ with self.assertRaises(Exception):
+ UserConfig(
+ **{'name': 'foo', 'password': 'bar', 'enabled': 'true'})
+
+ def test_name_pass_only(self):
+ settings = UserConfig(name='foo', password='bar')
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('bar', settings.password)
+ self.assertIsNone(settings.project_name)
+ self.assertIsNone(settings.email)
+ self.assertTrue(settings.enabled)
+
+ def test_config_with_name_pass_only(self):
+ settings = UserConfig(**{'name': 'foo', 'password': 'bar'})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('bar', settings.password)
+ self.assertIsNone(settings.project_name)
+ self.assertIsNone(settings.email)
+ self.assertTrue(settings.enabled)
+
+ def test_all(self):
+ settings = UserConfig(
+ name='foo', password='bar', project_name='proj-foo',
+ email='foo@bar.com', enabled=False)
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('bar', settings.password)
+ self.assertEqual('proj-foo', settings.project_name)
+ self.assertEqual('foo@bar.com', settings.email)
+ self.assertFalse(settings.enabled)
+
+ def test_config_all(self):
+ settings = UserConfig(
+ **{'name': 'foo', 'password': 'bar', 'project_name': 'proj-foo',
+ 'email': 'foo@bar.com', 'enabled': False})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('bar', settings.password)
+ self.assertEqual('proj-foo', settings.project_name)
+ self.assertEqual('foo@bar.com', settings.email)
+ self.assertFalse(settings.enabled)
--- /dev/null
+# 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.
+import unittest
+
+from snaps.config.network import PortConfig
+from snaps.config.vm_inst import (
+ FloatingIpConfig, VmInstanceConfig, FloatingIpConfigError,
+ VmInstanceConfigError)
+
+
+class VmInstanceConfigUnitTests(unittest.TestCase):
+ """
+ Tests the construction of the VmInstanceConfig class
+ """
+
+ def test_no_params(self):
+ with self.assertRaises(VmInstanceConfigError):
+ VmInstanceConfig()
+
+ def test_empty_config(self):
+ with self.assertRaises(VmInstanceConfigError):
+ VmInstanceConfig(config=dict())
+
+ def test_name_only(self):
+ with self.assertRaises(VmInstanceConfigError):
+ VmInstanceConfig(name='foo')
+
+ def test_config_with_name_only(self):
+ with self.assertRaises(VmInstanceConfigError):
+ VmInstanceConfig(config={'name': 'foo'})
+
+ def test_name_flavor_only(self):
+ with self.assertRaises(VmInstanceConfigError):
+ VmInstanceConfig(name='foo', flavor='bar')
+
+ def test_config_with_name_flavor_only(self):
+ with self.assertRaises(VmInstanceConfigError):
+ VmInstanceConfig(config={'name': 'foo', 'flavor': 'bar'})
+
+ def test_name_flavor_port_only(self):
+ port_settings = PortConfig(name='foo-port', network_name='bar-net')
+ settings = VmInstanceConfig(name='foo', flavor='bar',
+ port_settings=[port_settings])
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('bar', settings.flavor)
+ self.assertEqual(1, len(settings.port_settings))
+ self.assertEqual('foo-port', settings.port_settings[0].name)
+ self.assertEqual('bar-net', settings.port_settings[0].network_name)
+ self.assertEqual(0, len(settings.security_group_names))
+ self.assertEqual(0, len(settings.floating_ip_settings))
+ self.assertIsNone(settings.sudo_user)
+ self.assertEqual(900, settings.vm_boot_timeout)
+ self.assertEqual(300, settings.vm_delete_timeout)
+ self.assertEqual(180, settings.ssh_connect_timeout)
+ self.assertEqual(300, settings.cloud_init_timeout)
+ self.assertIsNone(settings.availability_zone)
+ self.assertIsNone(settings.volume_names)
+
+ def test_config_with_name_flavor_port_only(self):
+ port_settings = PortConfig(name='foo-port', network_name='bar-net')
+ settings = VmInstanceConfig(
+ **{'name': 'foo', 'flavor': 'bar', 'ports': [port_settings]})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('bar', settings.flavor)
+ self.assertEqual(1, len(settings.port_settings))
+ self.assertEqual('foo-port', settings.port_settings[0].name)
+ self.assertEqual('bar-net', settings.port_settings[0].network_name)
+ self.assertEqual(0, len(settings.security_group_names))
+ self.assertEqual(0, len(settings.floating_ip_settings))
+ self.assertIsNone(settings.sudo_user)
+ self.assertEqual(900, settings.vm_boot_timeout)
+ self.assertEqual(300, settings.vm_delete_timeout)
+ self.assertEqual(180, settings.ssh_connect_timeout)
+ self.assertEqual(300, settings.cloud_init_timeout)
+ self.assertIsNone(settings.availability_zone)
+ self.assertIsNone(settings.volume_names)
+
+ def test_all(self):
+ port_settings = PortConfig(name='foo-port', network_name='bar-net')
+ fip_settings = FloatingIpConfig(name='foo-fip', port_name='bar-port',
+ router_name='foo-bar-router')
+
+ settings = VmInstanceConfig(
+ name='foo', flavor='bar', port_settings=[port_settings],
+ security_group_names=['sec_grp_1'],
+ floating_ip_settings=[fip_settings], sudo_user='joe',
+ vm_boot_timeout=999, vm_delete_timeout=333,
+ ssh_connect_timeout=111, cloud_init_timeout=998,
+ availability_zone='server name', volume_names=['vol1'])
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('bar', settings.flavor)
+ self.assertEqual(1, len(settings.port_settings))
+ self.assertEqual('foo-port', settings.port_settings[0].name)
+ self.assertEqual('bar-net', settings.port_settings[0].network_name)
+ self.assertEqual(1, len(settings.security_group_names))
+ self.assertEqual('sec_grp_1', settings.security_group_names[0])
+ self.assertEqual(1, len(settings.floating_ip_settings))
+ self.assertEqual('foo-fip', settings.floating_ip_settings[0].name)
+ self.assertEqual('bar-port',
+ settings.floating_ip_settings[0].port_name)
+ self.assertEqual('foo-bar-router',
+ settings.floating_ip_settings[0].router_name)
+ self.assertEqual('joe', settings.sudo_user)
+ self.assertEqual(999, settings.vm_boot_timeout)
+ self.assertEqual(333, settings.vm_delete_timeout)
+ self.assertEqual(111, settings.ssh_connect_timeout)
+ self.assertEqual(998, settings.cloud_init_timeout)
+ self.assertEqual('server name', settings.availability_zone)
+ self.assertEqual('vol1', settings.volume_names[0])
+
+ def test_config_all(self):
+ port_settings = PortConfig(name='foo-port', network_name='bar-net')
+ fip_settings = FloatingIpConfig(name='foo-fip', port_name='bar-port',
+ router_name='foo-bar-router')
+
+ settings = VmInstanceConfig(
+ **{'name': 'foo', 'flavor': 'bar', 'ports': [port_settings],
+ 'security_group_names': ['sec_grp_1'],
+ 'floating_ips': [fip_settings], 'sudo_user': 'joe',
+ 'vm_boot_timeout': 999, 'vm_delete_timeout': 333,
+ 'ssh_connect_timeout': 111, 'cloud_init_timeout': 998,
+ 'availability_zone': 'server name', 'volume_names': ['vol2']})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('bar', settings.flavor)
+ self.assertEqual(1, len(settings.port_settings))
+ self.assertEqual('foo-port', settings.port_settings[0].name)
+ self.assertEqual('bar-net', settings.port_settings[0].network_name)
+ self.assertEqual(1, len(settings.security_group_names))
+ self.assertEqual(1, len(settings.floating_ip_settings))
+ self.assertEqual('foo-fip', settings.floating_ip_settings[0].name)
+ self.assertEqual('bar-port',
+ settings.floating_ip_settings[0].port_name)
+ self.assertEqual('foo-bar-router',
+ settings.floating_ip_settings[0].router_name)
+ self.assertEqual('joe', settings.sudo_user)
+ self.assertEqual(999, settings.vm_boot_timeout)
+ self.assertEqual(333, settings.vm_delete_timeout)
+ self.assertEqual(111, settings.ssh_connect_timeout)
+ self.assertEqual(998, settings.cloud_init_timeout)
+ self.assertEqual('server name', settings.availability_zone)
+ self.assertEqual('vol2', settings.volume_names[0])
+
+
+class FloatingIpConfigUnitTests(unittest.TestCase):
+ """
+ Tests the construction of the FloatingIpConfig class
+ """
+
+ def test_no_params(self):
+ with self.assertRaises(FloatingIpConfigError):
+ FloatingIpConfig()
+
+ def test_empty_config(self):
+ with self.assertRaises(FloatingIpConfigError):
+ FloatingIpConfig(**dict())
+
+ def test_name_only(self):
+ with self.assertRaises(FloatingIpConfigError):
+ FloatingIpConfig(name='foo')
+
+ def test_config_with_name_only(self):
+ with self.assertRaises(FloatingIpConfigError):
+ FloatingIpConfig(**{'name': 'foo'})
+
+ def test_name_port_only(self):
+ with self.assertRaises(FloatingIpConfigError):
+ FloatingIpConfig(name='foo', port_name='bar')
+
+ def test_config_with_name_port_only(self):
+ with self.assertRaises(FloatingIpConfigError):
+ FloatingIpConfig(**{'name': 'foo', 'port_name': 'bar'})
+
+ def test_name_router_only(self):
+ with self.assertRaises(FloatingIpConfigError):
+ FloatingIpConfig(name='foo', router_name='bar')
+
+ def test_config_with_name_router_only(self):
+ with self.assertRaises(FloatingIpConfigError):
+ FloatingIpConfig(**{'name': 'foo', 'router_name': 'bar'})
+
+ def test_name_port_router_name_only(self):
+ settings = FloatingIpConfig(name='foo', port_name='foo-port',
+ router_name='bar-router')
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('foo-port', settings.port_name)
+ self.assertIsNone(settings.port_id)
+ self.assertEqual('bar-router', settings.router_name)
+ self.assertIsNone(settings.subnet_name)
+ self.assertTrue(settings.provisioning)
+
+ def test_name_port_router_id_only(self):
+ settings = FloatingIpConfig(name='foo', port_id='foo-port',
+ router_name='bar-router')
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('foo-port', settings.port_id)
+ self.assertIsNone(settings.port_name)
+ self.assertEqual('bar-router', settings.router_name)
+ self.assertIsNone(settings.subnet_name)
+ self.assertTrue(settings.provisioning)
+
+ def test_config_with_name_port_router_only(self):
+ settings = FloatingIpConfig(
+ **{'name': 'foo', 'port_name': 'foo-port',
+ 'router_name': 'bar-router'})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('foo-port', settings.port_name)
+ self.assertIsNone(settings.port_id)
+ self.assertEqual('bar-router', settings.router_name)
+ self.assertIsNone(settings.subnet_name)
+ self.assertTrue(settings.provisioning)
+
+ def test_all(self):
+ settings = FloatingIpConfig(name='foo', port_name='foo-port',
+ router_name='bar-router',
+ subnet_name='bar-subnet',
+ provisioning=False)
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('foo-port', settings.port_name)
+ self.assertIsNone(settings.port_id)
+ self.assertEqual('bar-router', settings.router_name)
+ self.assertEqual('bar-subnet', settings.subnet_name)
+ self.assertFalse(settings.provisioning)
+
+ def test_config_all(self):
+ settings = FloatingIpConfig(
+ **{'name': 'foo', 'port_name': 'foo-port',
+ 'router_name': 'bar-router', 'subnet_name': 'bar-subnet',
+ 'provisioning': False})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('foo-port', settings.port_name)
+ self.assertIsNone(settings.port_id)
+ self.assertEqual('bar-router', settings.router_name)
+ self.assertEqual('bar-subnet', settings.subnet_name)
+ self.assertFalse(settings.provisioning)
--- /dev/null
+# 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.
+import unittest
+
+from snaps.config.volume import VolumeConfigError, VolumeConfig
+
+
+class VolumeConfigUnitTests(unittest.TestCase):
+ """
+ Tests the construction of the VolumeConfig class
+ """
+
+ def test_no_params(self):
+ with self.assertRaises(VolumeConfigError):
+ VolumeConfig()
+
+ def test_empty_config(self):
+ with self.assertRaises(VolumeConfigError):
+ VolumeConfig(**dict())
+
+ def test_name_only(self):
+ settings = VolumeConfig(name='foo')
+ self.assertEqual('foo', settings.name)
+ self.assertIsNone(settings.description)
+ self.assertEquals(1, settings.size)
+ self.assertIsNone(settings.image_name)
+ self.assertIsNone(settings.type_name)
+ self.assertIsNone(settings.availability_zone)
+ self.assertFalse(settings.multi_attach)
+
+ def test_config_with_name_only(self):
+ settings = VolumeConfig(**{'name': 'foo'})
+ self.assertEqual('foo', settings.name)
+ self.assertIsNone(settings.description)
+ self.assertEquals(1, settings.size)
+ self.assertIsNone(settings.image_name)
+ self.assertIsNone(settings.type_name)
+ self.assertIsNone(settings.availability_zone)
+ self.assertFalse(settings.multi_attach)
+
+ 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')
+
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('desc', settings.description)
+ self.assertEqual(2, settings.size)
+ self.assertEqual('image', settings.image_name)
+ self.assertEqual('type', settings.type_name)
+ self.assertEqual('zone1', settings.availability_zone)
+ self.assertTrue(settings.multi_attach)
+
+ 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)
+
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('desc', settings.description)
+ self.assertEqual(2, settings.size)
+ self.assertEqual('image', settings.image_name)
+ self.assertEqual('bar', settings.type_name)
+ self.assertEqual('zone1', settings.availability_zone)
+ self.assertTrue(settings.multi_attach)
+
+ def test_config_all(self):
+ settings = VolumeConfig(
+ **{'name': 'foo', 'description': 'desc', 'size': '2',
+ 'image_name': 'foo', 'type_name': 'bar',
+ 'availability_zone': 'zone1', 'multi_attach': 'true'})
+
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('desc', settings.description)
+ self.assertEqual(2, settings.size)
+ self.assertEqual('foo', settings.image_name)
+ self.assertEqual('bar', settings.type_name)
+ self.assertEqual('zone1', settings.availability_zone)
+ self.assertTrue(settings.multi_attach)
--- /dev/null
+# 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.
+import unittest
+
+from snaps.config.volume_type import (
+ VolumeTypeConfig, VolumeTypeEncryptionConfig, ControlLocation,
+ VolumeTypeConfigError)
+
+
+class VolumeTypeConfigUnitTests(unittest.TestCase):
+ """
+ Tests the construction of the VolumeTypeConfig class
+ """
+
+ def test_no_params(self):
+ with self.assertRaises(VolumeTypeConfigError):
+ VolumeTypeConfig()
+
+ def test_empty_config(self):
+ with self.assertRaises(VolumeTypeConfigError):
+ VolumeTypeConfig(**dict())
+
+ def test_name_only(self):
+ settings = VolumeTypeConfig(name='foo')
+ self.assertEqual('foo', settings.name)
+ self.assertIsNone(settings.description)
+ self.assertIsNone(settings.qos_spec_name)
+ self.assertIsNone(settings.encryption)
+ self.assertFalse(settings.public)
+
+ def test_config_with_name_only(self):
+ settings = VolumeTypeConfig(**{'name': 'foo'})
+ self.assertEqual('foo', settings.name)
+ self.assertIsNone(settings.description)
+ self.assertIsNone(settings.qos_spec_name)
+ self.assertIsNone(settings.encryption)
+ self.assertFalse(settings.public)
+
+ def test_all(self):
+ encryption_settings = VolumeTypeEncryptionConfig(
+ name='foo', provider_class='bar',
+ control_location=ControlLocation.back_end)
+ settings = VolumeTypeConfig(
+ name='foo', description='desc', encryption=encryption_settings,
+ qos_spec_name='spec_name', public=True)
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('desc', settings.description)
+ self.assertEqual('spec_name', settings.qos_spec_name)
+ self.assertEqual(encryption_settings, settings.encryption)
+ self.assertTrue(True, settings.public)
+
+ def test_all_string(self):
+ encryption_settings = {
+ 'name': 'foo', 'provider_class': 'bar',
+ 'control_location': 'back-end'}
+ settings = VolumeTypeConfig(
+ name='foo', description='desc', encryption=encryption_settings,
+ qos_spec_name='spec_name', public='true')
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('desc', settings.description)
+ self.assertEqual('spec_name', settings.qos_spec_name)
+ self.assertEqual(VolumeTypeEncryptionConfig(**encryption_settings),
+ settings.encryption)
+ self.assertTrue(settings.public)
+
+ def test_config_all(self):
+ encryption_settings = {
+ 'name': 'foo', 'provider_class': 'bar',
+ 'control_location': 'back-end'}
+ settings = VolumeTypeConfig(
+ **{'name': 'foo', 'description': 'desc',
+ 'encryption': encryption_settings,
+ 'qos_spec_name': 'spec_name', 'public': 'false'})
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('desc', settings.description)
+ self.assertEqual('spec_name', settings.qos_spec_name)
+ self.assertEqual(VolumeTypeEncryptionConfig(**encryption_settings),
+ settings.encryption)
+ self.assertFalse(settings.public)
--- /dev/null
+# 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.
+
+
+class UserConfig(object):
+ """
+ Class for holding user configurations
+ """
+ def __init__(self, **kwargs):
+
+ """
+ Constructor
+ :param name: the user's name (required)
+ :param password: the user's password (required)
+ :param project_name: the user's primary project name (optional)
+ :param domain_name: the user's domain name (default='Default'). For v3
+ APIs
+ :param email: the user's email address (optional)
+ :param enabled: denotes whether or not the user is enabled
+ (default True)
+ :param roles: dict where key is the role's name and value is the name
+ of the project to associate with the role (optional)
+ """
+
+ self.name = kwargs.get('name')
+ self.password = kwargs.get('password')
+ self.project_name = kwargs.get('project_name')
+ self.email = kwargs.get('email')
+ self.domain_name = kwargs.get('domain_name', 'Default')
+ self.enabled = kwargs.get('enabled', True)
+ self.roles = kwargs.get('roles', dict())
+
+ if not self.name or not self.password:
+ raise UserConfigException(
+ 'The attributes name and password are required for '
+ 'UserConfig')
+
+ if not isinstance(self.enabled, bool):
+ raise UserConfigException(
+ 'The attribute enabled must be of type boolean')
+
+
+class UserConfigException(Exception):
+ """
+ Raised when there is a problem with the values set in the UserConfig
+ class
+ """
--- /dev/null
+# 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 snaps.config.network import PortConfig
+
+
+class VmInstanceConfig(object):
+ """
+ Class responsible for holding configuration setting for a VM Instance
+ """
+
+ def __init__(self, **kwargs):
+ """
+ Constructor
+ :param name: the name of the VM
+ :param flavor: the VM's flavor name
+ :param port_settings: the port configuration settings (required)
+ :param security_group_names: a set of names of the security groups to
+ add to the VM
+ :param floating_ip_settings: the floating IP configuration settings
+ :param sudo_user: the sudo user of the VM that will override the
+ instance_settings.image_user when trying to
+ connect to the VM
+ :param vm_boot_timeout: the amount of time a thread will wait
+ for an instance to boot
+ :param vm_delete_timeout: the amount of time a thread will wait
+ for an instance to be deleted
+ :param ssh_connect_timeout: the amount of time a thread will wait
+ to obtain an SSH connection to a VM
+ :param cloud_init_timeout: the amount of time a thread will wait for
+ cloud-init to complete
+ :param availability_zone: the name of the compute server on which to
+ deploy the VM (optional)
+ :param volume_names: a list of the names of the volume to attach
+ (optional)
+ :param userdata: the string contents of any optional cloud-init script
+ to execute after the VM has been activated.
+ This value may also contain a dict who's key value
+ must contain the key 'cloud-init_file' which denotes
+ the location of some file containing the cloud-init
+ script
+ """
+ self.name = kwargs.get('name')
+ self.flavor = kwargs.get('flavor')
+ self.sudo_user = kwargs.get('sudo_user')
+ self.userdata = kwargs.get('userdata')
+
+ self.port_settings = list()
+ port_settings = kwargs.get('ports')
+ if not port_settings:
+ port_settings = kwargs.get('port_settings')
+ if port_settings:
+ for port_setting in port_settings:
+ if isinstance(port_setting, dict):
+ self.port_settings.append(PortConfig(**port_setting))
+ elif isinstance(port_setting, PortConfig):
+ self.port_settings.append(port_setting)
+
+ if kwargs.get('security_group_names'):
+ if isinstance(kwargs['security_group_names'], list):
+ self.security_group_names = kwargs['security_group_names']
+ elif isinstance(kwargs['security_group_names'], set):
+ self.security_group_names = kwargs['security_group_names']
+ elif isinstance(kwargs['security_group_names'], str):
+ self.security_group_names = [kwargs['security_group_names']]
+ else:
+ raise VmInstanceConfigError(
+ 'Invalid data type for security_group_names attribute')
+ else:
+ self.security_group_names = set()
+
+ self.floating_ip_settings = list()
+ floating_ip_settings = kwargs.get('floating_ips')
+ if not floating_ip_settings:
+ floating_ip_settings = kwargs.get('floating_ip_settings')
+ if floating_ip_settings:
+ for floating_ip_config in floating_ip_settings:
+ if isinstance(floating_ip_config, FloatingIpConfig):
+ self.floating_ip_settings.append(floating_ip_config)
+ else:
+ self.floating_ip_settings.append(FloatingIpConfig(
+ **floating_ip_config['floating_ip']))
+
+ self.vm_boot_timeout = kwargs.get('vm_boot_timeout', 900)
+ self.vm_delete_timeout = kwargs.get('vm_delete_timeout', 300)
+ self.ssh_connect_timeout = kwargs.get('ssh_connect_timeout', 180)
+ self.cloud_init_timeout = kwargs.get('cloud_init_timeout', 300)
+ self.availability_zone = kwargs.get('availability_zone')
+ self.volume_names = kwargs.get('volume_names')
+
+ if self.volume_names and not isinstance(self.volume_names, list):
+ raise VmInstanceConfigError('volume_names must be a list')
+
+ if not self.name or not self.flavor:
+ raise VmInstanceConfigError(
+ 'Instance configuration requires the attributes: name, flavor')
+
+ if len(self.port_settings) == 0:
+ raise VmInstanceConfigError(
+ 'Instance configuration requires port settings (aka. NICS)')
+
+
+class FloatingIpConfig(object):
+ """
+ Class responsible for holding configuration settings for a floating IP
+ """
+
+ def __init__(self, **kwargs):
+ """
+ Constructor
+ :param name: the name of the floating IP
+ :param port_name: the name of the router to the external network
+ :param router_name: the name of the router to the external network
+ :param subnet_name: the name of the subnet on which to attach the
+ floating IP
+ :param provisioning: when true, this floating IP can be used for
+ provisioning
+
+ TODO - provisioning flag is a hack as I have only observed a single
+ Floating IPs that actually works on an instance. Multiple floating IPs
+ placed on different subnets from the same port are especially
+ troublesome as you cannot predict which one will actually connect.
+ For now, it is recommended not to setup multiple floating IPs on an
+ instance unless absolutely necessary.
+ """
+ self.name = kwargs.get('name')
+ self.port_name = kwargs.get('port_name')
+ self.port_id = kwargs.get('port_id')
+ self.router_name = kwargs.get('router_name')
+ self.subnet_name = kwargs.get('subnet_name')
+ if kwargs.get('provisioning') is not None:
+ self.provisioning = kwargs['provisioning']
+ else:
+ self.provisioning = True
+
+ # if not self.name or not self.port_name or not self.router_name:
+ if not self.name or not self.router_name:
+ raise FloatingIpConfigError(
+ 'The attributes name, port_name and router_name are required')
+
+ if not self.port_name and not self.port_id:
+ raise FloatingIpConfigError(
+ 'The attributes port_name or port_id are required')
+
+
+class VmInstanceConfigError(Exception):
+ """
+ Exception to be thrown when an VM instance settings are incorrect
+ """
+
+
+class FloatingIpConfigError(Exception):
+ """
+ Exception to be thrown when an VM instance settings are incorrect
+ """
--- /dev/null
+# 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 neutronclient.common.utils import str2bool
+
+
+class VolumeConfig(object):
+ def __init__(self, **kwargs):
+ """
+ Constructor
+ :param name: the volume's name (required)
+ :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
+ (optional)
+ :param type_name: the associated volume's type name (optional)
+ :param availability_zone: the name of the compute server on which to
+ deploy the volume (optional)
+ :param multi_attach: when true, volume can be attached to more than one
+ server (default False)
+ """
+
+ self.name = kwargs.get('name')
+ self.description = kwargs.get('description')
+ self.size = int(kwargs.get('size', 1))
+ self.image_name = kwargs.get('image_name')
+ self.type_name = kwargs.get('type_name')
+ self.availability_zone = kwargs.get('availability_zone')
+
+ if kwargs.get('multi_attach'):
+ self.multi_attach = str2bool(str(kwargs.get('multi_attach')))
+ else:
+ self.multi_attach = False
+
+ if not self.name:
+ raise VolumeConfigError("The attribute name is required")
+
+
+class VolumeConfigError(Exception):
+ """
+ Exception to be thrown when an volume settings are incorrect
+ """
+
+ def __init__(self, message):
+ Exception.__init__(self, message)
--- /dev/null
+# 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.
+import enum
+from neutronclient.common.utils import str2bool
+
+
+class VolumeTypeConfig(object):
+ def __init__(self, **kwargs):
+ """
+ Constructor
+ :param name: the volume's name (required)
+ :param description: the volume's name (optional)
+ :param encryption: VolumeTypeEncryptionConfig (optional)
+ :param qos_spec_name: name of the QoS Spec to associate (optional)
+ :param public: volume visibility where True denotes global
+ (default - False)
+
+ TODO - Implement project_access parameter that will associate this
+ VolumeType to a list of project names
+ """
+
+ self.name = kwargs.get('name')
+ self.description = kwargs.get('description')
+ self.qos_spec_name = kwargs.get('qos_spec_name')
+
+ if 'encryption' in kwargs:
+ if isinstance(kwargs['encryption'], dict):
+ self.encryption = VolumeTypeEncryptionConfig(
+ **kwargs['encryption'])
+ elif isinstance(kwargs['encryption'],
+ VolumeTypeEncryptionConfig):
+ self.encryption = kwargs['encryption']
+ else:
+ self.encryption = None
+
+ if 'public' in kwargs:
+ if isinstance(kwargs['public'], str):
+ self.public = str2bool(kwargs['public'])
+ else:
+ self.public = kwargs['public']
+ else:
+ self.public = False
+
+ if not self.name:
+ raise VolumeTypeConfigError("The attribute name is required")
+
+ def __eq__(self, other):
+ return (self.name == other.name
+ and self.description == other.description
+ and self.qos_spec_name == other.qos_spec_name
+ and self.encryption == other.encryption
+ and self.public == other.public)
+
+
+class ControlLocation(enum.Enum):
+ """
+ QoS Specification consumer types
+ """
+ front_end = 'front-end'
+ back_end = 'back-end'
+
+
+class VolumeTypeEncryptionConfig(object):
+ def __init__(self, **kwargs):
+ """
+ Constructor
+ :param name: the volume's name (required)
+ :param provider_class: the volume's provider class (e.g. LuksEncryptor)
+ :param control_location: the notional service where encryption is
+ performed (e.g., front-end=Nova). The default
+ value is 'front-end.'
+ :param cipher: the encryption algorithm/mode to use
+ (e.g., aes-xts-plain64). If the field is left empty,
+ the provider default will be used
+ :param key_size: the size of the encryption key, in bits
+ (e.g., 128, 256). If the field is left empty, the
+ provider default will be used
+ """
+
+ self.name = kwargs.get('name')
+ self.provider_class = kwargs.get('provider_class')
+ self.control_location = kwargs.get('control_location')
+ if kwargs.get('control_location'):
+ self.control_location = map_control_location(
+ kwargs['control_location'])
+ else:
+ self.control_location = None
+
+ self.cipher = kwargs.get('cipher')
+ self.key_size = kwargs.get('key_size')
+
+ if (not self.name or not self.provider_class
+ or not self.control_location):
+ raise VolumeTypeConfigError(
+ 'The attributes name, provider_class, and control_location '
+ 'are required')
+
+ def __eq__(self, other):
+ return (self.name == other.name
+ and self.provider_class == other.provider_class
+ and self.control_location == other.control_location
+ and self.cipher == other.cipher
+ and self.key_size == other.key_size)
+
+
+def map_control_location(control_location):
+ """
+ Takes a the protocol value maps it to the Consumer enum. When None return
+ None
+ :param control_location: the value to map to the Enum
+ :return: a ControlLocation enum object
+ :raise: Exception if control_location parameter is invalid
+ """
+ if not control_location:
+ return None
+ elif isinstance(control_location, ControlLocation):
+ return control_location
+ else:
+ proto_str = str(control_location)
+ if proto_str == 'front-end':
+ return ControlLocation.front_end
+ elif proto_str == 'back-end':
+ return ControlLocation.back_end
+ else:
+ raise VolumeTypeConfigError('Invalid Consumer - ' + proto_str)
+
+
+class VolumeTypeConfigError(Exception):
+ """
+ Exception to be thrown when an volume settings are incorrect
+ """
+
+ def __init__(self, message):
+ Exception.__init__(self, message)
--- /dev/null
+# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs")
+# and others. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+class ClusterTemplate(object):
+ """
+ Class for OpenStack cluster template domain object
+ """
+
+ def __init__(self, **kwargs):
+ """
+ Constructor
+ :param id: the cluster template's UUID
+ :param name: the cluster template's name
+ :param image: name or ID of the base image in Glance used to boot the
+ cluster's servers. The image must have the attribute
+ 'os-distro' defined as appropriate for the cluster
+ driver
+ :param keypair: name or ID of the keypair to gain cluster machine
+ access
+ :param network_driver: The name of a network driver for providing the
+ networks for the containers. Note that this is
+ different and separate from the Neutron network
+ for the bay/cluster. The operation and
+ networking model are specific to the particular
+ driver
+ :param external_net: name or IDof the external Neutron network to
+ provide connectivity to the cluster
+ :param floating_ip_enabled: Whether enable or not using the floating IP
+ of cloud provider. Some cloud providers
+ used floating IP, some used public IP,
+ thus Magnum provide this option for
+ specifying the choice of using floating IP
+ :param docker_volume_size: The size in GB for the local storage on each
+ server for the Docker daemon to cache the
+ images and host the containers. Cinder
+ volumes provide the storage. The default is
+ 25 GB. For the devicemapper storage driver,
+ the minimum value is 3GB. For the overlay
+ storage driver, the minimum value is 1GB.
+ :param server_type: server type string
+ :param flavor: name or ID of the nova flavor for booting the node
+ servers
+ :param master_flavor: name or ID of the nova flavor of the master node
+ for this cluster
+ :param coe: ContainerOrchestrationEngine enum instance
+ :param fixed_net: name of a Neutron network to provide connectivity
+ to the internal network for the cluster
+ :param fixed_subnet: Fixed subnet that are using to allocate network
+ address for nodes in bay/cluster
+ :param registry_enabled: Docker images by default are pulled from the
+ public Docker registry, but in some cases,
+ users may want to use a private registry.
+ This option provides an alternative registry
+ based on the Registry V2: Magnum will create a
+ local registry in the bay/cluster backed by
+ swift to host the images
+ :param insecure_registry: The URL pointing to the user's own private
+ insecure docker registry to deploy and run
+ docker containers
+ :param docker_storage_driver: DockerStorageDriver enum instance to
+ manage storage for the images and
+ container's writable layer
+ :param dns_nameserver: The DNS nameserver for the servers and
+ containers in the bay/cluster to use.
+ This is configured in the private Neutron
+ network for the bay/cluster.
+ :param public: denotes whether or not the cluster type is public
+ :param tls_disabled: denotes whether or not TLS should be enabled
+ :param http_proxy: host:port for a proxy to use when direct HTTP
+ access from the servers to sites on the external
+ internet is blocked
+ :param https_proxy: host:port for a proxy to use when direct HTTPS
+ access from the servers to sites on the external
+ internet is blocked
+ :param no_proxy: comma separated list of IPs that should not be
+ redirected through the proxy
+ :param volume_driver: The name of a volume driver for managing the
+ persistent storage for the containers. The
+ functionality supported are specific to the
+ driver
+ :param master_lb_enabled: Since multiple masters may exist in a
+ bay/cluster, a Neutron load balancer is
+ created to provide the API endpoint for the
+ bay/cluster and to direct requests to the
+ masters. In some cases, such as when the
+ LBaaS service is not available, this option
+ can be set to false to create a bay/cluster
+ without the load balancer. In this case, one
+ of the masters will serve as the API endpoint
+ :param labels: Arbitrary labels in the form of a dict. The accepted
+ keys and valid values are defined in the bay/cluster
+ drivers. They are used as a way to pass additional
+ parameters that are specific to a bay/cluster driver.
+ """
+ self.id = kwargs.get('id')
+ self.name = kwargs.get('name')
+ self.image = kwargs.get('image')
+ self.keypair = kwargs.get('keypair')
+ self.network_driver = kwargs.get('network_driver')
+ self.external_net = kwargs.get('external_net')
+ self.floating_ip_enabled = kwargs.get('floating_ip_enabled')
+ self.docker_volume_size = int(kwargs.get('docker_volume_size', 3))
+ self.server_type = kwargs.get('server_type')
+ self.flavor = kwargs.get('flavor')
+ self.master_flavor = kwargs.get('master_flavor')
+ self.coe = kwargs.get('coe')
+ self.fixed_net = kwargs.get('fixed_net')
+ self.fixed_subnet = kwargs.get('fixed_subnet')
+ self.registry_enabled = kwargs.get('registry_enabled')
+ self.insecure_registry = kwargs.get('insecure_registry')
+ self.docker_storage_driver = kwargs.get('docker_storage_driver')
+ self.dns_nameserver = kwargs.get('dns_nameserver')
+ self.public = kwargs.get('public', False)
+ self.tls_disabled = kwargs.get('tls_disabled')
+ self.http_proxy = kwargs.get('http_proxy')
+ self.https_proxy = kwargs.get('https_proxy')
+ self.no_proxy = kwargs.get('no_proxy')
+ self.volume_driver = kwargs.get('volume_driver')
+ self.master_lb_enabled = kwargs.get('master_lb_enabled', True)
+ self.labels = kwargs.get('labels')
+
+ def __eq__(self, other):
+ labels_eq = False
+ if (self.labels and isinstance(self.labels, dict)
+ and len(self.labels) == 0):
+ if (other.labels and isinstance(other.labels, dict)
+ and len(other.labels) == 0):
+ labels_eq = True
+ elif not self.labels:
+ if (not other.labels or
+ (isinstance(other.labels, dict)
+ and len(other.labels) == 0)):
+ labels_eq = True
+ else:
+ labels_eq = self.labels == other.labels
+
+ return (self.name == other.name
+ and self.id == other.id
+ and self.image == other.image
+ and self.keypair == other.keypair
+ and self.network_driver == other.network_driver
+ and self.external_net == other.external_net
+ and self.floating_ip_enabled == other.floating_ip_enabled
+ and self.docker_volume_size == other.docker_volume_size
+ and self.server_type == other.server_type
+ and self.flavor == other.flavor
+ and self.master_flavor == other.master_flavor
+ and self.coe == other.coe
+ and self.fixed_net == other.fixed_net
+ and self.fixed_subnet == other.fixed_subnet
+ and self.registry_enabled == other.registry_enabled
+ and self.insecure_registry == other.insecure_registry
+ and self.docker_storage_driver == other.docker_storage_driver
+ and self.dns_nameserver == other.dns_nameserver
+ and self.public == other.public
+ and self.tls_disabled == other.tls_disabled
+ and self.http_proxy == other.http_proxy
+ and self.https_proxy == other.https_proxy
+ and self.no_proxy == other.no_proxy
+ and self.volume_driver == other.volume_driver
+ and self.master_lb_enabled == other.master_lb_enabled
+ and labels_eq)
"""
Constructor
:param name: the flavor's name
- :param flavor_id: the flavor's id
+ :param flavor_id or id: the flavor's id
:param ram: the flavor's RAM in MB
:param disk: the flavor's disk size in GB
:param vcpus: the flavor's number of virtual CPUs
:param is_public: denotes if flavor can be used by other projects
"""
self.name = kwargs.get('name')
- self.id = kwargs.get('id')
+ self.id = kwargs.get('flavor_id', kwargs.get('id'))
self.ram = kwargs.get('ram')
self.disk = kwargs.get('disk')
self.vcpus = kwargs.get('vcpus')
self.ephemeral = kwargs.get('ephemeral')
- self.swap = kwargs.get('swap')
+
+ if kwargs.get('swap'):
+ self.swap = int(kwargs.get('swap'))
+ else:
+ self.swap = None
+
self.rxtx_factor = kwargs.get('rxtx_factor')
self.is_public = kwargs.get('is_public')
def __init__(self, **kwargs):
"""
Constructor
+ :param name: the network's name
+ :param id: the network's 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
+ :param type: vlan, vxlan, flat, etc.
+ :param subnets: list of Subnet objects
"""
self.name = kwargs.get('name')
self.id = kwargs.get('id')
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())
def __eq__(self, other):
return (self.name == other.name and self.id == other.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)
+ self.external == other.external and
+ self.type == other.type and
+ self.subnets == other.subnets)
class Subnet:
def __init__(self, **kwargs):
"""
Constructor
+ :param name: the network's name
+ :param id: the subnet's ID
+ :param network_id: the network's ID
+ :param cidr: the CIDR
+ :param ip_version: the IP version
+ :param gateway_ip: the IP of the gateway
+ :param enable_dhcp: T/F if DHCP is enabled
+ :param dns_nameservers: list of DNS server IPs
+ :param host_routes: routes as returned in a dict by Neutron
+ :param ipv6_ra_mode: IPv6 RA Mode
+ :param ipv6_address_mode: IPv6 Address Mode
+ :param start: start IP address pool
+ :param end: end IP address pool
"""
self.name = kwargs.get('name')
self.id = kwargs.get('id')
+ self.network_id = kwargs.get('network_id')
self.cidr = kwargs.get('cidr')
self.ip_version = kwargs.get('ip_version')
self.gateway_ip = kwargs.get('gateway_ip')
def __eq__(self, other):
return (self.name == other.name and
self.id == other.id and
+ self.network_id == other.network_id and
self.cidr == other.cidr and
self.ip_version == other.ip_version and
self.gateway_ip == other.gateway_ip and
Constructor
:param name: the router's name
:param id: the router's id
+ :param status: the router's status
+ :param tenant_id: the router's project/tenant ID
+ :param admin_state_up: Router is up when True
+ :param external_gateway_info: dict() for populating external_network_id
+ and external_fixed_ips
+ external_network_id: ID of the external network to route
+ in dict under key 'external_fixed_ips'
+ external_fixed_ips: List IP addresses associated with the
+ external_network_id found in dict under
+ key 'network_id'
+ :param port_subnets: list of tuples where #1 is the Port domain object
+ and #2 is a list of associated Subnet domain
+ objects
"""
self.name = kwargs.get('name')
self.id = kwargs.get('id')
self.status = kwargs.get('status')
self.tenant_id = kwargs.get('tenant_id')
self.admin_state_up = kwargs.get('admin_state_up')
- self.external_gateway_info = kwargs.get('external_gateway_info')
+ 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')):
+ gateway_info = kwargs.get('external_gateway_info')
+
+ self.external_network_id = gateway_info.get('network_id')
+ self.external_fixed_ips = gateway_info.get('external_fixed_ips')
+ else:
+ self.external_fixed_ips = kwargs.get('external_fixed_ips', None)
+ self.external_network_id = kwargs.get('external_network_id', None)
def __eq__(self, other):
return (self.name == other.name and self.id == other.id and
self.status == other.status and
self.tenant_id == other.tenant_id and
self.admin_state_up == other.admin_state_up and
- self.external_gateway_info == other.external_gateway_info)
+ self.external_network_id == other.external_network_id and
+ self.external_fixed_ips == other.external_fixed_ips and
+ self.port_subnets == other.port_subnets)
class InterfaceRouter:
Constructor
:param name: the security group's name
:param id: the security group's id
+ :param description: the security group's description
+ :param project_id: the security group's project_id
+ :param rules: list of SecurityGroupRule objects associated to this
"""
self.name = kwargs.get('name')
self.id = kwargs.get('id')
self.description = kwargs.get('description')
self.project_id = kwargs.get('project_id', kwargs.get('tenant_id'))
+ self.rules = list()
+ if kwargs.get('rules') and isinstance(kwargs.get('rules'), list):
+ for rule in kwargs.get('rules'):
+ if isinstance(rule, SecurityGroupRule):
+ self.rules.append(rule)
+ else:
+ self.rules.append(SecurityGroupRule(**rule))
+
def __eq__(self, other):
- return (self.name == other.name and self.id == other.id and
- self.project_id == other.project_id)
+ return (self.name == other.name and
+ self.id == other.id and
+ self.description == other.description and
+ self.project_id == other.project_id and
+ self.rules == other.rules)
class SecurityGroupRule:
"""
Constructor
:param id: the security group rule's id
- :param sec_grp_id: the ID of the associated security group
+ :param security_group_id: the ID of the associated security group
:param description: the security group rule's description
:param direction: the security group rule's direction
:param ethertype: the security group rule's ethertype
"""
SNAPS domain object for a resource created by a heat template
"""
- def __init__(self, resource_type, resource_id):
+ def __init__(self, name, resource_type, resource_id, status,
+ status_reason):
"""
Constructor
- :param resource_type: the type
+ :param name: the resource's name
+ :param resource_type: the resource's type
:param resource_id: the ID attached to the resource of the given type
+ :param status: the resource's status code
+ :param status_reason: the resource's status code reason
"""
+ self.name = name
self.type = resource_type
self.id = resource_id
+ self.status = status
+ self.status_reason = status_reason
class Output:
--- /dev/null
+# 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.
+import unittest
+
+from snaps.config.cluster_template import (
+ ContainerOrchestrationEngine, ServerType, DockerStorageDriver)
+from snaps.domain.cluster_template import ClusterTemplate
+
+
+class ClusterTemplateUnitTests(unittest.TestCase):
+ """
+ Tests the construction of the ClusterTypeConfig class
+ """
+ def test_all_named(self):
+ labels = {'foo': 'bar'}
+ config = ClusterTemplate(
+ id='tmplt-id', name='foo', image='bar', keypair='keys',
+ network_driver='driver', external_net='external',
+ docker_volume_size=99, server_type=ServerType.baremetal.value,
+ flavor='testFlavor', master_flavor='masterFlavor',
+ coe=ContainerOrchestrationEngine.kubernetes.value,
+ fixed_net='fixedNet', fixed_subnet='fixedSubnet',
+ registry_enabled=False,
+ docker_storage_driver=DockerStorageDriver.overlay.value,
+ dns_nameserver='8.8.4.4', public=True, tls=False,
+ http_proxy='http://foo:8080', https_proxy='https://foo:443',
+ no_proxy='foo,bar', volume_driver='volDriver',
+ master_lb_enabled=False, labels=labels)
+ self.assertIsNotNone(config)
+ self.assertEqual('tmplt-id', config.id)
+ self.assertEqual('foo', config.name)
+ self.assertEqual('bar', config.image)
+ self.assertEqual('keys', config.keypair)
+ self.assertEqual('driver', config.network_driver)
+ self.assertEqual('external', config.external_net)
+ self.assertEqual(99, config.docker_volume_size)
+ self.assertEqual(ServerType.baremetal.value, config.server_type)
+ self.assertEqual('testFlavor', config.flavor)
+ self.assertEqual('masterFlavor', config.master_flavor)
+ self.assertEqual(ContainerOrchestrationEngine.kubernetes.value,
+ config.coe)
+ self.assertEqual('fixedNet', config.fixed_net)
+ self.assertEqual('fixedSubnet', config.fixed_subnet)
+ self.assertFalse(config.registry_enabled)
+ self.assertEqual(DockerStorageDriver.overlay.value,
+ config.docker_storage_driver)
+ self.assertEqual('8.8.4.4', config.dns_nameserver)
+ self.assertTrue(config.public)
+ self.assertFalse(config.tls_disabled)
+ self.assertEqual('http://foo:8080', config.http_proxy)
+ self.assertEqual('https://foo:443', config.https_proxy)
+ self.assertEqual('foo,bar', config.no_proxy)
+ self.assertEqual('volDriver', config.volume_driver)
+ self.assertFalse(config.master_lb_enabled)
+ self.assertEqual(labels, config.labels)
+
+ def test_all_config(self):
+ labels = {'foo': 'bar'}
+ config = ClusterTemplate(**{
+ 'id': 'tmplt-id', 'name': 'foo', 'image': 'bar', 'keypair': 'keys',
+ 'network_driver': 'driver', 'external_net': 'external',
+ 'docker_volume_size': '99', 'server_type': 'baremetal',
+ 'flavor': 'testFlavor', 'master_flavor': 'masterFlavor',
+ 'coe': 'kubernetes', 'fixed_net': 'fixedNet',
+ 'fixed_subnet': 'fixedSubnet', 'registry_enabled': False,
+ 'docker_storage_driver': 'overlay', 'dns_nameserver': '8.8.4.4',
+ 'public': 'true', 'tls': 'false', 'http_proxy': 'http://foo:8080',
+ 'https_proxy': 'https://foo:443', 'no_proxy': 'foo,bar',
+ 'volume_driver': 'volDriver', 'master_lb_enabled': False,
+ 'labels': labels})
+ self.assertIsNotNone(config)
+ self.assertEqual('tmplt-id', config.id)
+ self.assertEqual('foo', config.name)
+ self.assertEqual('bar', config.image)
+ self.assertEqual('keys', config.keypair)
+ self.assertEqual('driver', config.network_driver)
+ self.assertEqual('external', config.external_net)
+ self.assertEqual(99, config.docker_volume_size)
+ self.assertEqual(ServerType.baremetal.value, config.server_type)
+ self.assertEqual('testFlavor', config.flavor)
+ self.assertEqual('masterFlavor', config.master_flavor)
+ self.assertEqual(ContainerOrchestrationEngine.kubernetes.value,
+ config.coe)
+ self.assertEqual('fixedNet', config.fixed_net)
+ self.assertEqual('fixedSubnet', config.fixed_subnet)
+ self.assertFalse(config.registry_enabled)
+ self.assertEqual(DockerStorageDriver.overlay.value,
+ config.docker_storage_driver)
+ self.assertEqual('8.8.4.4', config.dns_nameserver)
+ self.assertTrue(config.public)
+ self.assertFalse(config.tls_disabled)
+ self.assertEqual('http://foo:8080', config.http_proxy)
+ self.assertEqual('https://foo:443', config.https_proxy)
+ self.assertEqual('foo,bar', config.no_proxy)
+ self.assertEqual('volDriver', config.volume_driver)
+ self.assertFalse(config.master_lb_enabled)
+ self.assertEqual(labels, config.labels)
"""
def test_construction_kwargs_1(self):
+ subnet = Subnet(
+ **{'name': 'foo', 'id': 'bar', 'network_id': 'foo-bar'})
network = Network(
**{'name': 'foo', 'id': 'bar', 'provider:network_type': 'flat',
'admin_state_up': False, 'shared': True,
- 'router:external': False})
+ 'router:external': False, 'subnets': [subnet]})
self.assertEqual('foo', network.name)
self.assertEqual('bar', network.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)
def test_construction_kwargs_2(self):
+ subnet = Subnet(
+ **{'name': 'foo', 'id': 'bar', 'network_id': 'foo-bar'})
network = Network(
**{'name': 'foo', 'id': 'bar', 'type': 'flat',
- 'admin_state_up': False, 'shared': True,
- 'external': False})
+ 'admin_state_up': False, 'shared': True, 'external': False,
+ 'subnets': [subnet]})
self.assertEqual('foo', network.name)
self.assertEqual('bar', network.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)
def test_construction_named(self):
+ subnet = Subnet(
+ **{'name': 'foo', 'id': 'bar', 'network_id': 'foo-bar'})
network = Network(
name='foo', id='bar', type='flat', admin_state_up=False,
- shared=True, external=False)
+ shared=True, external=False, subnets=[subnet])
self.assertEqual('foo', network.name)
self.assertEqual('bar', network.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)
class SubnetObjectTests(unittest.TestCase):
self.assertEqual('hello', router.status)
self.assertEqual('1234', router.tenant_id)
self.assertEqual('yes', router.admin_state_up)
- self.assertEqual('no', router.external_gateway_info)
+ self.assertIsNone(router.external_fixed_ips)
+ self.assertIsNone(router.external_network_id)
def test_construction_named(self):
router = Router(
self.assertEqual('hello', router.status)
self.assertEqual('1234', router.tenant_id)
self.assertEqual('yes', router.admin_state_up)
- self.assertEqual('no', router.external_gateway_info)
+ self.assertIsNone(router.external_fixed_ips)
+ self.assertIsNone(router.external_network_id)
+
+ def test_ext_gateway_named(self):
+ router = Router(
+ external_fixed_ips=['456', '789'], external_network_id='123',
+ admin_state_up='yes', tenant_id='1234', status='hello', id='id',
+ name='name')
+ self.assertEqual('name', router.name)
+ self.assertEqual('id', router.id)
+ self.assertEqual('hello', router.status)
+ self.assertEqual('1234', router.tenant_id)
+ self.assertEqual('yes', router.admin_state_up)
+ self.assertEqual(['456', '789'], router.external_fixed_ips)
+ self.assertEqual('123', router.external_network_id)
+
+ def test_ext_net_ips_named(self):
+ ext_gateway = {'network_id': '123',
+ 'external_fixed_ips': ['456', '789']}
+ router = Router(
+ external_gateway_info=ext_gateway, admin_state_up='yes',
+ tenant_id='1234', status='hello', id='id', name='name')
+ self.assertEqual('name', router.name)
+ self.assertEqual('id', router.id)
+ self.assertEqual('hello', router.status)
+ self.assertEqual('1234', router.tenant_id)
+ self.assertEqual('yes', router.admin_state_up)
+ self.assertEqual(['456', '789'], router.external_fixed_ips)
+ self.assertEqual('123', router.external_network_id)
class InterfaceRouterDomainObjectTests(unittest.TestCase):
def test_construction_proj_id_kwargs(self):
sec_grp = SecurityGroup(
**{'name': 'name', 'id': 'id', 'project_id': 'foo',
- 'description': 'test desc'})
+ 'description': 'test desc',
+ 'rules': [
+ {'id': 'id', 'security_group_id': 'grp_id',
+ 'description': 'desc', 'direction': 'dir',
+ 'ethertype': 'eType', 'port_range_min': '10.0.0.100',
+ 'port_range_max': '10.0.0.200', 'protocol': 'proto',
+ 'remote_group_id': 'group_id',
+ 'remote_ip_prefix': 'ip_prefix'}
+ ]})
self.assertEqual('name', sec_grp.name)
self.assertEqual('id', sec_grp.id)
self.assertEqual('test desc', sec_grp.description)
self.assertEqual('foo', sec_grp.project_id)
+ self.assertEqual(1, len(sec_grp.rules))
+ rule = sec_grp.rules[0]
+ self.assertEqual('id', rule.id)
+ self.assertEqual('grp_id', rule.security_group_id)
+ self.assertEqual('desc', rule.description)
+ self.assertEqual('dir', rule.direction)
+ self.assertEqual('eType', rule.ethertype)
+ self.assertEqual('10.0.0.100', rule.port_range_min)
+ self.assertEqual('10.0.0.200', rule.port_range_max)
+ self.assertEqual('proto', rule.protocol)
+ self.assertEqual('group_id', rule.remote_group_id)
+ self.assertEqual('ip_prefix', rule.remote_ip_prefix)
+
def test_construction_tenant_id_kwargs(self):
sec_grp = SecurityGroup(
**{'name': 'name', 'id': 'id',
self.assertEqual('id', sec_grp.id)
self.assertEqual('foo', sec_grp.project_id)
self.assertIsNone(sec_grp.description)
+ self.assertEqual(0, len(sec_grp.rules))
def test_construction_named(self):
+ rules = [SecurityGroupRule(
+ **{'id': 'id', 'security_group_id': 'grp_id',
+ 'description': 'desc', 'direction': 'dir',
+ 'ethertype': 'eType', 'port_range_min': '10.0.0.100',
+ 'port_range_max': '10.0.0.200', 'protocol': 'proto',
+ 'remote_group_id': 'group_id',
+ 'remote_ip_prefix': 'ip_prefix'}
+ )]
sec_grp = SecurityGroup(description='test desc', tenant_id='foo',
- id='id', name='name')
+ id='id', name='name', rules=rules)
self.assertEqual('name', sec_grp.name)
self.assertEqual('id', sec_grp.id)
self.assertEqual('test desc', sec_grp.description)
self.assertEqual('foo', sec_grp.project_id)
+ self.assertEqual(1, len(sec_grp.rules))
+ rule = sec_grp.rules[0]
+ self.assertEqual('id', rule.id)
+ self.assertEqual('grp_id', rule.security_group_id)
+ self.assertEqual('desc', rule.description)
+ self.assertEqual('dir', rule.direction)
+ self.assertEqual('eType', rule.ethertype)
+ self.assertEqual('10.0.0.100', rule.port_range_min)
+ self.assertEqual('10.0.0.200', rule.port_range_max)
+ self.assertEqual('proto', rule.protocol)
+ self.assertEqual('group_id', rule.remote_group_id)
+ self.assertEqual('ip_prefix', rule.remote_ip_prefix)
+
class SecurityGroupRuleDomainObjectTests(unittest.TestCase):
"""
"""
def test_construction_positional(self):
- resource = Resource('foo', 'bar')
+ resource = Resource('res_name', 'foo', 'bar', 'status', 'reason')
+ self.assertEqual('res_name', resource.name)
self.assertEqual('foo', resource.type)
self.assertEqual('bar', resource.id)
+ self.assertEqual('status', resource.status)
+ self.assertEqual('reason', resource.status_reason)
def test_construction_named(self):
- resource = Resource(resource_id='bar', resource_type='foo')
+ resource = Resource(
+ status_reason=None, status=None, resource_id='bar',
+ resource_type='foo', name='res_name')
+ self.assertEqual('res_name', resource.name)
self.assertEqual('foo', resource.type)
self.assertEqual('bar', resource.id)
+ self.assertIsNone(resource.status)
+ self.assertIsNone(resource.status_reason)
class OutputDomainObjectTests(unittest.TestCase):
"""
def test_construction_positional(self):
- vm_inst = VmInst('name', 'id', '456', '123', dict(), 'kp-name', list())
+ vm_inst = VmInst('name', 'id', '456', '123', list(), 'kp-name',
+ ['foo', 'bar'], ['123', '456'])
self.assertEqual('name', vm_inst.name)
self.assertEqual('id', vm_inst.id)
self.assertEqual('456', vm_inst.image_id)
self.assertEqual('123', vm_inst.flavor_id)
- self.assertEqual(dict(), vm_inst.networks)
+ self.assertEqual(list(), vm_inst.ports)
self.assertEqual('kp-name', vm_inst.keypair_name)
- self.assertEqual(list(), vm_inst.sec_grp_names)
+ self.assertEqual(['foo', 'bar'], vm_inst.sec_grp_names)
+ self.assertEqual(['123', '456'], vm_inst.volume_ids)
def test_construction_named(self):
- vm_inst = VmInst(sec_grp_names=list(), networks=dict(), inst_id='id',
- name='name', flavor_id='123', image_id='456',
- keypair_name='kp-name')
+ vm_inst = VmInst(
+ 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')
self.assertEqual('name', vm_inst.name)
self.assertEqual('id', vm_inst.id)
self.assertEqual('456', vm_inst.image_id)
self.assertEqual('123', vm_inst.flavor_id)
- self.assertEqual(dict(), vm_inst.networks)
+ self.assertEqual(list(), vm_inst.ports)
self.assertEqual('kp-name', vm_inst.keypair_name)
- self.assertEqual(list(), vm_inst.sec_grp_names)
+ self.assertEqual(['foo', 'bar'], vm_inst.sec_grp_names)
+ self.assertEqual(['123', '456'], vm_inst.volume_ids)
class FloatingIpDomainObjectTests(unittest.TestCase):
# limitations under the License.
import unittest
-from snaps.domain.volume import QoSSpec, VolumeType, VolumeTypeEncryption
+from snaps.domain.volume import (
+ QoSSpec, VolumeType, VolumeTypeEncryption, Volume)
+
+
+class VolumeDomainObjectTests(unittest.TestCase):
+ """
+ Tests the construction of the snaps.domain.volume.Volume class
+ """
+
+ def test_construction_positional(self):
+ volume = Volume('name1', 'id1', 'desc_val1', 2, 'type_val1',
+ 'avail_zone1', False, [{'attached_at': 'foo'}])
+ self.assertEqual('name1', volume.name)
+ self.assertEqual('id1', volume.id)
+ self.assertEqual('desc_val1', volume.description)
+ self.assertEqual(2, volume.size)
+ self.assertEqual('type_val1', volume.type)
+ self.assertEqual('avail_zone1', volume.availability_zone)
+ self.assertFalse(volume.multi_attach)
+ self.assertIsNotNone(volume.attachments)
+ self.assertTrue(isinstance(volume.attachments[0], dict))
+ self.assertEqual(1, len(volume.attachments))
+
+ def test_construction_named(self):
+ 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')
+ self.assertEqual('name2', volume.name)
+ self.assertEqual('id2', volume.id)
+ self.assertEqual('desc_val2', volume.description)
+ self.assertEqual(3, volume.size)
+ self.assertEqual('type_val2', volume.type)
+ self.assertEqual('avail_zone2', volume.availability_zone)
+ self.assertTrue(volume.multi_attach)
+ self.assertIsNotNone(volume.attachments)
+ self.assertTrue(isinstance(volume.attachments[0], dict))
+ self.assertEqual(1, len(volume.attachments))
class VolumeTypeDomainObjectTests(unittest.TestCase):
SNAPS domain object for Images. Should contain attributes that
are shared amongst cloud providers
"""
- def __init__(self, name, inst_id, image_id, flavor_id, networks,
- keypair_name, sec_grp_names):
+ def __init__(self, name, inst_id, image_id, flavor_id, ports,
+ keypair_name, sec_grp_names, volume_ids):
"""
Constructor
:param name: the image's name
:param inst_id: the instance's id
:param image_id: the instance's image id
:param flavor_id: the ID used to spawn this instance
- :param networks: dict of networks where the key is the network name and
- value is a list of associated IPs
+ :param ports: list of SNAPS-OO Port domain objects associated with this
+ server instance
: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
"""
self.name = name
self.id = inst_id
self.image_id = image_id
self.flavor_id = flavor_id
- self.networks = networks
+ self.ports = ports
self.keypair_name = keypair_name
self.sec_grp_names = sec_grp_names
+ self.volume_ids = volume_ids
def __eq__(self, other):
return (self.name == other.name and
self.id == other.id and
self.image_id == other.image_id and
self.flavor_id == other.flavor_id and
- self.networks == other.networks and
+ self.ports == other.ports and
self.keypair_name == other.keypair_name and
- self.sec_grp_names == other.sec_grp_names)
+ self.sec_grp_names == other.sec_grp_names and
+ self.volume_ids == other.volume_ids)
class FloatingIp:
# limitations under the License.
+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()):
+ """
+ Constructor
+ :param name: the volume's name
+ :param volume_id: the volume's id
+ :param description: the volume's description
+ :param size: the volume's size in GB
+ :param vol_type: the volume's type
+ :param availability_zone: the volume's availability zone
+ :param multi_attach: When true, volume can be attached to multiple VMs
+ :param attachments: List of dict objects containing the info on where
+ this volume is attached
+ """
+ self.name = name
+ self.id = volume_id
+ self.description = description
+ self.size = size
+ self.type = vol_type
+ self.availability_zone = availability_zone
+ self.multi_attach = multi_attach
+ self.attachments = attachments
+
+ def __eq__(self, other):
+ return (self.name == other.name and self.id == other.id
+ and self.description == other.description
+ and self.size == other.size
+ and self.type == other.type
+ and self.availability_zone == other.availability_zone
+ and self.multi_attach == other.multi_attach)
+
+
class VolumeType:
"""
SNAPS domain object for Volume Types. Should contain attributes that
--- /dev/null
+# 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.
+
+import logging
+
+from magnumclient.common.apiclient.exceptions import NotFound
+
+from snaps.openstack.openstack_creator import OpenStackMagnumObject
+from snaps.openstack.utils import magnum_utils
+
+__author__ = 'spisarski'
+
+logger = logging.getLogger('cluster_template')
+
+
+class OpenStackClusterTemplate(OpenStackMagnumObject):
+ """
+ Class responsible for managing an volume in OpenStack
+ """
+
+ def __init__(self, os_creds, cluster_template_config):
+ """
+ Constructor
+ :param os_creds: The OpenStack connection credentials
+ :param cluster_template_config: The volume type settings
+ :return:
+ """
+ super(self.__class__, self).__init__(os_creds)
+
+ self.cluster_template_config = cluster_template_config
+ self.__cluster_template = None
+
+ def initialize(self):
+ """
+ Loads the existing Volume
+ :return: The Volume domain object or None
+ """
+ super(self.__class__, self).initialize()
+
+ self.__cluster_template = magnum_utils.get_cluster_template(
+ self._magnum, template_config=self.cluster_template_config)
+
+ return self.__cluster_template
+
+ def create(self):
+ """
+ Creates the volume in OpenStack if it does not already exist and
+ returns the domain Volume object
+ :return: The Volume domain object or None
+ """
+ self.initialize()
+
+ if not self.__cluster_template:
+ self.__cluster_template = magnum_utils.create_cluster_template(
+ self._magnum, self.cluster_template_config)
+ logger.info(
+ 'Created volume type with name - %s',
+ self.cluster_template_config.name)
+
+ return self.__cluster_template
+
+ def clean(self):
+ """
+ Cleanse environment of all artifacts
+ :return: void
+ """
+ if self.__cluster_template:
+ try:
+ magnum_utils.delete_cluster_template(
+ self._magnum, self.__cluster_template.id)
+ except NotFound:
+ pass
+
+ self.__cluster_template = None
+
+ def get_cluster_template(self):
+ """
+ Returns the domain Volume object as it was populated when create() was
+ called
+ :return: the object
+ """
+ return self.__cluster_template
from novaclient.exceptions import NotFound
+from snaps.config.flavor import FlavorConfig
from snaps.openstack.openstack_creator import OpenStackComputeObject
from snaps.openstack.utils import nova_utils
"""
Constructor
:param os_creds: The OpenStack connection credentials
- :param flavor_settings: The flavor settings
+ :param flavor_settings: a FlavorConfig instance
:return:
"""
super(self.__class__, self).__init__(os_creds)
if self.flavor_settings.metadata:
nova_utils.set_flavor_keys(self._nova, self.__flavor,
self.flavor_settings.metadata)
- else:
- logger.info('Did not create flavor due to cleanup mode')
return self.__flavor
return self.__flavor
-class FlavorSettings:
+class FlavorSettings(FlavorConfig):
"""
Configuration settings for OpenStack flavor creation
"""
def __init__(self, **kwargs):
- """
- Constructor
- :param config: dict() object containing the configuration settings
- using the attribute names below as each member's the
- key and overrides any of the other parameters.
- :param name: the flavor's name (required)
- :param flavor_id: the string ID (default 'auto')
- :param ram: the required RAM in MB (required)
- :param disk: the size of the root disk in GB (required)
- :param vcpus: the number of virtual CPUs (required)
- :param ephemeral: the size of the ephemeral disk in GB (default 0)
- :param swap: the size of the dedicated 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: denotes whether or not the flavor is public
- (default True)
- :param metadata: freeform dict() for special metadata
- """
- self.name = kwargs.get('name')
-
- if kwargs.get('flavor_id'):
- self.flavor_id = kwargs['flavor_id']
- else:
- self.flavor_id = 'auto'
-
- self.ram = kwargs.get('ram')
- self.disk = kwargs.get('disk')
- self.vcpus = kwargs.get('vcpus')
-
- if kwargs.get('ephemeral'):
- self.ephemeral = kwargs['ephemeral']
- else:
- self.ephemeral = 0
-
- if kwargs.get('swap'):
- self.swap = kwargs['swap']
- else:
- self.swap = 0
-
- if kwargs.get('rxtx_factor'):
- self.rxtx_factor = kwargs['rxtx_factor']
- else:
- self.rxtx_factor = 1.0
-
- if kwargs.get('is_public') is not None:
- self.is_public = kwargs['is_public']
- else:
- self.is_public = True
-
- if kwargs.get('metadata'):
- self.metadata = kwargs['metadata']
- else:
- self.metadata = None
-
- if not self.name or not self.ram or not self.disk or not self.vcpus:
- raise FlavorSettingsError(
- 'The attributes name, ram, disk, and vcpus are required for'
- 'FlavorSettings')
-
- if not isinstance(self.ram, int):
- raise FlavorSettingsError('The ram attribute must be a integer')
-
- if not isinstance(self.disk, int):
- raise FlavorSettingsError('The ram attribute must be a integer')
-
- if not isinstance(self.vcpus, int):
- raise FlavorSettingsError('The vcpus attribute must be a integer')
-
- if self.ephemeral and not isinstance(self.ephemeral, int):
- raise FlavorSettingsError(
- 'The ephemeral attribute must be an integer')
-
- if self.swap and not isinstance(self.swap, int):
- raise FlavorSettingsError('The swap attribute must be an integer')
-
- if self.rxtx_factor and not isinstance(self.rxtx_factor, (int, float)):
- raise FlavorSettingsError(
- 'The is_public attribute must be an integer or float')
-
- if self.is_public and not isinstance(self.is_public, bool):
- raise FlavorSettingsError(
- 'The is_public attribute must be a boolean')
-
-
-class FlavorSettingsError(Exception):
- """
- Exception to be thrown when an flavor settings are incorrect
- """
+ from warnings import warn
+ warn('Use snaps.config.flavor.FlavorConfig instead',
+ DeprecationWarning)
+ super(self.__class__, self).__init__(**kwargs)
from snaps.openstack.openstack_creator import OpenStackCloudObject
from snaps.openstack.utils import glance_utils
+from snaps.config import image
__author__ = 'spisarski'
"""
Constructor
:param os_creds: The OpenStack connection credentials
- :param image_settings: The image settings
- :return:
+ :param image_settings: An snaps.config.image.ImageConfig object
"""
super(self.__class__, self).__init__(os_creds)
self.__glance = glance_utils.glance_client(self._os_creds)
self.__image = glance_utils.get_image(
self.__glance, image_settings=self.image_settings)
+
if self.__image:
logger.info('Found image with name - ' + self.image_settings.name)
return self.__image
self.__kernel_image = glance_utils.get_image(
self.__glance,
image_settings=self.image_settings.kernel_image_settings)
+
if self.image_settings.ramdisk_image_settings:
self.__ramdisk_image = glance_utils.get_image(
self.__glance,
self.__glance,
self.image_settings.kernel_image_settings)
extra_properties['kernel_id'] = self.__kernel_image.id
+
if self.image_settings.ramdisk_image_settings:
if not self.__ramdisk_image:
logger.info(
logger.info(
'Created image with name - %s', self.image_settings.name)
+
if self.__image and self.image_active(block=True):
logger.info(
'Image is now active with name - %s',
raise ImageCreationError(
'Image was not created or activated in the alloted amount'
'of time')
- else:
- logger.info('Did not create image due to cleanup mode')
return self.__image
Cleanse environment of all artifacts
:return: void
"""
- for image in [self.__image, self.__kernel_image, self.__ramdisk_image]:
- if image:
+ for img in [self.__image, self.__kernel_image, self.__ramdisk_image]:
+ if img:
try:
- glance_utils.delete_image(self.__glance, image)
+ glance_utils.delete_image(self.__glance, img)
except HTTPNotFound:
pass
return status == expected_status_code
-class ImageSettings:
- def __init__(self, **kwargs):
- """
- Constructor
- :param name: the image's name (required)
- :param image_user: the image's default sudo user (required)
- :param format or img_format: the image type (required)
- :param url or download_url: the image download location (requires url
- or img_file)
- :param image_file: the image file location (requires url or img_file)
- :param extra_properties: dict() object containing extra parameters to
- pass when loading the image;
- can be ids of kernel and initramfs images for
- a 3-part image
- :param nic_config_pb_loc: the file location to the Ansible Playbook
- that can configure multiple NICs
- :param kernel_image_settings: the settings for a kernel image
- :param ramdisk_image_settings: the settings for a kernel image
- :param exists: When True, an image with the given name must exist
- :param public: When True, an image will be created with public
- visibility
- """
-
- self.name = kwargs.get('name')
- self.image_user = kwargs.get('image_user')
- self.format = kwargs.get('format')
- if not self.format:
- self.format = kwargs.get('img_format')
-
- self.url = kwargs.get('url')
- if not self.url:
- self.url = kwargs.get('download_url')
- if self.url == 'None':
- self.url = None
-
- self.image_file = kwargs.get('image_file')
- if self.image_file == 'None':
- self.image_file = None
-
- self.extra_properties = kwargs.get('extra_properties')
- self.nic_config_pb_loc = kwargs.get('nic_config_pb_loc')
-
- kernel_image_settings = kwargs.get('kernel_image_settings')
- if kernel_image_settings:
- if isinstance(kernel_image_settings, dict):
- self.kernel_image_settings = ImageSettings(
- **kernel_image_settings)
- else:
- self.kernel_image_settings = kernel_image_settings
- else:
- self.kernel_image_settings = None
-
- ramdisk_image_settings = kwargs.get('ramdisk_image_settings')
- if ramdisk_image_settings:
- if isinstance(ramdisk_image_settings, dict):
- self.ramdisk_image_settings = ImageSettings(
- **ramdisk_image_settings)
- else:
- self.ramdisk_image_settings = ramdisk_image_settings
- else:
- self.ramdisk_image_settings = None
-
- if 'exists' in kwargs and kwargs['exists'] is True:
- self.exists = True
- else:
- self.exists = False
-
- if 'public' in kwargs and kwargs['public'] is True:
- self.public = True
- else:
- self.public = False
-
- if not self.name:
- raise ImageSettingsError("The attribute name is required")
-
- if not (self.url or self.image_file) and not self.exists:
- raise ImageSettingsError(
- 'URL or image file must be set or image must already exist')
-
- if not self.image_user:
- raise ImageSettingsError('Image user is required')
-
- if not self.format and not self.exists:
- raise ImageSettingsError(
- 'Format is required when the image should not already exist')
-
-
-class ImageSettingsError(Exception):
+class ImageSettings(image.ImageConfig):
"""
- Exception to be thrown when an image settings are incorrect
+ Deprecated, use snaps.config.image.ImageSettings instead
"""
-
- def __init__(self, message):
- Exception.__init__(self, message)
+ def __init__(self, **kwargs):
+ from warnings import warn
+ warn('Use snaps.config.image.ImageConfig instead', DeprecationWarning)
+ super(ImageSettings, self).__init__(**kwargs)
class ImageCreationError(Exception):
import logging
import time
-from neutronclient.common.exceptions import PortNotFoundClient
-from novaclient.exceptions import NotFound
+from novaclient.exceptions import NotFound, BadRequest
-from snaps.openstack.create_network import PortSettings
+from snaps.config.vm_inst import VmInstanceConfig, FloatingIpConfig
from snaps.openstack.openstack_creator import OpenStackComputeObject
-from snaps.openstack.utils import glance_utils
+from snaps.openstack.utils import glance_utils, cinder_utils, settings_utils
from snaps.openstack.utils import neutron_utils
from snaps.openstack.utils import nova_utils
+from snaps.openstack.utils.nova_utils import RebootType
from snaps.provisioning import ansible_utils
__author__ = 'spisarski'
self.initialize()
if len(self.__ports) == 0:
- self.__ports = self.__create_ports(self.instance_settings.port_settings)
+ self.__ports = self.__create_ports(
+ self.instance_settings.port_settings)
if not self.__vm:
self.__create_vm(block)
within the project
"""
server = nova_utils.get_server(
- self._nova, vm_inst_settings=self.instance_settings)
+ self._nova, self.__neutron,
+ vm_inst_settings=self.instance_settings)
if server:
if server.name == self.instance_settings.name:
self.__vm = server
' to VM that did not activate with name - ' +
self.instance_settings.name)
+ 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)
+
+ if volume and self.vm_active(block=True):
+ timeout = 30
+ vm = nova_utils.attach_volume(
+ self._nova, self.__neutron, self.__vm, volume, timeout)
+
+ if vm:
+ self.__vm = vm
+ else:
+ logger.warn('Volume [%s] not attached within timeout '
+ 'of [%s]', volume.name, timeout)
+ else:
+ logger.warn('Unable to attach volume named [%s]',
+ volume_name)
+
self.__apply_floating_ips()
def __apply_floating_ips(self):
# Apply floating IPs
for floating_ip_setting in self.instance_settings.floating_ip_settings:
- port = port_dict.get(floating_ip_setting.port_name)
+ self.add_floating_ip(floating_ip_setting)
- if not port:
- raise VmInstanceCreationError(
- 'Cannot find port object with name - ' +
- floating_ip_setting.port_name)
+ def add_floating_ip(self, floating_ip_setting):
+ """
+ Adds a floating IP to a running instance
+ :param floating_ip_setting - the floating IP configuration
+ :return: the floating ip object
+ """
+ port_dict = dict()
+ for key, port in self.__ports:
+ port_dict[key] = port
- # Setup Floating IP only if there is a router with an external
- # 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)
- floating_ip = neutron_utils.create_floating_ip(
- self.__neutron, ext_gateway)
- self.__floating_ip_dict[floating_ip_setting.name] = floating_ip
+ # Apply floating IP
+ port = port_dict.get(floating_ip_setting.port_name)
- 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)
- else:
- raise VmInstanceCreationError(
- 'Unable to add floating IP to port, cannot locate router '
- 'with an external gateway ')
+ if not port:
+ raise VmInstanceCreationError(
+ 'Cannot find port object with name - ' +
+ floating_ip_setting.port_name)
+
+ # Setup Floating IP only if there is a router with an external
+ # 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)
+ floating_ip = neutron_utils.create_floating_ip(
+ self.__neutron, ext_gateway)
+ 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(
+ 'Unable to add floating IP to port, cannot locate router '
+ 'with an external gateway ')
def __ext_gateway_by_router(self, router_name):
"""
"""
router = neutron_utils.get_router(
self.__neutron, router_name=router_name)
- if router and router.external_gateway_info:
+ if router and router.external_network_id:
network = neutron_utils.get_network_by_id(
- self.__neutron,
- router.external_gateway_info['network_id'])
+ self.__neutron, router.external_network_id)
if network:
return network.name
return None
# Cleanup floating IPs
for name, floating_ip in self.__floating_ip_dict.items():
- try:
- logger.info('Deleting Floating IP - ' + floating_ip.ip)
- neutron_utils.delete_floating_ip(self.__neutron, floating_ip)
- except Exception as e:
- logger.error('Error deleting Floating IP - ' + str(e))
+ logger.info('Deleting Floating IP - ' + floating_ip.ip)
+ neutron_utils.delete_floating_ip(self.__neutron, floating_ip)
+
self.__floating_ip_dict = dict()
# Cleanup ports
for name, port in self.__ports:
- logger.info('Deleting Port with ID - %S ' + port.id)
- try:
- neutron_utils.delete_port(self.__neutron, port)
- except PortNotFoundClient as e:
- logger.warning('Unexpected error deleting port - %s', e)
- pass
+ logger.info('Deleting Port with ID - %s ', port.id)
+ neutron_utils.delete_port(self.__neutron, port)
+
self.__ports = list()
- # Cleanup VM
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'])
+ if volume:
+ vm = nova_utils.detach_volume(
+ self._nova, self.__neutron, self.__vm, volume, 30)
+ if vm:
+ self.__vm = vm
+ else:
+ logger.warn(
+ 'Timeout waiting to detach volume %s', volume.name)
+ else:
+ logger.warn('Unable to detach volume with ID - [%s]',
+ volume_rec['id'])
+
+ # Cleanup VM
+ logger.info(
+ 'Deleting VM instance - ' + self.instance_settings.name)
+
try:
- logger.info(
- 'Deleting VM instance - ' + self.instance_settings.name)
nova_utils.delete_vm_instance(self._nova, self.__vm)
- except Exception as e:
- logger.error('Error deleting VM - %s', e)
+ except NotFound as e:
+ logger.warn('Instance already deleted - %s', e)
# Block until instance cannot be found or returns the status of
# DELETED
logger.info('Checking deletion status')
- try:
- if self.vm_deleted(block=True):
- logger.info(
- 'VM has been properly deleted VM with name - %s',
- self.instance_settings.name)
- self.__vm = None
- else:
- logger.error(
- 'VM not deleted within the timeout period of %s '
- 'seconds', self.instance_settings.vm_delete_timeout)
- except Exception as e:
+ if self.vm_deleted(block=True):
+ logger.info(
+ 'VM has been properly deleted VM with name - %s',
+ self.instance_settings.name)
+ self.__vm = None
+ else:
logger.error(
- 'Unexpected error while checking VM instance status - %s',
- e)
+ 'VM not deleted within the timeout period of %s '
+ 'seconds', self.instance_settings.vm_delete_timeout)
def __query_ports(self, port_settings):
"""
port = neutron_utils.get_port(
self.__neutron, port_settings=port_setting)
if not port:
- port = neutron_utils.create_port(self.__neutron, self._os_creds, port_setting)
+ port = neutron_utils.create_port(
+ self.__neutron, self._os_creds, port_setting)
if port:
ports.append((port_setting.name, port))
'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 '
Returns the latest version of this server object from OpenStack
:return: Server object
"""
- return nova_utils.get_server_object_by_id(self._nova, self.__vm.id)
+ return nova_utils.get_server_object_by_id(
+ self._nova, self.__neutron, self.__vm.id)
def get_console_output(self):
"""
"""
return nova_utils.get_server_info(self._nova, self.__vm)
- def config_nics(self):
- """
- Responsible for configuring NICs on RPM systems where the instance has
- more than one configured port
- :return: the value returned by ansible_utils.apply_ansible_playbook()
- """
- if len(self.__ports) > 1 and len(self.__floating_ip_dict) > 0:
- if self.vm_active(block=True) and self.vm_ssh_active(block=True):
- for key, port in self.__ports:
- port_index = self.__ports.index((key, port))
- if port_index > 0:
- nic_name = 'eth' + repr(port_index)
- retval = self.__config_nic(
- nic_name, port,
- self.__get_first_provisioning_floating_ip().ip)
- logger.info('Configured NIC - %s on VM - %s',
- nic_name, self.instance_settings.name)
- return retval
-
def __get_first_provisioning_floating_ip(self):
"""
Returns the first floating IP tagged with the Floating IP name if
for key, fip in self.__floating_ip_dict.items():
return fip
- def __config_nic(self, nic_name, port, ip):
- """
- Although ports/NICs can contain multiple IPs, this code currently only
- supports the first.
-
- :param nic_name: Name of the interface
- :param port: The port information containing the expected IP values.
- :param ip: The IP on which to apply the playbook.
- :return: the return value from ansible
- """
- port_ip = port.ips[0]['ip_address']
- variables = {
- 'floating_ip': ip,
- 'nic_name': nic_name,
- 'nic_ip': port_ip
- }
-
- if self.image_settings.nic_config_pb_loc and self.keypair_settings:
- return self.apply_ansible_playbook(
- self.image_settings.nic_config_pb_loc, variables)
- else:
- logger.warning(
- 'VM %s cannot self configure NICs eth1++. No playbook or '
- 'keypairs found.', self.instance_settings.name)
+ # When cannot be found above
+ if len(self.__floating_ip_dict) > 0:
+ for key, fip in self.__floating_ip_dict.items():
+ return fip
def apply_ansible_playbook(self, pb_file_loc, variables=None,
fip_name=None):
:param poll_interval: The polling interval in seconds
:return: T/F
"""
- return self.__vm_status_check(STATUS_ACTIVE, block,
- self.instance_settings.vm_boot_timeout,
- poll_interval)
+ if self.__vm_status_check(
+ 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)
+ return True
+ return False
def __vm_status_check(self, expected_status_code, block, timeout,
poll_interval):
return True
return False
+ def cloud_init_complete(self, block=False, poll_interval=POLL_INTERVAL):
+ """
+ Returns true when the VM's cloud-init routine has completed.
+ Note: this is currently done via SSH, therefore, if this instance does
+ not have a Floating IP or a running SSH server, this routine
+ will always return False or raise an Exception
+ :param block: When true, thread will block until active or timeout
+ value in seconds has been exceeded (False)
+ :param poll_interval: The polling interval
+ :return: T/F
+ """
+ # sleep and wait for VM status change
+ logger.info('Checking if cloud-init has completed')
+
+ timeout = self.instance_settings.cloud_init_timeout
+
+ if self.vm_active(block=True) and self.vm_ssh_active(block=True):
+ if block:
+ start = time.time()
+ else:
+ start = time.time() - timeout
+
+ while timeout > time.time() - start:
+ status = self.__cloud_init_complete()
+ if status:
+ logger.info('cloud-init complete for VM instance')
+ return True
+
+ logger.debug('Retry cloud-init query in ' + str(
+ poll_interval) + ' seconds')
+ time.sleep(poll_interval)
+ logger.debug('cloud-init complete timeout in ' + str(
+ timeout - (time.time() - start)))
+
+ logger.error('Timeout waiting for cloud-init to complete')
+ return False
+
+ def __cloud_init_complete(self):
+ """
+ Returns True when can create a SSH session else False
+ :return: T/F
+ """
+ if len(self.__floating_ip_dict) > 0:
+ ssh = self.ssh_client()
+ if ssh:
+ stdin1, stdout1, sterr1 = ssh.exec_command(
+ 'ls -l /var/lib/cloud/instance/boot-finished')
+ return stdout1.channel.recv_exit_status() == 0
+ return False
+
def get_floating_ip(self, fip_name=None):
"""
Returns the floating IP object byt name if found, else the first known,
:param fip_name: the name of the floating IP to return
:return: the SSH client or None
"""
- fip = None
if fip_name and self.__floating_ip_dict.get(fip_name):
return self.__floating_ip_dict.get(fip_name)
- if not fip:
+ else:
return self.__get_first_provisioning_floating_ip()
def ssh_client(self, fip_name=None):
self.keypair_settings.private_filepath,
proxy_settings=self._os_creds.proxy_settings)
else:
- logger.warning(
+ FloatingIPAllocationError(
'Cannot return an SSH client. No Floating IP configured')
def add_security_group(self, security_group):
logger.warning('Security group not removed - ' + str(e))
return False
-
-class VmInstanceSettings:
- """
- Class responsible for holding configuration setting for a VM Instance
- """
-
- def __init__(self, **kwargs):
+ def reboot(self, reboot_type=RebootType.soft):
"""
- Constructor
- :param name: the name of the VM
- :param flavor: the VM's flavor name
- :param port_settings: the port configuration settings (required)
- :param security_group_names: a set of names of the security groups to
- add to the VM
- :param floating_ip_settings: the floating IP configuration settings
- :param sudo_user: the sudo user of the VM that will override the
- instance_settings.image_user when trying to
- connect to the VM
- :param vm_boot_timeout: the amount of time a thread will sleep waiting
- for an instance to boot
- :param vm_delete_timeout: the amount of time a thread will sleep
- waiting for an instance to be deleted
- :param ssh_connect_timeout: the amount of time a thread will sleep
- waiting obtaining an SSH connection to a VM
- :param availability_zone: the name of the compute server on which to
- deploy the VM (optional)
- :param userdata: the string contents of any optional cloud-init script
- to execute after the VM has been activated.
- This value may also contain a dict who's key value
- must contain the key 'cloud-init_file' which denotes
- the location of some file containing the cloud-init
- script
- """
- self.name = kwargs.get('name')
- self.flavor = kwargs.get('flavor')
- self.sudo_user = kwargs.get('sudo_user')
- self.userdata = kwargs.get('userdata')
-
- self.port_settings = list()
- port_settings = kwargs.get('ports')
- if not port_settings:
- port_settings = kwargs.get('port_settings')
- if port_settings:
- for port_setting in port_settings:
- if isinstance(port_setting, dict):
- self.port_settings.append(PortSettings(**port_setting))
- elif isinstance(port_setting, PortSettings):
- self.port_settings.append(port_setting)
-
- if kwargs.get('security_group_names'):
- if isinstance(kwargs['security_group_names'], list):
- self.security_group_names = kwargs['security_group_names']
- elif isinstance(kwargs['security_group_names'], set):
- self.security_group_names = kwargs['security_group_names']
- elif isinstance(kwargs['security_group_names'], str):
- self.security_group_names = [kwargs['security_group_names']]
- else:
- raise VmInstanceSettingsError(
- 'Invalid data type for security_group_names attribute')
- else:
- self.security_group_names = set()
-
- self.floating_ip_settings = list()
- floating_ip_settings = kwargs.get('floating_ips')
- if not floating_ip_settings:
- floating_ip_settings = kwargs.get('floating_ip_settings')
- if floating_ip_settings:
- for floating_ip_config in floating_ip_settings:
- if isinstance(floating_ip_config, FloatingIpSettings):
- self.floating_ip_settings.append(floating_ip_config)
- else:
- self.floating_ip_settings.append(FloatingIpSettings(
- **floating_ip_config['floating_ip']))
-
- if kwargs.get('vm_boot_timeout'):
- self.vm_boot_timeout = kwargs['vm_boot_timeout']
- else:
- self.vm_boot_timeout = 900
-
- if kwargs.get('vm_delete_timeout'):
- self.vm_delete_timeout = kwargs['vm_delete_timeout']
- else:
- self.vm_delete_timeout = 300
-
- if kwargs.get('ssh_connect_timeout'):
- self.ssh_connect_timeout = kwargs['ssh_connect_timeout']
- else:
- self.ssh_connect_timeout = 180
+ Issues a reboot call
+ :param reboot_type: instance of
+ snaps.openstack.utils.nova_utils.RebootType
+ enumeration
+ :return:
+ """
+ nova_utils.reboot_server(
+ self._nova, self.__vm, reboot_type=reboot_type)
- if kwargs.get('availability_zone'):
- self.availability_zone = kwargs['availability_zone']
- else:
- self.availability_zone = None
- if not self.name or not self.flavor:
- raise VmInstanceSettingsError(
- 'Instance configuration requires the attributes: name, flavor')
+def generate_creator(os_creds, vm_inst, image_config, 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 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)
- if len(self.port_settings) == 0:
- raise VmInstanceSettingsError(
- 'Instance configuration requires port settings (aka. NICS)')
+ derived_inst_creator = OpenStackVmInstance(
+ os_creds, derived_inst_config, image_config, keypair_config)
+ derived_inst_creator.initialize()
+ return derived_inst_creator
-class FloatingIpSettings:
+class VmInstanceSettings(VmInstanceConfig):
"""
- Class responsible for holding configuration settings for a floating IP
+ Deprecated, use snaps.config.vm_inst.VmInstanceConfig instead
"""
-
def __init__(self, **kwargs):
- """
- Constructor
- :param name: the name of the floating IP
- :param port_name: the name of the router to the external network
- :param router_name: the name of the router to the external network
- :param subnet_name: the name of the subnet on which to attach the
- floating IP
- :param provisioning: when true, this floating IP can be used for
- provisioning
-
- TODO - provisioning flag is a hack as I have only observed a single
- Floating IPs that actually works on an instance. Multiple floating IPs
- placed on different subnets from the same port are especially
- troublesome as you cannot predict which one will actually connect.
- For now, it is recommended not to setup multiple floating IPs on an
- instance unless absolutely necessary.
- """
- self.name = kwargs.get('name')
- self.port_name = kwargs.get('port_name')
- self.port_id = kwargs.get('port_id')
- self.router_name = kwargs.get('router_name')
- self.subnet_name = kwargs.get('subnet_name')
- if kwargs.get('provisioning') is not None:
- self.provisioning = kwargs['provisioning']
- else:
- self.provisioning = True
-
- # if not self.name or not self.port_name or not self.router_name:
- if not self.name or not self.router_name:
- raise FloatingIpSettingsError(
- 'The attributes name, port_name and router_name are required')
+ from warnings import warn
+ warn('Use snaps.config.vm_inst.VmInstanceConfig instead',
+ DeprecationWarning)
+ super(self.__class__, self).__init__(**kwargs)
- if not self.port_name and not self.port_id:
- raise FloatingIpSettingsError(
- 'The attributes port_name or port_id are required')
-
-class VmInstanceSettingsError(Exception):
+class FloatingIpSettings(FloatingIpConfig):
"""
- Exception to be thrown when an VM instance settings are incorrect
+ Deprecated, use snaps.config.vm_inst.FloatingIpConfig instead
"""
+ def __init__(self, **kwargs):
+ from warnings import warn
+ warn('Use snaps.config.vm_inst.FloatingIpConfig instead',
+ DeprecationWarning)
+ super(self.__class__, self).__init__(**kwargs)
-class FloatingIpSettingsError(Exception):
+class VmInstanceCreationError(Exception):
"""
- Exception to be thrown when an VM instance settings are incorrect
+ Exception to be thrown when an VM instance cannot be created
"""
-class VmInstanceCreationError(Exception):
+class FloatingIPAllocationError(Exception):
"""
- Exception to be thrown when an VM instance cannot be created
+ Exception to be thrown when an VM instance cannot allocate a floating IP
"""
import logging
import os
-from neutronclient.common.utils import str2bool
from novaclient.exceptions import NotFound
from snaps import file_utils
+from snaps.config.keypair import KeypairConfig
from snaps.openstack.openstack_creator import OpenStackComputeObject
from snaps.openstack.utils import nova_utils
"""
Constructor - all parameters are required
:param os_creds: The credentials to connect with OpenStack
- :param keypair_settings: The settings used to create a keypair
+ :param keypair_settings: a KeypairConfig object
"""
super(self.__class__, self).__init__(os_creds)
self.keypair_settings.public_filepath)
os.chmod(expanded_path, 0o755)
os.remove(expanded_path)
+ logger.info('Deleted public key file [%s]', expanded_path)
if (self.keypair_settings.private_filepath and
file_utils.file_exists(
self.keypair_settings.private_filepath)):
self.keypair_settings.private_filepath)
os.chmod(expanded_path, 0o755)
os.remove(expanded_path)
+ logger.info('Deleted private key file [%s]', expanded_path)
def get_keypair(self):
"""
return self.__keypair
-class KeypairSettings:
+class KeypairSettings(KeypairConfig):
"""
Class representing a keypair configuration
"""
def __init__(self, **kwargs):
- """
- Constructor - all parameters are optional
- :param name: The keypair name.
- :param public_filepath: The path to/from the filesystem where the
- public key file is or will be stored
- :param private_filepath: The path where the generated private key file
- will be stored
- :param key_size: The number of bytes for the key size when it needs to
- be generated (Must be >=512 default 1024)
- :param delete_on_clean: when True, the key files will be deleted when
- OpenStackKeypair#clean() is called
- :return:
- """
-
- self.name = kwargs.get('name')
- self.public_filepath = kwargs.get('public_filepath')
- self.private_filepath = kwargs.get('private_filepath')
- self.key_size = int(kwargs.get('key_size', 1024))
-
- if kwargs.get('delete_on_clean') is not None:
- if isinstance(kwargs.get('delete_on_clean'), bool):
- self.delete_on_clean = kwargs.get('delete_on_clean')
- else:
- self.delete_on_clean = str2bool(kwargs.get('delete_on_clean'))
- else:
- self.delete_on_clean = None
-
- if not self.name:
- raise KeypairSettingsError('Name is a required attribute')
-
- if self.key_size < 512:
- raise KeypairSettingsError('key_size must be >=512')
-
-
-class KeypairSettingsError(Exception):
- """
- Exception to be thrown when keypair settings are incorrect
- """
+ from warnings import warn
+ warn('Use snaps.config.keypair.KeypairConfig instead',
+ DeprecationWarning)
+ super(self.__class__, self).__init__(**kwargs)
# limitations under the License.
import logging
-from neutronclient.common.exceptions import NotFound
+import enum
+from neutronclient.common.exceptions import NetworkNotFoundClient
+from snaps.config.network import NetworkConfig, SubnetConfig, PortConfig
from snaps.openstack.openstack_creator import OpenStackNetworkObject
-from snaps.openstack.utils import keystone_utils, neutron_utils
+from snaps.openstack.utils import neutron_utils
__author__ = 'spisarski'
# Attributes instantiated on create()
self.__network = None
- self.__subnets = list()
def initialize(self):
"""
self._neutron, network_settings=self.network_settings,
project_id=self.network_settings.get_project_id(self._os_creds))
- if self.__network:
- for subnet_setting in self.network_settings.subnet_settings:
- sub_inst = neutron_utils.get_subnet(
- self._neutron, subnet_settings=subnet_setting)
- if sub_inst:
- self.__subnets.append(sub_inst)
- logger.debug(
- "Subnet '%s' created successfully" % sub_inst.id)
-
return self.__network
def create(self):
self.__network = neutron_utils.create_network(
self._neutron, self._os_creds, self.network_settings)
logger.debug(
- "Network '%s' created successfully" % self.__network.id)
-
- for subnet_setting in self.network_settings.subnet_settings:
- sub_inst = neutron_utils.get_subnet(
- self._neutron, subnet_settings=subnet_setting)
- if not sub_inst:
- sub_inst = neutron_utils.create_subnet(
- self._neutron, subnet_setting, self._os_creds,
- self.__network)
- if sub_inst:
- self.__subnets.append(sub_inst)
- logger.debug(
- "Subnet '%s' created successfully" % sub_inst.id)
+ 'Network [%s] created successfully' % self.__network.id)
return self.__network
"""
Removes and deletes all items created in reverse order.
"""
- for subnet in self.__subnets:
- try:
- logger.info(
- 'Deleting subnet with name ' + subnet.name)
- neutron_utils.delete_subnet(self._neutron, subnet)
- except NotFound as e:
- logger.warning(
- 'Error deleting subnet with message - ' + str(e))
- pass
- self.__subnets = list()
-
if self.__network:
try:
neutron_utils.delete_network(self._neutron, self.__network)
- except NotFound:
+ except NetworkNotFoundClient:
pass
-
self.__network = None
def get_network(self):
"""
return self.__network
- def get_subnets(self):
- """
- Returns the OpenStack subnet objects
- :return:
- """
- return self.__subnets
-
-class NetworkSettings:
+class NetworkSettings(NetworkConfig):
"""
- Class representing a network configuration
+ Class to hold the configuration settings required for creating OpenStack
+ Network objects
+ deprecated
"""
def __init__(self, **kwargs):
- """
- Constructor - all parameters are optional
- :param name: The network name.
- :param admin_state_up: The administrative status of the network.
- True = up / False = down (default True)
- :param shared: Boolean value indicating whether this network is shared
- across all projects/tenants. By default, only
- administrative users can change this value.
- :param project_name: Admin-only. The name of the project that will own
- the network. This project can be different from
- the project that makes the create network request.
- However, only administrative users can specify a
- project ID other than their own. You cannot change
- this value through authorization policies.
- :param external: when true, will setup an external network
- (default False).
- :param network_type: the type of network (i.e. vlan|flat).
- :param physical_network: the name of the physical network
- (this is required when network_type is 'flat')
- :param segmentation_id: the id of the segmentation
- (this is required when network_type is 'vlan')
- :param subnets or subnet_settings: List of SubnetSettings objects.
- :return:
- """
-
- self.project_id = None
-
- self.name = kwargs.get('name')
- if kwargs.get('admin_state_up') is not None:
- self.admin_state_up = bool(kwargs['admin_state_up'])
- else:
- self.admin_state_up = True
-
- if kwargs.get('shared') is not None:
- self.shared = bool(kwargs['shared'])
- else:
- self.shared = None
-
- self.project_name = kwargs.get('project_name')
-
- if kwargs.get('external') is not None:
- self.external = bool(kwargs.get('external'))
- else:
- self.external = False
+ from warnings import warn
+ warn('Use snaps.config.network.NetworkConfig instead',
+ DeprecationWarning)
+ super(self.__class__, self).__init__(**kwargs)
- self.network_type = kwargs.get('network_type')
- self.physical_network = kwargs.get('physical_network')
- self.segmentation_id = kwargs.get('segmentation_id')
- self.subnet_settings = list()
- subnet_settings = kwargs.get('subnets')
- if not subnet_settings:
- subnet_settings = kwargs.get('subnet_settings')
- if subnet_settings:
- for subnet_config in subnet_settings:
- if isinstance(subnet_config, SubnetSettings):
- self.subnet_settings.append(subnet_config)
- else:
- self.subnet_settings.append(
- SubnetSettings(**subnet_config['subnet']))
-
- if not self.name or len(self.name) < 1:
- raise NetworkSettingsError('Name required for networks')
-
- def get_project_id(self, os_creds):
- """
- Returns the project ID for a given project_name or None
- :param os_creds: the credentials required for keystone client retrieval
- :return: the ID or None
- """
- if self.project_id:
- 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
-
- return None
-
- def dict_for_neutron(self, os_creds):
- """
- Returns a dictionary object representing this object.
- This is meant to be converted into JSON designed for use by the Neutron
- API
- TODO - expand automated testing to exercise all parameters
-
- :param os_creds: the OpenStack credentials
- :return: the dictionary object
- """
- out = dict()
-
- if self.name:
- out['name'] = self.name
- if self.admin_state_up is not None:
- out['admin_state_up'] = self.admin_state_up
- if self.shared:
- out['shared'] = self.shared
- if self.project_name:
- project_id = self.get_project_id(os_creds)
- if project_id:
- out['tenant_id'] = project_id
- else:
- raise NetworkSettingsError(
- 'Could not find project ID for project named - ' +
- self.project_name)
- if self.network_type:
- out['provider:network_type'] = self.network_type
- if self.physical_network:
- out['provider:physical_network'] = self.physical_network
- if self.segmentation_id:
- out['provider:segmentation_id'] = self.segmentation_id
- if self.external:
- out['router:external'] = self.external
- return {'network': out}
-
-
-class NetworkSettingsError(Exception):
+class IPv6Mode(enum.Enum):
"""
- Exception to be thrown when networks settings attributes are incorrect
+ A rule's direction
+ deprecated - use snaps.config.network.IPv6Mode
"""
+ slaac = 'slaac'
+ stateful = 'dhcpv6-stateful'
+ stateless = 'dhcpv6-stateless'
-class SubnetSettings:
+class SubnetSettings(SubnetConfig):
"""
- Class representing a subnet configuration
+ Class to hold the configuration settings required for creating OpenStack
+ Subnet objects
+ deprecated
"""
def __init__(self, **kwargs):
- """
- Constructor - all parameters are optional except cidr (subnet mask)
- :param cidr: The CIDR. REQUIRED if config parameter is None
- :param ip_version: The IP version, which is 4 or 6.
- :param name: The subnet name.
- :param project_name: The name of the project who owns the network.
- Only administrative users can specify a project ID
- other than their own. You cannot change this value
- through authorization policies.
- :param start: The start address for the allocation pools.
- :param end: The end address for the allocation pools.
- :param gateway_ip: The gateway IP address.
- :param enable_dhcp: Set to true if DHCP is enabled and false if DHCP is
- disabled.
- :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].
- :param host_routes: A list of host route dictionaries for the subnet.
- For example:
- "host_routes":[
- {
- "destination":"0.0.0.0/0",
- "nexthop":"123.456.78.9"
- },
- {
- "destination":"192.168.0.0/24",
- "nexthop":"192.168.0.1"
- }
- ]
- :param destination: The destination for static route
- :param nexthop: The next hop for the destination.
- :param ipv6_ra_mode: A valid value is dhcpv6-stateful,
- dhcpv6-stateless, or slaac.
- :param ipv6_address_mode: A valid value is dhcpv6-stateful,
- dhcpv6-stateless, or slaac.
- :raise: SubnetSettingsError when config does not have or cidr values
- are None
- """
- self.cidr = kwargs.get('cidr')
- if kwargs.get('ip_version'):
- self.ip_version = kwargs['ip_version']
- else:
- self.ip_version = 4
-
- # Optional attributes that can be set after instantiation
- self.name = kwargs.get('name')
- self.project_name = kwargs.get('project_name')
- self.start = kwargs.get('start')
- self.end = kwargs.get('end')
- self.gateway_ip = kwargs.get('gateway_ip')
- self.enable_dhcp = kwargs.get('enable_dhcp')
-
- if kwargs.get('dns_nameservers'):
- self.dns_nameservers = kwargs.get('dns_nameservers')
- else:
- self.dns_nameservers = ['8.8.8.8']
-
- self.host_routes = kwargs.get('host_routes')
- self.destination = kwargs.get('destination')
- self.nexthop = kwargs.get('nexthop')
- self.ipv6_ra_mode = kwargs.get('ipv6_ra_mode')
- self.ipv6_address_mode = kwargs.get('ipv6_address_mode')
+ from warnings import warn
+ warn('Use snaps.config.network.SubnetConfig instead',
+ DeprecationWarning)
+ super(self.__class__, self).__init__(**kwargs)
- if not self.name or not self.cidr:
- raise SubnetSettingsError('Name and cidr required for subnets')
- def dict_for_neutron(self, os_creds, network=None):
- """
- Returns a dictionary object representing this object.
- This is meant to be converted into JSON designed for use by the Neutron
- API
- :param os_creds: the OpenStack credentials
- :param network: The network object on which the subnet will be created
- (optional)
- :return: the dictionary object
- """
- out = {
- 'cidr': self.cidr,
- 'ip_version': self.ip_version,
- }
-
- if network:
- out['network_id'] = network.id
- 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 SubnetSettingsError(
- 'Could not find project ID for project named - ' +
- self.project_name)
- 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.enable_dhcp is not None:
- out['enable_dhcp'] = self.enable_dhcp
- if self.dns_nameservers and len(self.dns_nameservers) > 0:
- out['dns_nameservers'] = self.dns_nameservers
- if self.host_routes and len(self.host_routes) > 0:
- out['host_routes'] = self.host_routes
- if self.destination:
- out['destination'] = self.destination
- if self.nexthop:
- out['nexthop'] = self.nexthop
- if self.ipv6_ra_mode:
- out['ipv6_ra_mode'] = self.ipv6_ra_mode
- if self.ipv6_address_mode:
- out['ipv6_address_mode'] = self.ipv6_address_mode
- return out
-
-
-class SubnetSettingsError(Exception):
- """
- Exception to be thrown when subnet settings attributes are incorrect
- """
-
-
-class PortSettings:
+class PortSettings(PortConfig):
"""
- Class representing a port configuration
+ Class to hold the configuration settings required for creating OpenStack
+ Subnet objects
+ deprecated
"""
def __init__(self, **kwargs):
- """
- Constructor
- :param name: A symbolic name for the port (optional).
- :param network_name: The name of the network on which to create the
- port (required).
- :param admin_state_up: A boolean value denoting the administrative
- status of the port. True = up / False = down
- :param project_name: The name of the project who owns the network.
- Only administrative users can specify a project ID
- other than their own. You cannot change this value
- through authorization policies.
- :param mac_address: The MAC address. If you specify an address that is
- not valid, a Bad Request (400) status code is
- returned. If you do not specify a MAC address,
- OpenStack Networking tries to allocate one. If a
- failure occurs, a Service Unavailable (503) status
- code is returned.
- :param ip_addrs: A list of dict objects where each contains two keys
- 'subnet_name' and 'ip' values which will get mapped to
- self.fixed_ips. These values will be directly
- translated into the fixed_ips dict
- :param fixed_ips: A dict where the key is the subnet IDs and value is
- the IP address to assign to the port
- :param security_groups: One or more security group IDs.
- :param allowed_address_pairs: A dictionary containing a set of zero or
- more allowed address pairs. An address
- pair contains an IP address and MAC
- address.
- :param opt_value: The extra DHCP option value.
- :param opt_name: The extra DHCP option name.
- :param device_owner: The ID of the entity that uses this port.
- For example, a DHCP agent.
- :param device_id: The ID of the device that uses this port.
- For example, a virtual server.
- :return:
- """
- if 'port' in kwargs:
- kwargs = kwargs['port']
-
- self.network = None
-
- self.name = kwargs.get('name')
- self.network_name = kwargs.get('network_name')
-
- if kwargs.get('admin_state_up') is not None:
- self.admin_state_up = bool(kwargs['admin_state_up'])
- else:
- self.admin_state_up = True
-
- self.project_name = kwargs.get('project_name')
- self.mac_address = kwargs.get('mac_address')
- self.ip_addrs = kwargs.get('ip_addrs')
- self.fixed_ips = kwargs.get('fixed_ips')
- self.security_groups = kwargs.get('security_groups')
- self.allowed_address_pairs = kwargs.get('allowed_address_pairs')
- self.opt_value = kwargs.get('opt_value')
- self.opt_name = kwargs.get('opt_name')
- self.device_owner = kwargs.get('device_owner')
- self.device_id = kwargs.get('device_id')
-
- if not self.network_name:
- raise PortSettingsError(
- 'The attribute network_name is required')
-
- def __set_fixed_ips(self, neutron):
- """
- Sets the self.fixed_ips value
- :param neutron: the Neutron client
- :return: None
- """
- if not self.fixed_ips and self.ip_addrs:
- self.fixed_ips = list()
-
- 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:
- self.fixed_ips.append({'ip_address': ip_addr_dict['ip'],
- 'subnet_id': subnet.id})
- else:
- raise PortSettingsError(
- 'Invalid port configuration, subnet does not exist '
- 'with name - ' + ip_addr_dict['subnet_name'])
-
- def dict_for_neutron(self, neutron, os_creds):
- """
- Returns a dictionary object representing this object.
- This is meant to be converted into JSON designed for use by the Neutron
- API
-
- TODO - expand automated testing to exercise all parameters
- :param neutron: the Neutron client
- :param os_creds: the OpenStack credentials
- :return: the dictionary object
- """
- self.__set_fixed_ips(neutron)
-
- out = dict()
-
- project_id = None
- 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
-
- if not self.network:
- self.network = neutron_utils.get_network(
- neutron, network_name=self.network_name, project_id=project_id)
- if not self.network:
- raise PortSettingsError(
- 'Cannot locate network with name - ' + self.network_name)
-
- out['network_id'] = self.network.id
-
- if self.admin_state_up is not None:
- out['admin_state_up'] = self.admin_state_up
- if self.name:
- out['name'] = self.name
- if self.project_name:
- if project_id:
- out['tenant_id'] = project_id
- else:
- raise PortSettingsError(
- 'Could not find project ID for project named - ' +
- self.project_name)
- if self.mac_address:
- out['mac_address'] = self.mac_address
- if self.fixed_ips and len(self.fixed_ips) > 0:
- out['fixed_ips'] = self.fixed_ips
- if self.security_groups:
- out['security_groups'] = self.security_groups
- if self.allowed_address_pairs and len(self.allowed_address_pairs) > 0:
- out['allowed_address_pairs'] = self.allowed_address_pairs
- if self.opt_value:
- out['opt_value'] = self.opt_value
- if self.opt_name:
- out['opt_name'] = self.opt_name
- if self.device_owner:
- out['device_owner'] = self.device_owner
- if self.device_id:
- out['device_id'] = self.device_id
- return {'port': out}
-
- def __eq__(self, other):
- return (self.name == other.name and
- self.network_name == other.network_name and
- self.admin_state_up == other.admin_state_up and
- self.project_name == other.project_name and
- self.mac_address == other.mac_address and
- self.ip_addrs == other.ip_addrs and
- self.fixed_ips == other.fixed_ips and
- self.security_groups == other.security_groups and
- self.allowed_address_pairs == other.allowed_address_pairs and
- self.opt_value == other.opt_value and
- self.opt_name == other.opt_name and
- self.device_owner == other.device_owner and
- self.device_id == other.device_id)
-
-
-class PortSettingsError(Exception):
- """
- Exception to be thrown when port settings attributes are incorrect
- """
+ from warnings import warn
+ warn('Use snaps.config.network.PortConfig instead',
+ DeprecationWarning)
+ super(self.__class__, self).__init__(**kwargs)
from keystoneclient.exceptions import NotFound, Conflict
+from snaps.config.project import ProjectConfig
from snaps.openstack.openstack_creator import OpenStackIdentityObject
from snaps.openstack.utils import keystone_utils, neutron_utils, nova_utils
neutron_utils.update_quotas(neutron, self.__project.id, network_quotas)
-class ProjectSettings:
+class ProjectSettings(ProjectConfig):
"""
Class to hold the configuration settings required for creating OpenStack
project objects
+ deprecated
"""
def __init__(self, **kwargs):
-
- """
- Constructor
- :param name: the project's name (required)
- :param domain or domain_name: the project's domain name
- (default = 'Default').
- Field is used for v3 clients
- :param description: the description (optional)
- :param users: list of users to associat project to (optional)
- :param enabled: denotes whether or not the user is enabled
- (default True)
- """
-
- self.name = kwargs.get('name')
- self.domain_name = kwargs.get(
- 'domain', kwargs.get('domain', 'Default'))
-
- self.description = kwargs.get('description')
- if kwargs.get('enabled') is not None:
- self.enabled = kwargs['enabled']
- else:
- self.enabled = True
-
- self.users = kwargs.get('users', list())
-
- if not self.name:
- raise ProjectSettingsError(
- "The attribute name is required for ProjectSettings")
-
-
-class ProjectSettingsError(Exception):
- """
- Exception to be thrown when project settings attributes are incorrect
- """
+ from warnings import warn
+ warn('Use snaps.config.project.ProjectConfig instead',
+ DeprecationWarning)
+ super(self.__class__, self).__init__(**kwargs)
\ No newline at end of file
import enum
from cinderclient.exceptions import NotFound
+from snaps.config.qos import QoSConfig
from snaps.openstack.openstack_creator import OpenStackVolumeObject
from snaps.openstack.utils import cinder_utils
class Consumer(enum.Enum):
"""
QoS Specification consumer types
+ deprecated - use snaps.config.qos.Consumer
"""
front_end = 'front-end'
back_end = 'back-end'
both = 'both'
-class QoSSettings:
- def __init__(self, **kwargs):
- """
- Constructor
- :param name: the qos's name (required)
- :param consumer: the qos's consumer type (required)
- :param specs: dict of key/values
- """
-
- self.name = kwargs.get('name')
-
- if kwargs.get('consumer'):
- self.consumer = map_consumer(kwargs['consumer'])
- else:
- self.consumer = None
-
- self.specs = kwargs.get('specs')
- if not self.specs:
- self.specs = dict()
-
- if not self.name or not self.consumer:
- raise QoSSettingsError(
- "The attributes name and consumer are required")
-
-
-def map_consumer(consumer):
- """
- Takes a the protocol value maps it to the Consumer enum. When None return
- None
- :param consumer: the value to map to the Enum
- :return: the Protocol enum object
- :raise: Exception if value is invalid
- """
- if not consumer:
- return None
- elif isinstance(consumer, Consumer):
- return consumer
- else:
- proto_str = str(consumer)
- if proto_str == 'front-end':
- return Consumer.front_end
- elif proto_str == 'back-end':
- return Consumer.back_end
- elif proto_str == 'both':
- return Consumer.both
- else:
- raise QoSSettingsError('Invalid Consumer - ' + proto_str)
-
-
-class QoSSettingsError(Exception):
+class QoSSettings(QoSConfig):
"""
- Exception to be thrown when an qos settings are incorrect
+ Class to hold the configuration settings required for creating OpenStack
+ QoS objects
+ deprecated
"""
- def __init__(self, message):
- Exception.__init__(self, message)
+ def __init__(self, **kwargs):
+ from warnings import warn
+ warn('Use snaps.config.qos.QoSConfig instead',
+ DeprecationWarning)
+ super(self.__class__, self).__init__(**kwargs)
class QoSCreationError(Exception):
import logging
from neutronclient.common.exceptions import NotFound
-from snaps.openstack.create_network import PortSettings
+
+from snaps.config.router import RouterConfig
from snaps.openstack.openstack_creator import OpenStackNetworkObject
-from snaps.openstack.utils import neutron_utils, keystone_utils
+from snaps.openstack.utils import neutron_utils
__author__ = 'spisarski'
Constructor - all parameters are required
:param os_creds: The credentials to connect with OpenStack
:param router_settings: The settings used to create a router object
- (must be an instance of the RouterSettings
+ (must be an instance of the RouterConfig
class)
"""
super(self.__class__, self).__init__(os_creds)
self.__router = neutron_utils.get_router(
self._neutron, router_settings=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)
- if internal_subnet:
- self.__internal_subnets.append(internal_subnet)
- else:
- raise RouterCreationError(
- '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)
- if port:
- self.__ports.append(port)
+ 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)
+ if internal_subnet:
+ self.__internal_subnets.append(internal_subnet)
+ else:
+ raise RouterCreationError(
+ '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)
+ if port:
+ self.__ports.append(port)
return self.__router
'Error creating port with name - '
+ port_setting.name)
+ self.__router = neutron_utils.get_router_by_id(
+ self._neutron, self.__router.id)
return self.__router
def clean(self):
"""
-class RouterSettings:
+class RouterSettings(RouterConfig):
"""
- Class representing a router configuration
+ Class to hold the configuration settings required for creating OpenStack
+ router objects
+ deprecated
"""
def __init__(self, **kwargs):
- """
- Constructor - all parameters are optional
- :param name: The router name.
- :param project_name: The name of the project who owns the network. Only
- administrative users can specify a project ID
- other than their own. You cannot change this value
- through authorization policies.
- :param external_gateway: Name of the external network to which to route
- :param admin_state_up: The administrative status of the router.
- True = up / False = down (default True)
- :param external_fixed_ips: Dictionary containing the IP address
- parameters.
- :param internal_subnets: List of subnet names to which to connect this
- router for Floating IP purposes
- :param port_settings: List of PortSettings objects
- :return:
- """
- self.name = kwargs.get('name')
- self.project_name = kwargs.get('project_name')
- self.external_gateway = kwargs.get('external_gateway')
-
- self.admin_state_up = kwargs.get('admin_state_up')
- self.enable_snat = kwargs.get('enable_snat')
- self.external_fixed_ips = kwargs.get('external_fixed_ips')
- if kwargs.get('internal_subnets'):
- self.internal_subnets = kwargs['internal_subnets']
- else:
- self.internal_subnets = list()
-
- self.port_settings = list()
- if kwargs.get('interfaces', kwargs.get('port_settings')):
- interfaces = kwargs.get('interfaces', kwargs.get('port_settings'))
- for interface in interfaces:
- if isinstance(interface, PortSettings):
- self.port_settings.append(interface)
- else:
- self.port_settings.append(
- PortSettings(**interface['port']))
-
- if not self.name:
- raise RouterSettingsError('Name is required')
-
- def dict_for_neutron(self, neutron, os_creds):
- """
- Returns a dictionary object representing this object.
- This is meant to be converted into JSON designed for use by the Neutron
- API
-
- 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
- :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 RouterSettingsError(
- '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 RouterSettingsError(
- 'Could not find the external network named - ' +
- self.external_gateway)
-
- return {'router': out}
-
-
-class RouterSettingsError(Exception):
- """
- Exception to be thrown when router settings attributes are incorrect
- """
+ from warnings import warn
+ warn('Use snaps.config.router.RouterConfig instead',
+ DeprecationWarning)
+ super(self.__class__, self).__init__(**kwargs)
import enum
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
rule_setting = self.__get_setting_from_rule(existing_rule)
self.__rules[rule_setting] = existing_rule
+ self.__security_group = neutron_utils.get_security_group_by_id(
+ self._neutron, self.__security_group.id)
+
return self.__security_group
def create(self):
def __generate_rule_setting(self, rule):
"""
- Creates a SecurityGroupRuleSettings object for a given rule
+ Creates a SecurityGroupRuleConfig object for a given rule
:param rule: the rule from which to create the
- SecurityGroupRuleSettings object
- :return: the newly instantiated SecurityGroupRuleSettings object
+ SecurityGroupRuleConfig object
+ :return: the newly instantiated SecurityGroupRuleConfig object
"""
sec_grp = neutron_utils.get_security_group_by_id(
self._neutron, rule.security_group_id)
- setting = SecurityGroupRuleSettings(
+ setting = SecurityGroupRuleConfig(
description=rule.description,
direction=rule.direction,
ethertype=rule.ethertype,
return None
-class SecurityGroupSettings:
+class SecurityGroupSettings(SecurityGroupConfig):
"""
- Class representing a keypair configuration
+ Class to hold the configuration settings required for creating OpenStack
+ SecurityGroup objects
+ deprecated - use snaps.config.security_group.SecurityGroupConfig instead
"""
def __init__(self, **kwargs):
- """
- Constructor - all parameters are optional
- :param name: The keypair name.
- :param description: The security group's description
- :param project_name: The name of the project under which the security
- group will be created
- :return:
- """
- self.name = kwargs.get('name')
- self.description = kwargs.get('description')
- self.project_name = kwargs.get('project_name')
- self.rule_settings = list()
-
- rule_settings = kwargs.get('rules')
- if not rule_settings:
- rule_settings = kwargs.get('rule_settings')
-
- if rule_settings:
- for rule_setting in rule_settings:
- if isinstance(rule_setting, SecurityGroupRuleSettings):
- self.rule_settings.append(rule_setting)
- else:
- rule_setting['sec_grp_name'] = self.name
- self.rule_settings.append(SecurityGroupRuleSettings(
- **rule_setting))
-
- if not self.name:
- raise SecurityGroupSettingsError('The attribute name is required')
-
- for rule_setting in self.rule_settings:
- if rule_setting.sec_grp_name is not self.name:
- raise SecurityGroupSettingsError(
- 'Rule settings must correspond with the name of this '
- 'security group')
-
- def dict_for_neutron(self, keystone):
- """
- Returns a dictionary object representing this object.
- This is meant to be converted into JSON designed for use by the Neutron
- API
-
- TODO - expand automated testing to exercise all parameters
- :param keystone: the Keystone client
- :return: the dictionary object
- """
- out = dict()
-
- if self.name:
- out['name'] = self.name
- if self.description:
- out['description'] = self.description
- 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:
- raise SecurityGroupSettingsError(
- 'Could not find project ID for project named - ' +
- self.project_name)
-
- return {'security_group': out}
+ from warnings import warn
+ warn('Use snaps.config.security_group.SecurityGroupConfig instead',
+ DeprecationWarning)
+ super(self.__class__, self).__init__(**kwargs)
class Direction(enum.Enum):
"""
A rule's direction
+ deprecated - use snaps.config.security_group.Direction
"""
ingress = 'ingress'
egress = 'egress'
class Protocol(enum.Enum):
"""
A rule's protocol
+ deprecated - use snaps.config.security_group.Protocol
"""
- icmp = 'icmp'
- tcp = 'tcp'
- udp = 'udp'
+ ah = 51
+ dccp = 33
+ egp = 8
+ esp = 50
+ gre = 47
+ icmp = 1
+ icmpv6 = 58
+ igmp = 2
+ ipv6_encap = 41
+ ipv6_frag = 44
+ ipv6_icmp = 58
+ ipv6_nonxt = 59
+ ipv6_opts = 60
+ ipv6_route = 43
+ ospf = 89
+ pgm = 113
+ rsvp = 46
+ sctp = 132
+ tcp = 6
+ udp = 17
+ udplite = 136
+ vrrp = 112
+ any = 'any'
null = 'null'
class Ethertype(enum.Enum):
"""
A rule's ethertype
+ deprecated - use snaps.config.security_group.Ethertype
"""
IPv4 = 4
IPv6 = 6
-class SecurityGroupSettingsError(Exception):
- """
- Exception to be thrown when security group settings attributes are
- invalid
- """
-
-
-class SecurityGroupRuleSettings:
+class SecurityGroupRuleSettings(SecurityGroupRuleConfig):
"""
- Class representing a keypair configuration
+ Class to hold the configuration settings required for creating OpenStack
+ SecurityGroupRule objects
+ deprecated - use snaps.config.security_group.SecurityGroupRuleConfig
+ instead
"""
def __init__(self, **kwargs):
- """
- Constructor - all parameters are optional
- :param sec_grp_name: The security group's name on which to add the
- rule. (required)
- :param description: The rule's description
- :param direction: An enumeration of type
- create_security_group.RULE_DIRECTION (required)
- :param remote_group_id: The group ID to associate with this rule
- (this should be changed to group name once
- snaps support Groups) (optional)
- :param protocol: An enumeration of type
- create_security_group.RULE_PROTOCOL or a string value
- that will be mapped accordingly (optional)
- :param ethertype: An enumeration of type
- create_security_group.RULE_ETHERTYPE (optional)
- :param port_range_min: The minimum port number in the range that is
- matched by the security group rule. When the
- protocol is TCP or UDP, this value must be <=
- port_range_max. When the protocol is ICMP, this
- value must be an ICMP type.
- :param port_range_max: The maximum port number in the range that is
- matched by the security group rule. When the
- protocol is TCP or UDP, this value must be <=
- port_range_max. When the protocol is ICMP, this
- value must be an ICMP type.
- :param sec_grp_rule: The OpenStack rule object to a security group rule
- object to associate
- (note: Cannot be set using the config object nor
- can I see any real uses for this parameter)
- :param remote_ip_prefix: The remote IP prefix to associate with this
- metering rule packet (optional)
-
- TODO - Need to support the tenant...
- """
-
- self.description = kwargs.get('description')
- self.sec_grp_name = kwargs.get('sec_grp_name')
- self.remote_group_id = kwargs.get('remote_group_id')
- self.direction = None
- if kwargs.get('direction'):
- self.direction = map_direction(kwargs['direction'])
-
- self.protocol = None
- if kwargs.get('protocol'):
- self.protocol = map_protocol(kwargs['protocol'])
- else:
- self.protocol = Protocol.null
-
- self.ethertype = None
- if kwargs.get('ethertype'):
- self.ethertype = map_ethertype(kwargs['ethertype'])
-
- self.port_range_min = kwargs.get('port_range_min')
- self.port_range_max = kwargs.get('port_range_max')
- self.remote_ip_prefix = kwargs.get('remote_ip_prefix')
-
- if not self.direction or not self.sec_grp_name:
- raise SecurityGroupRuleSettingsError(
- 'direction and sec_grp_name are required')
-
- def dict_for_neutron(self, neutron):
- """
- 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
- :return: the dictionary object
- """
- out = dict()
-
- if self.description:
- out['description'] = self.description
- if self.direction:
- out['direction'] = self.direction.name
- if self.port_range_min:
- out['port_range_min'] = self.port_range_min
- if self.port_range_max:
- out['port_range_max'] = self.port_range_max
- if self.ethertype:
- out['ethertype'] = self.ethertype.name
- if self.protocol and self.protocol.name != 'null':
- out['protocol'] = self.protocol.name
- if self.sec_grp_name:
- sec_grp = neutron_utils.get_security_group(
- neutron, sec_grp_name=self.sec_grp_name)
- if sec_grp:
- out['security_group_id'] = sec_grp.id
- else:
- raise SecurityGroupRuleSettingsError(
- 'Cannot locate security group with name - ' +
- self.sec_grp_name)
- if self.remote_group_id:
- out['remote_group_id'] = self.remote_group_id
- if self.remote_ip_prefix:
- out['remote_ip_prefix'] = self.remote_ip_prefix
-
- return {'security_group_rule': out}
-
- def rule_eq(self, rule):
- """
- Returns True if this setting created the rule
- :param rule: the rule to evaluate
- :return: T/F
- """
- if self.description is not None:
- if (rule.description is not None and
- rule.description != ''):
- return False
- elif self.description != rule.description:
- if rule.description != '':
- return False
-
- if self.direction.name != rule.direction:
- return False
-
- if self.ethertype and rule.ethertype:
- if self.ethertype.name != rule.ethertype:
- return False
-
- if self.port_range_min and rule.port_range_min:
- if self.port_range_min != rule.port_range_min:
- return False
-
- if self.port_range_max and rule.port_range_max:
- if self.port_range_max != rule.port_range_max:
- return False
-
- if self.protocol and rule.protocol:
- if self.protocol.name != rule.protocol:
- return False
-
- if self.remote_group_id and rule.remote_group_id:
- if self.remote_group_id != rule.remote_group_id:
- return False
-
- if self.remote_ip_prefix and rule.remote_ip_prefix:
- if self.remote_ip_prefix != rule.remote_ip_prefix:
- return False
-
- return True
-
- def __eq__(self, other):
- return (
- self.description == other.description and
- self.direction == other.direction and
- self.port_range_min == other.port_range_min and
- self.port_range_max == other.port_range_max and
- self.ethertype == other.ethertype and
- self.protocol == other.protocol and
- self.sec_grp_name == other.sec_grp_name and
- self.remote_group_id == other.remote_group_id and
- self.remote_ip_prefix == other.remote_ip_prefix)
-
- def __hash__(self):
- return hash((self.sec_grp_name, self.description, self.direction,
- self.remote_group_id,
- self.protocol, self.ethertype, self.port_range_min,
- self.port_range_max, self.remote_ip_prefix))
-
-
-def map_direction(direction):
- """
- Takes a the direction value maps it to the Direction enum. When None return
- None
- :param direction: the direction value
- :return: the Direction enum object
- :raise: Exception if value is invalid
- """
- if not direction:
- return None
- if isinstance(direction, Direction):
- return direction
- else:
- dir_str = str(direction)
- if dir_str == 'egress':
- return Direction.egress
- elif dir_str == 'ingress':
- return Direction.ingress
- else:
- raise SecurityGroupRuleSettingsError(
- 'Invalid Direction - ' + dir_str)
-
-
-def map_protocol(protocol):
- """
- Takes a the protocol value maps it to the Protocol enum. When None return
- None
- :param protocol: the protocol value
- :return: the Protocol enum object
- :raise: Exception if value is invalid
- """
- if not protocol:
- return None
- elif isinstance(protocol, Protocol):
- return protocol
- else:
- proto_str = str(protocol)
- if proto_str == 'icmp':
- return Protocol.icmp
- elif proto_str == 'tcp':
- return Protocol.tcp
- elif proto_str == 'udp':
- return Protocol.udp
- elif proto_str == 'null':
- return Protocol.null
- else:
- raise SecurityGroupRuleSettingsError(
- 'Invalid Protocol - ' + proto_str)
-
-
-def map_ethertype(ethertype):
- """
- Takes a the ethertype value maps it to the Ethertype enum. When None return
- None
- :param ethertype: the ethertype value
- :return: the Ethertype enum object
- :raise: Exception if value is invalid
- """
- if not ethertype:
- return None
- elif isinstance(ethertype, Ethertype):
- return ethertype
- else:
- eth_str = str(ethertype)
- if eth_str == 'IPv6':
- return Ethertype.IPv6
- elif eth_str == 'IPv4':
- return Ethertype.IPv4
- else:
- raise SecurityGroupRuleSettingsError(
- 'Invalid Ethertype - ' + eth_str)
-
-
-class SecurityGroupRuleSettingsError(Exception):
- """
- Exception to be thrown when security group rule settings attributes are
- invalid
- """
+ from warnings import warn
+ warn('Use snaps.config.security_group.SecurityGroupRuleConfig instead',
+ DeprecationWarning)
+ super(self.__class__, self).__init__(**kwargs)
from heatclient.exc import HTTPNotFound
+import snaps
+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_router import OpenStackRouter
+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
+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
logger = logging.getLogger('create_stack')
-STACK_DELETE_TIMEOUT = 1200
-STACK_COMPLETE_TIMEOUT = 1200
-POLL_INTERVAL = 3
-STATUS_CREATE_FAILED = 'CREATE_FAILED'
-STATUS_CREATE_COMPLETE = 'CREATE_COMPLETE'
-STATUS_DELETE_COMPLETE = 'DELETE_COMPLETE'
-STATUS_DELETE_FAILED = 'DELETE_FAILED'
-
class OpenStackHeatStack(OpenStackCloudObject, object):
"""
Constructor
:param os_creds: The OpenStack connection credentials
:param stack_settings: The stack settings
- :param image_settings: A list of ImageSettings objects that were used
- for spawning this stack
- :param image_settings: A list of ImageSettings objects that were used
+ :param image_settings: A list of ImageConfig objects that were used
for spawning this stack
- :param keypair_settings: A list of KeypairSettings objects that were
+ :param keypair_settings: A list of KeypairConfig objects that were
used for spawning this stack
:return:
"""
self.initialize()
if self.__stack:
- logger.info('Found stack with name - ' + self.stack_settings.name)
+ logger.info('Found stack with name - %s', self.stack_settings.name)
return self.__stack
else:
self.__stack = heat_utils.create_stack(self.__heat_cli,
self.stack_settings)
logger.info(
- 'Created stack with name - ' + self.stack_settings.name)
+ 'Created stack with name - %s', self.stack_settings.name)
if self.__stack and self.stack_complete(block=True):
- logger.info(
- 'Stack is now active with name - ' +
- self.stack_settings.name)
+ logger.info('Stack is now active with name - %s',
+ self.stack_settings.name)
return self.__stack
else:
status = heat_utils.get_stack_status_reason(self.__heat_cli,
self.__stack.id)
- logger.error(
- 'ERROR: STACK CREATION FAILED: ' + status)
- raise StackCreationError(
- 'Failure while creating stack')
+ logger.error('ERROR: STACK CREATION FAILED: %s', status)
+ raise StackCreationError('Failure while creating stack')
def clean(self):
"""
"""
if self.__stack:
try:
- logger.info('Deleting stack - %s' + self.__stack.name)
+ logger.info('Deleting stack - %s', self.__stack.name)
heat_utils.delete_stack(self.__heat_cli, self.__stack)
try:
return heat_utils.get_stack_status(self.__heat_cli, self.__stack.id)
def stack_complete(self, block=False, timeout=None,
- poll_interval=POLL_INTERVAL):
+ poll_interval=snaps.config.stack.POLL_INTERVAL):
"""
Returns true when the stack status returns the value of
expected_status_code
"""
if not timeout:
timeout = self.stack_settings.stack_create_timeout
- return self._stack_status_check(STATUS_CREATE_COMPLETE, block, timeout,
- poll_interval, STATUS_CREATE_FAILED)
+ return self._stack_status_check(
+ snaps.config.stack.STATUS_CREATE_COMPLETE, block, timeout,
+ poll_interval, snaps.config.stack.STATUS_CREATE_FAILED)
- def stack_deleted(self, block=False, timeout=STACK_DELETE_TIMEOUT,
- poll_interval=POLL_INTERVAL):
+ def stack_deleted(self, block=False,
+ timeout=snaps.config.stack.STACK_DELETE_TIMEOUT,
+ poll_interval=snaps.config.stack.POLL_INTERVAL):
"""
Returns true when the stack status returns the value of
expected_status_code
:param poll_interval: The polling interval in seconds
:return: T/F
"""
- return self._stack_status_check(STATUS_DELETE_COMPLETE, block, timeout,
- poll_interval, STATUS_DELETE_FAILED)
+ return self._stack_status_check(
+ snaps.config.stack.STATUS_DELETE_COMPLETE, block, timeout,
+ poll_interval, snaps.config.stack.STATUS_DELETE_FAILED)
def get_network_creators(self):
"""
self.__heat_cli, neutron, self.__stack)
for stack_network in stack_networks:
- net_settings = settings_utils.create_network_settings(
+ net_settings = settings_utils.create_network_config(
neutron, stack_network)
net_creator = OpenStackNetwork(self._os_creds, net_settings)
out.append(net_creator)
return out
+ def get_security_group_creators(self):
+ """
+ Returns a list of security group creator objects as configured by the
+ heat template
+ :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)
+
+ for stack_security_group in stack_security_groups:
+ settings = settings_utils.create_security_group_config(
+ neutron, stack_security_group)
+ creator = OpenStackSecurityGroup(self._os_creds, settings)
+ out.append(creator)
+ creator.initialize()
+
+ return out
+
+ def get_router_creators(self):
+ """
+ Returns a list of router creator objects as configured by the heat
+ template
+ :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)
+
+ for routers in stack_routers:
+ settings = settings_utils.create_router_config(
+ neutron, routers)
+ creator = OpenStackRouter(self._os_creds, settings)
+ out.append(creator)
+ creator.initialize()
+
+ return out
+
def get_vm_inst_creators(self, heat_keypair_option=None):
"""
Returns a list of VM Instance creator objects as configured by the heat
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, self.__stack)
+ self.__heat_cli, nova, neutron, self.__stack)
- neutron = neutron_utils.neutron_client(self._os_creds)
glance = glance_utils.glance_client(self._os_creds)
for stack_server in stack_servers:
- vm_inst_settings = settings_utils.create_vm_inst_settings(
+ vm_inst_settings = settings_utils.create_vm_inst_config(
nova, neutron, stack_server)
- image_settings = settings_utils.determine_image_settings(
+ image_settings = settings_utils.determine_image_config(
glance, stack_server, self.image_settings)
- keypair_settings = settings_utils.determine_keypair_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)
return out
+ def get_volume_creators(self):
+ """
+ Returns a list of Volume creator objects as configured by the heat
+ template
+ :return: list() of OpenStackVolume objects
+ """
+
+ out = list()
+ cinder = cinder_utils.cinder_client(self._os_creds)
+
+ volumes = heat_utils.get_stack_volumes(
+ self.__heat_cli, cinder, self.__stack)
+
+ for volume in volumes:
+ settings = settings_utils.create_volume_config(volume)
+ creator = OpenStackVolume(self._os_creds, settings)
+ out.append(creator)
+
+ try:
+ creator.initialize()
+ except Exception as e:
+ logger.error(
+ 'Unexpected error initializing volume creator - %s', e)
+
+ return out
+
+ def get_volume_type_creators(self):
+ """
+ Returns a list of VolumeType creator objects as configured by the heat
+ template
+ :return: list() of OpenStackVolumeType objects
+ """
+
+ out = list()
+ cinder = cinder_utils.cinder_client(self._os_creds)
+
+ vol_types = heat_utils.get_stack_volume_types(
+ self.__heat_cli, cinder, self.__stack)
+
+ for volume in vol_types:
+ settings = settings_utils.create_volume_type_config(volume)
+ creator = OpenStackVolumeType(self._os_creds, settings)
+ out.append(creator)
+
+ try:
+ creator.initialize()
+ except Exception as e:
+ logger.error(
+ 'Unexpected error initializing volume type creator - %s',
+ e)
+
+ return out
+
+ def get_keypair_creators(self, outputs_pk_key=None):
+ """
+ Returns a list of keypair creator objects as configured by the heat
+ template
+ :return: list() of OpenStackKeypair objects
+ """
+
+ out = list()
+ nova = nova_utils.nova_client(self._os_creds)
+
+ keypairs = heat_utils.get_stack_keypairs(
+ self.__heat_cli, nova, self.__stack)
+
+ for keypair in keypairs:
+ settings = settings_utils.create_keypair_config(
+ self.__heat_cli, self.__stack, keypair, outputs_pk_key)
+ creator = OpenStackKeypair(self._os_creds, settings)
+ out.append(creator)
+
+ try:
+ creator.initialize()
+ except Exception as e:
+ logger.error(
+ 'Unexpected error initializing volume type creator - %s',
+ e)
+
+ return out
+
+ def get_flavor_creators(self):
+ """
+ Returns a list of Flavor creator objects as configured by the heat
+ template
+ :return: list() of OpenStackFlavor objects
+ """
+
+ out = list()
+ nova = nova_utils.nova_client(self._os_creds)
+
+ flavors = heat_utils.get_stack_flavors(
+ self.__heat_cli, nova, self.__stack)
+
+ for flavor in flavors:
+ settings = settings_utils.create_flavor_config(flavor)
+ creator = OpenStackFlavor(self._os_creds, settings)
+ out.append(creator)
+
+ try:
+ creator.initialize()
+ except Exception as e:
+ logger.error(
+ 'Unexpected error initializing volume creator - %s', e)
+
+ return out
+
def _stack_status_check(self, expected_status_code, block, timeout,
poll_interval, fail_status):
"""
'Timeout checking for stack status for ' + expected_status_code)
return False
- def _status(self, expected_status_code, fail_status=STATUS_CREATE_FAILED):
+ def _status(self, expected_status_code,
+ fail_status=snaps.config.stack.STATUS_CREATE_FAILED):
"""
Returns True when active else False
:param expected_status_code: stack status evaluated with this string
return False
if fail_status and status == fail_status:
+ resources = heat_utils.get_resources(
+ self.__heat_cli, self.__stack.id)
+ logger.error('Stack %s failed', self.__stack.name)
+ for resource in resources:
+ if (resource.status !=
+ snaps.config.stack.STATUS_CREATE_COMPLETE):
+ logger.error(
+ 'Resource: [%s] status: [%s] reason: [%s]',
+ resource.name, resource.status, resource.status_reason)
+ else:
+ logger.debug(
+ 'Resource: [%s] status: [%s] reason: [%s]',
+ resource.name, resource.status, resource.status_reason)
+
raise StackError('Stack had an error')
logger.debug('Stack status is - ' + status)
return status == expected_status_code
-class StackSettings:
- def __init__(self, **kwargs):
- """
- Constructor
- :param name: the stack's name (required)
- :param template: the heat template in dict() format (required if
- template_path attribute is None)
- :param template_path: the location of the heat template file (required
- if template attribute is None)
- :param env_values: k/v pairs of strings for substitution of template
- default values (optional)
- """
-
- self.name = kwargs.get('name')
- self.template = kwargs.get('template')
- self.template_path = kwargs.get('template_path')
- self.env_values = kwargs.get('env_values')
- if 'stack_create_timeout' in kwargs:
- self.stack_create_timeout = kwargs['stack_create_timeout']
- else:
- self.stack_create_timeout = STACK_COMPLETE_TIMEOUT
-
- if not self.name:
- raise StackSettingsError('name is required')
-
- if not self.template and not self.template_path:
- raise StackSettingsError('A Heat template is required')
-
- def __eq__(self, other):
- return (self.name == other.name and
- self.template == other.template and
- self.template_path == other.template_path and
- self.env_values == other.env_values and
- self.stack_create_timeout == other.stack_create_timeout)
-
-
-class StackSettingsError(Exception):
+class StackSettings(StackConfig):
"""
- Exception to be thrown when an stack settings are incorrect
+ Class to hold the configuration settings required for creating OpenStack
+ stack objects
+ deprecated
"""
+ def __init__(self, **kwargs):
+ from warnings import warn
+ warn('Use snaps.config.stack.StackConfig instead',
+ DeprecationWarning)
+ super(self.__class__, self).__init__(**kwargs)
+
class StackCreationError(Exception):
"""
from keystoneclient.exceptions import NotFound
+from snaps.config.user import UserConfig
from snaps.openstack.openstack_creator import OpenStackIdentityObject
from snaps.openstack.os_credentials import OSCreds
from snaps.openstack.utils import keystone_utils
auth_url=self._os_creds.auth_url,
project_name=project_name,
identity_api_version=self._os_creds.identity_api_version,
+ image_api_version=self._os_creds.image_api_version,
+ network_api_version=self._os_creds.network_api_version,
+ 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,
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,
cacert=self._os_creds.cacert)
-class UserSettings:
- def __init__(self, **kwargs):
-
- """
- Constructor
- :param name: the user's name (required)
- :param password: the user's password (required)
- :param project_name: the user's primary project name (optional)
- :param domain_name: the user's domain name (default='default'). For v3
- APIs
- :param email: the user's email address (optional)
- :param enabled: denotes whether or not the user is enabled
- (default True)
- :param roles: dict where key is the role's name and value is the name
- the project to associate with the role
- """
-
- self.name = kwargs.get('name')
- self.password = kwargs.get('password')
- self.project_name = kwargs.get('project_name')
- self.email = kwargs.get('email')
- self.domain_name = kwargs.get('domain_name', 'Default')
- self.enabled = kwargs.get('enabled', True)
- self.roles = kwargs.get('roles', dict())
-
- if not self.name or not self.password:
- raise UserSettingsException(
- 'The attributes name and password are required for '
- 'UserSettings')
-
- if not isinstance(self.enabled, bool):
- raise UserSettingsException('The attribute enabled must be of type'
- ' boolean')
-
-
-class UserSettingsException(Exception):
+class UserSettings(UserConfig):
"""
- Raised when there is a problem with the values set in the UserSettings
- class
+ Class to hold the configuration settings required for creating OpenStack
+ user objects
+ deprecated
"""
+
+ def __init__(self, **kwargs):
+ from warnings import warn
+ warn('Use snaps.config.user.UserConfig instead',
+ DeprecationWarning)
+ super(self.__class__, self).__init__(**kwargs)
--- /dev/null
+# 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.
+
+import logging
+import time
+
+from cinderclient.exceptions import NotFound
+
+from snaps.config.volume import VolumeConfig
+from snaps.openstack.openstack_creator import OpenStackVolumeObject
+from snaps.openstack.utils import cinder_utils
+
+__author__ = 'spisarski'
+
+logger = logging.getLogger('create_volume')
+
+VOLUME_ACTIVE_TIMEOUT = 300
+VOLUME_DELETE_TIMEOUT = 60
+POLL_INTERVAL = 3
+STATUS_ACTIVE = 'available'
+STATUS_IN_USE = 'in-use'
+STATUS_FAILED = 'error'
+STATUS_DELETED = 'deleted'
+
+
+class OpenStackVolume(OpenStackVolumeObject):
+ """
+ Class responsible for managing an volume in OpenStack
+ """
+
+ def __init__(self, os_creds, volume_settings):
+ """
+ Constructor
+ :param os_creds: The OpenStack connection credentials
+ :param volume_settings: The volume settings
+ :return:
+ """
+ super(self.__class__, self).__init__(os_creds)
+
+ self.volume_settings = volume_settings
+ self.__volume = None
+
+ def initialize(self):
+ """
+ Loads the existing Volume
+ :return: The Volume domain object or None
+ """
+ super(self.__class__, self).initialize()
+
+ self.__volume = cinder_utils.get_volume(
+ self._cinder, volume_settings=self.volume_settings)
+ return self.__volume
+
+ def create(self, block=False):
+ """
+ Creates the volume in OpenStack if it does not already exist and
+ returns the domain Volume object
+ :return: The Volume domain object or None
+ """
+ self.initialize()
+
+ if not self.__volume:
+ self.__volume = cinder_utils.create_volume(
+ self._cinder, self.volume_settings)
+
+ logger.info(
+ 'Created volume with name - %s', self.volume_settings.name)
+ if self.__volume:
+ if block:
+ if self.volume_active(block=True):
+ logger.info('Volume is now active with name - %s',
+ self.volume_settings.name)
+ return self.__volume
+ else:
+ raise VolumeCreationError(
+ 'Volume was not created or activated in the '
+ 'alloted amount of time')
+ else:
+ logger.info('Did not create volume due to cleanup mode')
+
+ return self.__volume
+
+ def clean(self):
+ """
+ Cleanse environment of all artifacts
+ :return: void
+ """
+ if self.__volume:
+ try:
+ if self.volume_active():
+ cinder_utils.delete_volume(self._cinder, self.__volume)
+ else:
+ logger.warn('Timeout waiting to delete volume %s',
+ self.__volume.name)
+ except NotFound:
+ pass
+
+ try:
+ if self.volume_deleted(block=True):
+ logger.info(
+ 'Volume has been properly deleted with name - %s',
+ self.volume_settings.name)
+ self.__vm = None
+ else:
+ logger.error(
+ 'Volume not deleted within the timeout period of %s '
+ 'seconds', VOLUME_DELETE_TIMEOUT)
+ except Exception as e:
+ logger.error(
+ 'Unexpected error while checking VM instance status - %s',
+ e)
+
+ self.__volume = None
+
+ def get_volume(self):
+ """
+ Returns the domain Volume object as it was populated when create() was
+ called
+ :return: the object
+ """
+ return self.__volume
+
+ def volume_active(self, block=False, timeout=VOLUME_ACTIVE_TIMEOUT,
+ poll_interval=POLL_INTERVAL):
+ """
+ Returns true when the volume 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._volume_status_check(STATUS_ACTIVE, block, timeout,
+ poll_interval)
+
+ def volume_in_use(self):
+ """
+ Returns true when the volume status returns the value of
+ expected_status_code
+ :return: T/F
+ """
+ return self._volume_status_check(STATUS_IN_USE, False, 0, 0)
+
+ def volume_deleted(self, block=False, poll_interval=POLL_INTERVAL):
+ """
+ Returns true when the VM status returns the value of
+ expected_status_code or instance retrieval throws a NotFound exception.
+ :param block: When true, thread will block until active or timeout
+ value in seconds has been exceeded (False)
+ :param poll_interval: The polling interval in seconds
+ :return: T/F
+ """
+ try:
+ return self._volume_status_check(
+ STATUS_DELETED, block, VOLUME_DELETE_TIMEOUT, poll_interval)
+ except NotFound as e:
+ logger.debug(
+ "Volume not found when querying status for %s with message "
+ "%s", STATUS_DELETED, e)
+ return True
+
+ def _volume_status_check(self, expected_status_code, block, timeout,
+ poll_interval):
+ """
+ Returns true when the volume status returns the value of
+ expected_status_code
+ :param expected_status_code: instance status evaluated with this string
+ value
+ :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
+ """
+ # sleep and wait for volume status change
+ if block:
+ start = time.time()
+ else:
+ start = time.time() - timeout + 1
+
+ while timeout > time.time() - start:
+ status = self._status(expected_status_code)
+ if status:
+ logger.debug('Volume is active with name - %s',
+ self.volume_settings.name)
+ return True
+
+ logger.debug('Retry querying volume status in %s seconds',
+ str(poll_interval))
+ time.sleep(poll_interval)
+ logger.debug('Volume status query timeout in %s',
+ str(timeout - (time.time() - start)))
+
+ logger.error(
+ 'Timeout checking for volume status for ' + expected_status_code)
+ return False
+
+ def _status(self, expected_status_code):
+ """
+ Returns True when active else False
+ :param expected_status_code: instance status evaluated with this string
+ value
+ :return: T/F
+ """
+ status = cinder_utils.get_volume_status(self._cinder, self.__volume)
+ if not status:
+ logger.warning(
+ 'Cannot volume status for volume with ID - %s',
+ self.__volume.id)
+ return False
+
+ if status == 'ERROR':
+ raise VolumeCreationError(
+ 'Instance had an error during deployment')
+ logger.debug('Instance status is - ' + status)
+ return status == expected_status_code
+
+
+class VolumeSettings(VolumeConfig):
+ """
+ Class to hold the configuration settings required for creating OpenStack
+ Volume Type Encryption objects
+ deprecated
+ """
+
+ def __init__(self, **kwargs):
+ from warnings import warn
+ warn('Use snaps.config.volume.VolumeConfig instead',
+ DeprecationWarning)
+ super(self.__class__, self).__init__(**kwargs)
+
+
+class VolumeCreationError(Exception):
+ """
+ Exception to be thrown when an volume cannot be created
+ """
+
+ def __init__(self, message):
+ Exception.__init__(self, message)
import logging
-import enum
from cinderclient.exceptions import NotFound
-from neutronclient.common.utils import str2bool
+from snaps.config.volume_type import (
+ VolumeTypeConfig, VolumeTypeEncryptionConfig)
from snaps.openstack.openstack_creator import OpenStackVolumeObject
from snaps.openstack.utils import cinder_utils
return self.__volume_type
- def create(self, block=False):
+ def create(self):
"""
Creates the volume in OpenStack if it does not already exist and
returns the domain Volume object
return self.__volume_type
-class VolumeTypeSettings:
- def __init__(self, **kwargs):
- """
- Constructor
- :param name: the volume's name (required)
- :param description: the volume's name (optional)
- :param encryption: VolumeTypeEncryptionSettings (optional)
- :param qos_spec_name: name of the QoS Spec to associate (optional)
- :param public: When True, an image will be created with public
- visibility (default - False)
-
- TODO - Implement project_access parameter that will associate this
- VolumeType to a list of project names
- """
-
- self.name = kwargs.get('name')
- self.description = kwargs.get('description')
- self.qos_spec_name = kwargs.get('qos_spec_name')
-
- if 'encryption' in kwargs:
- if isinstance(kwargs['encryption'], dict):
- self.encryption = VolumeTypeEncryptionSettings(
- **kwargs['encryption'])
- elif isinstance(kwargs['encryption'],
- VolumeTypeEncryptionSettings):
- self.encryption = kwargs['encryption']
- else:
- self.encryption = None
-
- if 'public' in kwargs:
- if isinstance(kwargs['public'], str):
- self.public = str2bool(kwargs['public'])
- else:
- self.public = kwargs['public']
- else:
- self.public = False
-
- if not self.name:
- raise VolumeTypeSettingsError("The attribute name is required")
-
- def __eq__(self, other):
- return (self.name == other.name
- and self.description == other.description
- and self.qos_spec_name == other.qos_spec_name
- and self.encryption == other.encryption
- and self.public == other.public)
-
-
-class ControlLocation(enum.Enum):
+class VolumeTypeSettings(VolumeTypeConfig):
"""
- QoS Specification consumer types
+ Class to hold the configuration settings required for creating OpenStack
+ Volume Type objects
+ deprecated
"""
- front_end = 'front-end'
- back_end = 'back-end'
-
-class VolumeTypeEncryptionSettings:
def __init__(self, **kwargs):
- """
- Constructor
- :param name: the volume's name (required)
- :param provider_class: the volume's provider class (e.g. LuksEncryptor)
- :param control_location: the notional service where encryption is
- performed (e.g., front-end=Nova). The default
- value is 'front-end.'
- :param cipher: the encryption algorithm/mode to use
- (e.g., aes-xts-plain64). If the field is left empty,
- the provider default will be used
- :param key_size: the size of the encryption key, in bits
- (e.g., 128, 256). If the field is left empty, the
- provider default will be used
- """
+ from warnings import warn
+ warn('Use snaps.config.volume_type.VolumeTypeConfig instead',
+ DeprecationWarning)
+ super(self.__class__, self).__init__(**kwargs)
- self.name = kwargs.get('name')
- self.provider_class = kwargs.get('provider_class')
- self.control_location = kwargs.get('control_location')
- if kwargs.get('control_location'):
- self.control_location = map_control_location(
- kwargs['control_location'])
- else:
- self.control_location = None
-
- self.cipher = kwargs.get('cipher')
- self.key_size = kwargs.get('key_size')
-
- if (not self.name or not self.provider_class
- or not self.control_location):
- raise VolumeTypeSettingsError(
- 'The attributes name, provider_class, and control_location '
- 'are required')
-
- def __eq__(self, other):
- return (self.name == other.name
- and self.provider_class == other.provider_class
- and self.control_location == other.control_location
- and self.cipher == other.cipher
- and self.key_size == other.key_size)
-
-
-def map_control_location(control_location):
- """
- Takes a the protocol value maps it to the Consumer enum. When None return
- None
- :param control_location: the value to map to the Enum
- :return: a ControlLocation enum object
- :raise: Exception if control_location parameter is invalid
- """
- if not control_location:
- return None
- elif isinstance(control_location, ControlLocation):
- return control_location
- else:
- proto_str = str(control_location)
- if proto_str == 'front-end':
- return ControlLocation.front_end
- elif proto_str == 'back-end':
- return ControlLocation.back_end
- else:
- raise VolumeTypeSettingsError('Invalid Consumer - ' + proto_str)
-
-
-class VolumeTypeSettingsError(Exception):
+
+class VolumeTypeEncryptionSettings(VolumeTypeEncryptionConfig):
"""
- Exception to be thrown when an volume settings are incorrect
+ Class to hold the configuration settings required for creating OpenStack
+ Volume Type Encryption objects
+ deprecated
"""
- def __init__(self, message):
- Exception.__init__(self, message)
+ def __init__(self, **kwargs):
+ from warnings import warn
+ warn('Use snaps.config.volume_type.VolumeTypeEncryptionConfig instead',
+ DeprecationWarning)
+ super(self.__class__, self).__init__(**kwargs)
class VolumeTypeCreationError(Exception):
# See the License for the specific language governing permissions and
# limitations under the License.
from snaps.domain.creator import CloudObject
-from snaps.openstack.utils import (nova_utils, neutron_utils, keystone_utils,
- cinder_utils)
+from snaps.openstack.utils import (
+ nova_utils, neutron_utils, keystone_utils, cinder_utils, magnum_utils)
__author__ = 'spisarski'
def clean(self):
raise NotImplementedError('Do not override abstract method')
+
+
+class OpenStackMagnumObject(OpenStackCloudObject):
+ """
+ Abstract class for all OpenStack compute creators
+ """
+
+ def __init__(self, os_creds):
+ """
+ Constructor
+ :param os_creds: the OpenStack credentials object
+ """
+ super(OpenStackMagnumObject, self).__init__(os_creds)
+ self._magnum = None
+
+ def initialize(self):
+ self._magnum = magnum_utils.magnum_client(self._os_creds)
+
+ def create(self):
+ raise NotImplementedError('Do not override abstract method')
+
+ def clean(self):
+ raise NotImplementedError('Do not override abstract method')
clients
:param volume_api_version: The OpenStack's API version to use
for Cinder clients
+ :param magnum_api_version: The OpenStack's API version to use
+ for magnum clients
:param user_domain_id: Used for v3 APIs (default='default')
:param user_domain_name: Used for v3 APIs (default='Default')
:param project_domain_id: Used for v3 APIs (default='default')
:param interface: Used to specify the endpoint type for keystone as
public, admin, internal
:param proxy_settings: instance of os_credentials.ProxySettings class
- :param cacert: Default to be True for http, or the certification file
- is specified for https verification, or set to be False
- to disable server certificate verification without cert
- file
+ :param cacert: True for https or the certification file for https
+ verification (default=False)
:param region_name: the region (optional default = None)
"""
self.username = kwargs.get('username')
if kwargs.get('volume_api_version') is None:
self.volume_api_version = cinder_utils.VERSION_2
else:
- self.volume_api_version = float(
- kwargs['volume_api_version'])
+ self.volume_api_version = float(kwargs['volume_api_version'])
+
+ if kwargs.get('magnum_api_version') is None:
+ self.magnum_api_version = 1
+ else:
+ self.magnum_api_version = float(kwargs['magnum_api_version'])
self.user_domain_id = kwargs.get('user_domain_id', 'default')
:param port: the HTTP proxy port
:param https_host: the HTTPS proxy host (defaults to host)
:param https_port: the HTTPS proxy port (defaults to port)
- :param port: the HTTP proxy port
:param ssh_proxy_cmd: the SSH proxy command string (optional)
"""
self.host = kwargs.get('host')
--- /dev/null
+# 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 magnumclient.common.apiclient.exceptions import BadRequest
+
+from snaps.config.cluster_template import ClusterTemplateConfig
+from snaps.config.flavor import FlavorConfig
+from snaps.config.keypair import KeypairConfig
+from snaps.openstack.cluster_template import OpenStackClusterTemplate
+from snaps.openstack.create_flavor import OpenStackFlavor
+from snaps.openstack.create_image import OpenStackImage
+from snaps.openstack.create_keypairs import OpenStackKeypair
+from snaps.openstack.tests import openstack_tests
+
+try:
+ from urllib.request import URLError
+except ImportError:
+ from urllib2 import URLError
+
+import logging
+import uuid
+
+from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase
+from snaps.openstack.utils import magnum_utils
+
+__author__ = 'spisarski'
+
+logger = logging.getLogger('cluster_template_tests')
+
+
+class CreateClusterTemplateTests(OSIntegrationTestCase):
+ """
+ Test for the OpenStackClusterTemplate class defined in py
+ without any QoS Specs or Encryption
+ """
+
+ def setUp(self):
+ """
+ Instantiates the CreateClusterTemplate object that is responsible for
+ downloading and creating an OS template config file within OpenStack
+ """
+ super(self.__class__, self).__start__()
+
+ 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)
+
+ metadata = self.image_metadata
+ if not metadata:
+ metadata = dict()
+ if 'extra_properties' not in metadata:
+ metadata['extra_properties'] = dict()
+ metadata['extra_properties']['os_distro'] = 'cirros'
+
+ os_image_settings = openstack_tests.cirros_image_settings(
+ name=self.guid + '-image', image_metadata=metadata)
+
+ 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))
+
+ keypair_priv_filepath = 'tmp/' + self.guid
+ keypair_pub_filepath = keypair_priv_filepath + '.pub'
+
+ self.keypair_creator = OpenStackKeypair(
+ self.os_creds, KeypairConfig(
+ name=self.guid + '-keypair',
+ public_filepath=keypair_pub_filepath,
+ private_filepath=keypair_priv_filepath))
+
+ self.cluster_template_creator = None
+
+ self.cluster_template_config = ClusterTemplateConfig(
+ name=self.cluster_type_name,
+ image=self.image_creator.image_settings.name,
+ keypair=self.keypair_creator.keypair_settings.name,
+ external_net=self.ext_net_name,
+ flavor=self.flavor_creator.flavor_settings.name)
+
+ try:
+ self.image_creator.create()
+ self.flavor_creator.create()
+ self.keypair_creator.create()
+ except:
+ self.tearDown()
+ raise
+
+ def tearDown(self):
+ """
+ Cleans the template config
+ """
+ if self.cluster_template_creator:
+ try:
+ self.cluster_template_creator.clean()
+ except:
+ pass
+ if self.keypair_creator:
+ try:
+ self.keypair_creator.clean()
+ except:
+ pass
+ if self.flavor_creator:
+ try:
+ self.flavor_creator.clean()
+ except:
+ pass
+ if self.image_creator:
+ try:
+ self.image_creator.clean()
+ except:
+ pass
+
+ super(self.__class__, self).__clean__()
+
+ def test_create_cluster_template(self):
+ """
+ Tests the creation of an OpenStack cluster template.
+ """
+ # Create ClusterTemplate
+ self.cluster_template_creator = OpenStackClusterTemplate(
+ self.os_creds, self.cluster_template_config)
+ created_cluster_template = self.cluster_template_creator.create()
+ self.assertIsNotNone(created_cluster_template)
+ self.assertEqual(self.cluster_template_config.name,
+ created_cluster_template.name)
+
+ retrieved_cluster_template1 = magnum_utils.get_cluster_template(
+ self.magnum, template_config=self.cluster_template_config)
+ self.assertIsNotNone(retrieved_cluster_template1)
+ self.assertEqual(created_cluster_template, retrieved_cluster_template1)
+
+ retrieved_cluster_template2 = magnum_utils.get_cluster_template_by_id(
+ self.magnum, created_cluster_template.id)
+ self.assertEqual(created_cluster_template, retrieved_cluster_template2)
+
+ def test_create_delete_cluster_template(self):
+ """
+ Tests the creation then deletion of an OpenStack template config to
+ ensure clean() does not raise an Exception.
+ """
+ # Create ClusterTemplate
+ self.cluster_template_creator = OpenStackClusterTemplate(
+ self.os_creds, self.cluster_template_config)
+ created_cluster_template = self.cluster_template_creator.create()
+ self.assertIsNotNone(created_cluster_template)
+
+ self.cluster_template_creator.clean()
+
+ tmplt = magnum_utils.get_cluster_template(
+ self.magnum, template_name=self.cluster_template_config.name)
+ self.assertIsNone(tmplt)
+
+ def test_create_same_cluster_template(self):
+ """
+ Tests the creation of an OpenStack cluster_template when one already
+ exists.
+ """
+ # Create ClusterTemplate
+ self.cluster_template_creator = OpenStackClusterTemplate(
+ self.os_creds, self.cluster_template_config)
+ cluster_template1 = self.cluster_template_creator.create()
+
+ retrieved_cluster_template = magnum_utils.get_cluster_template(
+ self.magnum, template_config=self.cluster_template_config)
+ self.assertEqual(cluster_template1, retrieved_cluster_template)
+
+ # Should be retrieving the instance data
+ os_cluster_template_2 = OpenStackClusterTemplate(
+ self.os_creds, self.cluster_template_config)
+ cluster_template2 = os_cluster_template_2.create()
+ self.assertEqual(cluster_template2, cluster_template2)
+
+ def test_create_cluster_template_bad_flavor(self):
+ """
+ Tests the creation of an OpenStack cluster template raises an
+ exception with an invalid flavor.
+ """
+ # Create ClusterTemplate
+ cluster_template_config = ClusterTemplateConfig(
+ name=self.cluster_type_name,
+ image=self.image_creator.image_settings.name,
+ keypair=self.keypair_creator.keypair_settings.name,
+ external_net=self.ext_net_name,
+ flavor='foo')
+
+ self.cluster_template_creator = OpenStackClusterTemplate(
+ self.os_creds, cluster_template_config)
+
+ with self.assertRaises(BadRequest):
+ self.cluster_template_creator.create()
+
+ def test_create_cluster_template_bad_master_flavor(self):
+ """
+ Tests the creation of an OpenStack cluster template raises an
+ exception with an invalid master flavor.
+ """
+ # Create ClusterTemplate
+ cluster_template_config = ClusterTemplateConfig(
+ name=self.cluster_type_name,
+ image=self.image_creator.image_settings.name,
+ keypair=self.keypair_creator.keypair_settings.name,
+ external_net=self.ext_net_name,
+ flavor=self.flavor_creator.flavor_settings.name,
+ master_flavor='foo')
+
+ self.cluster_template_creator = OpenStackClusterTemplate(
+ self.os_creds, cluster_template_config)
+
+ with self.assertRaises(BadRequest):
+ self.cluster_template_creator.create()
+
+ def test_create_cluster_template_bad_image(self):
+ """
+ Tests the creation of an OpenStack cluster template raises an
+ exception with an invalid image.
+ """
+ # Create ClusterTemplate
+ cluster_template_config = ClusterTemplateConfig(
+ name=self.cluster_type_name,
+ image='foo',
+ keypair=self.keypair_creator.keypair_settings.name,
+ external_net=self.ext_net_name,
+ flavor=self.flavor_creator.flavor_settings.name)
+
+ self.cluster_template_creator = OpenStackClusterTemplate(
+ self.os_creds, cluster_template_config)
+
+ with self.assertRaises(BadRequest):
+ self.cluster_template_creator.create()
+
+ def test_create_cluster_template_bad_network_driver(self):
+ """
+ Tests the creation of an OpenStack cluster template raises an
+ exception with an invalid keypair.
+ """
+ # Create ClusterTemplate
+ cluster_template_config = ClusterTemplateConfig(
+ name=self.cluster_type_name,
+ image=self.image_creator.image_settings.name,
+ keypair=self.keypair_creator.keypair_settings.name,
+ external_net=self.ext_net_name,
+ flavor=self.flavor_creator.flavor_settings.name,
+ network_driver='foo')
+
+ self.cluster_template_creator = OpenStackClusterTemplate(
+ self.os_creds, cluster_template_config)
+
+ with self.assertRaises(BadRequest):
+ self.cluster_template_creator.create()
+
+ def test_create_cluster_template_bad_volume_driver(self):
+ """
+ Tests the creation of an OpenStack cluster template raises an
+ exception with an invalid keypair.
+ """
+ # Create ClusterTemplate
+ cluster_template_config = ClusterTemplateConfig(
+ name=self.cluster_type_name,
+ image=self.image_creator.image_settings.name,
+ keypair=self.keypair_creator.keypair_settings.name,
+ external_net=self.ext_net_name,
+ flavor=self.flavor_creator.flavor_settings.name,
+ volume_driver='foo')
+
+ self.cluster_template_creator = OpenStackClusterTemplate(
+ self.os_creds, cluster_template_config)
+
+ with self.assertRaises(BadRequest):
+ self.cluster_template_creator.create()
from snaps.openstack.os_credentials import (
OSCredsError, OSCreds, ProxySettings, ProxySettingsError)
+from snaps.openstack.utils import cinder_utils
__author__ = 'spisarski'
self.assertEqual(2, os_creds.image_api_version)
self.assertEqual(2, os_creds.compute_api_version)
self.assertEqual(1, os_creds.heat_api_version)
+ self.assertEqual(cinder_utils.VERSION_2, os_creds.volume_api_version)
+ self.assertEqual(1, os_creds.magnum_api_version)
self.assertEqual('default', os_creds.user_domain_id)
self.assertEqual('Default', os_creds.user_domain_name)
self.assertEqual('default', os_creds.project_domain_id)
self.assertEqual(2, os_creds.image_api_version)
self.assertEqual(2, os_creds.compute_api_version)
self.assertEqual(1, os_creds.heat_api_version)
+ self.assertEqual(cinder_utils.VERSION_2, os_creds.volume_api_version)
+ self.assertEqual(1, os_creds.magnum_api_version)
self.assertEqual('default', os_creds.user_domain_id)
self.assertEqual('Default', os_creds.user_domain_name)
self.assertEqual('default', os_creds.project_domain_id)
'auth_url': 'http://foo.bar:5000/v2', 'project_name': 'hello',
'identity_api_version': '5', 'image_api_version': '6',
'compute_api_version': '7', 'heat_api_version': '8.0',
+ 'volume_api_version': '9.5', 'magnum_api_version': '10.6',
'cacert': 'true', 'region_name': 'test_region'})
self.assertEqual('foo', os_creds.username)
self.assertEqual('bar', os_creds.password)
self.assertEqual(6, os_creds.image_api_version)
self.assertEqual(7, os_creds.compute_api_version)
self.assertEqual(8.0, os_creds.heat_api_version)
+ self.assertEqual(9.5, os_creds.volume_api_version)
+ self.assertEqual(10.6, os_creds.magnum_api_version)
self.assertEqual('default', os_creds.user_domain_id)
self.assertEqual('Default', os_creds.user_domain_name)
self.assertEqual('default', os_creds.project_domain_id)
'auth_url': 'http://foo.bar:5000/v2', 'project_name': 'hello',
'identity_api_version': 5, 'image_api_version': 6,
'compute_api_version': 7, 'heat_api_version': 8.0,
+ 'volume_api_version': 9.5, 'magnum_api_version': 10.6,
'cacert': True, 'region_name': 'test_region'})
self.assertEqual('foo', os_creds.username)
self.assertEqual('bar', os_creds.password)
self.assertEqual(6, os_creds.image_api_version)
self.assertEqual(7, os_creds.compute_api_version)
self.assertEqual(8.0, os_creds.heat_api_version)
+ self.assertEqual(9.5, os_creds.volume_api_version)
+ self.assertEqual(10.6, os_creds.magnum_api_version)
self.assertEqual('default', os_creds.user_domain_id)
self.assertEqual('Default', os_creds.user_domain_name)
self.assertEqual('default', os_creds.project_domain_id)
self.assertEqual(2, os_creds.image_api_version)
self.assertEqual(2, os_creds.compute_api_version)
self.assertEqual(1, os_creds.heat_api_version)
+ self.assertEqual(cinder_utils.VERSION_2, os_creds.volume_api_version)
+ self.assertEqual(1, os_creds.magnum_api_version)
self.assertEqual('default', os_creds.user_domain_id)
self.assertEqual('Default', os_creds.user_domain_name)
self.assertEqual('default', os_creds.project_domain_id)
self.assertEqual(2, os_creds.image_api_version)
self.assertEqual(2, os_creds.compute_api_version)
self.assertEqual(1, os_creds.heat_api_version)
+ self.assertEqual(cinder_utils.VERSION_2, os_creds.volume_api_version)
+ self.assertEqual(1, os_creds.magnum_api_version)
self.assertEqual('domain1', os_creds.user_domain_id)
self.assertEqual('domain2', os_creds.user_domain_name)
self.assertEqual('domain3', os_creds.project_domain_id)
self.assertEqual(2, os_creds.image_api_version)
self.assertEqual(2, os_creds.compute_api_version)
self.assertEqual(1, os_creds.heat_api_version)
+ self.assertEqual(cinder_utils.VERSION_2, os_creds.volume_api_version)
+ self.assertEqual(1, os_creds.magnum_api_version)
self.assertEqual('domain1', os_creds.user_domain_id)
self.assertEqual('domain2', os_creds.user_domain_name)
self.assertEqual('domain3', os_creds.project_domain_id)
self.assertEqual(2, os_creds.image_api_version)
self.assertEqual(2, os_creds.compute_api_version)
self.assertEqual(1, os_creds.heat_api_version)
+ self.assertEqual(cinder_utils.VERSION_2, os_creds.volume_api_version)
+ self.assertEqual(1, os_creds.magnum_api_version)
self.assertEqual('default', os_creds.user_domain_id)
self.assertEqual('Default', os_creds.user_domain_name)
self.assertEqual('default', os_creds.project_domain_id)
# Keystone v2.0
#username: admin
#password: admin
-#os_auth_url: http://<host>:<port>/v2.0/
+#os_auth_url: http://<host>:<port>/
#project_name: admin
#ext_net: <external network name>
#http_proxy: <host>:<port>
#ssh_proxy_cmd: '/usr/local/bin/corkscrew <host> <port> %h %p'
#ssh_proxy_cmd: 'ssh <host> nc %h %p'
-# Keystone v2.0
+# Keystone v3
#username: admin
#password: admin
-#os_auth_url: http://<host>:<port>/v3
+#os_auth_url: http://<host>:<port>/
#project_name: admin
#identity_api_version: 3
#ext_net: <external network name>
\ No newline at end of file
import unittest
import uuid
+from snaps.config.flavor import FlavorConfig, FlavorConfigError
from snaps.openstack import create_flavor
-from snaps.openstack.create_flavor import FlavorSettings, OpenStackFlavor, \
- FlavorSettingsError
+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
"""
def test_no_params(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings()
def test_empty_config(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(config=dict())
def test_name_only(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(name='foo')
def test_config_with_name_only(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(config={'name': 'foo'})
def test_name_ram_only(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(name='foo', ram=1)
def test_config_with_name_ram_only(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(config={'name': 'foo', 'ram': 1})
def test_name_ram_disk_only(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(name='foo', ram=1, disk=1)
def test_config_with_name_ram_disk_only(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(config={'name': 'foo', 'ram': 1, 'disk': 1})
def test_ram_string(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(name='foo', ram='bar', disk=2, vcpus=3, ephemeral=4,
swap=5, rxtx_factor=6.0,
is_public=False)
def test_config_ram_string(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(
config={'name': 'foo', 'ram': 'bar', 'disk': 2, 'vcpus': 3,
'ephemeral': 4, 'swap': 5,
'rxtx_factor': 6.0, 'is_public': False})
def test_ram_float(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(name='foo', ram=1.5, disk=2, vcpus=3, ephemeral=4,
swap=5, rxtx_factor=6.0, is_public=False)
def test_config_ram_float(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(
config={'name': 'foo', 'ram': 1.5, 'disk': 2, 'vcpus': 3,
'ephemeral': 4, 'swap': 5,
'rxtx_factor': 6.0, 'is_public': False})
def test_disk_string(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(name='foo', ram=1, disk='bar', vcpus=3, ephemeral=4,
swap=5, rxtx_factor=6.0,
is_public=False)
def test_config_disk_string(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(
config={'name': 'foo', 'ram': 1, 'disk': 'bar', 'vcpus': 3,
'ephemeral': 4, 'swap': 5,
'rxtx_factor': 6.0, 'is_public': False})
def test_disk_float(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(name='foo', ram=1, disk=2.5, vcpus=3, ephemeral=4,
swap=5, rxtx_factor=6.0, is_public=False)
def test_config_disk_float(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(
config={'name': 'foo', 'ram': 1, 'disk': 2.5, 'vcpus': 3,
'ephemeral': 4, 'swap': 5,
'rxtx_factor': 6.0, 'is_public': False})
def test_vcpus_string(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(name='foo', ram=1, disk=2, vcpus='bar', ephemeral=4,
swap=5, rxtx_factor=6.0,
is_public=False)
def test_config_vcpus_string(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(
config={'name': 'foo', 'ram': 1, 'disk': 2, 'vcpus': 'bar',
'ephemeral': 4, 'swap': 5,
'rxtx_factor': 6.0, 'is_public': False})
def test_ephemeral_string(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(name='foo', ram=1, disk=2, vcpus=3, ephemeral='bar',
swap=5, rxtx_factor=6.0,
is_public=False)
def test_config_ephemeral_string(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(
config={'name': 'foo', 'ram': 1, 'disk': 2, 'vcpus': 3,
'ephemeral': 'bar', 'swap': 5,
'rxtx_factor': 6.0, 'is_public': False})
def test_ephemeral_float(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(name='foo', ram=1, disk=2, vcpus=3, ephemeral=4.5,
swap=5, rxtx_factor=6.0, is_public=False)
def test_config_ephemeral_float(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(
config={'name': 'foo', 'ram': 1, 'disk': 2, 'vcpus': 3,
'ephemeral': 4.5, 'swap': 5,
'rxtx_factor': 6.0, 'is_public': False})
def test_swap_string(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(name='foo', ram=1, disk=2, vcpus=3, ephemeral=4,
swap='bar', rxtx_factor=6.0,
is_public=False)
def test_config_swap_string(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(
config={'name': 'foo', 'ram': 1, 'disk': 2, 'vcpus': 3,
'ephemeral': 4, 'swap': 'bar',
'rxtx_factor': 6.0, 'is_public': False})
def test_swap_float(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(name='foo', ram=1, disk=2, vcpus=3, ephemeral=4,
swap=5.5, rxtx_factor=6.0, is_public=False)
def test_config_swap_float(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(
config={'name': 'foo', 'ram': 1, 'disk': 2, 'vcpus': 3,
'ephemeral': 4, 'swap': 5.5,
'rxtx_factor': 6.0, 'is_public': False})
def test_rxtx_string(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(name='foo', ram=1, disk=2, vcpus=3, ephemeral=4,
swap=5, rxtx_factor='bar', is_public=False)
def test_config_rxtx_string(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(
config={'name': 'foo', 'ram': 1, 'disk': 2, 'vcpus': 3,
'ephemeral': 4, 'swap': 5,
'rxtx_factor': 'bar', 'is_public': False})
def test_is_pub_string(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(name='foo', ram=1, disk=2, vcpus=3, ephemeral=4,
swap=5, rxtx_factor=6.0, is_public='bar')
def test_config_is_pub_string(self):
- with self.assertRaises(FlavorSettingsError):
+ with self.assertRaises(FlavorConfigError):
FlavorSettings(
config={'name': 'foo', 'ram': 1, 'disk': 2, 'vcpus': 3,
'ephemeral': 4, 'swap': 5,
Tests the creation of an OpenStack flavor.
"""
# Create Flavor
- flavor_settings = FlavorSettings(name=self.flavor_name, ram=1, disk=1,
- vcpus=1)
+ flavor_settings = FlavorConfig(
+ name=self.flavor_name, ram=1, disk=1, vcpus=1)
self.flavor_creator = OpenStackFlavor(self.os_creds, flavor_settings)
flavor = self.flavor_creator.create()
self.assertTrue(validate_flavor(self.nova, flavor_settings, flavor))
to ensure it has not been done twice.
"""
# Create Flavor
- flavor_settings = FlavorSettings(name=self.flavor_name, ram=1, disk=1,
- vcpus=1)
+ flavor_settings = FlavorConfig(
+ name=self.flavor_name, ram=1, disk=1, vcpus=1)
self.flavor_creator = OpenStackFlavor(self.os_creds, flavor_settings)
flavor = self.flavor_creator.create()
self.assertTrue(validate_flavor(self.nova, flavor_settings, flavor))
Tests the creation and cleanup of an OpenStack flavor.
"""
# Create Flavor
- flavor_settings = FlavorSettings(name=self.flavor_name, ram=1, disk=1,
- vcpus=1)
+ flavor_settings = FlavorConfig(
+ name=self.flavor_name, ram=1, disk=1, vcpus=1)
self.flavor_creator = OpenStackFlavor(self.os_creds, flavor_settings)
flavor = self.flavor_creator.create()
self.assertTrue(validate_flavor(self.nova, flavor_settings, flavor))
raise any exceptions.
"""
# Create Flavor
- flavor_settings = FlavorSettings(name=self.flavor_name, ram=1, disk=1,
- vcpus=1)
+ flavor_settings = FlavorConfig(
+ name=self.flavor_name, ram=1, disk=1, vcpus=1)
self.flavor_creator = OpenStackFlavor(self.os_creds, flavor_settings)
flavor = self.flavor_creator.create()
self.assertTrue(validate_flavor(self.nova, flavor_settings, flavor))
raise any exceptions.
"""
# Create Flavor
- flavor_settings = FlavorSettings(
+ flavor_settings = FlavorConfig(
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)
equals = False
break
- swap = str()
+ swap = None
if flavor_settings.swap != 0:
swap = flavor_settings.swap
import os
from snaps import file_utils
+from snaps.config.image import ImageConfigError
from snaps.openstack import create_image
-from snaps.openstack.create_image import (ImageSettings, ImageCreationError,
- ImageSettingsError)
+from snaps.openstack.create_image import ImageSettings, ImageCreationError
+from snaps.config.image import ImageConfig
from snaps.openstack.tests import openstack_tests
from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase
from snaps.openstack.utils import glance_utils
class ImageSettingsUnitTests(unittest.TestCase):
"""
Tests the construction of the ImageSettings class
+ To be removed once the deprecated class ImageSettings is finally removed
+ from the source tree
"""
def test_no_params(self):
- with self.assertRaises(ImageSettingsError):
+ with self.assertRaises(ImageConfigError):
ImageSettings()
def test_empty_config(self):
- with self.assertRaises(ImageSettingsError):
+ with self.assertRaises(ImageConfigError):
ImageSettings(**dict())
def test_name_only(self):
- with self.assertRaises(ImageSettingsError):
+ with self.assertRaises(ImageConfigError):
ImageSettings(name='foo')
def test_config_with_name_only(self):
- with self.assertRaises(ImageSettingsError):
+ with self.assertRaises(ImageConfigError):
ImageSettings(**{'name': 'foo'})
def test_name_user_only(self):
- with self.assertRaises(ImageSettingsError):
+ with self.assertRaises(ImageConfigError):
ImageSettings(name='foo', image_user='bar')
def test_config_with_name_user_only(self):
- with self.assertRaises(ImageSettingsError):
+ with self.assertRaises(ImageConfigError):
ImageSettings(**{'name': 'foo', 'image_user': 'bar'})
def test_name_user_format_only(self):
- with self.assertRaises(ImageSettingsError):
+ with self.assertRaises(ImageConfigError):
ImageSettings(name='foo', image_user='bar', img_format='qcow2')
def test_config_with_name_user_format_only(self):
- with self.assertRaises(ImageSettingsError):
+ with self.assertRaises(ImageConfigError):
ImageSettings(
**{'name': 'foo', 'image_user': 'bar', 'format': 'qcow2'})
self.assertEqual(image1.properties, retrieved_image.properties)
# Should be retrieving the instance data
- image_2_settings = ImageSettings(name=self.image_settings.name,
- image_user='foo', exists=True)
+ image_2_settings = ImageConfig(name=self.image_settings.name,
+ image_user='foo', exists=True)
os_image_2 = create_image.OpenStackImage(self.os_creds,
image_2_settings)
image2 = os_image_2.create()
Expect an ImageCreationError when the image name does not exist when a
file or URL has not been configured
"""
- os_image_settings = ImageSettings(name='foo', image_user='bar',
- exists=True)
+ os_image_settings = ImageConfig(name='foo', image_user='bar',
+ exists=True)
self.image_creator = create_image.OpenStackImage(self.os_creds,
os_image_settings)
name=self.image_name)
self.image_creator = create_image.OpenStackImage(
self.os_creds,
- create_image.ImageSettings(name=os_image_settings.name,
- image_user=os_image_settings.image_user,
- img_format=os_image_settings.format,
- url="http://foo.bar"))
+ ImageConfig(
+ name=os_image_settings.name,
+ image_user=os_image_settings.image_user,
+ img_format=os_image_settings.format,
+ url="http://foo.bar"))
try:
self.image_creator.create()
name=self.image_name)
self.image_creator = create_image.OpenStackImage(
self.os_creds,
- create_image.ImageSettings(name=os_image_settings.name,
- image_user=os_image_settings.image_user,
- img_format='foo',
- url=os_image_settings.url))
+ ImageConfig(
+ name=os_image_settings.name,
+ image_user=os_image_settings.image_user,
+ img_format='foo', url=os_image_settings.url))
with self.assertRaises(Exception):
self.image_creator.create()
name=self.image_name)
self.image_creator = create_image.OpenStackImage(
self.os_creds,
- create_image.ImageSettings(name=os_image_settings.name,
- image_user=os_image_settings.image_user,
- img_format=os_image_settings.format,
- image_file="/foo/bar.qcow"))
+ ImageConfig(
+ name=os_image_settings.name,
+ image_user=os_image_settings.image_user,
+ img_format=os_image_settings.format, image_file="/foo/bar.qcow"))
with self.assertRaises(IOError):
self.image_creator.create()
import os
from neutronclient.common.exceptions import InvalidIpForSubnetClient
+from novaclient.exceptions import BadRequest
from snaps import file_utils
-from snaps.openstack import create_network, create_router
-from snaps.openstack.create_flavor import OpenStackFlavor, FlavorSettings
-from snaps.openstack.create_image import OpenStackImage, ImageSettings
+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
+from snaps.config.router import RouterConfig
+from snaps.config.security_group import (
+ Protocol, SecurityGroupRuleConfig, Direction, SecurityGroupConfig)
+from snaps.config.vm_inst import (
+ VmInstanceConfig, FloatingIpConfig, VmInstanceConfigError,
+ FloatingIpConfigError)
+from snaps.config.volume import VolumeConfig
+from snaps.openstack import create_network, create_router, create_instance
+from snaps.openstack.create_flavor import OpenStackFlavor
+from snaps.openstack.create_image import OpenStackImage
from snaps.openstack.create_instance import (
- VmInstanceSettings, OpenStackVmInstance, FloatingIpSettings,
- VmInstanceSettingsError, FloatingIpSettingsError)
-from snaps.openstack.create_keypairs import OpenStackKeypair, KeypairSettings
-from snaps.openstack.create_network import (
- OpenStackNetwork, PortSettings, NetworkSettings)
-from snaps.openstack.create_router import OpenStackRouter, RouterSettings
-from snaps.openstack.create_security_group import (
- SecurityGroupSettings, OpenStackSecurityGroup, SecurityGroupRuleSettings,
- Direction, Protocol)
+ VmInstanceSettings, OpenStackVmInstance, FloatingIpSettings)
+from snaps.openstack.create_keypairs import OpenStackKeypair
+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.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.nova_utils import RebootType
+from snaps.openstack.utils import nova_utils, settings_utils, neutron_utils
__author__ = 'spisarski'
"""
def test_no_params(self):
- with self.assertRaises(VmInstanceSettingsError):
+ with self.assertRaises(VmInstanceConfigError):
VmInstanceSettings()
def test_empty_config(self):
- with self.assertRaises(VmInstanceSettingsError):
+ with self.assertRaises(VmInstanceConfigError):
VmInstanceSettings(config=dict())
def test_name_only(self):
- with self.assertRaises(VmInstanceSettingsError):
+ with self.assertRaises(VmInstanceConfigError):
VmInstanceSettings(name='foo')
def test_config_with_name_only(self):
- with self.assertRaises(VmInstanceSettingsError):
+ with self.assertRaises(VmInstanceConfigError):
VmInstanceSettings(config={'name': 'foo'})
def test_name_flavor_only(self):
- with self.assertRaises(VmInstanceSettingsError):
+ with self.assertRaises(VmInstanceConfigError):
VmInstanceSettings(name='foo', flavor='bar')
def test_config_with_name_flavor_only(self):
- with self.assertRaises(VmInstanceSettingsError):
+ with self.assertRaises(VmInstanceConfigError):
VmInstanceSettings(config={'name': 'foo', 'flavor': 'bar'})
def test_name_flavor_port_only(self):
- port_settings = PortSettings(name='foo-port', network_name='bar-net')
+ port_settings = PortConfig(name='foo-port', network_name='bar-net')
settings = VmInstanceSettings(name='foo', flavor='bar',
port_settings=[port_settings])
self.assertEqual('foo', settings.name)
self.assertEqual(300, settings.vm_delete_timeout)
self.assertEqual(180, settings.ssh_connect_timeout)
self.assertIsNone(settings.availability_zone)
+ self.assertIsNone(settings.volume_names)
def test_config_with_name_flavor_port_only(self):
- port_settings = PortSettings(name='foo-port', network_name='bar-net')
+ port_settings = PortConfig(name='foo-port', network_name='bar-net')
settings = VmInstanceSettings(
**{'name': 'foo', 'flavor': 'bar', 'ports': [port_settings]})
self.assertEqual('foo', settings.name)
self.assertEqual(300, settings.vm_delete_timeout)
self.assertEqual(180, settings.ssh_connect_timeout)
self.assertIsNone(settings.availability_zone)
+ self.assertIsNone(settings.volume_names)
def test_all(self):
- port_settings = PortSettings(name='foo-port', network_name='bar-net')
+ port_settings = PortConfig(name='foo-port', network_name='bar-net')
fip_settings = FloatingIpSettings(name='foo-fip', port_name='bar-port',
router_name='foo-bar-router')
- settings = VmInstanceSettings(name='foo', flavor='bar',
- port_settings=[port_settings],
- security_group_names=['sec_grp_1'],
- floating_ip_settings=[fip_settings],
- sudo_user='joe', vm_boot_timeout=999,
- vm_delete_timeout=333,
- ssh_connect_timeout=111,
- availability_zone='server name')
+ settings = VmInstanceSettings(
+ name='foo', flavor='bar', port_settings=[port_settings],
+ security_group_names=['sec_grp_1'],
+ floating_ip_settings=[fip_settings], sudo_user='joe',
+ vm_boot_timeout=999, vm_delete_timeout=333,
+ ssh_connect_timeout=111, availability_zone='server name',
+ volume_names=['vol1'])
self.assertEqual('foo', settings.name)
self.assertEqual('bar', settings.flavor)
self.assertEqual(1, len(settings.port_settings))
self.assertEqual(333, settings.vm_delete_timeout)
self.assertEqual(111, settings.ssh_connect_timeout)
self.assertEqual('server name', settings.availability_zone)
+ self.assertEqual('vol1', settings.volume_names[0])
def test_config_all(self):
- port_settings = PortSettings(name='foo-port', network_name='bar-net')
+ port_settings = PortConfig(name='foo-port', network_name='bar-net')
fip_settings = FloatingIpSettings(name='foo-fip', port_name='bar-port',
router_name='foo-bar-router')
'security_group_names': ['sec_grp_1'],
'floating_ips': [fip_settings], 'sudo_user': 'joe',
'vm_boot_timeout': 999, 'vm_delete_timeout': 333,
- 'ssh_connect_timeout': 111, 'availability_zone': 'server name'})
+ 'ssh_connect_timeout': 111, 'availability_zone': 'server name',
+ 'volume_names': ['vol2']})
self.assertEqual('foo', settings.name)
self.assertEqual('bar', settings.flavor)
self.assertEqual(1, len(settings.port_settings))
self.assertEqual(333, settings.vm_delete_timeout)
self.assertEqual(111, settings.ssh_connect_timeout)
self.assertEqual('server name', settings.availability_zone)
+ self.assertEqual('vol2', settings.volume_names[0])
class FloatingIpSettingsUnitTests(unittest.TestCase):
"""
def test_no_params(self):
- with self.assertRaises(FloatingIpSettingsError):
+ with self.assertRaises(FloatingIpConfigError):
FloatingIpSettings()
def test_empty_config(self):
- with self.assertRaises(FloatingIpSettingsError):
+ with self.assertRaises(FloatingIpConfigError):
FloatingIpSettings(**dict())
def test_name_only(self):
- with self.assertRaises(FloatingIpSettingsError):
+ with self.assertRaises(FloatingIpConfigError):
FloatingIpSettings(name='foo')
def test_config_with_name_only(self):
- with self.assertRaises(FloatingIpSettingsError):
+ with self.assertRaises(FloatingIpConfigError):
FloatingIpSettings(**{'name': 'foo'})
def test_name_port_only(self):
- with self.assertRaises(FloatingIpSettingsError):
+ with self.assertRaises(FloatingIpConfigError):
FloatingIpSettings(name='foo', port_name='bar')
def test_config_with_name_port_only(self):
- with self.assertRaises(FloatingIpSettingsError):
+ with self.assertRaises(FloatingIpConfigError):
FloatingIpSettings(**{'name': 'foo', 'port_name': 'bar'})
def test_name_router_only(self):
- with self.assertRaises(FloatingIpSettingsError):
+ with self.assertRaises(FloatingIpConfigError):
FloatingIpSettings(name='foo', router_name='bar')
def test_config_with_name_router_only(self):
- with self.assertRaises(FloatingIpSettingsError):
+ with self.assertRaises(FloatingIpConfigError):
FloatingIpSettings(**{'name': 'foo', 'router_name': 'bar'})
def test_name_port_router_name_only(self):
self.inst_creator = None
self.priv_net_config = openstack_tests.get_priv_net_config(
- net_name=guid + '-priv-net', subnet_name=guid + '-priv-subnet')
- self.port_settings = PortSettings(
+ net_name=guid + '-priv-net',
+ subnet_name=guid + '-priv-subnet',
+ netconf_override=self.netconf_override)
+ self.port_settings = PortConfig(
name=self.port_1_name,
network_name=self.priv_net_config.network_settings.name)
self.network_creator.create()
# Create Flavor
+ self.flavor_ram = 256
+ if (self.flavor_metadata and
+ self.flavor_metadata.get('hw:mem_page_size') == 'large'):
+ self.flavor_ram = 1024
self.flavor_creator = OpenStackFlavor(
self.admin_os_creds,
- FlavorSettings(name=guid + '-flavor-name', ram=256, disk=10,
- vcpus=1, metadata=self.flavor_metadata))
+ FlavorConfig(name=guid + '-flavor-name', ram=self.flavor_ram,
+ disk=10, vcpus=1, metadata=self.flavor_metadata))
self.flavor_creator.create()
except Exception as e:
self.tearDown()
Tests the creation of an OpenStack instance with a single port and
ensures that it's assigned IP address is the actual.
"""
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[self.port_settings])
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)
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)
+ router_name=guid + '-pub-router', external_net=self.ext_net_name,
+ netconf_override=self.netconf_override)
# Initialize for tearDown()
self.image_creator = None
# Create Flavor
self.flavor_creator = OpenStackFlavor(
self.admin_os_creds,
- FlavorSettings(name=guid + '-flavor-name', ram=256, disk=10,
- vcpus=2, metadata=self.flavor_metadata))
+ FlavorConfig(name=guid + '-flavor-name', ram=256, disk=10,
+ vcpus=2, metadata=self.flavor_metadata))
self.flavor_creator.create()
# Create Network
self.os_creds, net_config.network_settings)
self.network_creator.create()
- self.port_settings = PortSettings(
+ self.port_settings = PortConfig(
name=guid + '-port',
network_name=net_config.network_settings.name)
Tests the creation of an OpenStack instance with a single port with a
static IP without a Floating IP.
"""
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[self.port_settings])
vm_inst = self.inst_creator.create()
self.assertIsNotNone(nova_utils.get_server(
- self.nova, vm_inst_settings=instance_settings))
+ self.nova, self.neutron, vm_inst_settings=instance_settings))
# 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, vm_inst_settings=instance_settings))
+ self.nova, self.neutron, vm_inst_settings=instance_settings))
# Exception should not be thrown
self.inst_creator.clean()
self.pub_net_config = openstack_tests.get_pub_net_config(
net_name=guid + '-pub-net', subnet_name=guid + '-pub-subnet',
- router_name=guid + '-pub-router', external_net=self.ext_net_name)
+ router_name=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)
try:
# Create Flavor
self.flavor_creator = OpenStackFlavor(
self.admin_os_creds,
- FlavorSettings(name=guid + '-flavor-name', ram=256, disk=10,
- vcpus=2, metadata=self.flavor_metadata))
+ FlavorConfig(name=guid + '-flavor-name', ram=256, disk=10,
+ vcpus=2, metadata=self.flavor_metadata))
self.flavor_creator.create()
self.keypair_creator = OpenStackKeypair(
- self.os_creds, KeypairSettings(
+ self.os_creds, KeypairConfig(
name=self.keypair_name,
public_filepath=self.keypair_pub_filepath,
private_filepath=self.keypair_priv_filepath))
self.keypair_creator.create()
sec_grp_name = guid + '-sec-grp'
- rule1 = SecurityGroupRuleSettings(sec_grp_name=sec_grp_name,
- direction=Direction.ingress,
- protocol=Protocol.icmp)
- rule2 = SecurityGroupRuleSettings(sec_grp_name=sec_grp_name,
- direction=Direction.ingress,
- protocol=Protocol.tcp,
- port_range_min=22,
- port_range_max=22)
+ rule1 = SecurityGroupRuleConfig(
+ sec_grp_name=sec_grp_name, direction=Direction.ingress,
+ protocol=Protocol.icmp)
+ rule2 = SecurityGroupRuleConfig(
+ sec_grp_name=sec_grp_name, direction=Direction.ingress,
+ protocol=Protocol.tcp, port_range_min=22, port_range_max=22)
self.sec_grp_creator = OpenStackSecurityGroup(
self.os_creds,
- SecurityGroupSettings(name=sec_grp_name,
- rule_settings=[rule1, rule2]))
+ SecurityGroupConfig(
+ name=sec_grp_name, rule_settings=[rule1, rule2]))
self.sec_grp_creator.create()
except Exception as e:
self.tearDown()
'Unexpected exception cleaning keypair with message - %s',
e)
- if os.path.isfile(self.keypair_pub_filepath):
- os.remove(self.keypair_pub_filepath)
-
- if os.path.isfile(self.keypair_priv_filepath):
- os.remove(self.keypair_priv_filepath)
-
if self.flavor_creator:
try:
self.flavor_creator.clean()
"""
ip_1 = '10.55.1.100'
sub_settings = self.pub_net_config.network_settings.subnet_settings
- port_settings = PortSettings(
+ port_settings = PortConfig(
name=self.port_1_name,
network_name=self.pub_net_config.network_settings.name,
ip_addrs=[
{'subnet_name': sub_settings[0].name, 'ip': ip_1}])
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[port_settings],
- floating_ip_settings=[FloatingIpSettings(
+ floating_ip_settings=[FloatingIpConfig(
name=self.floating_ip_name, port_name=self.port_1_name,
router_name=self.pub_net_config.router_settings.name)])
self.image_creator.image_settings,
keypair_settings=self.keypair_creator.keypair_settings)
self.inst_creators.append(inst_creator)
- vm_inst = inst_creator.create()
+ vm_inst = inst_creator.create(block=True)
self.assertEqual(ip_1, inst_creator.get_port_ip(self.port_1_name))
self.assertTrue(inst_creator.vm_active(block=True))
Tests the ability to access a VM via SSH and a floating IP when it has
been assigned prior to being active.
"""
- port_settings = PortSettings(
+ port_settings = PortConfig(
name=self.port_1_name,
network_name=self.pub_net_config.network_settings.name)
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[port_settings],
- floating_ip_settings=[FloatingIpSettings(
+ security_group_names=[self.sec_grp_creator.sec_grp_settings.name],
+ floating_ip_settings=[FloatingIpConfig(
name=self.floating_ip_name, port_name=self.port_1_name,
router_name=self.pub_net_config.router_settings.name)])
ip = inst_creator.get_port_ip(port_settings.name)
self.assertTrue(check_dhcp_lease(inst_creator, ip))
- inst_creator.add_security_group(
- self.sec_grp_creator.get_security_group())
self.assertEqual(vm_inst.id, inst_creator.get_vm_inst().id)
self.assertTrue(validate_ssh_client(inst_creator))
Tests the ability to access a VM via SSH and a floating IP when it has
been assigned prior to being active.
"""
- port_settings = PortSettings(
+ port_settings = PortConfig(
+ name=self.port_1_name,
+ network_name=self.pub_net_config.network_settings.name)
+
+ instance_settings = VmInstanceConfig(
+ name=self.vm_inst_name,
+ flavor=self.flavor_creator.flavor_settings.name,
+ port_settings=[port_settings],
+ security_group_names=[self.sec_grp_creator.sec_grp_settings.name],
+ floating_ip_settings=[FloatingIpConfig(
+ name=self.floating_ip_name, port_name=self.port_1_name,
+ router_name=self.pub_net_config.router_settings.name)])
+
+ inst_creator = OpenStackVmInstance(
+ self.os_creds, instance_settings,
+ self.image_creator.image_settings,
+ keypair_settings=self.keypair_creator.keypair_settings)
+ self.inst_creators.append(inst_creator)
+
+ # block=True will force the create() method to block until the
+ vm_inst = inst_creator.create(block=True)
+ self.assertIsNotNone(vm_inst)
+
+ self.assertTrue(inst_creator.vm_active(block=True))
+
+ ip = inst_creator.get_port_ip(port_settings.name)
+ self.assertTrue(check_dhcp_lease(inst_creator, ip))
+
+ self.assertEqual(vm_inst.id, inst_creator.get_vm_inst().id)
+
+ self.assertTrue(validate_ssh_client(inst_creator))
+
+ def test_ssh_client_fip_after_reboot(self):
+ """
+ Tests the ability to access a VM via SSH and a floating IP after it has
+ been rebooted.
+ """
+ port_settings = PortConfig(
name=self.port_1_name,
network_name=self.pub_net_config.network_settings.name)
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[port_settings],
- floating_ip_settings=[FloatingIpSettings(
+ security_group_names=[self.sec_grp_creator.sec_grp_settings.name],
+ floating_ip_settings=[FloatingIpConfig(
name=self.floating_ip_name, port_name=self.port_1_name,
router_name=self.pub_net_config.router_settings.name)])
ip = inst_creator.get_port_ip(port_settings.name)
self.assertTrue(check_dhcp_lease(inst_creator, ip))
- inst_creator.add_security_group(
- self.sec_grp_creator.get_security_group())
self.assertEqual(vm_inst.id, inst_creator.get_vm_inst().id)
self.assertTrue(validate_ssh_client(inst_creator))
+ # Test default reboot which should be 'SOFT'
+ inst_creator.reboot()
+ # Lag time to allow for shutdown routine to take effect
+ time.sleep(10)
+ 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)
+ 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)
+ self.assertTrue(check_dhcp_lease(inst_creator, ip))
+ self.assertTrue(validate_ssh_client(inst_creator))
+
+ def test_ssh_client_fip_after_init(self):
+ """
+ Tests the ability to assign a floating IP to an already initialized
+ OpenStackVmInstance object. After the floating IP has been allocated
+ and assigned, this test will ensure that it can be accessed via SSH.
+ """
+ port_settings = PortConfig(
+ name=self.port_1_name,
+ network_name=self.pub_net_config.network_settings.name)
+
+ instance_settings = VmInstanceConfig(
+ name=self.vm_inst_name,
+ flavor=self.flavor_creator.flavor_settings.name,
+ port_settings=[port_settings],
+ security_group_names=[self.sec_grp_creator.sec_grp_settings.name])
+
+ inst_creator = OpenStackVmInstance(
+ self.os_creds, instance_settings,
+ self.image_creator.image_settings,
+ keypair_settings=self.keypair_creator.keypair_settings)
+ self.inst_creators.append(inst_creator)
+
+ # block=True will force the create() method to block until the
+ vm_inst = inst_creator.create(block=True)
+ self.assertIsNotNone(vm_inst)
+
+ self.assertTrue(inst_creator.vm_active(block=True))
+ ip = inst_creator.get_port_ip(port_settings.name)
+ self.assertTrue(check_dhcp_lease(inst_creator, ip))
+ self.assertEqual(vm_inst.id, inst_creator.get_vm_inst().id)
+
+ inst_creator.add_floating_ip(FloatingIpConfig(
+ name=self.floating_ip_name, port_name=self.port_1_name,
+ router_name=self.pub_net_config.router_settings.name))
+
+ self.assertTrue(validate_ssh_client(inst_creator))
+
+ def test_ssh_client_fip_reverse_engineer(self):
+ """
+ Tests the ability to assign a floating IP to a reverse engineered
+ OpenStackVmInstance object. After the floating IP has been allocated
+ and assigned, this test will ensure that it can be accessed via SSH.
+ """
+ port_settings = PortConfig(
+ name=self.port_1_name,
+ network_name=self.pub_net_config.network_settings.name)
+
+ instance_settings = VmInstanceConfig(
+ name=self.vm_inst_name,
+ flavor=self.flavor_creator.flavor_settings.name,
+ port_settings=[port_settings],
+ security_group_names=[self.sec_grp_creator.sec_grp_settings.name])
+
+ inst_creator = OpenStackVmInstance(
+ self.os_creds, instance_settings,
+ self.image_creator.image_settings,
+ keypair_settings=self.keypair_creator.keypair_settings)
+ self.inst_creators.append(inst_creator)
+
+ # block=True will force the create() method to block until the
+ vm_inst = inst_creator.create(block=True)
+ self.assertIsNotNone(vm_inst)
+
+ self.assertTrue(inst_creator.vm_active(block=True))
+
+ derived_inst_creator = create_instance.generate_creator(
+ self.os_creds, vm_inst, self.image_creator.image_settings,
+ self.keypair_creator.keypair_settings)
+
+ derived_inst_creator.add_floating_ip(FloatingIpConfig(
+ name=self.floating_ip_name, port_name=self.port_1_name,
+ router_name=self.pub_net_config.router_settings.name))
+ self.inst_creators.append(derived_inst_creator)
+
+ self.assertTrue(validate_ssh_client(
+ derived_inst_creator, fip_name=self.floating_ip_name))
+
def test_ssh_client_fip_second_creator(self):
"""
Tests the ability to access a VM via SSH and a floating IP via a
creator that is identical to the original creator.
"""
- port_settings = PortSettings(
+ port_settings = PortConfig(
name=self.port_1_name,
network_name=self.pub_net_config.network_settings.name)
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[port_settings],
- floating_ip_settings=[FloatingIpSettings(
+ security_group_names=[self.sec_grp_creator.sec_grp_settings.name],
+ floating_ip_settings=[FloatingIpConfig(
name=self.floating_ip_name, port_name=self.port_1_name,
router_name=self.pub_net_config.router_settings.name)])
ip = inst_creator.get_port_ip(port_settings.name)
self.assertTrue(check_dhcp_lease(inst_creator, ip))
- inst_creator.add_security_group(
- self.sec_grp_creator.get_security_group())
self.assertEqual(vm_inst.id, inst_creator.get_vm_inst().id)
self.assertTrue(validate_ssh_client(inst_creator))
self.assertTrue(validate_ssh_client(inst_creator2))
+class CreateInstanceIPv6NetworkTests(OSIntegrationTestCase):
+ """
+ Test for the CreateInstance class with a single NIC/Port with Floating IPs
+ """
+
+ def setUp(self):
+ """
+ Instantiates the CreateImage object that is responsible for downloading
+ and creating an OS image file within OpenStack
+ """
+ super(self.__class__, self).__start__()
+
+ self.nova = nova_utils.nova_client(self.os_creds)
+ self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+ self.keypair_priv_filepath = 'tmp/' + self.guid
+ self.keypair_pub_filepath = self.keypair_priv_filepath + '.pub'
+ self.keypair_name = self.guid + '-kp'
+ self.vm_inst_name = self.guid + '-inst'
+ self.port1_name = self.guid + 'port1'
+ self.port2_name = self.guid + 'port2'
+
+ # Initialize for tearDown()
+ self.image_creator = None
+ self.network_creator = None
+ self.router_creator = None
+ self.flavor_creator = None
+ self.keypair_creator = None
+ self.sec_grp_creator = None
+ self.inst_creator = None
+
+ os_image_settings = openstack_tests.cirros_image_settings(
+ name=self.guid + '-image', image_metadata=self.image_metadata)
+ try:
+ self.image_creator = OpenStackImage(
+ self.os_creds, os_image_settings)
+ self.image_creator.create()
+
+ 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.flavor_creator.create()
+
+ self.keypair_creator = OpenStackKeypair(
+ self.os_creds, KeypairConfig(
+ name=self.keypair_name,
+ public_filepath=self.keypair_pub_filepath,
+ private_filepath=self.keypair_priv_filepath))
+ self.keypair_creator.create()
+
+ sec_grp_name = self.guid + '-sec-grp'
+ rule1 = SecurityGroupRuleConfig(
+ sec_grp_name=sec_grp_name, direction=Direction.ingress,
+ protocol=Protocol.icmp)
+ rule2 = SecurityGroupRuleConfig(
+ sec_grp_name=sec_grp_name, direction=Direction.ingress,
+ protocol=Protocol.tcp, port_range_min=22, port_range_max=22)
+ self.sec_grp_creator = OpenStackSecurityGroup(
+ self.os_creds,
+ SecurityGroupConfig(
+ name=sec_grp_name, rule_settings=[rule1, rule2]))
+ self.sec_grp_creator.create()
+ except Exception as e:
+ self.tearDown()
+ raise e
+
+ def tearDown(self):
+ """
+ Cleans the created object
+ """
+ if self.inst_creator:
+ try:
+ self.inst_creator.clean()
+ except Exception as e:
+ logger.error(
+ 'Unexpected exception cleaning VM instance with message '
+ '- %s', e)
+
+ if self.keypair_creator:
+ try:
+ self.keypair_creator.clean()
+ except Exception as e:
+ logger.error(
+ 'Unexpected exception cleaning keypair 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.sec_grp_creator:
+ try:
+ self.sec_grp_creator.clean()
+ except Exception as e:
+ logger.error(
+ 'Unexpected exception cleaning security group with message'
+ ' - %s', e)
+
+ if self.router_creator:
+ try:
+ self.router_creator.clean()
+ except Exception as e:
+ logger.error(
+ 'Unexpected exception cleaning router with message - %s',
+ e)
+
+ if self.network_creator:
+ try:
+ self.network_creator.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()
+ except Exception as e:
+ logger.error(
+ 'Unexpected exception cleaning image with message - %s', e)
+
+ super(self.__class__, self).__clean__()
+
+ def test_v4fip_v6overlay(self):
+ """
+ Tests the ability to assign an IPv4 floating IP to an IPv6 overlay
+ network when the external network does not have an IPv6 subnet.
+ """
+ subnet_settings = SubnetConfig(
+ name=self.guid + '-subnet', cidr='1:1:0:0:0:0:0:0/64',
+ ip_version=6)
+ network_settings = NetworkConfig(
+ 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])
+
+ # Create Network
+ self.network_creator = OpenStackNetwork(
+ self.os_creds, network_settings)
+ self.network_creator.create()
+
+ # Create Router
+ self.router_creator = OpenStackRouter(
+ self.os_creds, router_settings)
+ self.router_creator.create()
+
+ port_settings = PortConfig(
+ name=self.port1_name, network_name=network_settings.name)
+
+ instance_settings = VmInstanceConfig(
+ name=self.vm_inst_name,
+ flavor=self.flavor_creator.flavor_settings.name,
+ port_settings=[port_settings],
+ security_group_names=[self.sec_grp_creator.sec_grp_settings.name],
+ floating_ip_settings=[FloatingIpConfig(
+ name='fip1', port_name=self.port1_name,
+ router_name=router_settings.name)])
+
+ self.inst_creator = OpenStackVmInstance(
+ self.os_creds, instance_settings,
+ self.image_creator.image_settings,
+ keypair_settings=self.keypair_creator.keypair_settings)
+
+ with self.assertRaises(BadRequest):
+ self.inst_creator.create(block=True)
+
+ def test_fip_v4and6_overlay(self):
+ """
+ Tests the ability to assign an IPv4 floating IP to an IPv6 overlay
+ network when the external network does not have an IPv6 subnet.
+ """
+ subnet4_settings = SubnetConfig(
+ name=self.guid + '-subnet4', cidr='10.0.1.0/24',
+ ip_version=4)
+ subnet6_settings = SubnetConfig(
+ name=self.guid + '-subnet6', cidr='1:1:0:0:0:0:0:0/64',
+ ip_version=6)
+ network_settings = NetworkConfig(
+ name=self.guid + '-net',
+ subnet_settings=[subnet4_settings, subnet6_settings])
+ router_settings = RouterConfig(
+ name=self.guid + '-router', external_gateway=self.ext_net_name,
+ internal_subnets=[subnet4_settings.name])
+
+ # Create Network
+ self.network_creator = OpenStackNetwork(
+ self.os_creds, network_settings)
+ self.network_creator.create()
+
+ # Create Router
+ self.router_creator = OpenStackRouter(
+ self.os_creds, router_settings)
+ self.router_creator.create()
+
+ port_settings = PortConfig(
+ name=self.port1_name, network_name=network_settings.name)
+
+ instance_settings = VmInstanceConfig(
+ name=self.vm_inst_name,
+ flavor=self.flavor_creator.flavor_settings.name,
+ port_settings=[port_settings],
+ security_group_names=[self.sec_grp_creator.sec_grp_settings.name],
+ floating_ip_settings=[FloatingIpConfig(
+ name='fip1', port_name=self.port1_name,
+ router_name=router_settings.name)])
+
+ self.inst_creator = OpenStackVmInstance(
+ self.os_creds, instance_settings,
+ self.image_creator.image_settings,
+ keypair_settings=self.keypair_creator.keypair_settings)
+
+ self.inst_creator.create(block=True)
+ ssh_client = self.inst_creator.ssh_client()
+ self.assertIsNotNone(ssh_client)
+
+
class CreateInstancePortManipulationTests(OSIntegrationTestCase):
"""
Test for the CreateInstance class with a single NIC/Port where mac and IP
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)
+ router_name=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)
# Create Flavor
self.flavor_creator = OpenStackFlavor(
self.admin_os_creds,
- FlavorSettings(name=guid + '-flavor-name', ram=256, disk=10,
- vcpus=2, metadata=self.flavor_metadata))
+ FlavorConfig(name=guid + '-flavor-name', ram=256, disk=10,
+ vcpus=2, metadata=self.flavor_metadata))
self.flavor_creator.create()
except Exception as e:
self.tearDown()
"""
ip = '10.55.0.101'
sub_settings = self.net_config.network_settings.subnet_settings
- port_settings = PortSettings(
+ 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': ip}])
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[port_settings])
"""
ip = '10.66.0.101'
sub_settings = self.net_config.network_settings.subnet_settings
- port_settings = PortSettings(
+ 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': ip}])
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[port_settings])
the MAC address is assigned.
"""
mac_addr = '0a:1b:2c:3d:4e:5f'
- port_settings = PortSettings(
+ port_settings = PortConfig(
name=self.port_1_name,
network_name=self.net_config.network_settings.name,
mac_address=mac_addr)
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[port_settings])
invalid MAC address value is being
assigned. This should raise an Exception
"""
- port_settings = PortSettings(
+ port_settings = PortConfig(
name=self.port_1_name,
network_name=self.net_config.network_settings.name,
mac_address='foo')
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[port_settings])
ip = '10.55.0.101'
mac_addr = '0a:1b:2c:3d:4e:5f'
sub_settings = self.net_config.network_settings.subnet_settings
- port_settings = PortSettings(
+ port_settings = PortConfig(
name=self.port_1_name,
network_name=self.net_config.network_settings.name,
mac_address=mac_addr,
ip_addrs=[{'subnet_name': sub_settings[0].name, 'ip': ip}])
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[port_settings])
ip = '10.55.0.101'
mac_addr = '0a:1b:2c:3d:4e:5f'
pair = {'ip_address': ip, 'mac_address': mac_addr}
- port_settings = PortSettings(
+ port_settings = PortConfig(
name=self.port_1_name,
network_name=self.net_config.network_settings.name,
allowed_address_pairs=[pair])
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[port_settings])
pair = {'ip_address': ip, 'mac_address': mac_addr}
pairs = set()
pairs.add((ip, mac_addr))
- port_settings = PortSettings(
+ port_settings = PortConfig(
name=self.port_1_name,
network_name=self.net_config.network_settings.name,
allowed_address_pairs=[pair])
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[port_settings])
pair = {'ip_address': ip, 'mac_address': mac_addr}
pairs = set()
pairs.add((ip, mac_addr))
- port_settings = PortSettings(
+ port_settings = PortConfig(
name=self.port_1_name,
network_name=self.net_config.network_settings.name,
allowed_address_pairs=[pair])
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[port_settings])
self.port_base_name = guid + 'port'
# Initialize for tearDown()
- self.image_creator = None
- self.flavor_creator = None
- self.network_creator = None
- self.inst_creators = list()
-
- self.priv_net_config = openstack_tests.get_priv_net_config(
- net_name=guid + '-priv-net', subnet_name=guid + '-priv-subnet')
-
- os_image_settings = openstack_tests.cirros_image_settings(
- name=guid + '-image', image_metadata=self.image_metadata)
-
- try:
- # Create Network
- self.network_creator = OpenStackNetwork(
- self.admin_os_creds, self.priv_net_config.network_settings)
- self.network_creator.create()
-
- # Create Flavor
- self.flavor_creator = OpenStackFlavor(
- self.admin_os_creds,
- FlavorSettings(name=guid + '-flavor-name', ram=512, disk=1,
- vcpus=1, metadata=self.flavor_metadata))
- self.flavor_creator.create()
-
- # Create Image
- self.image_creator = OpenStackImage(self.os_creds,
- os_image_settings)
- self.image_creator.create()
-
- except Exception as e:
- self.tearDown()
- raise e
-
- def tearDown(self):
- """
- Cleans the created object
- """
- for inst_creator in self.inst_creators:
- try:
- inst_creator.clean()
- except Exception as e:
- logger.error(
- 'Unexpected exception cleaning VM instance with message '
- '- %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.network_creator:
- try:
- self.network_creator.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()
- except Exception as e:
- logger.error(
- 'Unexpected exception cleaning image with message - %s', e)
-
- super(self.__class__, self).__clean__()
-
- def test_deploy_vm_to_each_compute_node(self):
- """
- Tests the creation of OpenStack VM instances to each compute node.
- """
- from snaps.openstack.utils import nova_utils
- nova = nova_utils.nova_client(self.admin_os_creds)
- zone_hosts = nova_utils.get_availability_zone_hosts(nova)
-
- # Create Instance on each server/zone
- ctr = 0
- for zone in zone_hosts:
- inst_name = self.vm_inst_name + '-' + zone
- ctr += 1
- port_settings = PortSettings(
- name=self.port_base_name + '-' + str(ctr),
- network_name=self.priv_net_config.network_settings.name)
-
- instance_settings = VmInstanceSettings(
- name=inst_name,
- flavor=self.flavor_creator.flavor_settings.name,
- availability_zone=zone,
- port_settings=[port_settings])
- inst_creator = OpenStackVmInstance(
- self.admin_os_creds, instance_settings,
- self.image_creator.image_settings)
- self.inst_creators.append(inst_creator)
- inst_creator.create()
-
- # Validate instances to ensure they've been deployed to the correct
- # server
- index = 0
- for zone in zone_hosts:
- creator = self.inst_creators[index]
- self.assertTrue(creator.vm_active(block=True))
- info = creator.get_vm_info()
- deployed_zone = info['OS-EXT-AZ:availability_zone']
- deployed_host = info['OS-EXT-SRV-ATTR:host']
- self.assertEqual(zone, deployed_zone + ':' + deployed_host)
- index += 1
-
-
-class CreateInstancePubPrivNetTests(OSIntegrationTestCase):
- """
- Test for the CreateInstance class with two NIC/Ports, eth0 with floating IP
- and eth1 w/o.
- These tests require a Centos image
- """
-
- def setUp(self):
- """
- Instantiates the CreateImage object that is responsible for downloading
- and creating an OS image file within OpenStack
- """
- super(self.__class__, self).__start__()
-
- self.nova = nova_utils.nova_client(self.os_creds)
-
- # Initialize for tearDown()
- self.image_creator = None
- self.network_creators = list()
- self.router_creators = list()
- self.flavor_creator = None
- self.keypair_creator = None
- self.sec_grp_creator = None
- self.inst_creator = None
-
- self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
- self.keypair_priv_filepath = 'tmp/' + self.guid
- self.keypair_pub_filepath = self.keypair_priv_filepath + '.pub'
- self.keypair_name = self.guid + '-kp'
- self.vm_inst_name = self.guid + '-inst'
- self.port_1_name = self.guid + '-port-1'
- self.port_2_name = self.guid + '-port-2'
- self.floating_ip_name = self.guid + 'fip1'
- self.priv_net_config = openstack_tests.get_priv_net_config(
- net_name=self.guid + '-priv-net',
- subnet_name=self.guid + '-priv-subnet',
- router_name=self.guid + '-priv-router',
- external_net=self.ext_net_name)
- self.pub_net_config = openstack_tests.get_pub_net_config(
- net_name=self.guid + '-pub-net',
- subnet_name=self.guid + '-pub-subnet',
- router_name=self.guid + '-pub-router',
- external_net=self.ext_net_name)
-
- image_name = self.__class__.__name__ + '-' + str(uuid.uuid4())
- os_image_settings = openstack_tests.centos_image_settings(
- name=image_name, image_metadata=self.image_metadata)
-
- try:
- # Create Image
- self.image_creator = OpenStackImage(self.os_creds,
- os_image_settings)
- self.image_creator.create()
+ self.image_creator = None
+ self.flavor_creator = None
+ self.network_creator = None
+ self.inst_creators = list()
- # First network is public
- self.network_creators.append(OpenStackNetwork(
- self.os_creds, self.pub_net_config.network_settings))
- # Second network is private
- self.network_creators.append(OpenStackNetwork(
- self.os_creds, self.priv_net_config.network_settings))
- for network_creator in self.network_creators:
- network_creator.create()
+ self.priv_net_config = openstack_tests.get_priv_net_config(
+ net_name=guid + '-priv-net', subnet_name=guid + '-priv-subnet',
+ netconf_override=self.netconf_override)
- self.router_creators.append(OpenStackRouter(
- self.os_creds, self.pub_net_config.router_settings))
- self.router_creators.append(OpenStackRouter(
- self.os_creds, self.priv_net_config.router_settings))
+ os_image_settings = openstack_tests.cirros_image_settings(
+ name=guid + '-image', image_metadata=self.image_metadata)
- # Create Routers
- for router_creator in self.router_creators:
- router_creator.create()
+ try:
+ # Create Network
+ self.network_creator = OpenStackNetwork(
+ self.admin_os_creds, self.priv_net_config.network_settings)
+ self.network_creator.create()
# Create Flavor
self.flavor_creator = OpenStackFlavor(
self.admin_os_creds,
- FlavorSettings(name=self.guid + '-flavor-name', ram=512,
- disk=10, vcpus=2,
- metadata=self.flavor_metadata))
+ FlavorConfig(name=guid + '-flavor-name', ram=512, disk=1,
+ vcpus=1, metadata=self.flavor_metadata))
self.flavor_creator.create()
- # Create Keypair
- self.keypair_creator = OpenStackKeypair(
- self.os_creds, KeypairSettings(
- name=self.keypair_name,
- public_filepath=self.keypair_pub_filepath,
- private_filepath=self.keypair_priv_filepath))
- self.keypair_creator.create()
+ # Create Image
+ self.image_creator = OpenStackImage(self.os_creds,
+ os_image_settings)
+ self.image_creator.create()
- sec_grp_name = self.guid + '-sec-grp'
- rule1 = SecurityGroupRuleSettings(sec_grp_name=sec_grp_name,
- direction=Direction.ingress,
- protocol=Protocol.icmp)
- rule2 = SecurityGroupRuleSettings(sec_grp_name=sec_grp_name,
- direction=Direction.ingress,
- protocol=Protocol.tcp,
- port_range_min=22,
- port_range_max=22)
- self.sec_grp_creator = OpenStackSecurityGroup(
- self.os_creds,
- SecurityGroupSettings(name=sec_grp_name,
- rule_settings=[rule1, rule2]))
- self.sec_grp_creator.create()
- except:
+ except Exception as e:
self.tearDown()
- raise
+ raise e
def tearDown(self):
"""
- Cleans the created objects
+ Cleans the created object
"""
- if self.inst_creator:
+ for inst_creator in self.inst_creators:
try:
- self.inst_creator.clean()
+ inst_creator.clean()
except Exception as e:
logger.error(
'Unexpected exception cleaning VM instance with message '
'- %s', e)
- if self.keypair_creator:
- try:
- self.keypair_creator.clean()
- except Exception as e:
- logger.error(
- 'Unexpected exception cleaning keypair with message - %s',
- e)
-
- if os.path.isfile(self.keypair_pub_filepath):
- os.remove(self.keypair_pub_filepath)
-
- if os.path.isfile(self.keypair_priv_filepath):
- os.remove(self.keypair_priv_filepath)
-
if self.flavor_creator:
try:
self.flavor_creator.clean()
'Unexpected exception cleaning flavor with message - %s',
e)
- for router_creator in self.router_creators:
- try:
- router_creator.clean()
- except Exception as e:
- logger.error(
- 'Unexpected exception cleaning router with message - %s',
- e)
-
- for network_creator in self.network_creators:
+ if self.network_creator:
try:
- network_creator.clean()
+ self.network_creator.clean()
except Exception as e:
logger.error(
'Unexpected exception cleaning network with message - %s',
e)
- if self.sec_grp_creator:
- try:
- self.sec_grp_creator.clean()
- except Exception as e:
- logger.error(
- 'Unexpected exception cleaning security group with message'
- ' - %s', e)
-
if self.image_creator and not self.image_creator.image_settings.exists:
try:
self.image_creator.clean()
super(self.__class__, self).__clean__()
- def test_dual_ports_dhcp(self):
+ def test_deploy_vm_to_each_compute_node(self):
"""
- Tests the creation of an OpenStack instance with a dual ports/NICs with
- a DHCP assigned IP.
- NOTE: This test and any others that call ansible will most likely fail
- unless you do one of two things:
- 1. Have a ~/.ansible.cfg (or alternate means) to
- set host_key_checking = False
- 2. Set the following environment variable in your executing shell:
- ANSIBLE_HOST_KEY_CHECKING=False
- Should this not be performed, the creation of the host ssh key will
- cause your ansible calls to fail.
+ Tests the creation of OpenStack VM instances to each compute node.
"""
- # Create ports/NICs for instance
- ports_settings = []
- ctr = 1
- for network_creator in self.network_creators:
- ports_settings.append(PortSettings(
- name=self.guid + '-port-' + str(ctr),
- network_name=network_creator.network_settings.name))
- ctr += 1
-
- # Create instance
- instance_settings = VmInstanceSettings(
- name=self.vm_inst_name,
- flavor=self.flavor_creator.flavor_settings.name,
- port_settings=ports_settings,
- floating_ip_settings=[FloatingIpSettings(
- name=self.floating_ip_name, port_name=self.port_1_name,
- router_name=self.pub_net_config.router_settings.name)])
-
- self.inst_creator = OpenStackVmInstance(
- self.os_creds, instance_settings,
- self.image_creator.image_settings,
- keypair_settings=self.keypair_creator.keypair_settings)
-
- vm_inst = self.inst_creator.create(block=True)
-
- self.assertEqual(vm_inst.id, self.inst_creator.get_vm_inst().id)
-
- # Effectively blocks until VM has been properly activated
- self.assertTrue(self.inst_creator.vm_active(block=True))
-
- ip = self.inst_creator.get_port_ip(ports_settings[0].name)
- self.assertTrue(check_dhcp_lease(self.inst_creator, ip))
+ from snaps.openstack.utils import nova_utils
+ nova = nova_utils.nova_client(self.admin_os_creds)
+ zone_hosts = nova_utils.get_availability_zone_hosts(nova)
- # Add security group to VM
- self.inst_creator.add_security_group(
- self.sec_grp_creator.get_security_group())
+ # Create Instance on each server/zone
+ ctr = 0
+ for zone in zone_hosts:
+ inst_name = self.vm_inst_name + '-' + zone
+ ctr += 1
+ port_settings = PortConfig(
+ name=self.port_base_name + '-' + str(ctr),
+ network_name=self.priv_net_config.network_settings.name)
- # Effectively blocks until VM's ssh port has been opened
- self.assertTrue(self.inst_creator.vm_ssh_active(block=True))
+ instance_settings = VmInstanceConfig(
+ name=inst_name,
+ flavor=self.flavor_creator.flavor_settings.name,
+ availability_zone=zone,
+ port_settings=[port_settings])
+ inst_creator = OpenStackVmInstance(
+ self.admin_os_creds, instance_settings,
+ self.image_creator.image_settings)
+ self.inst_creators.append(inst_creator)
+ inst_creator.create()
- self.assertEqual(0, self.inst_creator.config_nics())
+ # Validate instances to ensure they've been deployed to the correct
+ # server
+ index = 0
+ for zone in zone_hosts:
+ creator = self.inst_creators[index]
+ self.assertTrue(creator.vm_active(block=True))
+ info = creator.get_vm_info()
+ deployed_zone = info['OS-EXT-AZ:availability_zone']
+ deployed_host = info['OS-EXT-SRV-ATTR:host']
+ self.assertEqual(zone, deployed_zone + ':' + deployed_host)
+ index += 1
class InstanceSecurityGroupTests(OSIntegrationTestCase):
net_name=self.guid + '-pub-net',
subnet_name=self.guid + '-pub-subnet',
router_name=self.guid + '-pub-router',
- external_net=self.ext_net_name)
+ external_net=self.ext_net_name,
+ netconf_override=self.netconf_override)
# Initialize for tearDown()
self.image_creator = None
# Create Flavor
self.flavor_creator = OpenStackFlavor(
self.admin_os_creds,
- FlavorSettings(name=self.guid + '-flavor-name', ram=256,
- disk=10, vcpus=2,
- metadata=self.flavor_metadata))
+ FlavorConfig(name=self.guid + '-flavor-name', ram=256,
+ disk=10, vcpus=2,
+ metadata=self.flavor_metadata))
self.flavor_creator.create()
- self.port_settings = PortSettings(
+ self.port_settings = PortConfig(
name=self.guid + '-port',
network_name=net_config.network_settings.name)
except Exception as e:
Tests the addition of a security group created after the instance.
"""
# Create instance
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[self.port_settings])
self.assertIsNotNone(vm_inst)
# Create security group object to add to instance
- sec_grp_settings = SecurityGroupSettings(name=self.guid + '-name',
- description='hello group')
+ sec_grp_settings = SecurityGroupConfig(
+ name=self.guid + '-name', description='hello group')
sec_grp_creator = OpenStackSecurityGroup(self.os_creds,
sec_grp_settings)
sec_grp = sec_grp_creator.create()
Tests the addition of a security group that no longer exists.
"""
# Create instance
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[self.port_settings])
self.assertIsNotNone(vm_inst)
# Create security group object to add to instance
- sec_grp_settings = SecurityGroupSettings(name=self.guid + '-name',
- description='hello group')
+ sec_grp_settings = SecurityGroupConfig(
+ name=self.guid + '-name', description='hello group')
sec_grp_creator = OpenStackSecurityGroup(self.os_creds,
sec_grp_settings)
sec_grp = sec_grp_creator.create()
instance.
"""
# Create security group object to add to instance
- sec_grp_settings = SecurityGroupSettings(name=self.guid + '-name',
- description='hello group')
+ sec_grp_settings = SecurityGroupConfig(
+ name=self.guid + '-name', description='hello group')
sec_grp_creator = OpenStackSecurityGroup(self.os_creds,
sec_grp_settings)
sec_grp = sec_grp_creator.create()
self.sec_grp_creators.append(sec_grp_creator)
# Create instance
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
security_group_names=[sec_grp_settings.name],
place.
"""
# Create security group object to add to instance
- sec_grp_settings = SecurityGroupSettings(name=self.guid + '-name',
- description='hello group')
+ sec_grp_settings = SecurityGroupConfig(
+ name=self.guid + '-name', description='hello group')
sec_grp_creator = OpenStackSecurityGroup(self.os_creds,
sec_grp_settings)
sec_grp = sec_grp_creator.create()
self.sec_grp_creators.append(sec_grp_creator)
# Create instance
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[self.port_settings])
instance.
"""
# Create security group object to add to instance
- sec_grp_settings = SecurityGroupSettings(name=self.guid + '-name',
- description='hello group')
+ sec_grp_settings = SecurityGroupConfig(
+ name=self.guid + '-name', description='hello group')
sec_grp_creator = OpenStackSecurityGroup(self.os_creds,
sec_grp_settings)
sec_grp = sec_grp_creator.create()
self.sec_grp_creators.append(sec_grp_creator)
# Create instance
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
security_group_names=[sec_grp_settings.name],
return False
-def validate_ssh_client(instance_creator):
+def validate_ssh_client(instance_creator, fip_name=None):
"""
Returns True if instance_creator returns an SSH client that is valid
:param instance_creator: the object responsible for creating the VM
instance
+ :param fip_name: the name of the floating IP to use
:return: T/F
"""
ssh_active = instance_creator.vm_ssh_active(block=True)
if ssh_active:
- ssh_client = instance_creator.ssh_client()
+ ssh_client = instance_creator.ssh_client(fip_name=fip_name)
if ssh_client:
try:
out = ssh_client.exec_command('pwd')[1]
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)
+ router_name=guid + '-pub-router', external_net=self.ext_net_name,
+ netconf_override=self.netconf_override)
# Initialize for tearDown()
self.image_creator = None
# Create Flavor
self.flavor_creator = OpenStackFlavor(
self.admin_os_creds,
- FlavorSettings(name=guid + '-flavor-name', ram=256, disk=10,
- vcpus=2, metadata=self.flavor_metadata))
+ FlavorConfig(name=guid + '-flavor-name', ram=256, disk=10,
+ vcpus=2, metadata=self.flavor_metadata))
self.flavor_creator.create()
# Create Network
self.os_creds, net_config.network_settings)
self.network_creator.create()
- self.port_settings = PortSettings(
+ self.port_settings = PortConfig(
name=guid + '-port',
network_name=net_config.network_settings.name)
except Exception as e:
"""
Tests the creation of an OpenStack instance from a 3-part image.
"""
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[self.port_settings])
self.priv_net_config = openstack_tests.get_priv_net_config(
net_name=self.guid + '-priv-net',
subnet_name=self.guid + '-priv-subnet')
- self.port_settings = PortSettings(
+ self.port_settings = PortConfig(
name=self.port_1_name,
network_name=self.priv_net_config.network_settings.name)
# Create Flavor
self.flavor_creator = OpenStackFlavor(
self.os_creds,
- FlavorSettings(
+ FlavorConfig(
name=self.guid + '-flavor-name', ram=256, disk=10,
vcpus=1))
self.flavor_creator.create()
self.image_creator = OpenStackImage(self.os_creds, os_image_settings)
self.image_creator.create()
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[self.port_settings])
self.image_creator = OpenStackImage(self.os_creds, os_image_settings)
self.image_creator.create()
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[self.port_settings])
image_settings = self.image_creator.image_settings
test_image_creator = OpenStackImage(
self.os_creds,
- ImageSettings(name=image_settings.name,
- image_user=image_settings.image_user,
- exists=True))
+ ImageConfig(
+ name=image_settings.name, image_user=image_settings.image_user,
+ exists=True))
test_image_creator.create()
self.assertEqual(self.image_creator.get_image().id,
test_image_creator.get_image().id)
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[self.port_settings])
test_image = OpenStackImage(self.os_creds, test_image_settings)
test_image.create()
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[self.port_settings])
self.image_creator = OpenStackImage(self.os_creds, os_image_settings)
self.image_creator.create()
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[self.port_settings])
self.assertIsNotNone(self.image_creator.get_kernel_image())
self.assertIsNotNone(self.image_creator.get_ramdisk_image())
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[self.port_settings])
self.assertIsNotNone(self.image_creator.get_kernel_image())
self.assertIsNotNone(self.image_creator.get_ramdisk_image())
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[self.port_settings])
image_settings = self.image_creator.image_settings
test_image_creator = OpenStackImage(
self.os_creds,
- ImageSettings(name=image_settings.name,
- image_user=image_settings.image_user,
- exists=True))
+ ImageConfig(
+ name=image_settings.name, image_user=image_settings.image_user,
+ exists=True))
test_image_creator.create()
self.assertEqual(self.image_creator.get_image().id,
test_image_creator.get_image().id)
- instance_settings = VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[self.port_settings])
self.vm_inst2_name = self.guid + '-inst2'
self.port_1_name = self.guid + '-vm1-port'
self.port_2_name = self.guid + '-vm2-port'
- self.net_config_1 = NetworkSettings(
+ self.net_config_1 = NetworkConfig(
name=self.guid + '-net1',
subnet_settings=[
- create_network.SubnetSettings(
+ create_network.SubnetConfig(
cidr=cidr1, name=self.guid + '-subnet1',
gateway_ip=static_gateway_ip1)])
- self.net_config_2 = NetworkSettings(
+ self.net_config_2 = NetworkConfig(
name=self.guid + '-net2',
subnet_settings=[
- create_network.SubnetSettings(
+ create_network.SubnetConfig(
cidr=cidr2, name=self.guid + '-subnet2',
gateway_ip=static_gateway_ip2)])
network_creator.create()
port_settings = [
- create_network.PortSettings(
+ create_network.PortConfig(
name=self.guid + '-router-port1',
ip_addrs=[{
'subnet_name':
}],
network_name=self.net_config_1.name,
project_name=self.os_creds.project_name),
- create_network.PortSettings(
+ create_network.PortConfig(
name=self.guid + '-router-port2',
ip_addrs=[{
'subnet_name':
network_name=self.net_config_2.name,
project_name=self.os_creds.project_name)]
- router_settings = RouterSettings(name=self.guid + '-pub-router',
- port_settings=port_settings)
+ 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()
# Create Flavor
self.flavor_creator = OpenStackFlavor(
self.admin_os_creds,
- FlavorSettings(name=self.guid + '-flavor-name', ram=512,
- disk=10, vcpus=2,
- metadata=self.flavor_metadata))
+ FlavorConfig(name=self.guid + '-flavor-name', ram=512,
+ disk=10, vcpus=2,
+ metadata=self.flavor_metadata))
self.flavor_creator.create()
sec_grp_name = self.guid + '-sec-grp'
- rule1 = SecurityGroupRuleSettings(sec_grp_name=sec_grp_name,
- direction=Direction.ingress,
- protocol=Protocol.icmp)
+ rule1 = SecurityGroupRuleConfig(
+ sec_grp_name=sec_grp_name, direction=Direction.ingress,
+ protocol=Protocol.icmp)
self.sec_grp_creator = OpenStackSecurityGroup(
self.os_creds,
- SecurityGroupSettings(name=sec_grp_name,
- rule_settings=[rule1]))
+ SecurityGroupConfig(
+ name=sec_grp_name, rule_settings=[rule1]))
self.sec_grp_creator.create()
except:
self.tearDown()
ports_settings = []
ctr = 1
for network_creator in self.network_creators:
- ports_settings.append(PortSettings(
+ ports_settings.append(PortConfig(
name=self.guid + '-port-' + str(ctr),
network_name=network_creator.network_settings.name))
ctr += 1
# Configure instances
- instance1_settings = VmInstanceSettings(
+ instance1_settings = VmInstanceConfig(
name=self.vm_inst1_name,
flavor=self.flavor_creator.flavor_settings.name,
userdata=_get_ping_userdata(self.ip2),
- port_settings=[PortSettings(
+ port_settings=[PortConfig(
name=self.port_1_name,
ip_addrs=[{
'subnet_name':
'ip': self.ip1
}],
network_name=self.network_creators[0].network_settings.name)])
- instance2_settings = VmInstanceSettings(
+ instance2_settings = VmInstanceConfig(
name=self.vm_inst2_name,
flavor=self.flavor_creator.flavor_settings.name,
userdata=_get_ping_userdata(self.ip1),
- port_settings=[PortSettings(
+ port_settings=[PortConfig(
name=self.port_2_name,
ip_addrs=[{
'subnet_name':
self.assertTrue(check_ping(self.inst_creators[1]))
+class CreateInstanceVolumeTests(OSIntegrationTestCase):
+ """
+ Simple instance creation with an attached volume
+ """
+
+ def setUp(self):
+ """
+ Instantiates the CreateImage object that is responsible for downloading
+ and creating an OS image file
+ within OpenStack
+ """
+ super(self.__class__, self).__start__()
+
+ guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+ self.vm_inst_name = guid + '-inst'
+ self.nova = nova_utils.nova_client(self.os_creds)
+ self.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)
+
+ self.volume_settings1 = VolumeConfig(
+ name=self.__class__.__name__ + '-' + str(guid) + '-1')
+ self.volume_settings2 = VolumeConfig(
+ name=self.__class__.__name__ + '-' + str(guid) + '-2')
+
+ # Initialize for tearDown()
+ self.image_creator = None
+ self.flavor_creator = None
+
+ self.network_creator = None
+ self.inst_creator = None
+ self.volume_creator1 = None
+ self.volume_creator2 = None
+
+ try:
+ # Create Image
+ self.image_creator = OpenStackImage(self.os_creds,
+ os_image_settings)
+ self.image_creator.create()
+
+ # Create Flavor
+ self.flavor_creator = OpenStackFlavor(
+ self.admin_os_creds,
+ FlavorConfig(name=guid + '-flavor-name', ram=256, disk=1,
+ vcpus=2, metadata=self.flavor_metadata))
+ 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.volume_creator1 = OpenStackVolume(
+ self.os_creds, self.volume_settings1)
+ self.volume_creator1.create(block=True)
+
+ self.volume_creator2 = OpenStackVolume(
+ self.os_creds, self.volume_settings2)
+ self.volume_creator2.create(block=True)
+
+ 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.network_creator:
+ try:
+ self.network_creator.clean()
+ except Exception as e:
+ logger.error(
+ 'Unexpected exception cleaning network with message - %s',
+ e)
+
+ if self.volume_creator2:
+ try:
+ self.volume_creator2.clean()
+ except Exception as e:
+ logger.error(
+ 'Unexpected exception cleaning volume with message - %s',
+ e)
+
+ if self.volume_creator1:
+ try:
+ self.volume_creator1.clean()
+ except Exception as e:
+ logger.error(
+ 'Unexpected exception cleaning volume 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_with_one_volume(self):
+ """
+ Tests the creation of an OpenStack instance with a single volume.
+ """
+ instance_settings = VmInstanceConfig(
+ name=self.vm_inst_name,
+ flavor=self.flavor_creator.flavor_settings.name,
+ port_settings=[self.port_settings],
+ volume_names=[self.volume_settings1.name])
+
+ self.inst_creator = OpenStackVmInstance(
+ self.os_creds, instance_settings,
+ self.image_creator.image_settings)
+
+ vm_inst = self.inst_creator.create(block=True)
+ self.assertIsNotNone(nova_utils.get_server(
+ self.nova, self.neutron, vm_inst_settings=instance_settings))
+
+ self.assertIsNotNone(vm_inst)
+ self.assertEqual(1, len(vm_inst.volume_ids))
+ self.assertEqual(self.volume_creator1.get_volume().id,
+ vm_inst.volume_ids[0]['id'])
+
+ def test_create_instance_with_two_volumes(self):
+ """
+ Tests the creation of an OpenStack instance with a single volume.
+ """
+ instance_settings = VmInstanceConfig(
+ name=self.vm_inst_name,
+ flavor=self.flavor_creator.flavor_settings.name,
+ port_settings=[self.port_settings],
+ volume_names=[self.volume_settings1.name,
+ self.volume_settings2.name])
+
+ self.inst_creator = OpenStackVmInstance(
+ self.os_creds, instance_settings,
+ self.image_creator.image_settings)
+
+ vm_inst = self.inst_creator.create(block=True)
+ self.assertIsNotNone(nova_utils.get_server(
+ self.nova, self.neutron, vm_inst_settings=instance_settings))
+
+ self.assertIsNotNone(vm_inst)
+ self.assertEqual(2, len(vm_inst.volume_ids))
+ self.assertEqual(self.volume_creator1.get_volume().id,
+ vm_inst.volume_ids[0]['id'])
+ self.assertEqual(self.volume_creator2.get_volume().id,
+ vm_inst.volume_ids[1]['id'])
+
+
def check_dhcp_lease(inst_creator, ip, timeout=160):
"""
Returns true if the expected DHCP lease has been acquired
import os
from snaps import file_utils
+from snaps.config.keypair import KeypairConfig, KeypairConfigError
from snaps.openstack.create_keypairs import (
- KeypairSettings, OpenStackKeypair, KeypairSettingsError)
+ KeypairSettings, OpenStackKeypair)
from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase
from snaps.openstack.utils import nova_utils
"""
def test_no_params(self):
- with self.assertRaises(KeypairSettingsError):
+ with self.assertRaises(KeypairConfigError):
KeypairSettings()
def test_empty_config(self):
- with self.assertRaises(KeypairSettingsError):
+ with self.assertRaises(KeypairConfigError):
KeypairSettings(**dict())
def test_small_key_size(self):
- with self.assertRaises(KeypairSettingsError):
+ with self.assertRaises(KeypairConfigError):
KeypairSettings(name='foo', key_size=511)
def test_name_only(self):
Tests the creation of a generated keypair without saving to file
:return:
"""
- self.keypair_creator = OpenStackKeypair(self.os_creds, KeypairSettings(
+ self.keypair_creator = OpenStackKeypair(self.os_creds, KeypairConfig(
name=self.keypair_name))
self.keypair_creator.create()
Tests the creation of a generated keypair without saving to file
:return:
"""
- self.keypair_creator = OpenStackKeypair(self.os_creds, KeypairSettings(
+ self.keypair_creator = OpenStackKeypair(self.os_creds, KeypairConfig(
name=self.keypair_name, key_size=10000))
self.keypair_creator.create()
clean() does not raise an Exception.
"""
# Create Image
- self.keypair_creator = OpenStackKeypair(self.os_creds, KeypairSettings(
+ self.keypair_creator = OpenStackKeypair(self.os_creds, KeypairConfig(
name=self.keypair_name))
created_keypair = self.keypair_creator.create()
self.assertIsNotNone(created_keypair)
:return:
"""
self.keypair_creator = OpenStackKeypair(
- self.os_creds, KeypairSettings(name=self.keypair_name,
- public_filepath=self.pub_file_path))
+ self.os_creds, KeypairConfig(
+ name=self.keypair_name, public_filepath=self.pub_file_path))
self.keypair_creator.create()
keypair = nova_utils.keypair_exists(self.nova,
:return:
"""
self.keypair_creator = OpenStackKeypair(
- self.os_creds, KeypairSettings(
+ self.os_creds, KeypairConfig(
name=self.keypair_name, public_filepath=self.pub_file_path,
private_filepath=self.priv_file_path))
self.keypair_creator.create()
file_utils.save_keys_to_files(keys=keys,
pub_file_path=self.pub_file_path)
self.keypair_creator = OpenStackKeypair(
- self.os_creds, KeypairSettings(name=self.keypair_name,
- public_filepath=self.pub_file_path))
+ self.os_creds, KeypairConfig(
+ name=self.keypair_name, public_filepath=self.pub_file_path))
self.keypair_creator.create()
keypair = nova_utils.keypair_exists(self.nova,
:return:
"""
self.keypair_creator = OpenStackKeypair(
- self.os_creds, KeypairSettings(
+ self.os_creds, KeypairConfig(
name=self.keypair_name, public_filepath=self.pub_file_path,
private_filepath=self.priv_file_path))
self.keypair_creator.create()
:return:
"""
self.keypair_creator = OpenStackKeypair(
- self.os_creds, KeypairSettings(
+ self.os_creds, KeypairConfig(
name=self.keypair_name, public_filepath=self.pub_file_path,
private_filepath=self.priv_file_path, delete_on_clean=True))
self.keypair_creator.create()
:return:
"""
self.keypair_creator = OpenStackKeypair(
- self.os_creds, KeypairSettings(
+ self.os_creds, KeypairConfig(
name=self.keypair_name, public_filepath=self.pub_file_path,
private_filepath=self.priv_file_path, delete_on_clean=False))
self.keypair_creator.create()
keys=keys, pub_file_path=self.pub_file_path,
priv_file_path=self.priv_file_path)
self.keypair_creator = OpenStackKeypair(
- self.os_creds, KeypairSettings(
+ self.os_creds, KeypairConfig(
name=self.keypair_name, public_filepath=self.pub_file_path,
private_filepath=self.priv_file_path, delete_on_clean=False))
self.keypair_creator.create()
keys=keys, pub_file_path=self.pub_file_path,
priv_file_path=self.priv_file_path)
self.keypair_creator = OpenStackKeypair(
- self.os_creds, KeypairSettings(
+ self.os_creds, KeypairConfig(
name=self.keypair_name, public_filepath=self.pub_file_path,
private_filepath=self.priv_file_path, delete_on_clean=True))
self.keypair_creator.create()
import unittest
import uuid
+from snaps.config.network import (
+ NetworkConfig, SubnetConfig, SubnetConfigError, NetworkConfigError,
+ PortConfigError, IPv6Mode)
from snaps.openstack import create_router
-from snaps.openstack.create_network import (OpenStackNetwork, NetworkSettings,
- SubnetSettings, PortSettings,
- NetworkSettingsError,
- SubnetSettingsError,
- PortSettingsError)
+from snaps.openstack.create_network import (
+ OpenStackNetwork, NetworkSettings, SubnetSettings, PortSettings)
from snaps.openstack.tests import openstack_tests
-from snaps.openstack.tests.os_source_file_test import (OSIntegrationTestCase,
- OSComponentTestCase)
+from snaps.openstack.tests.os_source_file_test import (
+ OSIntegrationTestCase, OSComponentTestCase)
from snaps.openstack.utils import neutron_utils
from snaps.openstack.utils.tests import neutron_utils_tests
+from snaps.openstack.create_network import IPv6Mode as IPv6Mode_old
__author__ = 'spisarski'
"""
def test_no_params(self):
- with self.assertRaises(NetworkSettingsError):
+ with self.assertRaises(NetworkConfigError):
NetworkSettings()
def test_empty_config(self):
- with self.assertRaises(NetworkSettingsError):
+ with self.assertRaises(NetworkConfigError):
NetworkSettings(**dict())
def test_name_only(self):
"""
def test_no_params(self):
- with self.assertRaises(SubnetSettingsError):
+ with self.assertRaises(SubnetConfigError):
SubnetSettings()
def test_empty_config(self):
- with self.assertRaises(SubnetSettingsError):
+ with self.assertRaises(SubnetConfigError):
SubnetSettings(**dict())
def test_name_only(self):
- with self.assertRaises(SubnetSettingsError):
+ with self.assertRaises(SubnetConfigError):
SubnetSettings(name='foo')
def test_config_with_name_only(self):
- with self.assertRaises(SubnetSettingsError):
+ with self.assertRaises(SubnetConfigError):
SubnetSettings(**{'name': 'foo'})
def test_name_cidr_only(self):
self.assertIsNone(settings.ipv6_ra_mode)
self.assertIsNone(settings.ipv6_address_mode)
- def test_all(self):
+ def test_all_string_enums(self):
+ host_routes = {'destination': '0.0.0.0/0', 'nexthop': '123.456.78.9'}
+ settings = SubnetSettings(
+ name='foo', cidr='10.0.0.0/24', ip_version=6,
+ project_name='bar-project', start='10.0.0.2', end='10.0.0.101',
+ gateway_ip='10.0.0.1', enable_dhcp=False,
+ dns_nameservers=['8.8.8.8'], host_routes=[host_routes],
+ destination='dest', nexthop='hop', ipv6_ra_mode='dhcpv6-stateful',
+ ipv6_address_mode='slaac')
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('10.0.0.0/24', settings.cidr)
+ self.assertEqual(6, settings.ip_version)
+ self.assertEqual('bar-project', settings.project_name)
+ self.assertEqual('10.0.0.2', settings.start)
+ self.assertEqual('10.0.0.101', settings.end)
+ self.assertEqual('10.0.0.1', settings.gateway_ip)
+ self.assertEqual(False, settings.enable_dhcp)
+ self.assertEqual(1, len(settings.dns_nameservers))
+ self.assertEqual('8.8.8.8', settings.dns_nameservers[0])
+ self.assertEqual(1, len(settings.host_routes))
+ self.assertEqual(host_routes, settings.host_routes[0])
+ self.assertEqual('dest', settings.destination)
+ self.assertEqual('hop', settings.nexthop)
+ self.assertEqual(IPv6Mode_old.stateful.value,
+ settings.ipv6_ra_mode.value)
+ self.assertEqual(IPv6Mode_old.slaac.value,
+ settings.ipv6_address_mode.value)
+
+ def test_all_type_enums(self):
host_routes = {'destination': '0.0.0.0/0', 'nexthop': '123.456.78.9'}
- settings = SubnetSettings(name='foo', cidr='10.0.0.0/24', ip_version=6,
- project_name='bar-project',
- start='10.0.0.2', end='10.0.0.101',
- gateway_ip='10.0.0.1', enable_dhcp=False,
- dns_nameservers=['8.8.8.8'],
- host_routes=[host_routes],
- destination='dest',
- nexthop='hop',
- ipv6_ra_mode='dhcpv6-stateful',
- ipv6_address_mode='slaac')
+ settings = SubnetSettings(
+ name='foo', cidr='10.0.0.0/24', ip_version=6,
+ project_name='bar-project', start='10.0.0.2', end='10.0.0.101',
+ gateway_ip='10.0.0.1', enable_dhcp=False,
+ dns_nameservers=['8.8.8.8'], host_routes=[host_routes],
+ destination='dest', nexthop='hop',
+ ipv6_ra_mode=IPv6Mode_old.stateful,
+ ipv6_address_mode=IPv6Mode.slaac)
self.assertEqual('foo', settings.name)
self.assertEqual('10.0.0.0/24', settings.cidr)
self.assertEqual(6, settings.ip_version)
self.assertEqual(host_routes, settings.host_routes[0])
self.assertEqual('dest', settings.destination)
self.assertEqual('hop', settings.nexthop)
- self.assertEqual('dhcpv6-stateful', settings.ipv6_ra_mode)
- self.assertEqual('slaac', settings.ipv6_address_mode)
+ self.assertEqual(IPv6Mode.stateful.value, settings.ipv6_ra_mode.value)
+ self.assertEqual(IPv6Mode.slaac.value,
+ settings.ipv6_address_mode.value)
def test_config_all(self):
host_routes = {'destination': '0.0.0.0/0', 'nexthop': '123.456.78.9'}
'gateway_ip': '10.0.0.1', 'enable_dhcp': False,
'dns_nameservers': ['8.8.8.8'], 'host_routes': [host_routes],
'destination': 'dest', 'nexthop': 'hop',
- 'ipv6_ra_mode': 'dhcpv6-stateful',
+ 'ipv6_ra_mode': 'dhcpv6-stateless',
'ipv6_address_mode': 'slaac'})
self.assertEqual('foo', settings.name)
self.assertEqual('10.0.0.0/24', settings.cidr)
self.assertEqual(host_routes, settings.host_routes[0])
self.assertEqual('dest', settings.destination)
self.assertEqual('hop', settings.nexthop)
- self.assertEqual('dhcpv6-stateful', settings.ipv6_ra_mode)
- self.assertEqual('slaac', settings.ipv6_address_mode)
+ self.assertEqual(IPv6Mode.stateless.value, settings.ipv6_ra_mode.value)
+ self.assertEqual(IPv6Mode.slaac.value,
+ settings.ipv6_address_mode.value)
class PortSettingsUnitTests(unittest.TestCase):
"""
def test_no_params(self):
- with self.assertRaises(PortSettingsError):
+ with self.assertRaises(PortConfigError):
PortSettings()
def test_empty_config(self):
- with self.assertRaises(PortSettingsError):
+ with self.assertRaises(PortConfigError):
PortSettings(**dict())
def test_name_only(self):
- with self.assertRaises(PortSettingsError):
+ with self.assertRaises(PortConfigError):
PortSettings(name='foo')
def test_config_name_only(self):
- with self.assertRaises(PortSettingsError):
+ with self.assertRaises(PortConfigError):
PortSettings(**{'name': 'foo'})
def test_name_netname_only(self):
self.assertIsNone(settings.project_name)
self.assertIsNone(settings.mac_address)
self.assertIsNone(settings.ip_addrs)
- self.assertIsNone(settings.fixed_ips)
self.assertIsNone(settings.security_groups)
self.assertIsNone(settings.allowed_address_pairs)
self.assertIsNone(settings.opt_value)
self.assertIsNone(settings.project_name)
self.assertIsNone(settings.mac_address)
self.assertIsNone(settings.ip_addrs)
- self.assertIsNone(settings.fixed_ips)
self.assertIsNone(settings.security_groups)
self.assertIsNone(settings.allowed_address_pairs)
self.assertIsNone(settings.opt_value)
def test_all(self):
ip_addrs = [{'subnet_name', 'foo-sub', 'ip', '10.0.0.10'}]
- fixed_ips = {'sub_id', '10.0.0.10'}
allowed_address_pairs = {'10.0.0.101', '1234.5678'}
settings = PortSettings(name='foo', network_name='bar',
admin_state_up=False,
project_name='foo-project',
mac_address='1234', ip_addrs=ip_addrs,
- fixed_ips=fixed_ips,
security_groups=['foo_grp_id'],
allowed_address_pairs=allowed_address_pairs,
opt_value='opt value', opt_name='opt name',
self.assertEqual('foo-project', settings.project_name)
self.assertEqual('1234', settings.mac_address)
self.assertEqual(ip_addrs, settings.ip_addrs)
- self.assertEqual(fixed_ips, settings.fixed_ips)
self.assertEqual(1, len(settings.security_groups))
self.assertEqual('foo_grp_id', settings.security_groups[0])
self.assertEqual(allowed_address_pairs, settings.allowed_address_pairs)
def test_config_all(self):
ip_addrs = [{'subnet_name', 'foo-sub', 'ip', '10.0.0.10'}]
- fixed_ips = {'sub_id', '10.0.0.10'}
allowed_address_pairs = {'10.0.0.101', '1234.5678'}
settings = PortSettings(
**{'name': 'foo', 'network_name': 'bar', 'admin_state_up': False,
'project_name': 'foo-project', 'mac_address': '1234',
- 'ip_addrs': ip_addrs,
- 'fixed_ips': fixed_ips, 'security_groups': ['foo_grp_id'],
+ 'ip_addrs': ip_addrs, 'security_groups': ['foo_grp_id'],
'allowed_address_pairs': allowed_address_pairs,
- 'opt_value': 'opt value',
- 'opt_name': 'opt name', 'device_owner': 'owner',
- 'device_id': 'device number'})
+ 'opt_value': 'opt value', 'opt_name': 'opt name',
+ 'device_owner': 'owner', 'device_id': 'device number'})
self.assertEqual('foo', settings.name)
self.assertEqual('bar', settings.network_name)
self.assertFalse(settings.admin_state_up)
self.assertEqual('foo-project', settings.project_name)
self.assertEqual('1234', settings.mac_address)
self.assertEqual(ip_addrs, settings.ip_addrs)
- self.assertEqual(fixed_ips, settings.fixed_ips)
self.assertEqual(1, len(settings.security_groups))
self.assertEqual('foo_grp_id', settings.security_groups[0])
self.assertEqual(allowed_address_pairs, settings.allowed_address_pairs)
class CreateNetworkSuccessTests(OSIntegrationTestCase):
"""
- Test for the CreateNework class defined in create_nework.py
+ Test for the CreateNetwork class defined in create_nework.py
"""
def setUp(self):
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)
+ router_name=guid + '-pub-router', external_net=self.ext_net_name,
+ netconf_override=self.netconf_override)
self.neutron = neutron_utils.neutron_client(self.os_creds)
# Initialize for cleanup
self.net_creator = None
self.router_creator = None
- self.neutron = neutron_utils.neutron_client(self.os_creds)
def tearDown(self):
"""
"""
Tests the creation of an OpenStack network without a router.
"""
- # Create Nework
+ # Create Network
self.net_creator = OpenStackNetwork(self.os_creds,
self.net_config.network_settings)
self.net_creator.create()
"""
Tests the creation of an OpenStack network, it's deletion, then cleanup
"""
- # Create Nework
+ # Create Network
self.net_creator = OpenStackNetwork(self.os_creds,
self.net_config.network_settings)
self.net_creator.create()
neutron_utils_tests.validate_interface_router(
self.router_creator.get_internal_router_interface(),
self.router_creator.get_router(),
- self.net_creator.get_subnets()[0])
+ self.net_creator.get_network().subnets[0])
def test_create_networks_same_name(self):
"""
Tests the creation of an OpenStack network and ensures that the
OpenStackNetwork object will not create a second.
"""
- # Create Nework
+ # Create Network
self.net_creator = OpenStackNetwork(self.os_creds,
self.net_config.network_settings)
self.net_creator.create()
self.router_creator.get_router().id, retrieved_router.id)
+class CreateNetworkIPv6Tests(OSIntegrationTestCase):
+ """
+ Test for the CreateNetwork class defined in create_nework.py when
+ """
+
+ 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)
+
+ # 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_network_one_ipv6_subnet(self):
+ """
+ Tests the creation of an OpenStack network without a router.
+ """
+ # Create Network
+ subnet_settings = SubnetConfig(
+ name=self.guid + '-subnet', cidr='1:1:0:0:0:0:0:0/64',
+ ip_version=6)
+ network_settings = NetworkConfig(
+ name=self.guid + '-net', subnet_settings=[subnet_settings])
+
+ self.net_creator = OpenStackNetwork(self.os_creds, 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))
+
+ network = self.net_creator.get_network()
+ self.assertEqual(1, len(network.subnets))
+ subnet = network.subnets[0]
+
+ self.assertEqual(network.id, subnet.network_id)
+ self.assertEqual(subnet_settings.name, subnet.name)
+ self.assertEqual('1:1::/64', subnet.cidr)
+ self.assertEqual(6, subnet.ip_version)
+ self.assertEqual(0, len(subnet.dns_nameservers))
+
+ def test_create_network_ipv4_ipv6_subnet(self):
+ """
+ Tests the creation of an OpenStack network without a router.
+ """
+ # Create Network
+ subnet4_settings = SubnetConfig(
+ name=self.guid + '-subnet4', cidr='10.0.1.0/24', ip_version=4)
+ subnet6_settings = SubnetConfig(
+ name=self.guid + '-subnet6', cidr='1:1:0:0:0:0:0:0/64',
+ ip_version=6)
+
+ network_settings = NetworkConfig(
+ name=self.guid + '-net',
+ subnet_settings=[subnet4_settings, subnet6_settings])
+
+ self.net_creator = OpenStackNetwork(self.os_creds, network_settings)
+ self.net_creator.create()
+
+ # Validate network was created
+ network = self.net_creator.get_network()
+ self.assertEqual(2, len(network.subnets))
+
+ subnet4 = None
+ subnet6 = None
+ for subnet in network.subnets:
+ if subnet.name == subnet4_settings.name:
+ subnet4 = subnet
+ if subnet.name == subnet6_settings.name:
+ subnet6 = subnet
+
+ # Validate IPv4 subnet
+ self.assertEqual(network.id, subnet4.network_id)
+ 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))
+
+ # Validate IPv6 subnet
+ self.assertEqual(network.id, subnet6.network_id)
+ self.assertEqual(subnet6_settings.name, subnet6.name)
+ self.assertEqual('1:1::/64', subnet6.cidr)
+ self.assertEqual(6, subnet6.ip_version)
+ self.assertEqual(0, len(subnet6.dns_nameservers))
+
+
class CreateNetworkTypeTests(OSComponentTestCase):
"""
- Test for the CreateNework class defined in create_nework.py for testing
+ Test for the CreateNetwork class defined in create_nework.py for testing
creating networks of different types
"""
# Initialize for cleanup
self.net_creator = None
- self.neutron = neutron_utils.neutron_client(self.os_creds)
def tearDown(self):
"""
"""
# Create Network
network_type = 'vlan'
- net_settings = NetworkSettings(
+ net_settings = NetworkConfig(
name=self.net_config.network_settings.name,
subnet_settings=self.net_config.network_settings.subnet_settings,
network_type=network_type)
physical_network = 'datacentre'
segmentation_id = 2366
- net_settings = NetworkSettings(
+ net_settings = NetworkConfig(
name=self.net_config.network_settings.name,
subnet_settings=self.net_config.network_settings.subnet_settings,
network_type=network_type,
"""
# Create Network
network_type = 'vxlan'
- net_settings = NetworkSettings(
+ net_settings = NetworkConfig(
name=self.net_config.network_settings.name,
subnet_settings=self.net_config.network_settings.subnet_settings,
network_type=network_type)
# This value must be variable to work on all OpenStack pods
physical_network = 'datacentre'
- net_settings = NetworkSettings(
+ net_settings = NetworkConfig(
name=self.net_config.network_settings.name,
subnet_settings=self.net_config.network_settings.subnet_settings,
network_type=network_type, physical_network=physical_network)
"""
# Create Network
network_type = 'foo'
- net_settings = NetworkSettings(
+ net_settings = NetworkConfig(
name=self.net_config.network_settings.name,
subnet_settings=self.net_config.network_settings.subnet_settings,
network_type=network_type)
from keystoneclient.exceptions import BadRequest
+from snaps.config.security_group import SecurityGroupConfig
+from snaps.config.user import UserConfig
+from snaps.config.project import ProjectConfigError, ProjectConfig
from snaps.domain.project import ComputeQuotas, NetworkQuotas
from snaps.openstack.create_project import (
- OpenStackProject, ProjectSettings, ProjectSettingsError)
+ OpenStackProject, ProjectSettings)
from snaps.openstack.create_security_group import OpenStackSecurityGroup
-from snaps.openstack.create_security_group import SecurityGroupSettings
from snaps.openstack.create_user import OpenStackUser
-from snaps.openstack.create_user import UserSettings
from snaps.openstack.tests.os_source_file_test import OSComponentTestCase
from snaps.openstack.utils import keystone_utils, nova_utils, neutron_utils
"""
def test_no_params(self):
- with self.assertRaises(ProjectSettingsError):
+ with self.assertRaises(ProjectConfigError):
ProjectSettings()
def test_empty_config(self):
- with self.assertRaises(ProjectSettingsError):
+ with self.assertRaises(ProjectConfigError):
ProjectSettings(**dict())
def test_name_only(self):
"""
guid = str(uuid.uuid4())[:-19]
guid = self.__class__.__name__ + '-' + guid
- self.project_settings = ProjectSettings(
+ self.project_settings = ProjectConfig(
name=guid + '-name',
domain=self.os_creds.project_domain_name)
"""
guid = str(uuid.uuid4())[:-19]
self.guid = self.__class__.__name__ + '-' + guid
- self.project_settings = ProjectSettings(
+ self.project_settings = ProjectConfig(
name=self.guid + '-name',
domain=self.os_creds.project_domain_name)
self.assertIsNotNone(created_project)
user_creator = OpenStackUser(
- self.os_creds, UserSettings(
+ self.os_creds, UserConfig(
name=self.guid + '-user',
- password=self.guid, roles={'admin': self.project_settings.name},
+ password=self.guid,
+ roles={'admin': self.project_settings.name},
domain_name=self.os_creds.user_domain_name))
self.project_creator.assoc_user(user_creator.create())
self.user_creators.append(user_creator)
sec_grp_os_creds = user_creator.get_os_creds(
self.project_creator.get_project().name)
sec_grp_creator = OpenStackSecurityGroup(
- sec_grp_os_creds, SecurityGroupSettings(name=self.guid + '-name',
- description='hello group'))
+ sec_grp_os_creds, SecurityGroupConfig(
+ name=self.guid + '-name', description='hello group'))
sec_grp = sec_grp_creator.create()
self.assertIsNotNone(sec_grp)
self.sec_grp_creators.append(sec_grp_creator)
self.assertIsNotNone(created_project)
user_creator_1 = OpenStackUser(
- self.os_creds, UserSettings(
+ self.os_creds, UserConfig(
name=self.guid + '-user1', password=self.guid,
roles={'admin': self.project_settings.name},
domain_name=self.os_creds.user_domain_name))
self.user_creators.append(user_creator_1)
user_creator_2 = OpenStackUser(
- self.os_creds, UserSettings(
+ self.os_creds, UserConfig(
name=self.guid + '-user2', password=self.guid,
roles={'admin': self.project_settings.name},
domain_name=self.os_creds.user_domain_name))
sec_grp_creator = OpenStackSecurityGroup(
sec_grp_os_creds,
- SecurityGroupSettings(name=self.guid + '-name',
- description='hello group'))
+ SecurityGroupConfig(
+ name=self.guid + '-name', description='hello group'))
sec_grp = sec_grp_creator.create()
self.assertIsNotNone(sec_grp)
self.sec_grp_creators.append(sec_grp_creator)
# 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 snaps.config.qos import Consumer, QoSConfigError, QoSConfig
+from snaps.openstack.create_qos import Consumer as Consumer_old
try:
from urllib.request import URLError
import uuid
from snaps.openstack import create_qos
-from snaps.openstack.create_qos import (QoSSettings, QoSSettingsError,
- Consumer)
+from snaps.openstack.create_qos import QoSSettings
from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase
from snaps.openstack.utils import cinder_utils
"""
def test_no_params(self):
- with self.assertRaises(QoSSettingsError):
+ with self.assertRaises(QoSConfigError):
QoSSettings()
def test_empty_config(self):
- with self.assertRaises(QoSSettingsError):
+ with self.assertRaises(QoSConfigError):
QoSSettings(**dict())
def test_name_only(self):
- with self.assertRaises(QoSSettingsError):
+ with self.assertRaises(QoSConfigError):
QoSSettings(name='foo')
def test_config_with_name_only(self):
- with self.assertRaises(QoSSettingsError):
+ with self.assertRaises(QoSConfigError):
QoSSettings(**{'name': 'foo'})
def test_invalid_consumer(self):
- with self.assertRaises(QoSSettingsError):
+ with self.assertRaises(QoSConfigError):
QoSSettings(name='foo', consumer='bar')
def test_config_with_invalid_consumer(self):
- with self.assertRaises(QoSSettingsError):
+ with self.assertRaises(QoSConfigError):
QoSSettings(**{'name': 'foo', 'consumer': 'bar'})
def test_name_consumer(self):
- settings = QoSSettings(name='foo', consumer=Consumer.front_end)
+ settings = QoSSettings(name='foo', consumer=Consumer_old.front_end)
self.assertEqual('foo', settings.name)
- self.assertEqual(Consumer.front_end, settings.consumer)
+ self.assertEqual(Consumer_old.front_end.value, settings.consumer.value)
self.assertEqual(dict(), settings.specs)
def test_name_consumer_front_end_strings(self):
settings = QoSSettings(name='foo', consumer='front-end')
self.assertEqual('foo', settings.name)
- self.assertEqual(Consumer.front_end, settings.consumer)
+ self.assertEqual(Consumer_old.front_end.value, settings.consumer.value)
self.assertEqual(dict(), settings.specs)
def test_name_consumer_back_end_strings(self):
settings = QoSSettings(name='foo', consumer='back-end')
self.assertEqual('foo', settings.name)
- self.assertEqual(Consumer.back_end, settings.consumer)
+ self.assertEqual(Consumer_old.back_end.value, settings.consumer.value)
self.assertEqual(dict(), settings.specs)
def test_name_consumer_both_strings(self):
settings = QoSSettings(name='foo', consumer='both')
self.assertEqual('foo', settings.name)
- self.assertEqual(Consumer.both, settings.consumer)
+ self.assertEqual(Consumer_old.both.value, settings.consumer.value)
self.assertEqual(dict(), settings.specs)
def test_all(self):
specs = {'spec1': 'val1', 'spec2': 'val2'}
- settings = QoSSettings(name='foo', consumer=Consumer.both,
+ settings = QoSSettings(name='foo', consumer=Consumer_old.both,
specs=specs)
self.assertEqual('foo', settings.name)
- self.assertEqual(Consumer.both, settings.consumer)
+ self.assertEqual(Consumer_old.both.value, settings.consumer.value)
self.assertEqual(specs, settings.specs)
def test_config_all(self):
super(self.__class__, self).__start__()
guid = uuid.uuid4()
- self.qos_settings = QoSSettings(
+ self.qos_settings = QoSConfig(
name=self.__class__.__name__ + '-' + str(guid),
consumer=Consumer.both)
import unittest
import uuid
+from snaps.config.network import PortConfig, NetworkConfig
+from snaps.config.router import RouterConfigError, RouterConfig
from snaps.openstack import create_network
from snaps.openstack import create_router
-from snaps.openstack.create_network import (
- NetworkSettings, PortSettings)
from snaps.openstack.create_network import OpenStackNetwork
-from snaps.openstack.create_router import (
- RouterSettings, RouterSettingsError)
+from snaps.openstack.create_router import RouterSettings
from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase
-from snaps.openstack.utils import neutron_utils
+from snaps.openstack.utils import neutron_utils, settings_utils
__author__ = 'mmakati'
"""
def test_no_params(self):
- with self.assertRaises(RouterSettingsError):
+ with self.assertRaises(RouterConfigError):
RouterSettings()
def test_empty_config(self):
- with self.assertRaises(RouterSettingsError):
+ with self.assertRaises(RouterConfigError):
RouterSettings(**dict())
def test_name_only(self):
self.assertEqual('foo', settings.name)
self.assertIsNone(settings.project_name)
self.assertIsNone(settings.external_gateway)
- self.assertIsNone(settings.admin_state_up)
+ self.assertTrue(settings.admin_state_up)
self.assertIsNone(settings.enable_snat)
- self.assertIsNone(settings.external_fixed_ips)
self.assertIsNotNone(settings.internal_subnets)
self.assertTrue(isinstance(settings.internal_subnets, list))
self.assertEqual(0, len(settings.internal_subnets))
self.assertEqual('foo', settings.name)
self.assertIsNone(settings.project_name)
self.assertIsNone(settings.external_gateway)
- self.assertIsNone(settings.admin_state_up)
+ self.assertTrue(settings.admin_state_up)
self.assertIsNone(settings.enable_snat)
- self.assertIsNone(settings.external_fixed_ips)
self.assertIsNotNone(settings.internal_subnets)
self.assertTrue(isinstance(settings.internal_subnets, list))
self.assertEqual(0, len(settings.internal_subnets))
self.assertEqual(0, len(settings.port_settings))
def test_all(self):
- port_settings = PortSettings(name='foo', network_name='bar')
+ port_settings = PortConfig(name='foo', network_name='bar')
settings = RouterSettings(
name='foo', project_name='bar', external_gateway='foo_gateway',
- admin_state_up=True, enable_snat=False, external_fixed_ips=['ip1'],
+ admin_state_up=True, enable_snat=False,
internal_subnets=['10.0.0.1/24'], 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.assertEqual(['ip1'], settings.external_fixed_ips)
self.assertIsNotNone(settings.internal_subnets)
self.assertTrue(isinstance(settings.internal_subnets, list))
self.assertEqual(1, len(settings.internal_subnets))
settings = RouterSettings(
**{'name': 'foo', 'project_name': 'bar',
'external_gateway': 'foo_gateway', 'admin_state_up': True,
- 'enable_snat': False, 'external_fixed_ips': ['ip1'],
- 'internal_subnets': ['10.0.0.1/24'],
+ 'enable_snat': False, 'internal_subnets': ['10.0.0.1/24'],
'interfaces':
[{'port': {'name': 'foo-port',
'network_name': 'bar-net'}}]})
self.assertEqual('foo_gateway', settings.external_gateway)
self.assertTrue(settings.admin_state_up)
self.assertFalse(settings.enable_snat)
- self.assertEqual(['ip1'], settings.external_fixed_ips)
self.assertIsNotNone(settings.internal_subnets)
self.assertTrue(isinstance(settings.internal_subnets, list))
self.assertEqual(1, len(settings.internal_subnets))
self.assertEqual(['10.0.0.1/24'], settings.internal_subnets)
- self.assertEqual([PortSettings(**{'name': 'foo-port',
- 'network_name': 'bar-net'})],
- settings.port_settings)
+ self.assertEqual(
+ [PortConfig(**{'name': 'foo-port', 'network_name': 'bar-net'})],
+ settings.port_settings)
class CreateRouterSuccessTests(OSIntegrationTestCase):
"""
Test creation of a most basic router with minimal options.
"""
- router_settings = RouterSettings(name=self.guid + '-pub-router',
- external_gateway=self.ext_net_name)
+ 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)
router_settings=router_settings)
self.assertIsNotNone(router)
- self.assertTrue(verify_router_attributes(
- router, self.router_creator, ext_gateway=self.ext_net_name))
+ self.assertEqual(self.router_creator.get_router(), router)
+
+ self.check_router_recreation(router, router_settings)
def test_create_router_admin_user_to_new_project(self):
"""
Test creation of a most basic router with the admin user pointing
to the new project.
"""
- router_settings = RouterSettings(
+ router_settings = RouterConfig(
name=self.guid + '-pub-router', external_gateway=self.ext_net_name,
project_name=self.os_creds.project_name)
router_settings=router_settings)
self.assertIsNotNone(router)
- self.assertTrue(verify_router_attributes(
- router, self.router_creator, ext_gateway=self.ext_net_name))
+ self.assertEqual(self.router_creator.get_router(), router)
+
+ self.check_router_recreation(router, router_settings)
def test_create_router_new_user_to_admin_project(self):
"""
Test creation of a most basic router with the new user pointing
to the admin project.
"""
- router_settings = RouterSettings(
+ router_settings = RouterConfig(
name=self.guid + '-pub-router', external_gateway=self.ext_net_name,
project_name=self.admin_os_creds.project_name)
router_settings=router_settings)
self.assertIsNotNone(router)
- self.assertTrue(verify_router_attributes(
- router, self.router_creator, ext_gateway=self.ext_net_name))
+ self.assertEqual(self.router_creator.get_router(), router)
+
+ self.check_router_recreation(router, router_settings)
def test_create_delete_router(self):
"""
Test that clean() will not raise an exception if the router is deleted
by another process.
"""
- self.router_settings = RouterSettings(
+ self.router_settings = RouterConfig(
name=self.guid + '-pub-router', external_gateway=self.ext_net_name)
self.router_creator = create_router.OpenStackRouter(
"""
Test creation of a basic router with admin state down.
"""
- router_settings = RouterSettings(name=self.guid + '-pub-router',
- admin_state_up=False)
+ router_settings = RouterConfig(
+ name=self.guid + '-pub-router', admin_state_up=False)
self.router_creator = create_router.OpenStackRouter(self.os_creds,
router_settings)
router_settings=router_settings)
self.assertIsNotNone(router)
- self.assertTrue(verify_router_attributes(router, self.router_creator,
- admin_state=False))
+ self.assertEqual(self.router_creator.get_router(), router)
+
+ self.check_router_recreation(router, router_settings)
def test_create_router_admin_state_True(self):
"""
Test creation of a basic router with admin state Up.
"""
- router_settings = RouterSettings(name=self.guid + '-pub-router',
- admin_state_up=True)
+ router_settings = RouterConfig(
+ name=self.guid + '-pub-router', admin_state_up=True)
- self.router_creator = create_router.OpenStackRouter(self.os_creds,
- router_settings)
+ self.router_creator = create_router.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, router_settings=router_settings)
self.assertIsNotNone(router)
- self.assertTrue(verify_router_attributes(router, self.router_creator,
- admin_state=True))
+ self.assertEqual(self.router_creator.get_router(), router)
+
+ self.check_router_recreation(router, router_settings)
def test_create_router_private_network(self):
"""
Test creation of a router connected with two private networks and no
external gateway
"""
- network_settings1 = NetworkSettings(
+ network_settings1 = NetworkConfig(
name=self.guid + '-pub-net1',
subnet_settings=[
- create_network.SubnetSettings(
+ create_network.SubnetConfig(
cidr=cidr1, name=self.guid + '-pub-subnet1',
gateway_ip=static_gateway_ip1)])
- network_settings2 = NetworkSettings(
+ network_settings2 = NetworkConfig(
name=self.guid + '-pub-net2',
subnet_settings=[
- create_network.SubnetSettings(
+ create_network.SubnetConfig(
cidr=cidr2, name=self.guid + '-pub-subnet2',
gateway_ip=static_gateway_ip2)])
self.network_creator2.create()
port_settings = [
- create_network.PortSettings(
+ create_network.PortConfig(
name=self.guid + '-port1',
ip_addrs=[{
'subnet_name':
}],
network_name=network_settings1.name,
project_name=self.os_creds.project_name),
- create_network.PortSettings(
+ create_network.PortConfig(
name=self.guid + '-port2',
ip_addrs=[{
'subnet_name': network_settings2.subnet_settings[0].name,
network_name=network_settings2.name,
project_name=self.os_creds.project_name)]
- router_settings = RouterSettings(name=self.guid + '-pub-router',
- port_settings=port_settings)
+ 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 = neutron_utils.get_router(self.neutron,
- router_settings=router_settings)
+ router = neutron_utils.get_router(
+ self.neutron, router_settings=router_settings)
- self.assertTrue(verify_router_attributes(router, self.router_creator))
+ self.assertEqual(router, self.router_creator.get_router())
# Instantiate second identical creator to ensure a second router
# has not been created
router2 = router_creator2.create()
self.assertIsNotNone(self.router_creator.get_router(), router2)
+ self.check_router_recreation(router2, router_settings)
+
def test_create_router_external_network(self):
"""
Test creation of a router connected to an external network and a
private network.
"""
- network_settings = NetworkSettings(
+ network_settings = NetworkConfig(
name=self.guid + '-pub-net1',
subnet_settings=[
- create_network.SubnetSettings(
+ create_network.SubnetConfig(
cidr=cidr1, name=self.guid + '-pub-subnet1',
gateway_ip=static_gateway_ip1)])
self.network_creator1 = OpenStackNetwork(self.os_creds,
self.network_creator1.create()
port_settings = [
- create_network.PortSettings(
+ create_network.PortConfig(
name=self.guid + '-port1',
ip_addrs=[{
'subnet_name': network_settings.subnet_settings[0].name,
network_name=network_settings.name,
project_name=self.os_creds.project_name)]
- router_settings = RouterSettings(
+ 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_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, router_settings=router_settings)
+
+ self.assertEquals(router, self.router_creator.get_router())
- self.assertTrue(verify_router_attributes(
- router, self.router_creator, ext_gateway=self.ext_net_name))
+ self.check_router_recreation(router, router_settings)
+
+ def check_router_recreation(self, router, orig_settings):
+ """
+ Validates the derived RouterConfig with the original
+ :param router: the Router domain object to test
+ :param orig_settings: the original RouterConfig object that was
+ responsible for creating the router
+ :return: the derived RouterConfig object
+ """
+ derived_settings = settings_utils.create_router_config(
+ self.neutron, router)
+ self.assertIsNotNone(derived_settings)
+ self.assertEqual(
+ orig_settings.enable_snat, derived_settings.enable_snat)
+ self.assertEqual(orig_settings.external_gateway,
+ derived_settings.external_gateway)
+ self.assertEqual(orig_settings.name, derived_settings.name)
+ self.assertEqual(orig_settings.internal_subnets,
+ derived_settings.internal_subnets)
+
+ if orig_settings.external_gateway:
+ self.assertEqual(len(orig_settings.port_settings),
+ len(derived_settings.port_settings))
+ else:
+ self.assertEqual(len(orig_settings.port_settings),
+ len(derived_settings.port_settings))
+
+ if len(orig_settings.port_settings) > 0:
+ self.assertEqual(orig_settings.port_settings[0].name,
+ derived_settings.port_settings[0].name)
+
+ if len(orig_settings.port_settings) > 1:
+ self.assertEqual(orig_settings.port_settings[1].name,
+ derived_settings.port_settings[1].name)
+
+ return derived_settings
class CreateRouterNegativeTests(OSIntegrationTestCase):
"""
Test creating a router without a name.
"""
- with self.assertRaises(RouterSettingsError):
- router_settings = RouterSettings(
+ with self.assertRaises(RouterConfigError):
+ router_settings = RouterConfig(
name=None, external_gateway=self.ext_net_name)
self.router_creator = create_router.OpenStackRouter(
self.os_creds, router_settings)
"""
Test creating a router without a valid network gateway name.
"""
- with self.assertRaises(RouterSettingsError):
- router_settings = RouterSettings(name=self.guid + '-pub-router',
- external_gateway="Invalid_name")
+ with self.assertRaises(RouterConfigError):
+ router_settings = RouterConfig(
+ name=self.guid + '-pub-router',
+ external_gateway="Invalid_name")
self.router_creator = create_router.OpenStackRouter(
self.os_creds, router_settings)
self.router_creator.create()
-
-
-def verify_router_attributes(router_operational, router_creator,
- admin_state=True, ext_gateway=None):
- """
- Helper function to validate the attributes of router created with the one
- operational
- :param router_operational: Operational Router object returned from neutron
- utils of type snaps.domain.Router
- :param router_creator: router_creator object returned from creating a
- router in the router test functions
- :param admin_state: True if router is expected to be Up, else False
- :param ext_gateway: None if router is not connected to external gateway
- :return:
- """
-
- router = router_creator.get_router()
-
- if not router_operational:
- return False
- elif not router_creator:
- return False
- elif not (router_operational.name == router_creator.router_settings.name):
- return False
- elif not (router_operational.id == router.id):
- return False
- elif not (router_operational.status == router.status):
- return False
- elif not (router_operational.tenant_id == router.tenant_id):
- return False
- elif not (admin_state == router_operational.admin_state_up):
- return False
- elif (ext_gateway is None) and \
- (router_operational.external_gateway_info is not None):
- return False
- elif ext_gateway is not None:
- if router_operational.external_gateway_info is None:
- return False
- return True
import unittest
import uuid
+from snaps.config.security_group import (
+ SecurityGroupConfig, SecurityGroupRuleConfig,
+ SecurityGroupRuleConfigError, SecurityGroupConfigError)
from snaps.openstack import create_security_group
from snaps.openstack.create_security_group import (
SecurityGroupSettings, SecurityGroupRuleSettings, Direction, Ethertype,
- Protocol, SecurityGroupRuleSettingsError, SecurityGroupSettingsError)
+ Protocol)
from snaps.openstack.tests import validation_utils
from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase
from snaps.openstack.utils import neutron_utils
"""
def test_no_params(self):
- with self.assertRaises(SecurityGroupRuleSettingsError):
+ with self.assertRaises(SecurityGroupRuleConfigError):
SecurityGroupRuleSettings()
def test_empty_config(self):
- with self.assertRaises(SecurityGroupRuleSettingsError):
+ with self.assertRaises(SecurityGroupRuleConfigError):
SecurityGroupRuleSettings(**dict())
def test_name_only(self):
- with self.assertRaises(SecurityGroupRuleSettingsError):
+ with self.assertRaises(SecurityGroupRuleConfigError):
SecurityGroupRuleSettings(sec_grp_name='foo')
def test_config_with_name_only(self):
- with self.assertRaises(SecurityGroupRuleSettingsError):
+ with self.assertRaises(SecurityGroupRuleConfigError):
SecurityGroupRuleSettings(**{'sec_grp_name': 'foo'})
def test_name_and_direction(self):
settings = SecurityGroupRuleSettings(sec_grp_name='foo',
direction=Direction.ingress)
self.assertEqual('foo', settings.sec_grp_name)
- self.assertEqual(Direction.ingress, settings.direction)
+ self.assertEqual(Direction.ingress.value, settings.direction.value)
def test_config_name_and_direction(self):
settings = SecurityGroupRuleSettings(
**{'sec_grp_name': 'foo', 'direction': 'ingress'})
self.assertEqual('foo', settings.sec_grp_name)
- self.assertEqual(Direction.ingress, settings.direction)
+ self.assertEqual(Direction.ingress.value, settings.direction.value)
+
+ def test_proto_ah_str(self):
+ settings = SecurityGroupRuleSettings(
+ **{'sec_grp_name': 'foo', 'direction': 'ingress',
+ 'protocol': 'ah'})
+ self.assertEqual('foo', settings.sec_grp_name)
+ self.assertEqual(Direction.ingress.value, settings.direction.value)
+ self.assertEqual(Protocol.ah.value, settings.protocol.value)
+
+ def test_proto_ah_value(self):
+ settings = SecurityGroupRuleSettings(
+ **{'sec_grp_name': 'foo', 'direction': 'ingress',
+ 'protocol': 51})
+ self.assertEqual('foo', settings.sec_grp_name)
+ self.assertEqual(Direction.ingress.value, settings.direction.value)
+ self.assertEqual(Protocol.ah.value, settings.protocol.value)
+
+ def test_proto_any(self):
+ settings = SecurityGroupRuleSettings(
+ **{'sec_grp_name': 'foo', 'direction': 'ingress',
+ 'protocol': 'any'})
+ self.assertEqual('foo', settings.sec_grp_name)
+ self.assertEqual(Direction.ingress.value, settings.direction.value)
+ self.assertEqual(Protocol.null.value, settings.protocol.value)
+
+ def test_proto_null(self):
+ settings = SecurityGroupRuleSettings(
+ **{'sec_grp_name': 'foo', 'direction': 'ingress',
+ 'protocol': 'null'})
+ self.assertEqual('foo', settings.sec_grp_name)
+ self.assertEqual(Direction.ingress.value, settings.direction.value)
+ self.assertEqual(Protocol.null.value, settings.protocol.value)
def test_all(self):
settings = SecurityGroupRuleSettings(
remote_ip_prefix='prfx')
self.assertEqual('foo', settings.sec_grp_name)
self.assertEqual('fubar', settings.description)
- self.assertEqual(Direction.egress, settings.direction)
+ self.assertEqual(Direction.egress.value, settings.direction.value)
self.assertEqual('rgi', settings.remote_group_id)
- self.assertEqual(Protocol.icmp, settings.protocol)
- self.assertEqual(Ethertype.IPv6, settings.ethertype)
+ self.assertEqual(Protocol.icmp.value, settings.protocol.value)
+ self.assertEqual(Ethertype.IPv6.value, settings.ethertype.value)
self.assertEqual(1, settings.port_range_min)
self.assertEqual(2, settings.port_range_max)
self.assertEqual('prfx', settings.remote_ip_prefix)
'remote_ip_prefix': 'prfx'})
self.assertEqual('foo', settings.sec_grp_name)
self.assertEqual('fubar', settings.description)
- self.assertEqual(Direction.egress, settings.direction)
+ self.assertEqual(Direction.egress.value, settings.direction.value)
self.assertEqual('rgi', settings.remote_group_id)
- self.assertEqual(Protocol.tcp, settings.protocol)
- self.assertEqual(Ethertype.IPv6, settings.ethertype)
+ self.assertEqual(Protocol.tcp.value, settings.protocol.value)
+ self.assertEqual(Ethertype.IPv6.value, settings.ethertype.value)
self.assertEqual(1, settings.port_range_min)
self.assertEqual(2, settings.port_range_max)
self.assertEqual('prfx', settings.remote_ip_prefix)
"""
def test_no_params(self):
- with self.assertRaises(SecurityGroupSettingsError):
+ with self.assertRaises(SecurityGroupConfigError):
SecurityGroupSettings()
def test_empty_config(self):
- with self.assertRaises(SecurityGroupSettingsError):
+ with self.assertRaises(SecurityGroupConfigError):
SecurityGroupSettings(**dict())
def test_name_only(self):
rule_setting = SecurityGroupRuleSettings(
sec_grp_name='bar', direction=Direction.ingress,
description='test_rule_1')
- with self.assertRaises(SecurityGroupSettingsError):
+ with self.assertRaises(SecurityGroupConfigError):
SecurityGroupSettings(name='foo', rule_settings=[rule_setting])
def test_all(self):
self.assertEqual('foo', settings.project_name)
self.assertEqual(1, len(settings.rule_settings))
self.assertEqual('bar', settings.rule_settings[0].sec_grp_name)
- self.assertEqual(Direction.ingress,
- settings.rule_settings[0].direction)
+ self.assertEqual(Direction.ingress.value,
+ settings.rule_settings[0].direction.value)
class CreateSecurityGroupTests(OSIntegrationTestCase):
Tests the creation of an OpenStack Security Group without custom rules.
"""
# Create Image
- sec_grp_settings = SecurityGroupSettings(name=self.sec_grp_name,
- description='hello group')
+ sec_grp_settings = SecurityGroupConfig(name=self.sec_grp_name,
+ description='hello group')
self.sec_grp_creator = create_security_group.OpenStackSecurityGroup(
self.os_creds, sec_grp_settings)
self.sec_grp_creator.create()
Tests the creation of an OpenStack Security Group without custom rules.
"""
# Create Image
- sec_grp_settings = SecurityGroupSettings(
+ 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(
Tests the creation of an OpenStack Security Group without custom rules.
"""
# Create Image
- sec_grp_settings = SecurityGroupSettings(
+ sec_grp_settings = SecurityGroupConfig(
name=self.sec_grp_name, description='hello group',
project_name=self.os_creds.project_name)
self.sec_grp_creator = create_security_group.OpenStackSecurityGroup(
Tests the creation of an OpenStack Security Group without custom rules.
"""
# Create Image
- sec_grp_settings = SecurityGroupSettings(name=self.sec_grp_name,
- description='hello group')
+ sec_grp_settings = SecurityGroupConfig(name=self.sec_grp_name,
+ description='hello group')
self.sec_grp_creator = create_security_group.OpenStackSecurityGroup(
self.os_creds, sec_grp_settings)
created_sec_grp = self.sec_grp_creator.create()
# Create Image
sec_grp_rule_settings = list()
sec_grp_rule_settings.append(
- SecurityGroupRuleSettings(
+ SecurityGroupRuleConfig(
sec_grp_name=self.sec_grp_name, direction=Direction.ingress,
description='test_rule_1'))
- sec_grp_settings = SecurityGroupSettings(
+ sec_grp_settings = SecurityGroupConfig(
name=self.sec_grp_name, description='hello group',
rule_settings=sec_grp_rule_settings)
self.sec_grp_creator = create_security_group.OpenStackSecurityGroup(
# Create Image
sec_grp_rule_settings = list()
sec_grp_rule_settings.append(
- SecurityGroupRuleSettings(
+ SecurityGroupRuleConfig(
sec_grp_name=self.sec_grp_name, direction=Direction.egress,
protocol=Protocol.udp, ethertype=Ethertype.IPv4,
port_range_min=10, port_range_max=20,
description='test_rule_1'))
- sec_grp_settings = SecurityGroupSettings(
+ sec_grp_settings = SecurityGroupConfig(
name=self.sec_grp_name, description='hello group',
rule_settings=sec_grp_rule_settings)
self.sec_grp_creator = create_security_group.OpenStackSecurityGroup(
# Create Image
sec_grp_rule_settings = list()
sec_grp_rule_settings.append(
- SecurityGroupRuleSettings(
+ SecurityGroupRuleConfig(
sec_grp_name=self.sec_grp_name, direction=Direction.ingress,
description='test_rule_1'))
sec_grp_rule_settings.append(
- SecurityGroupRuleSettings(
+ SecurityGroupRuleConfig(
sec_grp_name=self.sec_grp_name, direction=Direction.egress,
protocol=Protocol.udp, ethertype=Ethertype.IPv6,
description='test_rule_2'))
sec_grp_rule_settings.append(
- SecurityGroupRuleSettings(
+ SecurityGroupRuleConfig(
sec_grp_name=self.sec_grp_name, direction=Direction.egress,
protocol=Protocol.udp, ethertype=Ethertype.IPv4,
port_range_min=10, port_range_max=20,
description='test_rule_3'))
- sec_grp_settings = SecurityGroupSettings(
+ sec_grp_settings = SecurityGroupConfig(
name=self.sec_grp_name, description='hello group',
rule_settings=sec_grp_rule_settings)
self.sec_grp_creator = create_security_group.OpenStackSecurityGroup(
# Create Image
sec_grp_rule_settings = list()
sec_grp_rule_settings.append(
- SecurityGroupRuleSettings(
+ SecurityGroupRuleConfig(
sec_grp_name=self.sec_grp_name, direction=Direction.ingress,
description='test_rule_1'))
- sec_grp_settings = SecurityGroupSettings(
+ sec_grp_settings = SecurityGroupConfig(
name=self.sec_grp_name, description='hello group',
rule_settings=sec_grp_rule_settings)
self.sec_grp_creator = create_security_group.OpenStackSecurityGroup(
validation_utils.objects_equivalent(self.sec_grp_creator.get_rules(),
rules)
- self.sec_grp_creator.add_rule(SecurityGroupRuleSettings(
+ self.sec_grp_creator.add_rule(SecurityGroupRuleConfig(
sec_grp_name=self.sec_grp_creator.sec_grp_settings.name,
direction=Direction.egress, protocol=Protocol.icmp,
description='test_rule_2'))
# Create Image
sec_grp_rule_settings = list()
sec_grp_rule_settings.append(
- SecurityGroupRuleSettings(
+ SecurityGroupRuleConfig(
sec_grp_name=self.sec_grp_name, direction=Direction.ingress,
description='test_rule_1'))
sec_grp_rule_settings.append(
- SecurityGroupRuleSettings(
+ SecurityGroupRuleConfig(
sec_grp_name=self.sec_grp_name, direction=Direction.egress,
protocol=Protocol.udp, ethertype=Ethertype.IPv6,
description='test_rule_2'))
sec_grp_rule_settings.append(
- SecurityGroupRuleSettings(
+ SecurityGroupRuleConfig(
sec_grp_name=self.sec_grp_name, direction=Direction.egress,
protocol=Protocol.udp, ethertype=Ethertype.IPv4,
port_range_min=10, port_range_max=20,
description='test_rule_3'))
- sec_grp_settings = SecurityGroupSettings(
+ sec_grp_settings = SecurityGroupConfig(
name=self.sec_grp_name, description='hello group',
rule_settings=sec_grp_rule_settings)
self.sec_grp_creator = create_security_group.OpenStackSecurityGroup(
# Create Image
sec_grp_rule_settings = list()
sec_grp_rule_settings.append(
- SecurityGroupRuleSettings(
+ SecurityGroupRuleConfig(
sec_grp_name=self.sec_grp_name, direction=Direction.ingress,
description='test_rule_1'))
sec_grp_rule_settings.append(
- SecurityGroupRuleSettings(
+ SecurityGroupRuleConfig(
sec_grp_name=self.sec_grp_name, direction=Direction.egress,
protocol=Protocol.udp, ethertype=Ethertype.IPv6,
description='test_rule_2'))
sec_grp_rule_settings.append(
- SecurityGroupRuleSettings(
+ SecurityGroupRuleConfig(
sec_grp_name=self.sec_grp_name, direction=Direction.egress,
protocol=Protocol.udp, ethertype=Ethertype.IPv4,
port_range_min=10, port_range_max=20,
description='test_rule_3'))
- sec_grp_settings = SecurityGroupSettings(
+ sec_grp_settings = SecurityGroupConfig(
name=self.sec_grp_name, description='hello group',
rule_settings=sec_grp_rule_settings)
self.sec_grp_creator = create_security_group.OpenStackSecurityGroup(
this is the only means to tell if the rule is custom or defaulted by
OpenStack
:param neutron: the neutron client
- :param rule_settings: collection of SecurityGroupRuleSettings objects
+ :param rule_settings: collection of SecurityGroupRuleConfig objects
:param rules: a collection of SecurityGroupRule domain objects
:return: T/F
"""
if rule_setting.description:
match = False
for rule in rules:
- if rule_setting.protocol == Protocol.null:
- setting_proto = None
- else:
- setting_proto = rule_setting.protocol.name
-
sec_grp = neutron_utils.get_security_group(
neutron, sec_grp_name=rule_setting.sec_grp_name)
if not sec_grp:
return False
+ proto_str = 'null'
+ if rule.protocol:
+ proto_str = rule.protocol
+
if (rule.description == rule_setting.description and
rule.direction == rule_setting.direction.name and
rule.ethertype == setting_eth_type.name and
rule.port_range_max == rule_setting.port_range_max and
rule.port_range_min == rule_setting.port_range_min and
- rule.protocol == setting_proto and
+ proto_str == str(rule_setting.protocol.value) and
rule.remote_group_id == rule_setting.remote_group_id and
rule.remote_ip_prefix == rule_setting.remote_ip_prefix and
- rule.security_group_id == sec_grp.id):
+ rule.security_group_id == sec_grp.id):
match = True
break
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+import os
import time
import pkg_resources
from heatclient.exc import HTTPBadRequest
+
+import snaps
from snaps import file_utils
-from snaps.openstack.create_flavor import OpenStackFlavor, FlavorSettings
+from snaps.config.flavor import FlavorConfig
+from snaps.config.image import ImageConfig
+from snaps.config.stack import StackConfigError, StackConfig
+from snaps.openstack.create_flavor import OpenStackFlavor
from snaps.openstack.create_image import OpenStackImage
try:
import unittest
import uuid
-from snaps.openstack import create_stack
from snaps.openstack.create_stack import (
- StackSettings, StackSettingsError, StackCreationError)
+ 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
"""
def test_no_params(self):
- with self.assertRaises(StackSettingsError):
+ with self.assertRaises(StackConfigError):
StackSettings()
def test_empty_config(self):
- with self.assertRaises(StackSettingsError):
+ with self.assertRaises(StackConfigError):
StackSettings(**dict())
def test_name_only(self):
- with self.assertRaises(StackSettingsError):
+ with self.assertRaises(StackConfigError):
StackSettings(name='foo')
def test_config_with_name_only(self):
- with self.assertRaises(StackSettingsError):
+ with self.assertRaises(StackConfigError):
StackSettings(**{'name': 'foo'})
def test_config_minimum_template(self):
self.assertEqual('foo', settings.template)
self.assertIsNone(settings.template_path)
self.assertIsNone(settings.env_values)
- self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT,
+ self.assertEqual(snaps.config.stack.STACK_COMPLETE_TIMEOUT,
settings.stack_create_timeout)
def test_config_minimum_template_path(self):
self.assertIsNone(settings.template)
self.assertEqual('foo', settings.template_path)
self.assertIsNone(settings.env_values)
- self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT,
+ self.assertEqual(snaps.config.stack.STACK_COMPLETE_TIMEOUT,
settings.stack_create_timeout)
def test_minimum_template(self):
self.assertEqual('foo', settings.template)
self.assertIsNone(settings.template_path)
self.assertIsNone(settings.env_values)
- self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT,
+ self.assertEqual(snaps.config.stack.STACK_COMPLETE_TIMEOUT,
settings.stack_create_timeout)
def test_minimum_template_path(self):
self.assertEqual('foo', settings.template_path)
self.assertIsNone(settings.template)
self.assertIsNone(settings.env_values)
- self.assertEqual(create_stack.STACK_COMPLETE_TIMEOUT,
+ self.assertEqual(snaps.config.stack.STACK_COMPLETE_TIMEOUT,
settings.stack_create_timeout)
def test_all(self):
class CreateStackSuccessTests(OSIntegrationTestCase):
"""
- Tests for the CreateStack class defined in create_stack.py
+ Tests for the OpenStackHeatStack class defined in create_stack.py
"""
def setUp(self):
- """
- Instantiates the CreateStack object that is responsible for downloading
- and creating an OS stack file within OpenStack
- """
+
super(self.__class__, self).__start__()
self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
# Create Flavor
self.flavor_creator = OpenStackFlavor(
self.admin_os_creds,
- FlavorSettings(name=self.guid + '-flavor-name', ram=256, disk=10,
- vcpus=1))
+ FlavorConfig(
+ name=self.guid + '-flavor-name', ram=256, disk=10, vcpus=1))
self.flavor_creator.create()
self.network_name = self.guid + '-net'
# Create Stack
# Set the default stack settings, then set any custom parameters sent
# from the app
- stack_settings = StackSettings(
+ 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 = create_stack.OpenStackHeatStack(self.heat_creds,
- stack_settings)
+ self.stack_creator = OpenStackHeatStack(
+ self.heat_creds, stack_settings)
created_stack = self.stack_creator.create()
self.assertIsNotNone(created_stack)
# Create Stack
# Set the default stack settings, then set any custom parameters sent
# from the app
- stack_settings = StackSettings(
+ stack_settings = StackConfig(
name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
template_path=self.heat_tmplt_path,
env_values=self.env_values, stack_create_timeout=0)
- self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
- stack_settings)
+ self.stack_creator = OpenStackHeatStack(
+ self.heat_creds, stack_settings)
with self.assertRaises(StackCreationError):
self.stack_creator.create()
# from the app
template_dict = heat_utils.parse_heat_template_str(
file_utils.read_file(self.heat_tmplt_path))
- stack_settings = StackSettings(
+ stack_settings = StackConfig(
name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
template=template_dict,
env_values=self.env_values)
- self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
- stack_settings)
+ self.stack_creator = OpenStackHeatStack(
+ self.heat_creds, stack_settings)
created_stack = self.stack_creator.create()
self.assertIsNotNone(created_stack)
# Create Stack
template_dict = heat_utils.parse_heat_template_str(
file_utils.read_file(self.heat_tmplt_path))
- stack_settings = StackSettings(
+ stack_settings = StackConfig(
name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
template=template_dict,
env_values=self.env_values)
- self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
- stack_settings)
+ self.stack_creator = OpenStackHeatStack(
+ self.heat_creds, stack_settings)
created_stack = self.stack_creator.create()
self.assertIsNotNone(created_stack)
self.assertEqual(created_stack.name, retrieved_stack.name)
self.assertEqual(created_stack.id, retrieved_stack.id)
self.assertEqual(0, len(self.stack_creator.get_outputs()))
- self.assertEqual(create_stack.STATUS_CREATE_COMPLETE,
+ self.assertEqual(snaps.config.stack.STATUS_CREATE_COMPLETE,
self.stack_creator.get_status())
# Delete Stack manually
while time.time() < end_time:
status = heat_utils.get_stack_status(self.heat_cli,
retrieved_stack.id)
- if status == create_stack.STATUS_DELETE_COMPLETE:
+ if status == snaps.config.stack.STATUS_DELETE_COMPLETE:
deleted = True
break
# Create Stack
template_dict = heat_utils.parse_heat_template_str(
file_utils.read_file(self.heat_tmplt_path))
- stack_settings = StackSettings(
+ stack_settings = StackConfig(
name=self.__class__.__name__ + '-' + str(self.guid) + '-stack',
template=template_dict,
env_values=self.env_values)
- self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
- stack_settings)
+ self.stack_creator = OpenStackHeatStack(
+ self.heat_creds, stack_settings)
created_stack1 = self.stack_creator.create()
retrieved_stack = heat_utils.get_stack_by_id(self.heat_cli,
self.assertEqual(0, len(self.stack_creator.get_outputs()))
# Should be retrieving the instance data
- stack_creator2 = create_stack.OpenStackHeatStack(self.heat_creds,
- stack_settings)
+ stack_creator2 = OpenStackHeatStack(self.heat_creds, stack_settings)
stack2 = stack_creator2.create()
self.assertEqual(created_stack1.id, stack2.id)
Tests the creation of an OpenStack stack from Heat template file and
the retrieval of the network creator.
"""
- stack_settings = StackSettings(
+ 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 = create_stack.OpenStackHeatStack(self.heat_creds,
- stack_settings)
+ self.stack_creator = OpenStackHeatStack(
+ self.heat_creds, stack_settings)
created_stack = self.stack_creator.create()
self.assertIsNotNone(created_stack)
self.assertIsNotNone(neutron_utils.get_network_by_id(
neutron, net_creators[0].get_network().id))
- self.assertEqual(1, len(net_creators[0].get_subnets()))
- subnet = net_creators[0].get_subnets()[0]
+ 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)
self.assertEqual(subnet, subnet_by_name)
Tests the creation of an OpenStack stack from Heat template file and
the retrieval of the network creator.
"""
- stack_settings = StackSettings(
+ 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 = create_stack.OpenStackHeatStack(self.heat_creds,
- stack_settings)
+ self.stack_creator = OpenStackHeatStack(
+ self.heat_creds, stack_settings)
created_stack = self.stack_creator.create()
self.assertIsNotNone(created_stack)
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)
vm_inst_by_name = nova_utils.get_server(
- nova, server_name=vm_inst_creators[0].get_vm_inst().name)
+ nova, neutron, 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, vm_inst_creators[0].get_vm_inst().id))
+ nova, neutron, vm_inst_creators[0].get_vm_inst().id))
-class CreateComplexStackTests(OSIntegrationTestCase):
+class CreateStackFloatingIpTests(OSIntegrationTestCase):
"""
- Tests for the CreateStack class defined in create_stack.py
+ Tests to ensure that floating IPs can be accessed via an
+ OpenStackVmInstance object obtained from the OpenStackHeatStack instance
"""
def setUp(self):
- """
- Instantiates the CreateStack object that is responsible for downloading
- and creating an OS stack file within OpenStack
- """
+
super(self.__class__, self).__start__()
self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
self.subnet_name = self.guid + '-subnet'
self.flavor1_name = self.guid + '-flavor1'
self.flavor2_name = self.guid + '-flavor2'
+ self.sec_grp_name = self.guid + '-sec_grp'
self.vm_inst1_name = self.guid + '-inst1'
self.vm_inst2_name = self.guid + '-inst2'
self.keypair_name = self.guid + '-kp'
'subnet_name': self.subnet_name,
'inst1_name': self.vm_inst1_name,
'inst2_name': self.vm_inst2_name,
- 'keypair_name': self.keypair_name}
+ 'keypair_name': self.keypair_name,
+ 'external_net_name': self.ext_net_name,
+ 'security_group_name': self.sec_grp_name}
self.heat_tmplt_path = pkg_resources.resource_filename(
'snaps.openstack.tests.heat', 'floating_ip_heat_template.yaml')
+ self.vm_inst_creators = list()
+
def tearDown(self):
"""
Cleans the stack and downloaded stack file
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_connect_via_ssh_heat_vm(self):
the retrieval of two VM instance creators and attempt to connect via
SSH to the first one with a floating IP.
"""
- stack_settings = StackSettings(
+ 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 = create_stack.OpenStackHeatStack(
+ self.stack_creator = OpenStackHeatStack(
self.heat_creds, stack_settings,
[self.image_creator.image_settings])
created_stack = self.stack_creator.create()
self.assertIsNotNone(created_stack)
- vm_inst_creators = self.stack_creator.get_vm_inst_creators(
+ self.vm_inst_creators = self.stack_creator.get_vm_inst_creators(
heat_keypair_option='private_key')
- self.assertIsNotNone(vm_inst_creators)
- self.assertEqual(2, len(vm_inst_creators))
+ self.assertIsNotNone(self.vm_inst_creators)
+ self.assertEqual(2, len(self.vm_inst_creators))
- for vm_inst_creator in 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))
self.assertEqual(0, len(vm_settings.floating_ip_settings))
+class CreateStackNestedResourceTests(OSIntegrationTestCase):
+ """
+ Tests to ensure that nested heat templates work
+ """
+
+ def setUp(self):
+
+ 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.stack_creator = None
+
+ self.image_creator = OpenStackImage(
+ self.heat_creds, openstack_tests.cirros_image_settings(
+ name=self.guid + '-image',
+ image_metadata=self.image_metadata))
+ self.image_creator.create()
+
+ self.flavor_creator = OpenStackFlavor(
+ self.admin_os_creds,
+ FlavorConfig(
+ name=self.guid + '-flavor-name', ram=256, disk=10, vcpus=1))
+ self.flavor_creator.create()
+
+ env_values = {
+ 'public_network': self.ext_net_name,
+ 'agent_image': self.image_creator.image_settings.name,
+ 'agent_flavor': self.flavor_creator.flavor_settings.name,
+ }
+
+ 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=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.heat_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
+
+ 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()
+ 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 CreateStackRouterTests(OSIntegrationTestCase):
+ """
+ Tests for the CreateStack class defined in create_stack.py where the
+ target is a Network, Subnet, and Router
+ """
+
+ def setUp(self):
+ """
+ Instantiates the CreateStack object that is responsible for downloading
+ and creating an OS stack file within OpenStack
+ """
+ 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.stack_creator = None
+
+ self.net_name = self.guid + '-net'
+ self.subnet_name = self.guid + '-subnet'
+ self.router_name = self.guid + '-router'
+
+ self.env_values = {
+ 'net_name': self.net_name,
+ 'subnet_name': self.subnet_name,
+ 'router_name': self.router_name,
+ 'external_net_name': self.ext_net_name}
+
+ self.heat_tmplt_path = pkg_resources.resource_filename(
+ 'snaps.openstack.tests.heat', 'router_heat_template.yaml')
+
+ 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.heat_creds, stack_settings)
+ self.created_stack = self.stack_creator.create()
+ self.assertIsNotNone(self.created_stack)
+
+ def tearDown(self):
+ """
+ Cleans the stack and downloaded stack file
+ """
+ if self.stack_creator:
+ try:
+ self.stack_creator.clean()
+ except:
+ pass
+
+ super(self.__class__, self).__clean__()
+
+ def test_retrieve_router_creator(self):
+ """
+ Tests the creation of an OpenStack stack from Heat template file and
+ the retrieval of an OpenStackRouter creator/state machine instance
+ """
+ router_creators = self.stack_creator.get_router_creators()
+ self.assertEqual(1, len(router_creators))
+
+ creator = router_creators[0]
+ self.assertEqual(self.router_name, creator.router_settings.name)
+
+ router = creator.get_router()
+
+ ext_net = neutron_utils.get_network(
+ self.neutron, network_name=self.ext_net_name)
+ self.assertEqual(ext_net.id, router.external_network_id)
+
+
+class CreateStackVolumeTests(OSIntegrationTestCase):
+ """
+ Tests to ensure that floating IPs can be accessed via an
+ OpenStackVolume object obtained from the OpenStackHeatStack instance
+ """
+
+ def setUp(self):
+
+ 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.stack_creator = None
+
+ self.volume_name = self.guid + '-volume'
+ self.volume_type_name = self.guid + '-volume-type'
+
+ self.env_values = {
+ 'volume_name': self.volume_name,
+ 'volume_type_name': self.volume_type_name}
+
+ self.heat_tmplt_path = pkg_resources.resource_filename(
+ 'snaps.openstack.tests.heat', 'volume_heat_template.yaml')
+
+ 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.heat_creds, stack_settings)
+ self.created_stack = self.stack_creator.create()
+ self.assertIsNotNone(self.created_stack)
+
+ def tearDown(self):
+ """
+ Cleans the stack and downloaded stack file
+ """
+ if self.stack_creator:
+ try:
+ self.stack_creator.clean()
+ except:
+ pass
+
+ super(self.__class__, self).__clean__()
+
+ def test_retrieve_volume_creator(self):
+ """
+ Tests the creation of an OpenStack stack from Heat template file and
+ the retrieval of an OpenStackVolume creator/state machine instance
+ """
+ volume_creators = self.stack_creator.get_volume_creators()
+ self.assertEqual(1, len(volume_creators))
+
+ creator = volume_creators[0]
+ self.assertEqual(self.volume_name, creator.volume_settings.name)
+ self.assertEqual(self.volume_name, creator.get_volume().name)
+ self.assertEqual(self.volume_type_name,
+ creator.volume_settings.type_name)
+ self.assertEqual(self.volume_type_name, creator.get_volume().type)
+ self.assertEqual(1, creator.volume_settings.size)
+ self.assertEqual(1, creator.get_volume().size)
+
+ def test_retrieve_volume_type_creator(self):
+ """
+ Tests the creation of an OpenStack stack from Heat template file and
+ the retrieval of an OpenStackVolume creator/state machine instance
+ """
+ volume_type_creators = self.stack_creator.get_volume_type_creators()
+ self.assertEqual(1, len(volume_type_creators))
+
+ creator = volume_type_creators[0]
+ self.assertIsNotNone(creator)
+
+ volume_type = creator.get_volume_type()
+ self.assertIsNotNone(volume_type)
+
+ self.assertEqual(self.volume_type_name, volume_type.name)
+ self.assertTrue(volume_type.public)
+ self.assertIsNone(volume_type.qos_spec)
+
+ # TODO - Add encryption back and find out why it broke in Pike
+ # encryption = volume_type.encryption
+ # self.assertIsNotNone(encryption)
+ # self.assertIsNone(encryption.cipher)
+ # self.assertEqual('front-end', encryption.control_location)
+ # self.assertIsNone(encryption.key_size)
+ # self.assertEqual(u'nova.volume.encryptors.luks.LuksEncryptor',
+ # encryption.provider)
+ # self.assertEqual(volume_type.id, encryption.volume_type_id)
+
+
+class CreateStackFlavorTests(OSIntegrationTestCase):
+ """
+ Tests to ensure that floating IPs can be accessed via an
+ OpenStackFlavor object obtained from the OpenStackHeatStack instance
+ """
+
+ def setUp(self):
+
+ 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.stack_creator = None
+
+ self.heat_tmplt_path = pkg_resources.resource_filename(
+ 'snaps.openstack.tests.heat', 'flavor_heat_template.yaml')
+
+ stack_settings = StackConfig(
+ 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.assertIsNotNone(self.created_stack)
+
+ def tearDown(self):
+ """
+ Cleans the stack and downloaded stack file
+ """
+ if self.stack_creator:
+ try:
+ self.stack_creator.clean()
+ except:
+ pass
+
+ super(self.__class__, self).__clean__()
+
+ def test_retrieve_flavor_creator(self):
+ """
+ Tests the creation of an OpenStack stack from Heat template file and
+ the retrieval of an OpenStackVolume creator/state machine instance
+ """
+ flavor_creators = self.stack_creator.get_flavor_creators()
+ self.assertEqual(1, len(flavor_creators))
+
+ creator = flavor_creators[0]
+ self.assertTrue(creator.get_flavor().name.startswith(self.guid))
+ self.assertEqual(1024, creator.get_flavor().ram)
+ self.assertEqual(200, creator.get_flavor().disk)
+ self.assertEqual(8, creator.get_flavor().vcpus)
+ self.assertEqual(0, creator.get_flavor().ephemeral)
+ self.assertIsNone(creator.get_flavor().swap)
+ self.assertEqual(1.0, creator.get_flavor().rxtx_factor)
+ self.assertTrue(creator.get_flavor().is_public)
+
+
+class CreateStackKeypairTests(OSIntegrationTestCase):
+ """
+ Tests to ensure that floating IPs can be accessed via an
+ OpenStackKeypair object obtained from the OpenStackHeatStack instance
+ """
+
+ def setUp(self):
+
+ 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.stack_creator = None
+
+ self.keypair_name = self.guid + '-kp'
+
+ self.env_values = {
+ 'keypair_name': self.keypair_name}
+
+ self.heat_tmplt_path = pkg_resources.resource_filename(
+ 'snaps.openstack.tests.heat', 'keypair_heat_template.yaml')
+
+ 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.heat_creds, stack_settings)
+ self.created_stack = self.stack_creator.create()
+ self.assertIsNotNone(self.created_stack)
+
+ self.keypair_creators = list()
+
+ def tearDown(self):
+ """
+ Cleans the stack and downloaded stack file
+ """
+ if self.stack_creator:
+ try:
+ self.stack_creator.clean()
+ except:
+ pass
+ for keypair_creator in self.keypair_creators:
+ try:
+ keypair_creator.clean()
+ except:
+ pass
+
+ super(self.__class__, self).__clean__()
+
+ def test_retrieve_keypair_creator(self):
+ """
+ Tests the creation of an OpenStack stack from Heat template file and
+ the retrieval of an OpenStackKeypair creator/state machine instance
+ """
+ self.kp_creators = self.stack_creator.get_keypair_creators(
+ 'private_key')
+ self.assertEqual(1, len(self.kp_creators))
+
+ self.keypair_creator = self.kp_creators[0]
+
+ self.assertEqual(self.keypair_name,
+ self.keypair_creator.get_keypair().name)
+ self.assertIsNotNone(
+ self.keypair_creator.keypair_settings.private_filepath)
+
+ private_file_contents = file_utils.read_file(
+ self.keypair_creator.keypair_settings.private_filepath)
+ self.assertTrue(private_file_contents.startswith(
+ '-----BEGIN RSA PRIVATE KEY-----'))
+
+ keypair = nova_utils.get_keypair_by_id(
+ self.nova, self.keypair_creator.get_keypair().id)
+ self.assertIsNotNone(keypair)
+ self.assertEqual(self.keypair_creator.get_keypair(), keypair)
+
+
+class CreateStackSecurityGroupTests(OSIntegrationTestCase):
+ """
+ Tests for the OpenStackHeatStack class to ensure it returns an
+ OpenStackSecurityGroup object
+ """
+
+ def setUp(self):
+ """
+ Instantiates the CreateStack object that is responsible for downloading
+ and creating an OS stack file within OpenStack
+ """
+ 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.stack_creator = None
+
+ self.security_group_name = self.guid + '-sec-grp'
+
+ self.env_values = {
+ 'security_group_name': self.security_group_name}
+
+ self.heat_tmplt_path = pkg_resources.resource_filename(
+ 'snaps.openstack.tests.heat', 'security_group_heat_template.yaml')
+
+ 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.heat_creds, stack_settings)
+ self.created_stack = self.stack_creator.create()
+ self.assertIsNotNone(self.created_stack)
+
+ def tearDown(self):
+ """
+ Cleans the stack and downloaded stack file
+ """
+ if self.stack_creator:
+ try:
+ self.stack_creator.clean()
+ except:
+ pass
+
+ super(self.__class__, self).__clean__()
+
+ def test_retrieve_security_group_creator(self):
+ """
+ Tests the creation of an OpenStack stack from Heat template file and
+ the retrieval of an OpenStackSecurityGroup creator/state machine
+ instance
+ """
+ sec_grp_creators = self.stack_creator.get_security_group_creators()
+ self.assertEqual(1, len(sec_grp_creators))
+
+ creator = sec_grp_creators[0]
+ sec_grp = creator.get_security_group()
+
+ self.assertEqual(self.security_group_name, sec_grp.name)
+ self.assertEqual('Test description', sec_grp.description)
+ self.assertEqual(2, len(sec_grp.rules))
+
+ has_ssh_rule = False
+ has_icmp_rule = False
+
+ for rule in sec_grp.rules:
+ if (rule.security_group_id == sec_grp.id
+ and rule.direction == 'egress'
+ and rule.ethertype == 'IPv4'
+ and rule.port_range_min == 22
+ and rule.port_range_max == 22
+ and rule.protocol == 'tcp'
+ and rule.remote_group_id is None
+ and rule.remote_ip_prefix == '0.0.0.0/0'):
+ has_ssh_rule = True
+ if (rule.security_group_id == sec_grp.id
+ and rule.direction == 'ingress'
+ and rule.ethertype == 'IPv4'
+ and rule.port_range_min is None
+ and rule.port_range_max is None
+ and rule.protocol == 'icmp'
+ and rule.remote_group_id is None
+ and rule.remote_ip_prefix == '0.0.0.0/0'):
+ has_icmp_rule = True
+
+ self.assertTrue(has_ssh_rule)
+ self.assertTrue(has_icmp_rule)
+
+
class CreateStackNegativeTests(OSIntegrationTestCase):
"""
- Negative test cases for the CreateStack class
+ Negative test cases for the OpenStackHeatStack class with poor
+ configuration
"""
def setUp(self):
+
super(self.__class__, self).__start__()
self.heat_creds = self.admin_os_creds
"""
Expect an StackCreationError when the stack file does not exist
"""
- stack_settings = StackSettings(name=self.stack_name,
- template_path=self.heat_tmplt_path)
- self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
- stack_settings)
+ stack_settings = StackConfig(name=self.stack_name,
+ template_path=self.heat_tmplt_path)
+ self.stack_creator = OpenStackHeatStack(
+ self.heat_creds, stack_settings)
with self.assertRaises(HTTPBadRequest):
self.stack_creator.create()
"""
Expect an StackCreationError when the stack file does not exist
"""
- stack_settings = StackSettings(name=self.stack_name,
- template_path='foo')
- self.stack_creator = create_stack.OpenStackHeatStack(self.heat_creds,
- stack_settings)
+ stack_settings = StackConfig(
+ name=self.stack_name, template_path='foo')
+ self.stack_creator = OpenStackHeatStack(
+ self.heat_creds, stack_settings)
with self.assertRaises(IOError):
self.stack_creator.create()
+
+
+class CreateStackFailureTests(OSIntegrationTestCase):
+ """
+ Tests for the OpenStackHeatStack class defined in create_stack.py for
+ when failures occur. Failures are being triggered by allocating 1 million
+ CPUs.
+ """
+
+ def setUp(self):
+
+ 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.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(
+ name=self.guid + 'image', image_file=self.tmp_file.name,
+ image_user='foo', img_format='qcow2'))
+ self.image_creator.create()
+
+ # Create Flavor
+ self.flavor_creator = OpenStackFlavor(
+ self.admin_os_creds,
+ FlavorConfig(
+ name=self.guid + '-flavor-name', ram=256, disk=10,
+ vcpus=1000000))
+ self.flavor_creator.create()
+
+ self.network_name = self.guid + '-net'
+ self.subnet_name = self.guid + '-subnet'
+ self.vm_inst_name = self.guid + '-inst'
+
+ self.env_values = {
+ 'image_name': self.image_creator.image_settings.name,
+ 'flavor_name': self.flavor_creator.flavor_settings.name,
+ 'net_name': self.network_name,
+ 'subnet_name': self.subnet_name,
+ 'inst_name': self.vm_inst_name}
+
+ self.heat_tmplt_path = pkg_resources.resource_filename(
+ 'snaps.openstack.tests.heat', 'test_heat_template.yaml')
+
+ 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
+
+ if self.flavor_creator:
+ try:
+ self.flavor_creator.clean()
+ except:
+ pass
+
+ if self.tmp_file:
+ try:
+ os.remove(self.tmp_file.name)
+ except:
+ pass
+
+ super(self.__class__, self).__clean__()
+
+ def test_stack_failure(self):
+ """
+ Tests the creation of an OpenStack stack from Heat template file that
+ should always fail due to too many CPU cores
+ """
+ # Create Stack
+ # Set the default stack settings, then set any custom parameters sent
+ # from the app
+ 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.heat_creds, stack_settings)
+
+ with self.assertRaises(StackError):
+ try:
+ self.stack_creator.create()
+ except StackError:
+ resources = heat_utils.get_resources(
+ self.heat_cli, self.stack_creator.get_stack().id)
+
+ found = False
+ for resource in resources:
+ if (resource.status ==
+ snaps.config.stack.STATUS_CREATE_COMPLETE):
+ found = True
+ self.assertTrue(found)
+ raise
import unittest
import uuid
+from snaps.config.user import UserConfig
from snaps.openstack.create_user import OpenStackUser, UserSettings
from snaps.openstack.tests.os_source_file_test import OSComponentTestCase
from snaps.openstack.utils import keystone_utils
"""
guid = str(uuid.uuid4())[:-19]
guid = self.__class__.__name__ + '-' + guid
- self.user_settings = UserSettings(
+ self.user_settings = UserConfig(
name=guid + '-name',
password=guid + '-password',
roles={'admin': self.os_creds.project_name},
--- /dev/null
+# 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 cinderclient.exceptions import NotFound, BadRequest
+
+from snaps.config.volume import VolumeConfig, VolumeConfigError
+from snaps.config.volume_type import VolumeTypeConfig
+from snaps.openstack.create_image import OpenStackImage
+from snaps.openstack.create_volume_type import OpenStackVolumeType
+from snaps.openstack.tests import openstack_tests
+
+try:
+ from urllib.request import URLError
+except ImportError:
+ from urllib2 import URLError
+
+import logging
+import unittest
+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
+
+__author__ = 'spisarski'
+
+logger = logging.getLogger('create_volume_tests')
+
+
+class VolumeSettingsUnitTests(unittest.TestCase):
+ """
+ Tests the construction of the VolumeSettings class
+ """
+
+ def test_no_params(self):
+ with self.assertRaises(VolumeConfigError):
+ VolumeSettings()
+
+ def test_empty_config(self):
+ with self.assertRaises(VolumeConfigError):
+ VolumeSettings(**dict())
+
+ def test_name_only(self):
+ settings = VolumeSettings(name='foo')
+ self.assertEqual('foo', settings.name)
+ self.assertIsNone(settings.description)
+ self.assertEquals(1, settings.size)
+ self.assertIsNone(settings.image_name)
+ self.assertIsNone(settings.type_name)
+ self.assertIsNone(settings.availability_zone)
+ self.assertFalse(settings.multi_attach)
+
+ def test_config_with_name_only(self):
+ settings = VolumeSettings(**{'name': 'foo'})
+ self.assertEqual('foo', settings.name)
+ self.assertIsNone(settings.description)
+ self.assertEquals(1, settings.size)
+ self.assertIsNone(settings.image_name)
+ self.assertIsNone(settings.type_name)
+ self.assertIsNone(settings.availability_zone)
+ self.assertFalse(settings.multi_attach)
+
+ def test_all_strings(self):
+ settings = VolumeSettings(
+ name='foo', description='desc', size='2', image_name='image',
+ type_name='type', availability_zone='zone1', multi_attach='true')
+
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('desc', settings.description)
+ self.assertEqual(2, settings.size)
+ self.assertEqual('image', settings.image_name)
+ self.assertEqual('type', settings.type_name)
+ self.assertEqual('zone1', settings.availability_zone)
+ self.assertTrue(settings.multi_attach)
+
+ def test_all_correct_type(self):
+ settings = VolumeSettings(
+ name='foo', description='desc', size=2, image_name='image',
+ type_name='bar', availability_zone='zone1', multi_attach=True)
+
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('desc', settings.description)
+ self.assertEqual(2, settings.size)
+ self.assertEqual('image', settings.image_name)
+ self.assertEqual('bar', settings.type_name)
+ self.assertEqual('zone1', settings.availability_zone)
+ self.assertTrue(settings.multi_attach)
+
+ def test_config_all(self):
+ settings = VolumeSettings(
+ **{'name': 'foo', 'description': 'desc', 'size': '2',
+ 'image_name': 'foo', 'type_name': 'bar',
+ 'availability_zone': 'zone1', 'multi_attach': 'true'})
+
+ self.assertEqual('foo', settings.name)
+ self.assertEqual('desc', settings.description)
+ self.assertEqual(2, settings.size)
+ self.assertEqual('foo', settings.image_name)
+ self.assertEqual('bar', settings.type_name)
+ self.assertEqual('zone1', settings.availability_zone)
+ self.assertTrue(settings.multi_attach)
+
+
+class CreateSimpleVolumeSuccessTests(OSIntegrationTestCase):
+ """
+ Test for the CreateVolume class defined in create_volume.py
+ """
+
+ def setUp(self):
+ """
+ Instantiates the CreateVolume object that is responsible for
+ downloading and creating an OS volume file within OpenStack
+ """
+ super(self.__class__, self).__start__()
+
+ guid = uuid.uuid4()
+ self.volume_settings = VolumeConfig(
+ name=self.__class__.__name__ + '-' + str(guid))
+
+ self.cinder = cinder_utils.cinder_client(self.os_creds)
+ self.volume_creator = None
+
+ def tearDown(self):
+ """
+ Cleans the volume and downloaded volume file
+ """
+ if self.volume_creator:
+ self.volume_creator.clean()
+
+ super(self.__class__, self).__clean__()
+
+ def test_create_volume_simple(self):
+ """
+ Tests the creation of a simple OpenStack volume.
+ """
+ # Create Volume
+ self.volume_creator = OpenStackVolume(
+ self.os_creds, self.volume_settings)
+ created_volume = self.volume_creator.create(block=True)
+ self.assertIsNotNone(created_volume)
+
+ retrieved_volume = cinder_utils.get_volume(
+ self.cinder, volume_settings=self.volume_settings)
+
+ self.assertIsNotNone(retrieved_volume)
+ self.assertEqual(created_volume.id, retrieved_volume.id)
+ self.assertTrue(created_volume == retrieved_volume)
+
+ def test_create_delete_volume(self):
+ """
+ Tests the creation then deletion of an OpenStack volume to ensure
+ clean() does not raise an Exception.
+ """
+ # Create Volume
+ self.volume_creator = OpenStackVolume(
+ self.os_creds, self.volume_settings)
+ created_volume = self.volume_creator.create(block=True)
+ self.assertIsNotNone(created_volume)
+
+ retrieved_volume = cinder_utils.get_volume(
+ self.cinder, volume_settings=self.volume_settings)
+ self.assertIsNotNone(retrieved_volume)
+ self.assertEqual(created_volume, retrieved_volume)
+
+ # Delete Volume manually
+ self.volume_creator.clean()
+
+ self.assertIsNone(cinder_utils.get_volume(
+ self.cinder, volume_settings=self.volume_settings))
+
+ # Must not throw an exception when attempting to cleanup non-existent
+ # volume
+ self.volume_creator.clean()
+ self.assertIsNone(self.volume_creator.get_volume())
+
+ def test_create_same_volume(self):
+ """
+ Tests the creation of an OpenStack volume when one already exists.
+ """
+ # Create Volume
+ self.volume_creator = OpenStackVolume(
+ self.os_creds, self.volume_settings)
+ volume1 = self.volume_creator.create(block=True)
+
+ retrieved_volume = cinder_utils.get_volume(
+ self.cinder, volume_settings=self.volume_settings)
+ self.assertEqual(volume1, retrieved_volume)
+
+ # Should be retrieving the instance data
+ os_volume_2 = OpenStackVolume(
+ self.os_creds, self.volume_settings)
+ volume2 = os_volume_2.create(block=True)
+ self.assertEqual(volume1, volume2)
+
+
+class CreateSimpleVolumeFailureTests(OSIntegrationTestCase):
+ """
+ Test for the CreateVolume class defined in create_volume.py
+ """
+
+ def setUp(self):
+ """
+ Instantiates the CreateVolume object that is responsible for
+ downloading and creating an OS volume file within OpenStack
+ """
+ super(self.__class__, self).__start__()
+
+ self.guid = uuid.uuid4()
+ self.cinder = cinder_utils.cinder_client(self.os_creds)
+ self.volume_creator = None
+
+ def tearDown(self):
+ """
+ Cleans the volume and downloaded volume file
+ """
+ if self.volume_creator:
+ self.volume_creator.clean()
+
+ super(self.__class__, self).__clean__()
+
+ def test_create_volume_bad_size(self):
+ """
+ Tests the creation of an OpenStack volume with a negative size to
+ ensure it raises a BadRequest exception.
+ """
+ volume_settings = VolumeConfig(
+ name=self.__class__.__name__ + '-' + str(self.guid), size=-1)
+
+ # Create Volume
+ self.volume_creator = OpenStackVolume(self.os_creds, volume_settings)
+
+ with self.assertRaises(BadRequest):
+ self.volume_creator.create(block=True)
+
+ def test_create_volume_bad_type(self):
+ """
+ Tests the creation of an OpenStack volume with a type that does not
+ exist to ensure it raises a NotFound exception.
+ """
+ volume_settings = VolumeConfig(
+ name=self.__class__.__name__ + '-' + str(self.guid),
+ type_name='foo')
+
+ # Create Volume
+ self.volume_creator = OpenStackVolume(self.os_creds, volume_settings)
+
+ with self.assertRaises(NotFound):
+ self.volume_creator.create(block=True)
+
+ def test_create_volume_bad_image(self):
+ """
+ Tests the creation of an OpenStack volume with an image that does not
+ exist to ensure it raises a BadRequest exception.
+ """
+ volume_settings = VolumeConfig(
+ name=self.__class__.__name__ + '-' + str(self.guid),
+ image_name='foo')
+
+ # Create Volume
+ self.volume_creator = OpenStackVolume(self.os_creds, volume_settings)
+
+ 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):
+ """
+ Test cases for the CreateVolume when attempting to associate it to a
+ Volume Type
+ """
+
+ def setUp(self):
+ super(self.__class__, self).__start__()
+
+ guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+ self.volume_name = guid + '-vol'
+ self.volume_type_name = guid + '-vol-type'
+
+ self.volume_type_creator = OpenStackVolumeType(
+ self.os_creds, VolumeTypeConfig(name=self.volume_type_name))
+ self.volume_type_creator.create()
+ self.volume_creator = None
+
+ def tearDown(self):
+ if self.volume_creator:
+ self.volume_creator.clean()
+ if self.volume_type_creator:
+ self.volume_type_creator.clean()
+
+ super(self.__class__, self).__clean__()
+
+ def test_bad_volume_type(self):
+ """
+ Expect a NotFound to be raised when the volume type does not exist
+ """
+ self.volume_creator = OpenStackVolume(
+ self.os_creds,
+ VolumeConfig(name=self.volume_name, type_name='foo'))
+
+ with self.assertRaises(NotFound):
+ self.volume_creator.create()
+
+ def test_valid_volume_type(self):
+ """
+ Expect a NotFound to be raised when the volume type does not exist
+ """
+ self.volume_creator = OpenStackVolume(
+ self.os_creds,
+ VolumeConfig(
+ name=self.volume_name, type_name=self.volume_type_name))
+
+ created_volume = self.volume_creator.create(block=True)
+ self.assertIsNotNone(created_volume)
+ self.assertEqual(self.volume_type_name, created_volume.type)
+
+
+class CreateVolumeWithImageTests(OSIntegrationTestCase):
+ """
+ Test cases for the CreateVolume when attempting to associate it to an Image
+ """
+
+ def setUp(self):
+ super(self.__class__, self).__start__()
+
+ self.cinder = cinder_utils.cinder_client(self.os_creds)
+
+ guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+ self.volume_name = guid + '-vol'
+ self.image_name = guid + '-image'
+
+ os_image_settings = openstack_tests.cirros_image_settings(
+ name=self.image_name, image_metadata=self.image_metadata)
+ # Create Image
+ self.image_creator = OpenStackImage(self.os_creds,
+ os_image_settings)
+ self.image_creator.create()
+ self.volume_creator = None
+
+ def tearDown(self):
+ if self.volume_creator:
+ try:
+ self.volume_creator.clean()
+ except:
+ pass
+ if self.image_creator:
+ try:
+ self.image_creator.clean()
+ except:
+ pass
+
+ super(self.__class__, self).__clean__()
+
+ def test_bad_image_name(self):
+ """
+ Tests OpenStackVolume#create() method to ensure a volume is NOT created
+ when associating it to an invalid image name
+ """
+ self.volume_creator = OpenStackVolume(
+ self.os_creds,
+ VolumeConfig(name=self.volume_name, image_name='foo'))
+
+ with self.assertRaises(BadRequest):
+ self.volume_creator.create(block=True)
+
+ def test_valid_volume_image(self):
+ """
+ Tests OpenStackVolume#create() method to ensure a volume is NOT created
+ when associating it to an invalid image name
+ """
+ self.volume_creator = OpenStackVolume(
+ self.os_creds,
+ VolumeConfig(name=self.volume_name, image_name=self.image_name))
+
+ created_volume = self.volume_creator.create(block=True)
+ self.assertIsNotNone(created_volume)
+ self.assertEqual(
+ self.volume_creator.volume_settings.name, created_volume.name)
+ self.assertTrue(self.volume_creator.volume_active())
+
+ retrieved_volume = cinder_utils.get_volume_by_id(
+ self.cinder, created_volume.id)
+
+ self.assertEqual(created_volume, retrieved_volume)
# 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 snaps.openstack.create_qos import QoSSettings, Consumer, OpenStackQoS
+from snaps.config.volume_type import (
+ VolumeTypeConfig, VolumeTypeEncryptionConfig, VolumeTypeConfigError,
+ ControlLocation)
+from snaps.config.qos import QoSConfig, Consumer
+from snaps.openstack.create_qos import OpenStackQoS
try:
from urllib.request import URLError
from snaps.openstack import create_volume_type
from snaps.openstack.create_volume_type import (
- VolumeTypeSettings, VolumeTypeSettingsError, VolumeTypeEncryptionSettings,
- ControlLocation)
+ VolumeTypeSettings, VolumeTypeEncryptionSettings)
from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase
from snaps.openstack.utils import cinder_utils
"""
def test_no_params(self):
- with self.assertRaises(VolumeTypeSettingsError):
+ with self.assertRaises(VolumeTypeConfigError):
VolumeTypeSettings()
def test_empty_config(self):
- with self.assertRaises(VolumeTypeSettingsError):
+ with self.assertRaises(VolumeTypeConfigError):
VolumeTypeSettings(**dict())
def test_name_only(self):
class CreateSimpleVolumeTypeSuccessTests(OSIntegrationTestCase):
"""
- Test for the OpenStackVolumeType class defined in create_volume_type.py
+ Test for the OpenStackVolumeType class defined in py
without any QoS Specs or Encryption
"""
super(self.__class__, self).__start__()
guid = uuid.uuid4()
- self.volume_type_settings = VolumeTypeSettings(
+ self.volume_type_settings = VolumeTypeConfig(
name=self.__class__.__name__ + '-' + str(guid))
self.cinder = cinder_utils.cinder_client(self.os_creds)
self.volume_type_name = guid + '-vol_type'
self.volume_type_creator = None
- qos_settings = QoSSettings(
+ qos_settings = QoSConfig(
name=guid + '-qos-spec', consumer=Consumer.both)
self.qos_creator = OpenStackQoS(self.os_creds, qos_settings)
self.qos_creator.create()
"""
self.volume_type_creator = create_volume_type.OpenStackVolumeType(
self.os_creds,
- VolumeTypeSettings(
+ VolumeTypeConfig(
name=self.volume_type_name,
qos_spec_name=self.qos_creator.qos_settings.name))
"""
Creates a Volume Type object with encryption
"""
- encryption_settings = VolumeTypeEncryptionSettings(
+ encryption_settings = VolumeTypeEncryptionConfig(
name='foo', provider_class='bar',
control_location=ControlLocation.back_end)
self.volume_type_creator = create_volume_type.OpenStackVolumeType(
self.os_creds,
- VolumeTypeSettings(
+ VolumeTypeConfig(
name=self.volume_type_name,
encryption=encryption_settings))
"""
Creates a Volume Type object with encryption and an associated QoS Spec
"""
- encryption_settings = VolumeTypeEncryptionSettings(
+ encryption_settings = VolumeTypeEncryptionConfig(
name='foo', provider_class='bar',
control_location=ControlLocation.back_end)
self.volume_type_creator = create_volume_type.OpenStackVolumeType(
self.os_creds,
- VolumeTypeSettings(
+ VolumeTypeConfig(
name=self.volume_type_name,
encryption=encryption_settings,
qos_spec_name=self.qos_creator.qos_settings.name))
--- /dev/null
+##############################################################################
+# 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.
+##############################################################################
+heat_template_version: 2013-05-23
+
+parameters:
+ public_network:
+ type: string
+ constraints:
+ - custom_constraint: neutron.network
+ agent_flavor:
+ type: string
+ agent_image:
+ type: string
+ volume_size:
+ type: number
+ description: Size of the volume to be created.
+ default: 1
+ constraints:
+ - range: { min: 1, max: 1024 }
+ description: must be between 1 and 1024 Gb.
+ agent_count:
+ type: number
+ default: 1
+ constraints:
+ - range: { min: 1, max: 512 }
+ description: must be between 1 and 512 agents.
+ availability_zone:
+ type: string
+ default: nova
+
+resources:
+ slaves:
+ type: OS::Heat::ResourceGroup
+ depends_on: [subnet, network_router_interface,
+ open_security_group, key_pair]
+ properties:
+ count: {get_param: agent_count}
+ resource_def: {
+ type: "agent.yaml",
+ properties: {
+ public_network: {get_param: public_network},
+ agent_network: {get_resource: network},
+ flavor: {get_param: agent_flavor},
+ image: {get_param: agent_image},
+ availability_zone: {get_param: availability_zone},
+ open_security_group: {get_resource: open_security_group},
+ key_name: {get_resource: key_pair},
+ volume_size: {get_param: volume_size}
+ }
+ }
+
+ network:
+ type: OS::Neutron::Net
+ properties:
+ name: network
+
+ subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: network }
+ cidr: 172.16.0.0/16
+ gateway_ip: 172.16.0.1
+
+ network_router:
+ type: OS::Neutron::Router
+ properties:
+ external_gateway_info:
+ network: { get_param: public_network }
+
+ network_router_interface:
+ type: OS::Neutron::RouterInterface
+ properties:
+ router_id: { get_resource: network_router }
+ subnet_id: { get_resource: subnet }
+
+ key_pair:
+ type: OS::Nova::KeyPair
+ properties:
+ save_private_key: true
+ name: agent_keypair
+
+ open_security_group:
+ type: OS::Neutron::SecurityGroup
+ properties:
+ description: An open security group to allow all access to the slaves
+ rules:
+ - remote_ip_prefix: 0.0.0.0/0
+ protocol: tcp
+ port_range_min: 22
+ port_range_max: 22
+ - remote_ip_prefix: 0.0.0.0/0
+ protocol: icmp
+
+outputs:
+ slave_ips: {
+ description: "Slave addresses",
+ value: { get_attr: [ slaves, agent_ip] }
+ }
+ private_key:
+ description: "SSH Private Key"
+ value: { get_attr: [ key_pair, private_key ]}
--- /dev/null
+##############################################################################
+# 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.
+##############################################################################
+heat_template_version: 2013-05-23
+
+parameters:
+ flavor:
+ type: string
+ default: test
+ image:
+ type: string
+ default: 'Ubuntu 16.04'
+ key_name:
+ type: string
+ default: test_key
+ username:
+ type: string
+ default: test_user
+ open_security_group:
+ type: string
+ volume_size:
+ type: number
+ description: Size of the volume to be created.
+ default: 1
+ constraints:
+ - range: { min: 1, max: 1024 }
+ description: must be between 1 and 1024 Gb.
+ agent_network:
+ type: string
+ constraints:
+ - custom_constraint: neutron.network
+ public_network:
+ type: string
+ constraints:
+ - custom_constraint: neutron.network
+ availability_zone:
+ type: string
+ default: nova
+
+resources:
+ agent:
+ type: "OS::Nova::Server"
+ properties:
+ name: agent
+ image: { get_param: image }
+ flavor: { get_param: flavor }
+ key_name: { get_param: key_name }
+ networks:
+ - port: { get_resource: agent_port }
+ user_data: { get_resource: agent_config }
+ user_data_format: RAW
+ availability_zone: { get_param: availability_zone}
+
+ agent_config:
+ type: "OS::Heat::CloudConfig"
+ properties:
+ cloud_config:
+ users:
+ - name: { get_param: username }
+ groups: users
+ shell: /bin/bash
+ sudo: "ALL=(ALL) NOPASSWD:ALL"
+ ssh_authorized_keys:
+ - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDEbnDiqZ8RjQJJzJPf074J41XlYED+zYBzaUZ5UkkUquXzymyUmoWaFBXJP+XPu4Ns44U/S8614+JxGk96tjUdJlIjL0Ag8HP6KLtTNCabucKcEASpgJIVWqJvE3E9upZLIEiTGsF8I8S67T2qq1J1uvtxyeZmyjm7NMamjyFXE53dhR2EHqSutyKK1CK74NkRY9wr3qWUIt35kLdKSVSfrr4gOOicDALbIRu77skHIvrjt+wK1VWphBdMg6ytuq5mIE6pjWAU3Gwl4aTxOU0z43ARzCLq8HVf8s/dKjYMj8plNqaIfceMbaEUqpNHv/xbvtGNG7N0aB/a4pkUQL07
+ - default
+ package_update: false
+ package_upgrade: false
+ manage_etc_hosts: localhost
+
+ agent_port:
+ type: "OS::Neutron::Port"
+ properties:
+ network_id: { get_param: agent_network }
+ security_groups:
+ - { get_param: open_security_group }
+
+ floating_ip:
+ type: OS::Neutron::FloatingIP
+ properties:
+ floating_network_id: { get_param: public_network }
+ port_id: { get_resource: agent_port }
+
+ agent_volume:
+ type: OS::Cinder::Volume
+ properties:
+ size: { get_param: volume_size }
+
+ agent_volume_att:
+ type: OS::Cinder::VolumeAttachment
+ properties:
+ instance_uuid: { get_resource: agent }
+ volume_id: { get_resource: agent_volume}
+
+outputs:
+ agent_ip:
+ description: The floating IP address of the agent on the public network
+ value: { get_attr: [ floating_ip, floating_ip_address ] }
-# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs")
+##############################################################################
+# Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
# and others. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# 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.
----
-- name: Configure NIC
- hosts: all
- become: yes
- become_method: sudo
- become_user: root
+##############################################################################
+heat_template_version: 2015-04-30
- tasks:
- - name: Setup /etc/sysconfig/network-scripts/ifcfg-eth1 file
- action: template owner=root group=root mode=644 src=../templates/ifcfg-interface dest=/etc/sysconfig/network-scripts/ifcfg-{{nic_name}}
- - name : Restart Network
- command: systemctl restart network
\ No newline at end of file
+description: Simple template to deploy a single volume with encryption
+
+resources:
+ flavor:
+ type: OS::Nova::Flavor
+ properties:
+ ram: 1024
+ vcpus: 8
+ disk: 200
label: Keypair name
description: The name of the stack's keypair
default: keypair_name
+ security_group_name:
+ type: string
+ label: Security Group name
+ description: The name of the stack's security group
+ default: security_group_name
inst1_name:
type: string
label: First VM name
router: { get_resource: management_router }
subnet: { get_resource: subnet }
+ server_security_group:
+ type: OS::Neutron::SecurityGroup
+ properties:
+ description: Add security group rules for server
+ name: { get_param: security_group_name }
+ rules:
+ - remote_ip_prefix: 0.0.0.0/0
+ protocol: tcp
+ port_range_min: 22
+ port_range_max: 22
+ - remote_ip_prefix: 0.0.0.0/0
+ protocol: icmp
+
floating_ip:
type: OS::Neutron::FloatingIP
properties:
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 }
--- /dev/null
+##############################################################################
+# 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.
+##############################################################################
+heat_template_version: 2015-04-30
+
+description: >
+ Test template that simply deploys a keypair with a generated key
+
+parameters:
+ keypair_name:
+ type: string
+ label: Keypair name
+ description: The name of the stack's keypair
+ default: keypair_name
+
+resources:
+ keypair:
+ type: OS::Nova::KeyPair
+ properties:
+ name: { get_param: keypair_name }
+ save_private_key: True
+
+outputs:
+ private_key:
+ description: "SSH Private Key"
+ value: { get_attr: [ keypair, private_key ]}
--- /dev/null
+##############################################################################
+# 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.
+##############################################################################
+heat_template_version: 2015-04-30
+
+description: >
+ Sample template with two VMs instantiated against different images and
+ flavors on the same network and the first one has a floating IP
+
+parameters:
+ net_name:
+ type: string
+ label: Test network name
+ description: The name of the stack's network
+ default: test_net
+ subnet_name:
+ type: string
+ label: Test subnet name
+ description: The name of the stack's subnet
+ default: test_subnet
+ router_name:
+ type: string
+ label: Test router name
+ description: The name of the stack's router
+ default: mgmt_router
+ external_net_name:
+ type: string
+ description: Name of the external network which management network will connect to
+ default: external
+
+resources:
+ network:
+ type: OS::Neutron::Net
+ properties:
+ name: { get_param: net_name }
+
+ subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ name: { get_param: subnet_name }
+ ip_version: 4
+ cidr: 10.1.2.0/24
+ network: { get_resource: network }
+
+ management_router:
+ type: OS::Neutron::Router
+ properties:
+ name: { get_param: router_name }
+ external_gateway_info:
+ network: { get_param: external_net_name }
+
+ management_router_interface:
+ type: OS::Neutron::RouterInterface
+ properties:
+ router: { get_resource: management_router }
+ subnet: { get_resource: subnet }
--- /dev/null
+##############################################################################
+# 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.
+##############################################################################
+heat_template_version: 2015-04-30
+
+description: >
+ Sample template for creating a single SecurityGroup
+
+parameters:
+ security_group_name:
+ type: string
+ label: Security Group name
+ description: The name of the stack's security group
+ default: security_group_name
+
+resources:
+ server_security_group:
+ type: OS::Neutron::SecurityGroup
+ properties:
+ description: Test description
+ name: { get_param: security_group_name }
+ rules:
+ - direction: egress
+ ethertype: IPv4
+ port_range_min: 22
+ port_range_max: 22
+ protocol: tcp
+ remote_ip_prefix: 0.0.0.0/0
+ - direction: ingress
+ ethertype: IPv4
+ protocol: icmp
+ remote_ip_prefix: 0.0.0.0/0
--- /dev/null
+##############################################################################
+# 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.
+##############################################################################
+heat_template_version: 2015-04-30
+
+description: Simple template to deploy a single volume with encryption
+
+parameters:
+ volume_name:
+ type: string
+ label: Volume name
+ description: The name of the volume
+ default: test-vol-name
+ volume_type_name:
+ type: string
+ label: Volume Type name
+ description: The name of the volume type
+ default: test-vol-type-name
+
+resources:
+ volume_type:
+ type: OS::Cinder::VolumeType
+ properties:
+ name: { get_param: volume_type_name }
+
+# encryption_vol_type:
+# type: OS::Cinder::EncryptedVolumeType
+# properties:
+# provider: nova.volume.encryptors.luks.LuksEncryptor
+# control_location: front-end
+# volume_type: { get_resource: volume_type }
+
+ volume:
+ type: OS::Cinder::Volume
+ properties:
+ name: { get_param: volume_name }
+ size: 1
+# volume_type: { get_resource: encryption_vol_type }
+ volume_type: { get_resource: volume_type }
import pkg_resources
from snaps import file_utils
-from snaps.openstack.create_image import ImageSettings
-from snaps.openstack.create_network import NetworkSettings, SubnetSettings
-from snaps.openstack.create_router import RouterSettings
+from snaps.config.image import ImageConfig
+from snaps.config.network import NetworkConfig, SubnetConfig
+from snaps.config.router import RouterConfig
from snaps.openstack.os_credentials import OSCreds, ProxySettings
__author__ = 'spisarski'
elif config.get('OS_INSECURE'):
https_cacert = False
- interface = 'admin'
+ interface = 'public'
if config.get('OS_INTERFACE'):
interface = config.get('OS_INTERFACE')
logger.debug('Image metadata - ' + str(metadata))
if metadata and 'config' in metadata:
- return ImageSettings(**metadata['config'])
+ return ImageConfig(**metadata['config'])
disk_file = None
- if metadata:
+ if metadata and ('disk_url' in metadata or 'disk_file' in metadata):
disk_url = metadata.get('disk_url')
disk_file = metadata.get('disk_file')
elif not disk_url:
disk_url = default_url
- else:
- disk_url = disk_url
- if metadata and \
- ('kernel_file' in metadata or 'kernel_url' in metadata) and \
- kernel_settings is None:
- kernel_image_settings = ImageSettings(
+ if (metadata
+ and ('kernel_file' in metadata or 'kernel_url' in metadata)
+ and kernel_settings is None):
+ kernel_image_settings = ImageConfig(
name=image_name + '-kernel', image_user=image_user,
- img_format=image_format,
- image_file=metadata.get('kernel_file'),
+ img_format=image_format, image_file=metadata.get('kernel_file'),
url=metadata.get('kernel_url'), public=public)
else:
kernel_image_settings = kernel_settings
- if metadata and \
- ('ramdisk_file' in metadata or 'ramdisk_url' in metadata) and \
- ramdisk_settings is None:
- ramdisk_image_settings = ImageSettings(
+ if (metadata
+ and ('ramdisk_file' in metadata or 'ramdisk_url' in metadata)
+ and ramdisk_settings is None):
+ ramdisk_image_settings = ImageConfig(
name=image_name + '-ramdisk', image_user=image_user,
img_format=image_format,
image_file=metadata.get('ramdisk_file'),
if metadata and 'extra_properties' in metadata:
extra_properties = metadata['extra_properties']
- return ImageSettings(name=image_name, image_user=image_user,
- img_format=image_format, image_file=disk_file,
- url=disk_url, extra_properties=extra_properties,
- kernel_image_settings=kernel_image_settings,
- ramdisk_image_settings=ramdisk_image_settings,
- public=public,
- nic_config_pb_loc=nic_config_pb_loc)
+ return ImageConfig(name=image_name, image_user=image_user,
+ img_format=image_format, image_file=disk_file,
+ url=disk_url, extra_properties=extra_properties,
+ kernel_image_settings=kernel_image_settings,
+ ramdisk_image_settings=ramdisk_image_settings,
+ public=public,
+ nic_config_pb_loc=nic_config_pb_loc)
def cirros_image_settings(name=None, url=None, image_metadata=None,
def file_image_test_settings(name, file_path, image_user=CIRROS_USER):
- return ImageSettings(name=name, image_user=image_user,
- img_format=DEFAULT_IMAGE_FORMAT, image_file=file_path)
+ return ImageConfig(name=name, image_user=image_user,
+ img_format=DEFAULT_IMAGE_FORMAT, image_file=file_path)
def centos_image_settings(name, url=None, image_metadata=None,
else:
metadata = image_metadata
- pb_path = pkg_resources.resource_filename(
- 'snaps.provisioning.ansible_pb.centos-network-setup.playbooks',
- 'configure_host.yml')
return create_image_settings(
image_name=name, image_user=CENTOS_USER,
image_format=DEFAULT_IMAGE_FORMAT, metadata=metadata, disk_url=url,
default_url=CENTOS_DEFAULT_IMAGE_URL,
kernel_settings=kernel_settings, ramdisk_settings=ramdisk_settings,
- public=public, nic_config_pb_loc=pb_path)
+ public=public)
def ubuntu_image_settings(name, url=None, image_metadata=None,
else:
metadata = image_metadata
- pb_path = pkg_resources.resource_filename(
- 'snaps.provisioning.ansible_pb.ubuntu-network-setup.playbooks',
- 'configure_host.yml')
return create_image_settings(
image_name=name, image_user=UBUNTU_USER,
image_format=DEFAULT_IMAGE_FORMAT, metadata=metadata, disk_url=url,
default_url=UBUNTU_DEFAULT_IMAGE_URL,
kernel_settings=kernel_settings, ramdisk_settings=ramdisk_settings,
- public=public, nic_config_pb_loc=pb_path)
+ public=public)
def get_priv_net_config(net_name, subnet_name, router_name=None,
- cidr='10.55.0.0/24', external_net=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)
+ 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):
+ 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)
+ external_gateway=external_net,
+ netconf_override=netconf_override)
class OSNetworkConfig:
"""
Represents the settings required for the creation of a network in OpenStack
+ where netconf_override is used to reconfigure the network_type,
+ physical_network and segmentation_id
"""
def __init__(self, net_name, subnet_name=None, subnet_cidr=None,
- router_name=None, external_gateway=None):
-
+ router_name=None, external_gateway=None,
+ netconf_override=None):
+ """
+ :param netconf_override: dict() containing the reconfigured network_type,
+ physical_network and segmentation_id
+ """
+
+ network_conf = None
if subnet_name and subnet_cidr:
- self.network_settings = NetworkSettings(
+ network_conf = NetworkConfig(
name=net_name, subnet_settings=[
- SubnetSettings(cidr=subnet_cidr, name=subnet_name)])
+ SubnetConfig(cidr=subnet_cidr, name=subnet_name)])
else:
- self.network_settings = NetworkSettings(name=net_name)
+ network_conf = NetworkConfig(name=net_name)
+ if netconf_override:
+ network_conf.network_type = netconf_override.get('network_type')
+ network_conf.physical_network = netconf_override.get(
+ 'physical_network')
+ network_conf.segmentation_id = netconf_override.get(
+ 'segmentation_id')
+ self.network_settings = network_conf
if router_name:
if subnet_name:
- self.router_settings = RouterSettings(
+ self.router_settings = RouterConfig(
name=router_name, external_gateway=external_gateway,
internal_subnets=[subnet_name])
else:
- self.router_settings = RouterSettings(
+ self.router_settings = RouterConfig(
name=router_name, external_gateway=external_gateway)
# limitations under the License.
import logging
import pkg_resources
-import requests
-from requests.packages.urllib3.exceptions import InsecureRequestWarning
import uuid
import unittest
from snaps import file_utils
-from snaps.openstack.create_project import ProjectSettings
-from snaps.openstack.create_user import UserSettings
+from snaps.config.project import ProjectConfig
+from snaps.config.user import UserConfig
from snaps.openstack.tests import openstack_tests
from snaps.openstack.utils import deploy_utils, keystone_utils
dev_os_env_file = pkg_resources.resource_filename(
'snaps.openstack.tests.conf', 'os_env.yaml')
-requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
-
class OSComponentTestCase(unittest.TestCase):
def __init__(self, method_name='runTest', os_creds=None, ext_net_name=None,
use_keystone=True, flavor_metadata=None, image_metadata=None,
- log_level=logging.DEBUG):
+ netconf_override=None, log_level=logging.DEBUG):
"""
Super for integration tests requiring a connection to OpenStack
:param method_name: default 'runTest'
'ramdisk_url': '{URI}/cirros-0.3.4-x86_64-initramfs'})
:param flavor_metadata: dict() to be sent directly into the Nova client
generally used for page sizes
+ :param netconf_override: dict() containing the configured network_type,
+ physical_network and segmentation_id
:param log_level: the logging level of your test run (default DEBUG)
"""
super(OSIntegrationTestCase, self).__init__(
method_name=method_name, os_creds=os_creds,
ext_net_name=ext_net_name, image_metadata=image_metadata,
log_level=log_level)
+ self.netconf_override = netconf_override
self.use_keystone = use_keystone
self.keystone = None
self.flavor_metadata = flavor_metadata
@staticmethod
def parameterize(testcase_klass, os_creds, ext_net_name,
use_keystone=False, flavor_metadata=None,
- image_metadata=None, log_level=logging.DEBUG):
+ image_metadata=None, netconf_override=None,
+ log_level=logging.DEBUG):
"""
Create a suite containing all tests taken from the given
subclass, passing them the parameter 'param'.
for name in test_names:
suite.addTest(testcase_klass(name, os_creds, ext_net_name,
use_keystone, flavor_metadata,
- image_metadata, log_level))
+ image_metadata, netconf_override,
+ log_level))
return suite
"""
guid = self.__class__.__name__ + '-' + str(uuid.uuid4())[:-19]
project_name = guid + '-proj'
self.project_creator = deploy_utils.create_project(
- self.admin_os_creds, ProjectSettings(
+ self.admin_os_creds, ProjectConfig(
name=project_name,
domain=self.admin_os_creds.project_domain_name))
self.user_creator = deploy_utils.create_user(
- self.admin_os_creds, UserSettings(
+ self.admin_os_creds, UserConfig(
name=guid + '-user', password=guid,
project_name=project_name, roles={
'admin': self.project_creator.project_settings.name},
from cinderclient.client import Client
from cinderclient.exceptions import NotFound
-from snaps.domain.volume import QoSSpec, VolumeType, VolumeTypeEncryption
+from snaps.domain.volume import (
+ QoSSpec, VolumeType, VolumeTypeEncryption, Volume)
from snaps.openstack.utils import keystone_utils
__author__ = 'spisarski'
region_name=os_creds.region_name)
+def get_volume(cinder, volume_name=None, volume_settings=None):
+ """
+ Returns an OpenStack volume object for a given name
+ :param cinder: the Cinder client
+ :param volume_name: the volume name to lookup
+ :param volume_settings: the volume settings used for lookups
+ :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)
+
+
+def __get_os_volume_by_id(cinder, volume_id):
+ """
+ Returns an OpenStack volume object for a given name
+ :param cinder: the Cinder client
+ :param volume_id: the volume ID to lookup
+ :return: the SNAPS-OO Domain Volume object or None
+ """
+ return cinder.volumes.get(volume_id)
+
+
+def get_volume_by_id(cinder, volume_id):
+ """
+ Returns an OpenStack volume object for a given name
+ :param cinder: the Cinder client
+ :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)
+ 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)
+
+
+def get_volume_status(cinder, volume):
+ """
+ Returns a new OpenStack Volume object for a given OpenStack volume object
+ :param cinder: the Cinder client
+ :param volume: the domain Volume object
+ :return: the OpenStack Volume object
+ """
+ os_volume = cinder.volumes.get(volume.id)
+ return os_volume.status
+
+
+def create_volume(cinder, volume_settings):
+ """
+ Creates and returns OpenStack volume object with an external URL
+ :param cinder: the cinder 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,
+ 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)
+
+
+def delete_volume(cinder, volume):
+ """
+ Deletes an volume from OpenStack
+ :param cinder: the cinder client
+ :param volume: the volume to delete
+ """
+ logger.info('Deleting volume named - %s', volume.name)
+ return cinder.volumes.delete(volume.id)
+
+
def get_volume_type(cinder, volume_type_name=None, volume_type_settings=None):
"""
Returns an OpenStack volume type object for a given name
"""
Creates a network on which the CMTSs can attach
:param os_creds: The OpenStack credentials object
- :param router_settings: The RouterSettings instance
+ :param router_settings: The RouterConfig instance
:param cleanup: Denotes whether or not this is being called for cleanup
:return: A reference to the network creator objects for each network from
which network elements such as the subnet, router, interface
"""
Creates a keypair that can be applied to an instance
:param os_creds: The OpenStack credentials object
- :param keypair_settings: The KeypairSettings object
+ :param keypair_settings: The KeypairConfig object
:param cleanup: Denotes whether or not this is being called for cleanup
:return: A reference to the keypair creator object
"""
"""
Creates a VM instance
:param os_creds: The OpenStack credentials
- :param instance_settings: Instance of VmInstanceSettings
+ :param instance_settings: Instance of VmInstanceConfig
:param image_settings: The object containing image settings
:param keypair_creator: The object responsible for creating the keypair
associated with this VM instance. (optional)
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
+import os
import yaml
from heatclient.client import Client
from snaps import file_utils
from snaps.domain.stack import Stack, Resource, Output
-from snaps.openstack.utils import keystone_utils, neutron_utils, nova_utils
+from snaps.openstack.utils import (
+ keystone_utils, neutron_utils, nova_utils, cinder_utils)
__author__ = 'spisarski'
:param os_creds: the OpenStack credentials
:return: the client
"""
- logger.debug('Retrieving Nova Client')
+ logger.debug('Retrieving Heat Client')
return Client(os_creds.heat_api_version,
session=keystone_utils.keystone_session(os_creds),
region_name=os_creds.region_name)
if stack_settings.env_values:
args['parameters'] = stack_settings.env_values
+ if stack_settings.resource_files:
+ resources = dict()
+ for res_file in stack_settings.resource_files:
+ heat_resource_contents = file_utils.read_file(res_file)
+ base_filename = os.path.basename(res_file)
+
+ if heat_resource_contents and base_filename:
+ resources[base_filename] = heat_resource_contents
+ args['files'] = resources
+
stack = heat_cli.stacks.create(**args)
return get_stack_by_id(heat_cli, stack_id=stack['stack']['id'])
heat_cli.stacks.delete(stack.id)
-def __get_os_resources(heat_cli, stack):
+def __get_os_resources(heat_cli, res_id):
"""
Returns all of the OpenStack resource objects for a given stack
:param heat_cli: the OpenStack heat client
- :param stack: the SNAPS-OO Stack domain object
+ :param res_id: the resource ID
:return: a list
"""
- return heat_cli.resources.list(stack.id)
+ return heat_cli.resources.list(res_id)
-def get_resources(heat_cli, stack):
+def get_resources(heat_cli, res_id, res_type=None):
"""
Returns all of the OpenStack resource objects for a given stack
:param heat_cli: the OpenStack heat client
- :param stack: the SNAPS-OO Stack domain object
- :return: a list
+ :param res_id: the SNAPS-OO Stack domain object
+ :param res_type: the type name to filter
+ :return: a list of Resource domain objects
"""
- os_resources = __get_os_resources(heat_cli, stack)
+ os_resources = __get_os_resources(heat_cli, res_id)
if os_resources:
out = list()
for os_resource in os_resources:
- out.append(Resource(resource_type=os_resource.resource_type,
- resource_id=os_resource.physical_resource_id))
+ if ((res_type and os_resource.resource_type == res_type)
+ or not res_type):
+ out.append(Resource(
+ name=os_resource.resource_name,
+ resource_type=os_resource.resource_type,
+ resource_id=os_resource.physical_resource_id,
+ status=os_resource.resource_status,
+ status_reason=os_resource.resource_status_reason))
return out
for given stack
:param heat_cli: the OpenStack heat client
:param stack: the SNAPS-OO Stack domain object
- :return: a list
+ :return: a list of Output domain objects
"""
out = list()
def get_stack_networks(heat_cli, neutron, stack):
"""
- Returns an instance of NetworkSettings for each network owned by this stack
+ Returns a list of Network domain objects deployed by this stack
:param heat_cli: the OpenStack heat client object
:param neutron: the OpenStack neutron client object
:param stack: the SNAPS-OO Stack domain object
- :return: a list of NetworkSettings
+ :return: a list of Network objects
"""
out = list()
- resources = get_resources(heat_cli, stack)
+ resources = get_resources(heat_cli, stack.id, 'OS::Neutron::Net')
for resource in resources:
- if resource.type == 'OS::Neutron::Net':
- network = neutron_utils.get_network_by_id(
- neutron, resource.id)
- if network:
- out.append(network)
+ network = neutron_utils.get_network_by_id(neutron, resource.id)
+ if network:
+ out.append(network)
return out
-def get_stack_servers(heat_cli, nova, stack):
+def get_stack_routers(heat_cli, neutron, stack):
"""
- Returns an instance of NetworkSettings for each network owned by this stack
+ Returns a list of Network domain objects deployed by this stack
:param heat_cli: the OpenStack heat client object
- :param nova: the OpenStack nova client object
+ :param neutron: the OpenStack neutron client object
:param stack: the SNAPS-OO Stack domain object
- :return: a list of NetworkSettings
+ :return: a list of Network objects
"""
out = list()
- resources = get_resources(heat_cli, stack)
+ resources = get_resources(heat_cli, stack.id, 'OS::Neutron::Router')
for resource in resources:
- if resource.type == 'OS::Nova::Server':
- try:
+ router = neutron_utils.get_router_by_id(neutron, resource.id)
+ if router:
+ out.append(router)
+
+ return out
+
+
+def get_stack_security_groups(heat_cli, neutron, stack):
+ """
+ Returns a list of SecurityGroup domain objects deployed by this stack
+ :param heat_cli: the OpenStack heat client object
+ :param neutron: the OpenStack neutron client object
+ :param stack: the SNAPS-OO Stack domain object
+ :return: a list of SecurityGroup objects
+ """
+
+ out = list()
+ resources = get_resources(heat_cli, stack.id, 'OS::Neutron::SecurityGroup')
+ for resource in resources:
+ security_group = neutron_utils.get_security_group_by_id(
+ neutron, resource.id)
+ if security_group:
+ out.append(security_group)
+
+ return out
+
+
+def get_stack_servers(heat_cli, nova, neutron, stack):
+ """
+ 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 stack: the SNAPS-OO Stack domain object
+ :return: a list of VMInst domain objects
+ """
+
+ out = list()
+ srvr_res = get_resources(heat_cli, stack.id, 'OS::Nova::Server')
+ for resource in srvr_res:
+ try:
+ server = nova_utils.get_server_object_by_id(
+ nova, neutron, resource.id)
+ if server:
+ out.append(server)
+ except NotFound:
+ 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)
+ 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, resource.id)
+ nova, neutron, res_srvr.id)
if server:
out.append(server)
- except NotFound:
- logger.warn(
- 'VmInst cannot be located with ID %s', resource.id)
+
+ return out
+
+
+def get_stack_keypairs(heat_cli, nova, stack):
+ """
+ Returns a list of Keypair domain objects associated with a Stack
+ :param heat_cli: the OpenStack heat client object
+ :param nova: the OpenStack nova client object
+ :param stack: the SNAPS-OO Stack domain object
+ :return: a list of VMInst domain objects
+ """
+
+ out = list()
+ resources = get_resources(heat_cli, stack.id, 'OS::Nova::KeyPair')
+ for resource in resources:
+ try:
+ keypair = nova_utils.get_keypair_by_id(nova, resource.id)
+ if keypair:
+ out.append(keypair)
+ except NotFound:
+ logger.warn('Keypair cannot be located with ID %s', resource.id)
+
+ return out
+
+
+def get_stack_volumes(heat_cli, cinder, stack):
+ """
+ Returns an instance of Volume domain objects created by this stack
+ :param heat_cli: the OpenStack heat client object
+ :param cinder: the OpenStack cinder client object
+ :param stack: the SNAPS-OO Stack domain object
+ :return: a list of Volume domain objects
+ """
+
+ out = list()
+ resources = get_resources(heat_cli, stack.id, 'OS::Cinder::Volume')
+ for resource in resources:
+ try:
+ server = cinder_utils.get_volume_by_id(cinder, resource.id)
+ if server:
+ out.append(server)
+ except NotFound:
+ logger.warn('Volume cannot be located with ID %s', resource.id)
+
+ return out
+
+
+def get_stack_volume_types(heat_cli, cinder, stack):
+ """
+ Returns an instance of VolumeType domain objects created by this stack
+ :param heat_cli: the OpenStack heat client object
+ :param cinder: the OpenStack cinder client object
+ :param stack: the SNAPS-OO Stack domain object
+ :return: a list of VolumeType domain objects
+ """
+
+ out = list()
+ resources = get_resources(heat_cli, stack.id, 'OS::Cinder::VolumeType')
+ for resource in resources:
+ try:
+ vol_type = cinder_utils.get_volume_type_by_id(cinder, resource.id)
+ if vol_type:
+ out.append(vol_type)
+ except NotFound:
+ logger.warn('VolumeType cannot be located with ID %s', resource.id)
+
+ return out
+
+
+def get_stack_flavors(heat_cli, nova, stack):
+ """
+ Returns an instance of Flavor SNAPS domain object for each flavor created
+ by this stack
+ :param heat_cli: the OpenStack heat client object
+ :param nova: the OpenStack cinder client object
+ :param stack: the SNAPS-OO Stack domain object
+ :return: a list of Volume domain objects
+ """
+
+ out = list()
+ resources = get_resources(heat_cli, stack.id, 'OS::Nova::Flavor')
+ for resource in resources:
+ try:
+ flavor = nova_utils.get_flavor_by_id(nova, resource.id)
+ if flavor:
+ out.append(flavor)
+ except NotFound:
+ logger.warn('Flavor cannot be located with ID %s', resource.id)
return out
: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 ProjectSettings object
+ :param project_settings: a ProjectConfig object
:param project_name: the name to query
:return: the SNAPS-OO Project domain object or None
"""
"""
os_role = get_role_by_id(keystone, role.id)
- logger.info('Granting role %s to project %s', role.name, project)
+ logger.info('Granting role %s to project %s', role.name, project.name)
if keystone.version == V2_VERSION_STR:
keystone.roles.add_user_role(user, os_role, tenant=project)
else:
--- /dev/null
+#
+# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs")
+# and others. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# This utility makes it easy to create OpenStack objects
+import logging
+import re
+import socket
+import struct
+
+import os
+import time
+from keystoneauth1.exceptions import Unauthorized
+
+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
+from snaps.config.project import ProjectConfig
+from snaps.config.qos import QoSConfig
+from snaps.config.router import RouterConfig
+from snaps.config.security_group import SecurityGroupConfig
+from snaps.config.user import UserConfig
+from snaps.config.vm_inst import VmInstanceConfig
+from snaps.config.volume import VolumeConfig
+from snaps.config.volume_type import VolumeTypeConfig
+from snaps.openstack.create_flavor import OpenStackFlavor
+from snaps.openstack.create_image import OpenStackImage
+from snaps.openstack.create_keypairs import OpenStackKeypair
+from snaps.openstack.create_network import OpenStackNetwork
+from snaps.openstack.create_project import OpenStackProject
+from snaps.openstack.create_qos import OpenStackQoS
+from snaps.openstack.create_router import OpenStackRouter
+from snaps.openstack.create_security_group import OpenStackSecurityGroup
+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.provisioning import ansible_utils
+
+logger = logging.getLogger('lanuch_utils')
+DEFAULT_CREDS_KEY = 'admin'
+
+
+def launch_config(config, tmplt_file, deploy, clean, clean_image):
+ """
+ Launches all objects and applies any configured ansible playbooks
+ :param config: the environment configuration dict object
+ :param tmplt_file: the path to the SNAPS-OO template file
+ :param deploy: when True deploy
+ :param clean: when True clean
+ :param clean_image: when True clean the image when clean is True
+ """
+ os_config = config.get('openstack')
+
+ creators = list()
+ vm_dict = dict()
+ images_dict = dict()
+ flavors_dict = dict()
+ networks_dict = dict()
+ routers_dict = dict()
+ os_creds_dict = dict()
+
+ if os_config:
+ os_creds_dict = __get_creds_dict(os_config)
+
+ # Create projects
+ projects_dict = __create_instances(
+ os_creds_dict, OpenStackProject, ProjectConfig,
+ os_config.get('projects'), 'project', clean)
+ creators.append(projects_dict)
+
+ # Create users
+ users_dict = __create_instances(
+ os_creds_dict, OpenStackUser, UserConfig,
+ os_config.get('users'), 'user', clean)
+ creators.append(users_dict)
+
+ # Associate new users to projects
+ if not clean:
+ for project_creator in projects_dict.values():
+ users = project_creator.project_settings.users
+ for user_name in users:
+ user_creator = users_dict.get(user_name)
+ if user_creator:
+ project_creator.assoc_user(
+ user_creator.get_user())
+
+ # Create flavors
+ flavors_dict = __create_instances(
+ os_creds_dict, OpenStackFlavor, FlavorConfig,
+ os_config.get('flavors'), 'flavor', clean, users_dict)
+ creators.append(flavors_dict)
+
+ # Create QoS specs
+ qos_dict = __create_instances(
+ os_creds_dict, OpenStackQoS, QoSConfig,
+ os_config.get('qos_specs'), 'qos_spec', clean, users_dict)
+ creators.append(qos_dict)
+
+ # Create volume types
+ vol_type_dict = __create_instances(
+ os_creds_dict, OpenStackVolumeType, VolumeTypeConfig,
+ os_config.get('volume_types'), 'volume_type', clean,
+ users_dict)
+ creators.append(vol_type_dict)
+
+ # Create volume types
+ vol_dict = __create_instances(
+ os_creds_dict, OpenStackVolume, VolumeConfig,
+ os_config.get('volumes'), 'volume', clean, users_dict)
+ creators.append(vol_dict)
+
+ # Create images
+ images_dict = __create_instances(
+ os_creds_dict, OpenStackImage, ImageConfig,
+ os_config.get('images'), 'image', clean, users_dict)
+ creators.append(images_dict)
+
+ # Create networks
+ networks_dict = __create_instances(
+ os_creds_dict, OpenStackNetwork, NetworkConfig,
+ os_config.get('networks'), 'network', clean, users_dict)
+ creators.append(networks_dict)
+
+ # Create routers
+ routers_dict = __create_instances(
+ os_creds_dict, OpenStackRouter, RouterConfig,
+ os_config.get('routers'), 'router', clean, users_dict)
+ creators.append(routers_dict)
+
+ # Create keypairs
+ keypairs_dict = __create_instances(
+ os_creds_dict, OpenStackKeypair, KeypairConfig,
+ os_config.get('keypairs'), 'keypair', clean, users_dict)
+ creators.append(keypairs_dict)
+
+ # Create security groups
+ creators.append(__create_instances(
+ os_creds_dict, OpenStackSecurityGroup,
+ SecurityGroupConfig,
+ os_config.get('security_groups'), 'security_group', clean,
+ users_dict))
+
+ # Create instance
+ vm_dict = __create_vm_instances(
+ os_creds_dict, users_dict, os_config.get('instances'),
+ images_dict, keypairs_dict, clean)
+ creators.append(vm_dict)
+ logger.info(
+ 'Completed creating/retrieving all configured instances')
+
+ # Must enter either block
+ if clean:
+ # Cleanup Environment
+ __cleanup(creators, clean_image)
+ elif deploy:
+ # Provision VMs
+ ansible_config = config.get('ansible')
+ if ansible_config and vm_dict:
+ if not __apply_ansible_playbooks(
+ ansible_config, os_creds_dict, vm_dict, images_dict,
+ flavors_dict, networks_dict, routers_dict, tmplt_file):
+ logger.error("Problem applying ansible playbooks")
+
+
+def __get_creds_dict(os_conn_config):
+ """
+ Returns a dict of OSCreds where the key is the creds name.
+ For backwards compatibility, credentials not contained in a list (only
+ one) will be returned with the key of None
+ :param os_conn_config: the credential configuration
+ :return: a dict of OSCreds objects
+ """
+ if 'connection' in os_conn_config:
+ return {DEFAULT_CREDS_KEY: __get_os_credentials(os_conn_config)}
+ elif 'connections' in os_conn_config:
+ out = dict()
+ for os_conn_dict in os_conn_config['connections']:
+ config = os_conn_dict.get('connection')
+ if not config:
+ raise Exception('Invalid connection format')
+
+ name = config.get('name')
+ if not name:
+ raise Exception('Connection config requires a name field')
+
+ out[name] = __get_os_credentials(os_conn_dict)
+ return out
+
+
+def __get_creds(os_creds_dict, os_user_dict, inst_config):
+ """
+ Returns the appropriate credentials
+ :param os_creds_dict: a dictionary of OSCreds objects where the name is the
+ key
+ :param os_user_dict: a dictionary of OpenStackUser objects where the name
+ is the key
+ :param inst_config:
+ :return: an OSCreds instance or None
+ """
+ os_creds = os_creds_dict.get(DEFAULT_CREDS_KEY)
+ if 'os_user' in inst_config:
+ os_user_conf = inst_config['os_user']
+ if 'name' in os_user_conf:
+ user_creator = os_user_dict.get(os_user_conf['name'])
+ if user_creator:
+ return user_creator.get_os_creds(
+ project_name=os_user_conf.get('project_name'))
+ elif 'os_creds_name' in inst_config:
+ if 'os_creds_name' in inst_config:
+ os_creds = os_creds_dict[inst_config['os_creds_name']]
+ return os_creds
+
+
+def __get_os_credentials(os_conn_config):
+ """
+ Returns an object containing all of the information required to access
+ OpenStack APIs
+ :param os_conn_config: The configuration holding the credentials
+ :return: an OSCreds instance
+ """
+ config = os_conn_config.get('connection')
+ if not config:
+ raise Exception('Invalid connection configuration')
+
+ proxy_settings = None
+ http_proxy = config.get('http_proxy')
+ if http_proxy:
+ tokens = re.split(':', http_proxy)
+ ssh_proxy_cmd = config.get('ssh_proxy_cmd')
+ proxy_settings = ProxySettings(host=tokens[0], port=tokens[1],
+ ssh_proxy_cmd=ssh_proxy_cmd)
+ else:
+ if 'proxy_settings' in config:
+ host = config['proxy_settings'].get('host')
+ port = config['proxy_settings'].get('port')
+ if host and host != 'None' and port and port != 'None':
+ proxy_settings = ProxySettings(**config['proxy_settings'])
+
+ if proxy_settings:
+ config['proxy_settings'] = proxy_settings
+ else:
+ if config.get('proxy_settings'):
+ del config['proxy_settings']
+
+ return OSCreds(**config)
+
+
+def __parse_ports_config(config):
+ """
+ Parses the "ports" configuration
+ :param config: The dictionary to parse
+ :return: a list of PortConfig objects
+ """
+ out = list()
+ for port_config in config:
+ out.append(PortConfig(**port_config.get('port')))
+ return out
+
+
+def __create_instances(os_creds_dict, creator_class, config_class, config,
+ config_key, cleanup=False, os_users_dict=None):
+ """
+ Returns a dictionary of SNAPS creator objects where the key is the name
+ :param os_creds_dict: Dictionary of OSCreds objects where the key is the
+ name
+ :param config: The list of configurations for the same type
+ :param config_key: The list of configurations for the same type
+ :param cleanup: Denotes whether or not this is being called for cleanup
+ :return: dictionary
+ """
+ out = {}
+
+ if config:
+ for config_dict in config:
+ inst_config = config_dict.get(config_key)
+ if inst_config:
+ creds = __get_creds(os_creds_dict, os_users_dict, inst_config)
+ if creds:
+ creator = creator_class(
+ creds,
+ config_class(**inst_config))
+
+ if creator:
+ if cleanup:
+ try:
+ creator.initialize()
+ except Unauthorized as e:
+ logger.warn(
+ 'Unable to initialize creator [%s] - %s',
+ creator, e)
+ else:
+ creator.create()
+
+ out[inst_config['name']] = creator
+
+ logger.info('Initialized configured %ss', config_key)
+
+ return out
+
+
+def __create_vm_instances(os_creds_dict, os_users_dict, instances_config,
+ image_dict, keypairs_dict, cleanup=False):
+ """
+ Returns a dictionary of OpenStackVmInstance objects where the key is the
+ instance name
+ :param os_creds_dict: Dictionary of OSCreds objects where the key is the
+ name
+ :param os_users_dict: Dictionary of OpenStackUser objects where the key is
+ the username
+ :param instances_config: The list of VM instance configurations
+ :param image_dict: A dictionary of images that will probably be used to
+ instantiate the VM instance
+ :param keypairs_dict: A dictionary of keypairs that will probably be used
+ to instantiate the VM instance
+ :param cleanup: Denotes whether or not this is being called for cleanup
+ :return: dictionary
+ """
+ vm_dict = {}
+
+ if instances_config:
+ for instance_config in instances_config:
+ conf = instance_config.get('instance')
+ if conf:
+ if image_dict:
+ image_creator = image_dict.get(conf.get('imageName'))
+ if image_creator:
+ instance_settings = VmInstanceConfig(
+ **instance_config['instance'])
+ kp_creator = keypairs_dict.get(
+ conf.get('keypair_name'))
+
+ try:
+ vm_dict[conf[
+ 'name']] = deploy_utils.create_vm_instance(
+ __get_creds(
+ os_creds_dict, os_users_dict, conf),
+ instance_settings,
+ image_creator.image_settings,
+ keypair_creator=kp_creator,
+ init_only=cleanup)
+ except Unauthorized as e:
+ if not cleanup:
+ logger.warn('Unable to initialize VM - %s', e)
+ raise
+ else:
+ raise Exception('Image creator instance not found.'
+ ' Cannot instantiate')
+ else:
+ if not cleanup:
+ raise Exception('Image dictionary is None. Cannot '
+ 'instantiate')
+ else:
+ raise Exception('Instance configuration is None. Cannot '
+ 'instantiate')
+ logger.info('Created configured instances')
+
+ return vm_dict
+
+
+def __apply_ansible_playbooks(ansible_configs, os_creds_dict, vm_dict,
+ image_dict, flavor_dict, networks_dict,
+ routers_dict, tmplt_file):
+ """
+ Applies ansible playbooks to running VMs with floating IPs
+ :param ansible_configs: a list of Ansible configurations
+ :param os_creds_dict: Dictionary of OSCreds objects where the key is the
+ name
+ :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
+ name is the key
+ :param flavor_dict: the dictionary of newly instantiated flavors where the
+ name is the key
+ :param networks_dict: the dictionary of newly instantiated networks where
+ the name is the key
+ :param routers_dict: the dictionary of newly instantiated routers where
+ the name is the key
+ :param tmplt_file: the path of the SNAPS-OO template file for setting the
+ CWD so playbook location is relative to the deployment
+ file
+ :return: t/f - true if successful
+ """
+ logger.info("Applying Ansible Playbooks")
+ if ansible_configs:
+ # Set CWD so the deployment file's playbook location can leverage
+ # relative paths
+ orig_cwd = os.getcwd()
+ env_dir = os.path.dirname(tmplt_file)
+ os.chdir(env_dir)
+
+ # Apply playbooks
+ for ansible_config in ansible_configs:
+ # Ensure all hosts are accepting SSH session requests
+ for vm_name in ansible_config['hosts']:
+ vm_inst = vm_dict.get(vm_name)
+ if vm_inst:
+ if not vm_inst.vm_ssh_active(block=True):
+ logger.warning(
+ 'Timeout waiting for instance to respond to '
+ '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)
+
+ # Return to original directory
+ os.chdir(orig_cwd)
+
+ return True
+
+
+def __apply_ansible_playbook(ansible_config, os_creds, 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 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
+ name is the key
+ :param flavor_dict: the dictionary of newly instantiated flavors where the
+ name is the key
+ :param networks_dict: the dictionary of newly instantiated networks where
+ the name is the key
+ :param routers_dict: the dictionary of newly instantiated routers where
+ the name is the key
+ """
+ if ansible_config:
+ (remote_user, floating_ips, private_key_filepath,
+ proxy_settings) = __get_connection_info(
+ ansible_config, vm_dict)
+ if floating_ips:
+ for key, vm_creator in vm_dict.items():
+ fip = vm_creator.get_floating_ip()
+ if fip and fip.ip in floating_ips:
+ if not vm_creator.cloud_init_complete(block=True):
+ raise Exception(
+ 'Cannot apply playbooks as cloud-init has not '
+ 'completed')
+
+ variables = __get_variables(
+ ansible_config.get('variables'), os_creds, vm_dict, image_dict,
+ flavor_dict, networks_dict, routers_dict)
+
+ retval = ansible_utils.apply_playbook(
+ ansible_config['playbook_location'], floating_ips, remote_user,
+ 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'):
+ post_proc_config = ansible_config['post_processing']
+ if 'sleep' in post_proc_config:
+ time.sleep(post_proc_config['sleep'])
+ if 'reboot' in post_proc_config:
+ 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
+
+
+def __get_connection_info(ansible_config, vm_dict):
+ """
+ Returns a tuple of data required for connecting to the running VMs
+ (remote_user, [floating_ips], private_key_filepath, proxy_settings)
+ :param ansible_config: the configuration settings
+ :param vm_dict: the dictionary of VMs where the VM name is the key
+ :return: tuple where the first element is the user and the second is a list
+ of floating IPs and the third is the
+ private key file location and the fourth is an instance of the
+ snaps.ProxySettings class
+ (note: in order to work, each of the hosts need to have the same sudo_user
+ and private key file location values)
+ """
+ if ansible_config.get('hosts'):
+ hosts = ansible_config['hosts']
+ if len(hosts) > 0:
+ floating_ips = list()
+ remote_user = None
+ pk_file = None
+ proxy_settings = None
+ for host in hosts:
+ vm = vm_dict.get(host)
+ if vm:
+ fip = vm.get_floating_ip()
+ if fip:
+ remote_user = vm.get_image_user()
+
+ if fip:
+ floating_ips.append(fip.ip)
+ else:
+ raise Exception(
+ 'Could not find floating IP for VM - ' +
+ vm.name)
+
+ pk_file = vm.keypair_settings.private_filepath
+ proxy_settings = vm.get_os_creds().proxy_settings
+ else:
+ logger.error('Could not locate VM with name - ' + host)
+
+ return remote_user, floating_ips, pk_file, proxy_settings
+ return None
+
+
+def __get_variables(var_config, os_creds, 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 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
+ name is the key
+ :param flavor_dict: the dictionary of newly instantiated flavors where the
+ name is the key
+ :param networks_dict: the dictionary of newly instantiated networks where
+ the name is the key
+ :param routers_dict: the dictionary of newly instantiated routers where
+ the name is the key
+ :return: dictionary or None
+ """
+ if var_config and vm_dict and len(vm_dict) > 0:
+ variables = dict()
+ for key, value in var_config.items():
+ value = __get_variable_value(
+ value, os_creds, vm_dict, image_dict, flavor_dict,
+ networks_dict, routers_dict)
+ if key and value:
+ variables[key] = value
+ logger.info(
+ "Set Jinga2 variable with key [%s] the value [%s]",
+ key, value)
+ else:
+ raise Exception(
+ 'Key - [' + str(key) + '] or Value [' + str(value)
+ + '] must not be None')
+ return variables
+ return None
+
+
+def __get_variable_value(var_config_values, os_creds, 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 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
+ name is the key
+ :param flavor_dict: the dictionary of newly instantiated flavors where the
+ name is the key
+ :param networks_dict: the dictionary of newly instantiated networks where
+ the name is the key
+ :param routers_dict: the dictionary of newly instantiated routers where
+ the name is the key
+ :return:
+ """
+ if var_config_values['type'] == 'string':
+ return __get_string_variable_value(var_config_values)
+ 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)
+ 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)
+ if var_config_values['type'] == 'port':
+ return __get_vm_port_variable_value(var_config_values, vm_dict)
+ if var_config_values['type'] == 'floating_ip':
+ return __get_vm_fip_variable_value(var_config_values, vm_dict)
+ if var_config_values['type'] == 'image':
+ 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)
+ return None
+
+
+def __get_string_variable_value(var_config_values):
+ """
+ Returns the associated string value
+ :param var_config_values: the configuration dictionary
+ :return: the value contained in the dictionary with the key 'value'
+ """
+ return var_config_values['value']
+
+
+def __get_vm_attr_variable_value(var_config_values, vm_dict):
+ """
+ Returns the associated value contained on a VM instance
+ :param var_config_values: the configuration dictionary
+ :param vm_dict: the dictionary containing all VMs where the key is the VM's
+ name
+ :return: the value
+ """
+ vm = vm_dict.get(var_config_values['vm_name'])
+ if vm:
+ if var_config_values['value'] == 'floating_ip':
+ return vm.get_floating_ip().ip
+ if var_config_values['value'] == 'image_user':
+ return vm.get_image_user()
+
+
+def __get_os_creds_variable_value(var_config_values, os_creds):
+ """
+ Returns the associated OS credentials value
+ :param var_config_values: the configuration dictionary
+ :param os_creds: the admin OpenStack OSCreds object
+ :return: the value
+ """
+ if os_creds:
+ if var_config_values['value'] == 'username':
+ logger.info("Returning OS username")
+ return os_creds.username
+ elif var_config_values['value'] == 'password':
+ logger.info("Returning OS password")
+ return os_creds.password
+ elif var_config_values['value'] == 'auth_url':
+ logger.info("Returning OS auth_url")
+ return os_creds.auth_url
+ elif var_config_values['value'] == 'project_name':
+ logger.info("Returning OS project_name")
+ return os_creds.project_name
+
+
+def __get_network_variable_value(var_config_values, networks_dict):
+ """
+ Returns the associated network value
+ :param var_config_values: the configuration dictionary
+ :param networks_dict: the dictionary containing all networks where the key
+ is the network name
+ :return: the value
+ """
+ net_name = var_config_values.get('network_name')
+
+ if net_name and networks_dict.get(net_name):
+ network_creator = networks_dict[net_name]
+
+ if 'subnet_name' in var_config_values:
+ subnet_name = var_config_values.get('subnet_name')
+ if subnet_name:
+ for subnet in network_creator.get_network().subnets:
+ if subnet_name == subnet.name:
+ if 'value' in var_config_values:
+ if 'gateway_ip' == var_config_values['value']:
+ return subnet.gateway_ip
+ if 'ip_range' == var_config_values['value']:
+ return subnet.start + ' ' + subnet.end
+ if 'cidr_ip' == var_config_values['value']:
+ cidr_split = subnet.cidr.split('/')
+ return cidr_split[0]
+ if 'netmask' == var_config_values['value']:
+ cidr_split = subnet.cidr.split('/')
+ cidr_bits = 32 - int(cidr_split[1])
+ netmask = socket.inet_ntoa(
+ struct.pack(
+ '!I', (1 << 32) - (1 << cidr_bits)))
+ return netmask
+ if 'broadcast_ip' == var_config_values['value']:
+ end_split = subnet.end.split('.')
+ broadcast_ip = (
+ end_split[0] + '.' + end_split[1] + '.'
+ + end_split[2] + '.255')
+ return broadcast_ip
+
+
+def __get_router_variable_value(var_config_values, routers_dict, os_creds):
+ """
+ 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
+ :return: the value
+ """
+ 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)
+
+ 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']
+
+
+def __get_vm_port_variable_value(var_config_values, vm_dict):
+ """
+ Returns the associated OS credentials value
+ :param var_config_values: the configuration dictionary
+ :param vm_dict: the dictionary containing all VMs where the key is the VM's
+ name
+ :return: the value
+ """
+ port_name = var_config_values.get('port_name')
+ vm_name = var_config_values.get('vm_name')
+
+ if port_name and vm_name:
+ vm = vm_dict.get(vm_name)
+ if vm:
+ for vm_port in vm.get_vm_inst().ports:
+ if vm_port.name == port_name:
+ port_value_id = var_config_values.get('port_value')
+ if port_value_id:
+ if port_value_id == 'mac_address':
+ return vm_port.mac_address
+ if port_value_id == 'ip_address':
+ return vm_port.ips[0]['ip_address']
+
+
+def __get_vm_fip_variable_value(var_config_values, vm_dict):
+ """
+ Returns the floating IP value if found
+ :param var_config_values: the configuration dictionary
+ :param vm_dict: the dictionary containing all VMs where the key is the VM's
+ name
+ :return: the floating IP string value or None
+ """
+ fip_name = var_config_values.get('fip_name')
+ vm_name = var_config_values.get('vm_name')
+
+ if vm_name:
+ vm = vm_dict.get(vm_name)
+ if vm:
+ fip = vm.get_floating_ip(fip_name)
+ if fip:
+ return fip.ip
+
+
+def __get_image_variable_value(var_config_values, image_dict):
+ """
+ Returns the associated image value
+ :param var_config_values: the configuration dictionary
+ :param image_dict: the dictionary containing all images where the key is
+ the name
+ :return: the value
+ """
+ if image_dict:
+ if var_config_values.get('image_name'):
+ image_creator = image_dict.get(var_config_values['image_name'])
+ if image_creator:
+ if (var_config_values.get('value')
+ and var_config_values['value'] == 'id'):
+ return image_creator.get_image().id
+ if (var_config_values.get('value')
+ and var_config_values['value'] == 'user'):
+ return image_creator.image_settings.image_user
+
+
+def __get_flavor_variable_value(var_config_values, flavor_dict):
+ """
+ Returns the associated flavor value
+ :param var_config_values: the configuration dictionary
+ :param flavor_dict: the dictionary containing all flavor creators where the
+ key is the name
+ :return: the value or None
+ """
+ if flavor_dict:
+ if var_config_values.get('flavor_name'):
+ flavor_creator = flavor_dict.get(var_config_values['flavor_name'])
+ if flavor_creator:
+ if (var_config_values.get('value')
+ and var_config_values['value'] == 'id'):
+ return flavor_creator.get_flavor().id
+
+
+def __cleanup(creators, clean_image=False):
+ """
+ Cleans up environment
+ :param creators: the list of creators by type
+ :param clean_image: when true
+ :return:
+ """
+ for creator_dict in reversed(creators):
+ for key, creator in creator_dict.items():
+ if ((isinstance(creator, OpenStackImage) and clean_image)
+ or not isinstance(creator, OpenStackImage)):
+ creator.clean()
--- /dev/null
+# 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.
+import logging
+
+from magnumclient.client import Client
+
+from snaps.domain.cluster_template import ClusterTemplate
+from snaps.openstack.utils import keystone_utils
+
+__author__ = 'spisarski'
+
+logger = logging.getLogger('magnum_utils')
+
+
+def magnum_client(os_creds):
+ """
+ Retrieves the Magnum client
+ :param os_creds: the OpenStack credentialsf
+ :return: the client
+ """
+ logger.debug('Retrieving Magnum Client')
+ return Client(str(os_creds.magnum_api_version),
+ session=keystone_utils.keystone_session(os_creds))
+
+
+def get_cluster_template(magnum, template_config=None, template_name=None):
+ """
+ Returns the first ClusterTemplate domain object that matches the parameters
+ :param magnum: the Magnum client
+ :param template_config: a ClusterTemplateConfig object (optional)
+ :param template_name: the name of the template to lookup
+ :return: ClusterTemplate object or None
+ """
+ name = None
+ if template_config:
+ name = template_config.name
+ elif template_name:
+ name = template_name
+
+ os_templates = magnum.cluster_templates.list()
+ for os_template in os_templates:
+ if os_template.name == name:
+ return __map_os_cluster_template(os_template)
+
+
+def get_cluster_template_by_id(magnum, tmplt_id):
+ """
+ Returns the first ClusterTemplate domain object that matches the parameters
+ :param magnum: the Magnum client
+ :param tmplt_id: the template's ID
+ :return: ClusterTemplate object or None
+ """
+ return __map_os_cluster_template(magnum.cluster_templates.get(tmplt_id))
+
+
+def create_cluster_template(magnum, cluster_template_config):
+ """
+ Creates a Magnum Cluster Template object in OpenStack
+ :param magnum: the Magnum client
+ :param cluster_template_config: a ClusterTemplateConfig object
+ :return: a SNAPS ClusterTemplate domain object
+ """
+ config_dict = cluster_template_config.magnum_dict()
+ os_cluster_template = magnum.cluster_templates.create(**config_dict)
+ logger.info('Creating cluster template named [%s]',
+ cluster_template_config.name)
+ return __map_os_cluster_template(os_cluster_template)
+
+
+def delete_cluster_template(magnum, tmplt_id):
+ """
+ Deletes a Cluster Template from OpenStack
+ :param magnum: the Magnum client
+ :param tmplt_id: the cluster template ID to delete
+ """
+ logger.info('Deleting cluster template with ID [%s]', tmplt_id)
+ magnum.cluster_templates.delete(tmplt_id)
+
+
+def __map_os_cluster_template(os_tmplt):
+ """
+ Returns a SNAPS ClusterTemplate object from an OpenStack ClusterTemplate
+ object
+ :param os_tmplt: the OpenStack ClusterTemplate object
+ :return: SNAPS ClusterTemplate object
+ """
+ return ClusterTemplate(
+ id=os_tmplt.uuid,
+ name=os_tmplt.name,
+ image=os_tmplt.image_id,
+ keypair=os_tmplt.keypair_id,
+ network_driver=os_tmplt.network_driver,
+ external_net=os_tmplt.external_network_id,
+ floating_ip_enabled=os_tmplt.floating_ip_enabled,
+ docker_volume_size=os_tmplt.docker_volume_size,
+ server_type=os_tmplt.server_type,
+ flavor=os_tmplt.flavor_id,
+ master_flavor=os_tmplt.master_flavor_id,
+ coe=os_tmplt.coe,
+ fixed_net=os_tmplt.fixed_network,
+ fixed_subnet=os_tmplt.fixed_subnet,
+ registry_enabled=os_tmplt.registry_enabled,
+ insecure_registry=os_tmplt.insecure_registry,
+ docker_storage_driver=os_tmplt.docker_storage_driver,
+ dns_nameserver=os_tmplt.dns_nameserver,
+ public=os_tmplt.public,
+ tls_disabled=os_tmplt.tls_disabled,
+ http_proxy=os_tmplt.http_proxy,
+ https_proxy=os_tmplt.https_proxy,
+ no_proxy=os_tmplt.no_proxy,
+ volume_driver=os_tmplt.volume_driver,
+ master_lb_enabled=os_tmplt.master_lb_enabled,
+ labels=os_tmplt.labels
+ )
:param network_settings: A dictionary containing the network configuration
and is responsible for creating the network
request JSON body
- :return: a SNAPS-OO Network domain object
+ :return: a SNAPS-OO Network domain object if found else None
"""
- if neutron and network_settings:
- logger.info('Creating network with name ' + network_settings.name)
- json_body = network_settings.dict_for_neutron(os_creds)
- os_network = neutron.create_network(body=json_body)
- return Network(**os_network['network'])
- else:
- raise NeutronException('Failded to create network')
+ logger.info('Creating network with name ' + network_settings.name)
+ json_body = network_settings.dict_for_neutron(os_creds)
+ os_network = neutron.create_network(body=json_body)
+
+ if os_network:
+ network = get_network_by_id(neutron, os_network['network']['id'])
+
+ subnets = list()
+ for subnet_settings in network_settings.subnet_settings:
+ try:
+ subnets.append(
+ create_subnet(neutron, subnet_settings, os_creds, network))
+ except:
+ logger.error(
+ 'Unexpected error creating subnet [%s] for network [%s]',
+ subnet_settings.name, network.name)
+
+ for subnet in subnets:
+ delete_subnet(neutron, subnet)
+
+ delete_network(neutron, network)
+
+ raise
+
+ return get_network_by_id(neutron, network.id)
def delete_network(neutron, network):
:param network: a SNAPS-OO Network domain object
"""
if neutron and network:
+ if network.subnets:
+ for subnet in network.subnets:
+ logger.info('Deleting subnet with name ' + subnet.name)
+ try:
+ delete_subnet(neutron, subnet)
+ except NotFound:
+ pass
+
logger.info('Deleting network with name ' + network.name)
neutron.delete_network(network.id)
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
- :param network_settings: the NetworkSettings object used to create filter
+ :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
:return: a SNAPS-OO Network domain object
networks = neutron.list_networks(**net_filter)
for network, netInsts in networks.items():
for inst in netInsts:
- return Network(**inst)
+ return __map_network(neutron, inst)
-def get_network_by_id(neutron, network_id):
+def __get_os_network_by_id(neutron, network_id):
"""
- Returns the network object (dictionary) with the given ID else None
+ Returns the OpenStack network object (dictionary) with the given ID else
+ None
:param neutron: the client
:param network_id: the id of the network to retrieve
:return: a SNAPS-OO Network domain object
networks = neutron.list_networks(**{'id': network_id})
for network in networks['networks']:
if network['id'] == network_id:
- return Network(**network)
+ return network
+
+
+def get_network_by_id(neutron, network_id):
+ """
+ Returns the SNAPS Network domain object for the given ID else None
+ :param neutron: the client
+ :param network_id: the id of the network to retrieve
+ :return: a SNAPS-OO Network domain object
+ """
+ os_network = __get_os_network_by_id(neutron, network_id)
+ if os_network:
+ return __map_network(neutron, os_network)
-def create_subnet(neutron, subnet_settings, os_creds, network=None):
+def __map_network(neutron, os_network):
+ """
+ Returns the network object (dictionary) with the given ID else None
+ :param neutron: the client
+ :param os_network: the OpenStack Network dict
+ :return: a SNAPS-OO Network domain object
+ """
+ subnets = get_subnets_by_network_id(neutron, os_network['id'])
+ os_network['subnets'] = subnets
+ return Network(**os_network)
+
+
+def create_subnet(neutron, subnet_settings, os_creds, network):
"""
Creates a network subnet for OpenStack
:param neutron: the client
- :param network: the network object
:param subnet_settings: A dictionary containing the subnet configuration
and is responsible for creating the subnet request
JSON body
:param os_creds: the OpenStack credentials
+ :param network: the network object
:return: a SNAPS-OO Subnet domain object
"""
if neutron and network and subnet_settings:
:param network: the SNAPS-OO Network domain object
:return: a list of Subnet objects
"""
+ return get_subnets_by_network_id(neutron, network.id)
+
+
+def get_subnets_by_network_id(neutron, network_id):
+ """
+ Returns a list of SNAPS-OO Subnet domain objects
+ :param neutron: the OpenStack neutron client
+ :param network_id: the subnet's ID
+ :return: a list of Subnet objects
+ """
out = list()
- os_subnets = neutron.list_subnets(network_id=network.id)
+ os_subnets = neutron.list_subnets(network_id=network_id)
for os_subnet in os_subnets['subnets']:
out.append(Subnet(**os_subnet))
json_body = router_settings.dict_for_neutron(neutron, os_creds)
logger.info('Creating router with name - ' + router_settings.name)
os_router = neutron.create_router(json_body)
- return Router(**os_router['router'])
+ return __map_router(neutron, os_router['router'])
else:
logger.error("Failed to create router.")
raise NeutronException('Failed to create router')
"""
router = neutron.show_router(router_id)
if router:
- return Router(**router['router'])
+ return __map_router(neutron, router['router'])
def get_router(neutron, router_settings=None, router_name=None):
values if not None, else finds the first with the value of the router_name
parameter, else None
:param neutron: the client
- :param router_settings: the RouterSettings object
+ :param router_settings: the RouterConfig object
:param router_name: the name of the network to retrieve
:return: a SNAPS-OO Router domain object
"""
return None
routers = neutron.list_routers(**router_filter)
+
for routerInst in routers['routers']:
- return Router(**routerInst)
+ return __map_router(neutron, routerInst)
+
return None
+def __map_router(neutron, os_router):
+ """
+ Takes an OpenStack router instance and maps it to a SNAPS Router domain
+ object
+ :param neutron: the neutron client
+ :param os_router: the OpenStack Router object
+ :return:
+ """
+ device_ports = neutron.list_ports(
+ **{'device_id': os_router['id']})['ports']
+ port_subnets = list()
+
+ # Order by create date
+ sorted_ports = sorted(
+ device_ports, key=lambda dev_port: dev_port['created_at'])
+
+ for port in sorted_ports:
+ subnets = list()
+ for fixed_ip in port['fixed_ips']:
+ subnet = get_subnet_by_id(neutron, fixed_ip['subnet_id'])
+ if subnet and subnet.network_id == port['network_id']:
+ subnets.append(subnet)
+ port_subnets.append((Port(**port), subnets))
+
+ os_router['port_subnets'] = port_subnets
+ return Router(**os_router)
+
+
def add_interface_router(neutron, router, subnet=None, port=None):
"""
Adds an interface router for OpenStack for either a subnet or port.
"""
Returns the first port object (dictionary) found for the given query
:param neutron: the client
- :param port_settings: the PortSettings object used for generating the query
+ :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
:return: a SNAPS-OO Port domain object
if port_settings.network_name:
network = get_network(neutron,
network_name=port_settings.network_name)
- port_filter['network_id'] = network.id
+ if network:
+ port_filter['network_id'] = network.id
elif port_name:
port_filter['name'] = port_name
sec_grp_settings.name)
os_group = neutron.create_security_group(
sec_grp_settings.dict_for_neutron(keystone))
- return SecurityGroup(**os_group['security_group'])
+ return __map_os_security_group(neutron, os_group['security_group'])
def delete_security_group(neutron, sec_grp):
the security group will be used, else if the query parameters are None then
None will be returned
:param neutron: the client
- :param sec_grp_settings: an instance of SecurityGroupSettings config object
+ :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
secuity group to retrieve
groups = neutron.list_security_groups(**sec_grp_filter)
for group in groups['security_groups']:
- return SecurityGroup(**group)
+ return __map_os_security_group(neutron, group)
+
+
+def __map_os_security_group(neutron, os_sec_grp):
+ """
+ Creates a SecurityGroup SNAPS domain object from an OpenStack Security
+ Group dict
+ :param neutron: the neutron client for performing rule lookups
+ :param os_sec_grp: the OpenStack Security Group dict object
+ :return: a SecurityGroup object
+ """
+ os_sec_grp['rules'] = get_rules_by_security_group_id(
+ neutron, os_sec_grp['id'])
+ return SecurityGroup(**os_sec_grp)
def get_security_group_by_id(neutron, sec_grp_id):
groups = neutron.list_security_groups(**{'id': sec_grp_id})
for group in groups['security_groups']:
if group['id'] == sec_grp_id:
- return SecurityGroup(**group)
+ return __map_os_security_group(neutron, group)
return None
def create_security_group_rule(neutron, sec_grp_rule_settings):
"""
- Creates a security group object in OpenStack
+ Creates a security group rule in OpenStack
:param neutron: the client
:param sec_grp_rule_settings: the security group rule settings
:return: a SNAPS-OO SecurityGroupRule domain object
def delete_security_group_rule(neutron, sec_grp_rule):
"""
- Deletes a security group object from OpenStack
+ Deletes a security group rule object from OpenStack
:param neutron: the client
:param sec_grp_rule: the SNAPS SecurityGroupRule object to delete
"""
:param neutron: the client
:param sec_grp: a list of SNAPS SecurityGroupRule domain objects
"""
+ return get_rules_by_security_group_id(neutron, sec_grp.id)
+
+
+def get_rules_by_security_group_id(neutron, sec_grp_id):
+ """
+ Retrieves all of the rules for a given security group by it's ID
+ :param neutron: the client
+ :param sec_grp_id: the ID of the associated security group
+ """
logger.info('Retrieving security group rules associate with the '
- 'security group - %s', sec_grp.name)
+ 'security group with ID - %s', sec_grp_id)
out = list()
rules = neutron.list_security_group_rules(
- **{'security_group_id': sec_grp.id})
+ **{'security_group_id': sec_grp_id})
for rule in rules['security_group_rules']:
- if rule['security_group_id'] == sec_grp.id:
+ if rule['security_group_id'] == sec_grp_id:
out.append(SecurityGroupRule(**rule))
return out
def get_rule_by_id(neutron, sec_grp, rule_id):
"""
- Deletes a security group object from OpenStack
+ Returns a SecurityGroupRule object from OpenStack
:param neutron: the client
:param sec_grp: the SNAPS SecurityGroup domain object
:param rule_id: the rule's ID
out = list()
for network in neutron.list_networks(
**{'router:external': True})['networks']:
- out.append(Network(**network))
+ out.append(__map_network(neutron, network))
return out
:param ports: a list of tuple 2 where index 0 is the port name and index 1
is the SNAPS-OO Port object
:return: a list of tuple 2 (port_id, SNAPS FloatingIp) objects when ports
- is not None else a list of Port objects
+ is not None else a list of FloatingIp objects
"""
out = list()
fips = neutron.list_floatingips()
def get_network_quotas(neutron, project_id):
"""
- Returns a list of all available keypairs
+ Returns a list of NetworkQuotas objects
:param neutron: the neutron client
:param project_id: the project's ID of the quotas to lookup
:return: an object of type NetworkQuotas or None if not found
import logging
+import enum
import os
+import time
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from novaclient.client import Client
-from novaclient.exceptions import NotFound
+from novaclient.exceptions import NotFound, ClientException
from snaps import file_utils
from snaps.domain.flavor import Flavor
region_name=os_creds.region_name)
-def create_server(nova, neutron, glance, instance_settings, image_settings,
- keypair_settings=None):
+def create_server(nova, neutron, glance, instance_config, image_config,
+ keypair_config=None):
"""
Creates a VM instance
:param nova: the nova client (required)
:param neutron: the neutron client for retrieving ports (required)
:param glance: the glance client (required)
- :param instance_settings: the VM instance settings object (required)
- :param image_settings: the VM's image settings object (required)
- :param keypair_settings: the VM's keypair settings object (optional)
+ :param instance_config: the VMInstConfig object (required)
+ :param image_config: the VM's ImageConfig object (required)
+ :param keypair_config: the VM's KeypairConfig object (optional)
:return: a snaps.domain.VmInst object
"""
ports = list()
- for port_setting in instance_settings.port_settings:
+ for port_setting in instance_config.port_settings:
ports.append(neutron_utils.get_port(
neutron, port_settings=port_setting))
nics = []
kv['port-id'] = port.id
nics.append(kv)
- logger.info('Creating VM with name - ' + instance_settings.name)
+ logger.info('Creating VM with name - ' + instance_config.name)
keypair_name = None
- if keypair_settings:
- keypair_name = keypair_settings.name
+ if keypair_config:
+ keypair_name = keypair_config.name
- flavor = get_flavor_by_name(nova, instance_settings.flavor)
+ flavor = get_flavor_by_name(nova, instance_config.flavor)
if not flavor:
raise NovaException(
- 'Flavor not found with name - %s', instance_settings.flavor)
+ 'Flavor not found with name - %s', instance_config.flavor)
- image = glance_utils.get_image(glance, image_settings=image_settings)
+ image = glance_utils.get_image(glance, image_settings=image_config)
if image:
userdata = None
- if instance_settings.userdata:
- if isinstance(instance_settings.userdata, str):
- userdata = instance_settings.userdata + '\n'
- elif (isinstance(instance_settings.userdata, dict) and
- 'script_file' in instance_settings.userdata):
+ if instance_config.userdata:
+ if isinstance(instance_config.userdata, str):
+ userdata = instance_config.userdata + '\n'
+ elif (isinstance(instance_config.userdata, dict) and
+ 'script_file' in instance_config.userdata):
try:
userdata = file_utils.read_file(
- instance_settings.userdata['script_file'])
+ instance_config.userdata['script_file'])
except Exception as e:
logger.warn('error reading userdata file %s - %s',
- instance_settings.userdata, e)
- args = {'name': instance_settings.name,
+ instance_config.userdata, e)
+ args = {'name': instance_config.name,
'flavor': flavor,
'image': image,
'nics': nics,
'key_name': keypair_name,
'security_groups':
- instance_settings.security_group_names,
+ instance_config.security_group_names,
'userdata': userdata}
- if instance_settings.availability_zone:
- args['availability_zone'] = instance_settings.availability_zone
+ if instance_config.availability_zone:
+ args['availability_zone'] = instance_config.availability_zone
server = nova.servers.create(**args)
- return __map_os_server_obj_to_vm_inst(server)
+ return __map_os_server_obj_to_vm_inst(neutron, server)
else:
raise NovaException(
'Cannot create instance, image cannot be located with name %s',
- image_settings.name)
+ image_config.name)
-def get_server(nova, vm_inst_settings=None, server_name=None):
+def get_server(nova, neutron, vm_inst_settings=None, server_name=None):
"""
Returns a VmInst object for the first server instance found.
:param nova: the Nova client
- :param vm_inst_settings: the VmInstanceSettings object from which to build
+ :param neutron: the Neutron 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
servers = nova.servers.list(search_opts=search_opts)
for server in servers:
- return __map_os_server_obj_to_vm_inst(server)
+ return __map_os_server_obj_to_vm_inst(neutron, server)
-def __map_os_server_obj_to_vm_inst(os_server):
+def get_server_connection(nova, vm_inst_settings=None, server_name=None):
+ """
+ Returns a VmInst object for the first server instance found.
+ :param nova: the Nova 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
+ :return: a snaps.domain.VmInst object or None if not found
+ """
+ search_opts = dict()
+ if vm_inst_settings:
+ search_opts['name'] = vm_inst_settings.name
+ elif server_name:
+ search_opts['name'] = server_name
+
+ servers = nova.servers.list(search_opts=search_opts)
+ for server in servers:
+ return server.links[0]
+
+
+def __map_os_server_obj_to_vm_inst(neutron, os_server):
"""
Returns a VmInst object for an OpenStack Server object
+ :param neutron: the Neutron client (when None, ports will be empty)
:param os_server: the OpenStack server object
:return: an equivalent SNAPS-OO VmInst domain object
"""
if sec_group.get('name'):
sec_grp_names.append(sec_group.get('name'))
+ 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)
+
+ volumes = None
+ if hasattr(os_server, 'os-extended-volumes:volumes_attached'):
+ volumes = getattr(os_server, 'os-extended-volumes:volumes_attached')
+
return VmInst(
name=os_server.name, inst_id=os_server.id,
image_id=os_server.image['id'], flavor_id=os_server.flavor['id'],
- networks=os_server.networks, keypair_name=os_server.key_name,
- sec_grp_names=sec_grp_names)
+ ports=out_ports, keypair_name=os_server.key_name,
+ sec_grp_names=sec_grp_names, volume_ids=volumes)
def __get_latest_server_os_object(nova, server):
return None
-def get_latest_server_object(nova, server):
+def get_latest_server_object(nova, neutron, server):
"""
Returns a server with a given id
:param nova: the Nova client
+ :param neutron: the Neutron client
:param server: the old server object
: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(server)
+ return __map_os_server_obj_to_vm_inst(neutron, server)
-def get_server_object_by_id(nova, server_id):
+def get_server_object_by_id(nova, neutron, server_id):
"""
Returns a server with a given id
:param nova: the Nova client
+ :param neutron: the Neutron client
:param server_id: the server's id
: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(server)
+ return __map_os_server_obj_to_vm_inst(neutron, server)
def get_server_security_group_names(nova, server):
return None
+def reboot_server(nova, server, reboot_type=None):
+ """
+ Returns a dictionary of a VMs info as returned by OpenStack
+ :param nova: the Nova client
+ :param server: the old server object
+ :param reboot_type: Acceptable values 'SOFT', 'HARD'
+ (api uses SOFT as the default)
+ :return: a dict of the info if VM exists else None
+ """
+ vm = __get_latest_server_os_object(nova, server)
+ if vm:
+ vm.reboot(reboot_type=reboot_type.value)
+ else:
+ raise ServerNotFoundError('Cannot locate server')
+
+
def create_keys(key_size=2048):
"""
Generates public and private keys
return None
+def get_keypair_by_id(nova, kp_id):
+ """
+ Returns a list of all available keypairs
+ :param nova: the Nova client
+ :param kp_id: the ID of the keypair to return
+ :return: the keypair object
+ """
+ keypair = nova.keypairs.get(kp_id)
+ return Keypair(name=keypair.name, kp_id=keypair.id,
+ public_key=keypair.public_key)
+
+
def delete_keypair(nova, key):
"""
Deletes a keypair object from OpenStack
return out
+def get_hypervisor_hosts(nova):
+ """
+ Returns the host names of all nova nodes with active hypervisors
+ :param nova: the Nova client
+ :return: a list of hypervisor host names
+ """
+ out = list()
+ hypervisors = nova.hypervisors.list()
+ for hypervisor in hypervisors:
+ if hypervisor.state == "up":
+ out.append(hypervisor.hypervisor_hostname)
+
+ return out
+
+
def delete_vm_instance(nova, vm_inst):
"""
Deletes a VM instance
:param vm: the OpenStack server object (VM) to alter
:param security_group_name: the name of the security group to add
"""
- nova.servers.add_security_group(str(vm.id), security_group_name)
+ try:
+ nova.servers.add_security_group(str(vm.id), security_group_name)
+ except ClientException as e:
+ sec_grp_names = get_server_security_group_names(nova, vm)
+ if security_group_name in sec_grp_names:
+ logger.warn('Security group [%s] already added to VM [%s]',
+ security_group_name, vm.name)
+ return
+
+ logger.error('Unexpected error while adding security group [%s] - %s',
+ security_group_name, e)
+ raise
def remove_security_group(nova, vm, security_group):
update_values['cores'] = compute_quotas.cores
update_values['instances'] = compute_quotas.instances
update_values['injected_files'] = compute_quotas.injected_files
- update_values['injected_file_content_bytes'] = compute_quotas.injected_file_content_bytes
+ update_values['injected_file_content_bytes'] = (
+ compute_quotas.injected_file_content_bytes)
update_values['ram'] = compute_quotas.ram
update_values['fixed_ips'] = compute_quotas.fixed_ips
update_values['key_pairs'] = compute_quotas.key_pairs
return nova.quotas.update(project_id, **update_values)
+def attach_volume(nova, neutron, server, volume, timeout=None):
+ """
+ Attaches a volume to a server
+ :param nova: the nova client
+ :param neutron: the neutron client
+ :param server: the VMInst domain object
+ :param volume: the Volume domain object
+ :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
+ """
+ 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
+
+ return None
+ else:
+ return get_server_object_by_id(nova, neutron, server.id)
+
+
+def detach_volume(nova, neutron, server, volume, timeout=None):
+ """
+ Attaches a volume to a server
+ :param nova: the nova client
+ :param neutron: the neutron client
+ :param server: the VMInst domain object
+ :param volume: the Volume domain object
+ :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
+ """
+ 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
+ for vol_dict in vm.volume_ids:
+ if volume.id == vol_dict['id']:
+ found = True
+
+ if not found:
+ return vm
+
+ return None
+ else:
+ return get_server_object_by_id(nova, neutron, server.id)
+
+
+class RebootType(enum.Enum):
+ """
+ A rule's direction
+ """
+ soft = 'SOFT'
+ hard = 'HARD'
+
+
class NovaException(Exception):
"""
Exception when calls to the Keystone client cannot be served properly
"""
+
+
+class ServerNotFoundError(Exception):
+ """
+ Exception when operations to a VM/Server is requested and the OpenStack
+ Server instance cannot be located
+ """
import uuid
from snaps import file_utils
-from snaps.openstack.create_instance import (
- VmInstanceSettings, FloatingIpSettings)
-from snaps.openstack.create_keypairs import KeypairSettings
-from snaps.openstack.create_network import (
- PortSettings, SubnetSettings, NetworkSettings)
+from snaps.config.flavor import FlavorConfig
+from snaps.config.keypair import KeypairConfig
+from snaps.config.network import SubnetConfig, PortConfig, NetworkConfig
+from snaps.config.router import RouterConfig
+from snaps.config.security_group import (
+ SecurityGroupRuleConfig, SecurityGroupConfig)
+from snaps.config.vm_inst import VmInstanceConfig, FloatingIpConfig
+from snaps.config.volume import VolumeConfig
+from snaps.config.volume_type import (
+ ControlLocation, VolumeTypeEncryptionConfig, VolumeTypeConfig)
from snaps.openstack.utils import (
neutron_utils, nova_utils, heat_utils, glance_utils)
-def create_network_settings(neutron, network):
+def create_network_config(neutron, network):
"""
- Returns a NetworkSettings object
+ Returns a NetworkConfig object
:param neutron: the neutron client
:param network: a SNAPS-OO Network domain object
:return:
"""
- return NetworkSettings(
+ return NetworkConfig(
name=network.name, network_type=network.type,
- subnet_settings=create_subnet_settings(neutron, network))
+ subnet_settings=create_subnet_config(neutron, network))
-def create_subnet_settings(neutron, network):
+def create_security_group_config(neutron, security_group):
"""
- Returns a list of SubnetSettings objects for a given network
+ Returns a SecurityGroupConfig object
+ :param neutron: the neutron client
+ :param security_group: a SNAPS-OO SecurityGroup domain object
+ :return:
+ """
+ rules = neutron_utils.get_rules_by_security_group(neutron, security_group)
+
+ rule_settings = list()
+ for rule in rules:
+ rule_settings.append(SecurityGroupRuleConfig(
+ sec_grp_name=security_group.name, description=rule.description,
+ direction=rule.direction, ethertype=rule.ethertype,
+ port_range_min=rule.port_range_min,
+ port_range_max=rule.port_range_max, protocol=rule.protocol,
+ remote_group_id=rule.remote_group_id,
+ remote_ip_prefix=rule.remote_ip_prefix))
+
+ return SecurityGroupConfig(
+ name=security_group.name, description=security_group.description,
+ rule_settings=rule_settings)
+
+
+def create_subnet_config(neutron, network):
+ """
+ Returns a list of SubnetConfig objects for a given network
:param neutron: the OpenStack neutron client
:param network: the SNAPS-OO Network domain object
:return: a list
kwargs['host_routes'] = subnet.host_routes
kwargs['ipv6_ra_mode'] = subnet.ipv6_ra_mode
kwargs['ipv6_address_mode'] = subnet.ipv6_address_mode
- out.append(SubnetSettings(**kwargs))
+ out.append(SubnetConfig(**kwargs))
return out
-def create_vm_inst_settings(nova, neutron, server):
+def create_router_config(neutron, router):
+ """
+ Returns a RouterConfig object
+ :param neutron: the neutron client
+ :param router: a SNAPS-OO Router domain object
+ :return:
+ """
+ ext_net_name = None
+
+ if router.external_network_id:
+ network = neutron_utils.get_network_by_id(
+ neutron, router.external_network_id)
+ if network:
+ ext_net_name = network.name
+
+ out_ports = list()
+ if router.port_subnets:
+ for port, subnets in router.port_subnets:
+ network = neutron_utils.get_network_by_id(
+ neutron, port.network_id)
+
+ ip_addrs = list()
+ if network and router.external_fixed_ips:
+ for ext_fixed_ips in router.external_fixed_ips:
+ for subnet in subnets:
+ if ext_fixed_ips['subnet_id'] == subnet.id:
+ ip_addrs.append(ext_fixed_ips['ip_address'])
+ else:
+ for ip in port.ips:
+ ip_addrs.append(ip['ip_address'])
+
+ ports = neutron_utils.get_ports(neutron, network, ip_addrs)
+ for out_port in ports:
+ out_ports.append(out_port)
+
+ port_settings = __create_port_configs(neutron, out_ports)
+
+ filtered_settings = list()
+ for port_setting in port_settings:
+ if port_setting.network_name != ext_net_name:
+ filtered_settings.append(port_setting)
+
+ return RouterConfig(
+ name=router.name, external_gateway=ext_net_name,
+ admin_state_up=router.admin_state_up,
+ port_settings=filtered_settings)
+
+
+def create_volume_config(volume):
+ """
+ Returns a VolumeConfig object
+ :param volume: a SNAPS-OO Volume object
+ """
+
+ return VolumeConfig(
+ name=volume.name, description=volume.description,
+ size=volume.size, type_name=volume.type,
+ availability_zone=volume.availability_zone,
+ multi_attach=volume.multi_attach)
+
+
+def create_volume_type_config(volume_type):
"""
- Returns a NetworkSettings object
+ Returns a VolumeTypeConfig object
+ :param volume_type: a SNAPS-OO VolumeType object
+ """
+
+ control = None
+ if volume_type.encryption:
+ if (volume_type.encryption.control_location
+ == ControlLocation.front_end.value):
+ control = ControlLocation.front_end
+ else:
+ control = ControlLocation.back_end
+
+ if volume_type and volume_type.encryption:
+ encrypt_settings = VolumeTypeEncryptionConfig(
+ name=volume_type.encryption.__class__,
+ provider_class=volume_type.encryption.provider,
+ control_location=control,
+ cipher=volume_type.encryption.cipher,
+ key_size=volume_type.encryption.key_size)
+ else:
+ encrypt_settings = None
+
+ qos_spec_name = None
+ if volume_type.qos_spec:
+ qos_spec_name = volume_type.qos_spec.name
+
+ return VolumeTypeConfig(
+ name=volume_type.name, encryption=encrypt_settings,
+ qos_spec_name=qos_spec_name, public=volume_type.public)
+
+
+def create_flavor_config(flavor):
+ """
+ Returns a FlavorConfig object
+ :param flavor: a FlavorConfig object
+ """
+ return FlavorConfig(
+ name=flavor.name, flavor_id=flavor.id, ram=flavor.ram,
+ disk=flavor.disk, vcpus=flavor.vcpus, ephemeral=flavor.ephemeral,
+ swap=flavor.swap, rxtx_factor=flavor.rxtx_factor,
+ is_public=flavor.is_public)
+
+
+def create_keypair_config(heat_cli, stack, keypair, pk_output_key):
+ """
+ Instantiates a KeypairConfig object from a Keypair domain objects
+ :param heat_cli: the heat client
+ :param stack: the Stack domain object
+ :param keypair: the Keypair SNAPS domain object
+ :param pk_output_key: the key to the heat template's outputs for retrieval
+ of the private key file
+ :return: a KeypairConfig object
+ """
+ if pk_output_key:
+ outputs = heat_utils.get_outputs(heat_cli, stack)
+ for output in outputs:
+ if output.key == pk_output_key:
+ # Save to file
+ guid = uuid.uuid4()
+ key_file = file_utils.save_string_to_file(
+ output.value, str(guid), 0o400)
+
+ # Use outputs, file and resources for the KeypairConfig
+ return KeypairConfig(
+ name=keypair.name, private_filepath=key_file.name)
+
+ return KeypairConfig(name=keypair.name)
+
+
+def create_vm_inst_config(nova, neutron, server):
+ """
+ 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 neutron: the neutron client
:param server: a SNAPS-OO VmInst domain object
kwargs = dict()
kwargs['name'] = server.name
kwargs['flavor'] = flavor_name
- kwargs['port_settings'] = __create_port_settings(
- neutron, server.networks)
+
+ kwargs['port_settings'] = __create_port_configs(neutron, server.ports)
kwargs['security_group_names'] = server.sec_grp_names
- kwargs['floating_ip_settings'] = __create_floatingip_settings(
+ kwargs['floating_ip_settings'] = __create_floatingip_config(
neutron, kwargs['port_settings'])
- return VmInstanceSettings(**kwargs)
+ return VmInstanceConfig(**kwargs)
-def __create_port_settings(neutron, networks):
+def __create_port_configs(neutron, ports):
"""
- Returns a list of port settings based on the networks parameter
+ Returns a list of PortConfig objects based on the networks parameter
:param neutron: the neutron client
- :param networks: a dict where the key is the network name and the value
- is a list of IP addresses
+ :param ports: a list of SNAPS-OO Port domain objects
:return:
"""
out = list()
- for net_name, ips in networks.items():
- network = neutron_utils.get_network(neutron, network_name=net_name)
- ports = neutron_utils.get_ports(neutron, network, ips)
- for port in ports:
+ for port in ports:
+ if port.device_owner != 'network:dhcp':
+ ip_addrs = list()
+ for ip_dict in port.ips:
+ subnet = neutron_utils.get_subnet_by_id(
+ neutron, ip_dict['subnet_id'])
+ ip_addrs.append({'subnet_name': subnet.name,
+ 'ip': ip_dict['ip_address']})
+
+ network = neutron_utils.get_network_by_id(neutron, port.network_id)
kwargs = dict()
if port.name:
kwargs['name'] = port.name
kwargs['mac_address'] = port.mac_address
kwargs['allowed_address_pairs'] = port.allowed_address_pairs
kwargs['admin_state_up'] = port.admin_state_up
- out.append(PortSettings(**kwargs))
+ kwargs['ip_addrs'] = ip_addrs
+ out.append(PortConfig(**kwargs))
return out
-def __create_floatingip_settings(neutron, port_settings):
+def __create_floatingip_config(neutron, port_settings):
"""
- Returns a list of FloatingIPSettings objects as they pertain to an
+ Returns a list of FloatingIpConfig objects as they pertain to an
existing deployed server instance
:param neutron: the neutron client
- :param port_settings: list of SNAPS-OO PortSettings objects
- :return: a list of FloatingIPSettings objects or an empty list if no
+ :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
"""
base_fip_name = 'fip-'
if subnet:
kwargs['subnet_name'] = subnet.name
- out.append(FloatingIpSettings(**kwargs))
+ out.append(FloatingIpConfig(**kwargs))
fip_ctr += 1
return out
-def determine_image_settings(glance, server, image_settings):
+def determine_image_config(glance, server, image_settings):
"""
- Returns a ImageSettings object from the list that matches the name in one
+ Returns a ImageConfig object from the list that matches the name in one
of the image_settings parameter
:param glance: the glance client
:param server: a SNAPS-OO VmInst domain object
- :param image_settings: list of ImageSettings objects
- :return: ImageSettings or None
+ :param image_settings: list of ImageConfig objects
+ :return: ImageConfig or None
"""
if image_settings:
for image_setting in image_settings:
return image_setting
-def determine_keypair_settings(heat_cli, stack, server, keypair_settings=None,
- priv_key_key=None):
+def determine_keypair_config(heat_cli, stack, server, keypair_settings=None,
+ priv_key_key=None):
"""
- Returns a KeypairSettings object from the list that matches the
+ Returns a KeypairConfig object from the list that matches the
server.keypair_name value in the keypair_settings parameter if not None,
else if the output_key is not None, the output's value when contains the
string 'BEGIN RSA PRIVATE KEY', this value will be stored into a file and
- encoded into the KeypairSettings object returned
+ encoded into the KeypairConfig object returned
:param heat_cli: the OpenStack heat client
:param stack: a SNAPS-OO Stack domain object
:param server: a SNAPS-OO VmInst domain object
- :param keypair_settings: list of KeypairSettings objects
+ :param keypair_settings: list of KeypairConfig objects
:param priv_key_key: the stack options that holds the private key value
- :return: KeypairSettings or None
+ :return: KeypairConfig or None
"""
# Existing keypair being used by Heat Template
if keypair_settings:
key_file = file_utils.save_string_to_file(
output.value, str(guid), 0o400)
- # Use outputs, file and resources for the KeypairSettings
- return KeypairSettings(
+ # Use outputs, file and resources for the KeypairConfig
+ return KeypairConfig(
name=server.keypair_name, private_filepath=key_file.name)
import logging
import uuid
+import time
from cinderclient.exceptions import NotFound, BadRequest
-from snaps.openstack.create_qos import QoSSettings, Consumer
-from snaps.openstack.create_volume_type import (
- VolumeTypeSettings, VolumeTypeEncryptionSettings, ControlLocation)
+from snaps.config.volume import VolumeConfig
+from snaps.config.volume_type import (
+ VolumeTypeConfig, ControlLocation, VolumeTypeEncryptionConfig)
+from snaps.config.qos import Consumer, QoSConfig
+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
cinder.volumes.list()
+class CinderUtilsVolumeTests(OSComponentTestCase):
+ """
+ Test for the CreateVolume class defined in create_volume.py
+ """
+
+ def setUp(self):
+ """
+ Instantiates the CreateVolume object that is responsible for
+ downloading and creating an OS volume file within OpenStack
+ """
+ guid = uuid.uuid4()
+ self.volume_name = self.__class__.__name__ + '-' + str(guid)
+ self.volume = None
+ self.cinder = cinder_utils.cinder_client(self.os_creds)
+
+ def tearDown(self):
+ """
+ Cleans the remote OpenStack objects
+ """
+ if self.volume:
+ try:
+ cinder_utils.delete_volume(self.cinder, self.volume)
+ except NotFound:
+ pass
+
+ self.assertTrue(volume_deleted(self.cinder, self.volume))
+
+ def test_create_simple_volume(self):
+ """
+ Tests the cinder_utils.create_volume()
+ """
+ volume_settings = VolumeConfig(name=self.volume_name)
+ self.volume = cinder_utils.create_volume(
+ self.cinder, 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.assertIsNotNone(volume)
+ validation_utils.objects_equivalent(self.volume, volume)
+
+ def test_create_delete_volume(self):
+ """
+ Tests the cinder_utils.create_volume()
+ """
+ volume_settings = VolumeConfig(name=self.volume_name)
+ self.volume = cinder_utils.create_volume(
+ self.cinder, 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.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))
+
+
+def volume_active(cinder, volume):
+ """
+ Returns true if volume becomes active
+ :param cinder:
+ :param volume:
+ :return:
+ """
+ end_time = time.time() + create_volume.VOLUME_ACTIVE_TIMEOUT
+ while time.time() < end_time:
+ status = cinder_utils.get_volume_status(cinder, volume)
+ if status == create_volume.STATUS_ACTIVE:
+ return True
+ elif status == create_volume.STATUS_FAILED:
+ return False
+ time.sleep(3)
+
+ return False
+
+
+def volume_deleted(cinder, volume):
+ """
+ Returns true if volume becomes active
+ :param cinder:
+ :param volume:
+ :return:
+ """
+ end_time = time.time() + create_volume.VOLUME_ACTIVE_TIMEOUT
+ while time.time() < end_time:
+ try:
+ status = cinder_utils.get_volume_status(cinder, volume)
+ if status == create_volume.STATUS_DELETED:
+ return True
+ except NotFound:
+ return True
+
+ time.sleep(3)
+
+ return False
+
+
class CinderUtilsQoSTests(OSComponentTestCase):
"""
Test for the CreateQos class defined in create_qos.py
"""
Tests the cinder_utils.create_qos()
"""
- qos_settings = QoSSettings(name=self.qos_name, specs=self.specs,
- consumer=Consumer.both)
+ qos_settings = QoSConfig(
+ name=self.qos_name, specs=self.specs, consumer=Consumer.both)
self.qos = cinder_utils.create_qos(self.cinder, qos_settings)
self.assertIsNotNone(self.qos)
"""
Tests the cinder_utils.create_qos()
"""
- qos_settings = QoSSettings(name=self.qos_name, specs=self.specs,
- consumer=Consumer.front_end)
+ qos_settings = QoSConfig(
+ name=self.qos_name, specs=self.specs, consumer=Consumer.front_end)
self.qos = cinder_utils.create_qos(self.cinder, qos_settings)
self.assertIsNotNone(self.qos)
"""
Tests the cinder_utils.create_qos()
"""
- qos_settings = QoSSettings(name=self.qos_name, specs=self.specs,
- consumer=Consumer.back_end)
+ qos_settings = QoSConfig(
+ name=self.qos_name, specs=self.specs, consumer=Consumer.back_end)
self.qos = cinder_utils.create_qos(self.cinder, qos_settings)
self.assertIsNotNone(self.qos)
"""
Tests the cinder_utils.create_qos()
"""
- qos_settings = QoSSettings(name=self.qos_name, consumer=Consumer.both)
+ qos_settings = QoSConfig(name=self.qos_name, consumer=Consumer.both)
self.qos = cinder_utils.create_qos(self.cinder, qos_settings)
self.assertIsNotNone(self.qos)
self.assertEqual(self.qos_name, self.qos.name)
"""
guid = uuid.uuid4()
volume_type_name = self.__class__.__name__ + '-' + str(guid)
- self.volume_type_settings = VolumeTypeSettings(name=volume_type_name)
+ self.volume_type_settings = VolumeTypeConfig(name=volume_type_name)
self.volume_type = None
self.cinder = cinder_utils.cinder_client(self.os_creds)
volume_type_name = self.__class__.__name__ + '-' + str(guid) + '-type'
self.volume_type = cinder_utils.create_volume_type(
- self.cinder, VolumeTypeSettings(name=volume_type_name))
+ self.cinder, VolumeTypeConfig(name=volume_type_name))
def tearDown(self):
"""
Tests the cinder_utils.create_volume_encryption(),
get_volume_encryption(), and get_volume_encryption_by_id()
"""
- encryption_settings = VolumeTypeEncryptionSettings(
+ encryption_settings = VolumeTypeEncryptionConfig(
name=self.encryption_name, provider_class='foo',
control_location=ControlLocation.front_end)
self.encryption = cinder_utils.create_volume_encryption(
"""
Primarily tests the cinder_utils.delete_volume_type_encryption()
"""
- encryption_settings = VolumeTypeEncryptionSettings(
+ encryption_settings = VolumeTypeEncryptionConfig(
name=self.encryption_name, provider_class='LuksEncryptor',
control_location=ControlLocation.back_end)
self.encryption = cinder_utils.create_volume_encryption(
Tests the cinder_utils.create_volume_encryption() with all valid
settings
"""
- encryption_settings = VolumeTypeEncryptionSettings(
+ encryption_settings = VolumeTypeEncryptionConfig(
name=self.encryption_name, provider_class='foo',
cipher='bar', control_location=ControlLocation.back_end,
key_size=1)
Tests the cinder_utils.create_volume_encryption() raises an exception
when the provider class does not exist
"""
- encryption_settings = VolumeTypeEncryptionSettings(
+ encryption_settings = VolumeTypeEncryptionConfig(
name=self.encryption_name, provider_class='foo',
cipher='bar', control_location=ControlLocation.back_end,
key_size=-1)
self.vol_type_name = self.__class__.__name__ + '-' + str(guid)
self.specs = {'foo': 'bar'}
self.cinder = cinder_utils.cinder_client(self.os_creds)
- qos_settings = QoSSettings(name=self.qos_name, specs=self.specs,
- consumer=Consumer.both)
+ qos_settings = QoSConfig(
+ name=self.qos_name, specs=self.specs, consumer=Consumer.both)
self.qos = cinder_utils.create_qos(self.cinder, qos_settings)
self.volume_type = None
Tests the cinder_utils.create_volume_type() where encryption has been
configured
"""
- encryption_settings = VolumeTypeEncryptionSettings(
+ encryption_settings = VolumeTypeEncryptionConfig(
name='foo', provider_class='bar',
control_location=ControlLocation.back_end)
- volume_type_settings = VolumeTypeSettings(
+ volume_type_settings = VolumeTypeConfig(
name=self.vol_type_name, encryption=encryption_settings)
self.volume_type = cinder_utils.create_volume_type(
self.cinder, volume_type_settings)
"""
Tests the cinder_utils.create_volume_type() with an associated QoS Spec
"""
- volume_type_settings = VolumeTypeSettings(
+ volume_type_settings = VolumeTypeConfig(
name=self.vol_type_name, qos_spec_name=self.qos_name)
self.volume_type = cinder_utils.create_volume_type(
self.cinder, volume_type_settings)
Tests the cinder_utils.create_volume_type() when the QoS Spec name
does not exist
"""
- volume_type_settings = VolumeTypeSettings(
+ volume_type_settings = VolumeTypeConfig(
name=self.vol_type_name, qos_spec_name='foo')
self.volume_type = cinder_utils.create_volume_type(
Tests the cinder_utils.create_volume_type() with encryption and an
associated QoS Spec
"""
- encryption_settings = VolumeTypeEncryptionSettings(
+ encryption_settings = VolumeTypeEncryptionConfig(
name='foo', provider_class='bar',
control_location=ControlLocation.back_end)
- volume_type_settings = VolumeTypeSettings(
+ volume_type_settings = VolumeTypeConfig(
name=self.vol_type_name, qos_spec_name=self.qos_name,
encryption=encryption_settings)
self.volume_type = cinder_utils.create_volume_type(
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
+import os
+
import pkg_resources
import uuid
import time
-from snaps.openstack import create_stack
-from snaps.openstack.create_flavor import OpenStackFlavor, FlavorSettings
+import snaps.config.stack as stack_config
+from snaps.config.flavor import FlavorConfig
+from snaps.openstack.create_flavor import OpenStackFlavor
from snaps.openstack.create_image import OpenStackImage
from snaps.openstack.create_instance import OpenStackVmInstance
-from snaps.openstack.create_stack import StackSettings
+from snaps.openstack.create_stack import StackConfig
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)
+ heat_utils, neutron_utils, nova_utils, settings_utils, glance_utils,
+ cinder_utils)
__author__ = 'spisarski'
# Create Flavor
self.flavor_creator = OpenStackFlavor(
self.os_creds,
- FlavorSettings(name=guid + '-flavor', ram=256, disk=10, vcpus=1))
+ FlavorConfig(name=guid + '-flavor', ram=256, disk=10, vcpus=1))
self.flavor_creator.create()
env_values = {'image_name': self.image_creator.image_settings.name,
'inst_name': self.vm_inst_name}
heat_tmplt_path = pkg_resources.resource_filename(
'snaps.openstack.tests.heat', 'test_heat_template.yaml')
- self.stack_settings1 = StackSettings(
+ self.stack_settings1 = StackConfig(
name=stack_name1, template_path=heat_tmplt_path,
env_values=env_values)
- self.stack_settings2 = StackSettings(
+ self.stack_settings2 = StackConfig(
name=stack_name2, template_path=heat_tmplt_path,
env_values=env_values)
self.stack1 = None
def tearDown(self):
"""
- Cleans the image and downloaded image file
+ Cleans the stack and image
"""
if self.stack1:
try:
self.stack1.id)
self.assertEqual(self.stack1, stack_query_3)
- resources = heat_utils.get_resources(self.heat_client, self.stack1)
+ resources = heat_utils.get_resources(self.heat_client, self.stack1.id)
self.assertIsNotNone(resources)
self.assertEqual(4, len(resources))
self.assertIsNotNone(outputs)
self.assertEqual(0, len(outputs))
- # Wait until stack deployment has completed
- end_time = time.time() + create_stack.STACK_COMPLETE_TIMEOUT
- is_active = False
- while time.time() < end_time:
- status = heat_utils.get_stack_status(self.heat_client,
- self.stack1.id)
- if status == create_stack.STATUS_CREATE_COMPLETE:
- is_active = True
- break
- elif status == create_stack.STATUS_CREATE_FAILED:
- is_active = False
- break
-
- time.sleep(3)
-
- self.assertTrue(is_active)
+ self.assertTrue(stack_active(self.heat_client, self.stack1))
neutron = neutron_utils.neutron_client(self.os_creds)
networks = heat_utils.get_stack_networks(
nova = nova_utils.nova_client(self.os_creds)
servers = heat_utils.get_stack_servers(
- self.heat_client, nova, self.stack1)
+ self.heat_client, nova, neutron, self.stack1)
self.assertIsNotNone(servers)
self.assertEqual(1, len(servers))
self.assertEqual(self.vm_inst_name, servers[0].name)
self.stack1.id)
self.assertEqual(self.stack1, stack1_query_3)
- end_time = time.time() + create_stack.STACK_COMPLETE_TIMEOUT
- is_active = False
- while time.time() < end_time:
- status = heat_utils.get_stack_status(self.heat_client,
- self.stack1.id)
- if status == create_stack.STATUS_CREATE_COMPLETE:
- is_active = True
- break
- elif status == create_stack.STATUS_CREATE_FAILED:
- is_active = False
- break
-
- time.sleep(3)
-
- self.assertTrue(is_active)
+ self.assertTrue(stack_active(self.heat_client, self.stack1))
self.stack2 = heat_utils.create_stack(self.heat_client,
self.stack_settings2)
self.stack2.id)
self.assertEqual(self.stack2, stack2_query_3)
- end_time = time.time() + create_stack.STACK_COMPLETE_TIMEOUT
-
- is_active = False
- while time.time() < end_time:
- status = heat_utils.get_stack_status(self.heat_client,
- self.stack2.id)
- if status == create_stack.STATUS_CREATE_COMPLETE:
- is_active = True
- break
- elif status == create_stack.STATUS_CREATE_FAILED:
- is_active = False
- break
-
- time.sleep(3)
-
- self.assertTrue(is_active)
+ self.assertTrue(stack_active(self.heat_client, self.stack2))
class HeatUtilsCreateComplexStackTests(OSComponentTestCase):
'external_net_name': self.ext_net_name}
heat_tmplt_path = pkg_resources.resource_filename(
'snaps.openstack.tests.heat', 'floating_ip_heat_template.yaml')
- stack_settings = StackSettings(
+ 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.stack = heat_utils.create_stack(self.heat_client, stack_settings)
- # Wait until stack deployment has completed
- end_time = time.time() + create_stack.STACK_COMPLETE_TIMEOUT
- is_active = False
- while time.time() < end_time:
- status = heat_utils.get_stack_status(self.heat_client,
- self.stack.id)
- if status == create_stack.STATUS_CREATE_COMPLETE:
- is_active = True
- break
- elif status == create_stack.STATUS_CREATE_FAILED:
- is_active = False
- break
+ self.assertTrue(stack_active(self.heat_client, self.stack))
- time.sleep(3)
- self.assertTrue(is_active)
+ self.keypair1_settings = None
+ self.keypair2_settings = None
def tearDown(self):
"""
- Cleans the image and downloaded image file
+ Cleans the stack and image
"""
if self.stack:
try:
heat_utils.delete_stack(self.heat_client, self.stack)
# Wait until stack deployment has completed
- end_time = time.time() + create_stack.STACK_COMPLETE_TIMEOUT
+ end_time = (time.time() +
+ stack_config.STACK_COMPLETE_TIMEOUT)
is_deleted = False
while time.time() < end_time:
status = heat_utils.get_stack_status(self.heat_client,
self.stack.id)
- if status == create_stack.STATUS_DELETE_COMPLETE:
+ if status == stack_config.STATUS_DELETE_COMPLETE:
is_deleted = True
break
- elif status == create_stack.STATUS_DELETE_FAILED:
+ elif status == stack_config.STATUS_DELETE_FAILED:
is_deleted = False
break
neutron = neutron_utils.neutron_client(self.os_creds)
glance = glance_utils.glance_client(self.os_creds)
servers = heat_utils.get_stack_servers(
- self.heat_client, nova, self.stack)
+ self.heat_client, nova, neutron, self.stack)
for server in servers:
- vm_settings = settings_utils.create_vm_inst_settings(
+ vm_settings = settings_utils.create_vm_inst_config(
nova, neutron, server)
- img_settings = settings_utils.determine_image_settings(
+ img_settings = settings_utils.determine_image_config(
glance, server,
[self.image_creator1.image_settings,
self.image_creator2.image_settings])
except:
pass
+ if self.keypair1_settings:
+ expanded_path = os.path.expanduser(
+ self.keypair1_settings.private_filepath)
+ os.chmod(expanded_path, 0o755)
+ os.remove(expanded_path)
+
+ if self.keypair2_settings:
+ expanded_path = os.path.expanduser(
+ self.keypair2_settings.private_filepath)
+ os.chmod(expanded_path, 0o755)
+ os.remove(expanded_path)
+
def test_get_settings_from_stack(self):
"""
Tests that a heat template with floating IPs and can have the proper
settings derived from settings_utils.py.
"""
- resources = heat_utils.get_resources(self.heat_client, self.stack)
+ resources = heat_utils.get_resources(self.heat_client, self.stack.id)
self.assertIsNotNone(resources)
- self.assertEqual(11, len(resources))
+ self.assertEqual(12, len(resources))
options = heat_utils.get_outputs(self.heat_client, self.stack)
self.assertIsNotNone(options)
self.assertEqual(1, len(networks))
self.assertEqual(self.network_name, networks[0].name)
- network_settings = settings_utils.create_network_settings(
+ network_settings = settings_utils.create_network_config(
neutron, networks[0])
self.assertIsNotNone(network_settings)
self.assertEqual(self.network_name, network_settings.name)
glance = glance_utils.glance_client(self.os_creds)
servers = heat_utils.get_stack_servers(
- self.heat_client, nova, self.stack)
+ self.heat_client, nova, neutron, self.stack)
self.assertIsNotNone(servers)
self.assertEqual(2, len(servers))
- image_settings = settings_utils.determine_image_settings(
+ image_settings = settings_utils.determine_image_config(
glance, servers[0],
[self.image_creator1.image_settings,
self.image_creator2.image_settings])
self.assertEqual(
self.image_creator2.image_settings.name, image_settings.name)
- image_settings = settings_utils.determine_image_settings(
+ image_settings = settings_utils.determine_image_config(
glance, servers[1],
[self.image_creator1.image_settings,
self.image_creator2.image_settings])
self.assertEqual(
self.image_creator2.image_settings.name, image_settings.name)
- keypair1_settings = settings_utils.determine_keypair_settings(
+ self.keypair1_settings = settings_utils.determine_keypair_config(
self.heat_client, self.stack, servers[0],
priv_key_key='private_key')
- self.assertIsNotNone(keypair1_settings)
- self.assertEqual(self.keypair_name, keypair1_settings.name)
+ self.assertIsNotNone(self.keypair1_settings)
+ self.assertEqual(self.keypair_name, self.keypair1_settings.name)
- keypair2_settings = settings_utils.determine_keypair_settings(
+ self.keypair2_settings = settings_utils.determine_keypair_config(
self.heat_client, self.stack, servers[1],
priv_key_key='private_key')
- self.assertIsNotNone(keypair2_settings)
- self.assertEqual(self.keypair_name, keypair2_settings.name)
+ self.assertIsNotNone(self.keypair2_settings)
+ self.assertEqual(self.keypair_name, self.keypair2_settings.name)
+
+
+class HeatUtilsRouterTests(OSComponentTestCase):
+ """
+ Test Heat volume functionality
+ """
+
+ def setUp(self):
+ """
+ Instantiates OpenStack instances that cannot be spawned by Heat
+ """
+ guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+ stack_name = guid + '-stack'
+
+ self.net_name = guid + '-net'
+ self.subnet_name = guid + '-subnet'
+ self.router_name = guid + '-router'
+
+ env_values = {
+ 'net_name': self.net_name,
+ 'subnet_name': self.subnet_name,
+ 'router_name': self.router_name,
+ 'external_net_name': self.ext_net_name}
+
+ heat_tmplt_path = pkg_resources.resource_filename(
+ 'snaps.openstack.tests.heat', 'router_heat_template.yaml')
+ self.stack_settings = StackConfig(
+ 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)
+
+ def tearDown(self):
+ """
+ Cleans the image and downloaded image file
+ """
+ if self.stack:
+ try:
+ heat_utils.delete_stack(self.heat_client, self.stack)
+ except:
+ pass
+
+ def test_create_router_with_stack(self):
+ """
+ Tests the creation of an OpenStack router with Heat and the retrieval
+ of the Router Domain objects from heat_utils#get_stack_routers().
+ """
+ self.stack = heat_utils.create_stack(
+ self.heat_client, self.stack_settings)
+
+ # Wait until stack deployment has completed
+ end_time = time.time() + stack_config.STACK_COMPLETE_TIMEOUT
+ is_active = False
+ while time.time() < end_time:
+ status = heat_utils.get_stack_status(self.heat_client,
+ self.stack.id)
+ if status == stack_config.STATUS_CREATE_COMPLETE:
+ is_active = True
+ break
+ elif status == stack_config.STATUS_CREATE_FAILED:
+ is_active = False
+ break
+
+ time.sleep(3)
+
+ self.assertTrue(is_active)
+
+ routers = heat_utils.get_stack_routers(
+ self.heat_client, self.neutron, self.stack)
+
+ self.assertEqual(1, len(routers))
+
+ router = routers[0]
+ self.assertEqual(self.router_name, router.name)
+
+ ext_net = neutron_utils.get_network(
+ self.neutron, network_name=self.ext_net_name)
+ self.assertEqual(ext_net.id, router.external_network_id)
+
+
+class HeatUtilsVolumeTests(OSComponentTestCase):
+ """
+ Test Heat volume functionality
+ """
+
+ def setUp(self):
+ """
+ Instantiates OpenStack instances that cannot be spawned by Heat
+ """
+ guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+ stack_name = guid + '-stack'
+ self.volume_name = guid + '-vol'
+ self.volume_type_name = guid + '-vol-type'
+
+ env_values = {
+ 'volume_name': self.volume_name,
+ 'volume_type_name': self.volume_type_name}
+
+ heat_tmplt_path = pkg_resources.resource_filename(
+ 'snaps.openstack.tests.heat', 'volume_heat_template.yaml')
+ self.stack_settings = StackConfig(
+ 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)
+
+ def tearDown(self):
+ """
+ Cleans the stack
+ """
+ if self.stack:
+ try:
+ heat_utils.delete_stack(self.heat_client, self.stack)
+ except:
+ pass
+
+ def test_create_vol_with_stack(self):
+ """
+ Tests the creation of an OpenStack volume with Heat.
+ """
+ self.stack = heat_utils.create_stack(
+ self.heat_client, self.stack_settings)
+ self.assertTrue(stack_active(self.heat_client, self.stack))
+
+ volumes = heat_utils.get_stack_volumes(
+ self.heat_client, self.cinder, self.stack)
+
+ self.assertEqual(1, len(volumes))
+
+ volume = volumes[0]
+ self.assertEqual(self.volume_name, volume.name)
+ self.assertEqual(self.volume_type_name, volume.type)
+ self.assertEqual(1, volume.size)
+ self.assertEqual(False, volume.multi_attach)
+
+ def test_create_vol_types_with_stack(self):
+ """
+ Tests the creation of an OpenStack volume with Heat.
+ """
+ self.stack = heat_utils.create_stack(
+ self.heat_client, self.stack_settings)
+ self.assertTrue(stack_active(self.heat_client, self.stack))
+
+ volume_types = heat_utils.get_stack_volume_types(
+ self.heat_client, self.cinder, self.stack)
+
+ self.assertEqual(1, len(volume_types))
+
+ volume_type = volume_types[0]
+
+ self.assertEqual(self.volume_type_name, volume_type.name)
+ self.assertTrue(volume_type.public)
+ self.assertIsNone(volume_type.qos_spec)
+
+ # TODO - Add encryption back and find out why it broke in Pike
+ # encryption = volume_type.encryption
+ # self.assertIsNotNone(encryption)
+ # self.assertIsNone(encryption.cipher)
+ # self.assertEqual('front-end', encryption.control_location)
+ # self.assertIsNone(encryption.key_size)
+ # self.assertEqual(u'nova.volume.encryptors.luks.LuksEncryptor',
+ # encryption.provider)
+ # self.assertEqual(volume_type.id, encryption.volume_type_id)
+
+
+class HeatUtilsFlavorTests(OSComponentTestCase):
+ """
+ Test Heat volume functionality
+ """
+
+ def setUp(self):
+ """
+ Instantiates OpenStack instances that cannot be spawned by Heat
+ """
+ guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+ self.name_prefix = guid
+ stack_name = guid + '-stack'
+
+ heat_tmplt_path = pkg_resources.resource_filename(
+ 'snaps.openstack.tests.heat', 'flavor_heat_template.yaml')
+ 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)
+
+ def tearDown(self):
+ """
+ Cleans the stack
+ """
+ if self.stack:
+ try:
+ heat_utils.delete_stack(self.heat_client, self.stack)
+ except:
+ pass
+
+ def test_create_flavor_with_stack(self):
+ """
+ Tests the creation of an OpenStack volume with Heat.
+ """
+ self.stack = heat_utils.create_stack(
+ self.heat_client, self.stack_settings)
+
+ self.assertTrue(stack_active(self.heat_client, self.stack))
+
+ flavors = heat_utils.get_stack_flavors(
+ self.heat_client, self.nova, self.stack)
+
+ self.assertEqual(1, len(flavors))
+
+ flavor = flavors[0]
+ self.assertTrue(flavor.name.startswith(self.name_prefix))
+ self.assertEqual(1024, flavor.ram)
+ self.assertEqual(200, flavor.disk)
+ self.assertEqual(8, flavor.vcpus)
+ self.assertEqual(0, flavor.ephemeral)
+ self.assertIsNone(flavor.swap)
+ self.assertEqual(1.0, flavor.rxtx_factor)
+ self.assertTrue(flavor.is_public)
+
+
+class HeatUtilsKeypairTests(OSComponentTestCase):
+ """
+ Test Heat volume functionality
+ """
+
+ def setUp(self):
+ """
+ Instantiates OpenStack instances that cannot be spawned by Heat
+ """
+ guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+ stack_name = guid + '-stack'
+ self.keypair_name = guid + '-kp'
+
+ env_values = {'keypair_name': self.keypair_name}
+
+ heat_tmplt_path = pkg_resources.resource_filename(
+ 'snaps.openstack.tests.heat', 'keypair_heat_template.yaml')
+ self.stack_settings = StackConfig(
+ 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)
+
+ def tearDown(self):
+ """
+ Cleans the stack
+ """
+ if self.stack:
+ try:
+ heat_utils.delete_stack(self.heat_client, self.stack)
+ except:
+ pass
+
+ def test_create_keypair_with_stack(self):
+ """
+ Tests the creation of an OpenStack keypair with Heat.
+ """
+ self.stack = heat_utils.create_stack(
+ self.heat_client, self.stack_settings)
+ self.assertTrue(stack_active(self.heat_client, self.stack))
+
+ keypairs = heat_utils.get_stack_keypairs(
+ self.heat_client, self.nova, self.stack)
+
+ self.assertEqual(1, len(keypairs))
+ keypair = keypairs[0]
+
+ self.assertEqual(self.keypair_name, keypair.name)
+
+ outputs = heat_utils.get_outputs(self.heat_client, self.stack)
+
+ for output in outputs:
+ if output.key == 'private_key':
+ self.assertTrue(output.value.startswith(
+ '-----BEGIN RSA PRIVATE KEY-----'))
+
+ keypair = nova_utils.get_keypair_by_id(self.nova, keypair.id)
+ self.assertIsNotNone(keypair)
+
+ self.assertEqual(self.keypair_name, keypair.name)
+
+
+class HeatUtilsSecurityGroupTests(OSComponentTestCase):
+ """
+ Test Heat volume functionality
+ """
+
+ def setUp(self):
+ """
+ Instantiates OpenStack instances that cannot be spawned by Heat
+ """
+ guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+ stack_name = guid + '-stack'
+ self.sec_grp_name = guid + '-sec-grp'
+
+ env_values = {'security_group_name': self.sec_grp_name}
+
+ heat_tmplt_path = pkg_resources.resource_filename(
+ 'snaps.openstack.tests.heat', 'security_group_heat_template.yaml')
+ self.stack_settings = StackConfig(
+ 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)
+
+ def tearDown(self):
+ """
+ Cleans the stack
+ """
+ if self.stack:
+ try:
+ heat_utils.delete_stack(self.heat_client, self.stack)
+ except:
+ pass
+
+ def test_create_security_group_with_stack(self):
+ """
+ Tests the creation of an OpenStack SecurityGroup with Heat.
+ """
+ self.stack = heat_utils.create_stack(
+ self.heat_client, self.stack_settings)
+ self.assertTrue(stack_active(self.heat_client, self.stack))
+
+ sec_grp = heat_utils.get_stack_security_groups(
+ self.heat_client, self.neutron, self.stack)[0]
+
+ self.assertEqual(self.sec_grp_name, sec_grp.name)
+ self.assertEqual('Test description', sec_grp.description)
+ self.assertEqual(2, len(sec_grp.rules))
+
+ has_ssh_rule = False
+ has_icmp_rule = False
+
+ for rule in sec_grp.rules:
+ if (rule.security_group_id == sec_grp.id
+ and rule.direction == 'egress'
+ and rule.ethertype == 'IPv4'
+ and rule.port_range_min == 22
+ and rule.port_range_max == 22
+ and rule.protocol == 'tcp'
+ and rule.remote_group_id is None
+ and rule.remote_ip_prefix == '0.0.0.0/0'):
+ has_ssh_rule = True
+ if (rule.security_group_id == sec_grp.id
+ and rule.direction == 'ingress'
+ and rule.ethertype == 'IPv4'
+ and rule.port_range_min is None
+ and rule.port_range_max is None
+ and rule.protocol == 'icmp'
+ and rule.remote_group_id is None
+ and rule.remote_ip_prefix == '0.0.0.0/0'):
+ has_icmp_rule = True
+
+ self.assertTrue(has_ssh_rule)
+ self.assertTrue(has_icmp_rule)
+
+
+def stack_active(heat_cli, stack):
+ """
+ Blocks until stack application has successfully completed or failed
+ :param heat_cli: the Heat client
+ :param stack: the Stack domain object
+ :return: T/F
+ """
+ # Wait until stack deployment has completed
+ end_time = time.time() + stack_config.STACK_COMPLETE_TIMEOUT
+ is_active = False
+ while time.time() < end_time:
+ status = heat_utils.get_stack_status(heat_cli, stack.id)
+ if status == stack_config.STATUS_CREATE_COMPLETE:
+ is_active = True
+ break
+ elif status == stack_config.STATUS_CREATE_FAILED:
+ is_active = False
+ break
+
+ time.sleep(3)
+
+ return is_active
# limitations under the License.
import uuid
-from snaps.openstack.create_project import ProjectSettings
-from snaps.openstack.create_user import UserSettings
+from snaps.config.project import ProjectConfig
+from snaps.config.user import UserConfig
from snaps.openstack.tests.os_source_file_test import OSComponentTestCase
from snaps.openstack.utils import keystone_utils, neutron_utils
"""
Tests the keystone_utils.create_user() function
"""
- user_settings = UserSettings(
+ user_settings = UserConfig(
name=self.username,
password=str(uuid.uuid4()),
domain_name=self.os_creds.user_domain_name)
"""
Tests the keyston_utils.create_project() funtion
"""
- project_settings = ProjectSettings(
+ project_settings = ProjectConfig(
name=self.project_name, domain=self.os_creds.project_domain_name)
self.project = keystone_utils.create_project(self.keystone,
project_settings)
Tests the keystone_utils function grant_user_role_to_project()
:return:
"""
- user_settings = UserSettings(
+ user_settings = UserConfig(
name=self.username, password=str(uuid.uuid4()),
domain_name=self.os_creds.user_domain_name)
self.user = keystone_utils.create_user(self.keystone, user_settings)
self.assertEqual(self.username, self.user.name)
- project_settings = ProjectSettings(
+ project_settings = ProjectConfig(
name=self.project_name, domain=self.os_creds.project_domain_name)
self.project = keystone_utils.create_project(self.keystone,
project_settings)
--- /dev/null
+# 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.
+import logging
+import uuid
+
+from magnumclient.common.apiclient.exceptions import BadRequest
+
+from snaps.config.cluster_template import (
+ ClusterTemplateConfig, ServerType, ContainerOrchestrationEngine,
+ DockerStorageDriver)
+from snaps.config.flavor import FlavorConfig
+from snaps.config.keypair import KeypairConfig
+from snaps.openstack.create_flavor import OpenStackFlavor
+from snaps.openstack.create_image import OpenStackImage
+from snaps.openstack.create_keypairs import OpenStackKeypair
+from snaps.openstack.os_credentials import OSCreds
+from snaps.openstack.tests import openstack_tests
+from snaps.openstack.tests.os_source_file_test import OSComponentTestCase
+from snaps.openstack.utils import magnum_utils
+
+__author__ = 'spisarski'
+
+logger = logging.getLogger('magnum_utils_tests')
+
+
+class MagnumSmokeTests(OSComponentTestCase):
+ """
+ Tests to ensure that the magnum client can communicate with the cloud
+ """
+
+ def test_connect_success(self):
+ """
+ Tests to ensure that the proper credentials can connect.
+ """
+ magnum = magnum_utils.magnum_client(self.os_creds)
+
+ # This should not throw an exception
+ self.assertIsNotNone(magnum.clusters.list())
+
+ def test_nova_connect_fail(self):
+ """
+ Tests to ensure that the improper credentials cannot connect.
+ """
+
+ with self.assertRaises(RuntimeError):
+ magnum_utils.magnum_client(
+ OSCreds(username='user', password='pass',
+ auth_url=self.os_creds.auth_url,
+ project_name=self.os_creds.project_name,
+ proxy_settings=self.os_creds.proxy_settings))
+
+
+class MagnumUtilsClusterTypeTests(OSComponentTestCase):
+ """
+ Tests individual functions within magnum_utils.py
+ """
+
+ 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)
+
+ metadata = self.image_metadata
+ if not metadata:
+ metadata = dict()
+ if 'extra_properties' not in metadata:
+ metadata['extra_properties'] = dict()
+ metadata['extra_properties']['os_distro'] = 'cirros'
+
+ os_image_settings = openstack_tests.cirros_image_settings(
+ name=self.guid + '-image', image_metadata=metadata)
+
+ 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))
+
+ keypair_priv_filepath = 'tmp/' + self.guid
+ keypair_pub_filepath = keypair_priv_filepath + '.pub'
+
+ self.keypair_creator = OpenStackKeypair(
+ self.os_creds, KeypairConfig(
+ name=self.guid + '-keypair',
+ public_filepath=keypair_pub_filepath,
+ private_filepath=keypair_priv_filepath))
+
+ self.cluster_template = None
+
+ try:
+ self.image_creator.create()
+ self.flavor_creator.create()
+ self.keypair_creator.create()
+ except:
+ self.tearDown()
+ raise
+
+ def tearDown(self):
+ if self.cluster_template:
+ try:
+ magnum_utils.delete_cluster_template(
+ self.magnum, self.cluster_template.id)
+ except:
+ pass
+ if self.keypair_creator:
+ try:
+ self.keypair_creator.clean()
+ except:
+ pass
+ if self.flavor_creator:
+ try:
+ self.flavor_creator.clean()
+ except:
+ pass
+ if self.image_creator:
+ try:
+ self.image_creator.clean()
+ except:
+ pass
+
+ def test_create_cluster_template_simple(self):
+ config = ClusterTemplateConfig(
+ name=self.cluster_type_name,
+ image=self.image_creator.image_settings.name,
+ keypair=self.keypair_creator.keypair_settings.name,
+ external_net=self.ext_net_name,
+ flavor=self.flavor_creator.flavor_settings.name)
+
+ self.cluster_template = magnum_utils.create_cluster_template(
+ self.magnum, config)
+ self.assertIsNotNone(self.cluster_template)
+ self.assertTrue(
+ validate_cluster_template(config, self.cluster_template))
+
+ template_by_name = magnum_utils.get_cluster_template(
+ self.magnum, template_name=config.name)
+ self.assertEqual(self.cluster_template, template_by_name)
+ template_by_id = magnum_utils.get_cluster_template_by_id(
+ self.magnum, self.cluster_template.id)
+ self.assertEqual(self.cluster_template, template_by_id)
+
+ def test_create_cluster_template_all(self):
+ config = ClusterTemplateConfig(
+ name=self.cluster_type_name,
+ image=self.image_creator.image_settings.name,
+ keypair=self.keypair_creator.keypair_settings.name,
+ network_driver='flannel', external_net=self.ext_net_name,
+ floating_ip_enabled=True, docker_volume_size=100,
+ server_type=ServerType.vm,
+ flavor=self.flavor_creator.flavor_settings.name,
+ master_flavor=self.flavor_creator.flavor_settings.name,
+ coe=ContainerOrchestrationEngine.kubernetes,
+ fixed_net='foo', fixed_subnet='bar',
+ registry_enabled=True, insecure_registry='localhost',
+ docker_storage_driver=DockerStorageDriver.overlay,
+ dns_nameserver='8.8.4.4', public=True, tls_disabled=True,
+ http_proxy=None, https_proxy=None, volume_driver='cinder',
+ master_lb_enabled=False, labels={'foo': 'bar'})
+
+ self.cluster_template = magnum_utils.create_cluster_template(
+ self.magnum, config)
+ self.assertIsNotNone(self.cluster_template)
+ self.assertTrue(
+ validate_cluster_template(config, self.cluster_template))
+
+ template_by_name = magnum_utils.get_cluster_template(
+ self.magnum, template_name=config.name)
+ self.assertEqual(self.cluster_template, template_by_name)
+ template_by_id = magnum_utils.get_cluster_template_by_id(
+ self.magnum, self.cluster_template.id)
+ self.assertEqual(self.cluster_template, template_by_id)
+
+ def test_create_cluster_template_bad_image(self):
+ config = ClusterTemplateConfig(
+ name=self.cluster_type_name,
+ image='foo',
+ keypair=self.keypair_creator.keypair_settings.name,
+ external_net=self.ext_net_name,
+ flavor=self.flavor_creator.flavor_settings.name)
+
+ with self.assertRaises(BadRequest):
+ self.cluster_template = magnum_utils.create_cluster_template(
+ self.magnum, config)
+
+ def test_create_cluster_template_bad_ext_net(self):
+ config = ClusterTemplateConfig(
+ name=self.cluster_type_name,
+ image=self.image_creator.image_settings.name,
+ keypair=self.keypair_creator.keypair_settings.name,
+ external_net='foo',
+ flavor=self.flavor_creator.flavor_settings.name)
+
+ with self.assertRaises(BadRequest):
+ self.cluster_template = magnum_utils.create_cluster_template(
+ self.magnum, config)
+
+ def test_create_cluster_template_bad_flavor(self):
+ config = ClusterTemplateConfig(
+ name=self.cluster_type_name,
+ image=self.image_creator.image_settings.name,
+ keypair=self.keypair_creator.keypair_settings.name,
+ external_net=self.ext_net_name,
+ flavor='foo')
+
+ with self.assertRaises(BadRequest):
+ self.cluster_template = magnum_utils.create_cluster_template(
+ self.magnum, config)
+
+ def test_create_cluster_template_bad_master_flavor(self):
+ config = ClusterTemplateConfig(
+ name=self.cluster_type_name,
+ image=self.image_creator.image_settings.name,
+ keypair=self.keypair_creator.keypair_settings.name,
+ external_net=self.ext_net_name,
+ flavor=self.flavor_creator.flavor_settings.name,
+ master_flavor='foo')
+
+ with self.assertRaises(BadRequest):
+ self.cluster_template = magnum_utils.create_cluster_template(
+ self.magnum, config)
+
+ def test_create_cluster_template_bad_network_driver(self):
+ config = ClusterTemplateConfig(
+ name=self.cluster_type_name,
+ image=self.image_creator.image_settings.name,
+ keypair=self.keypair_creator.keypair_settings.name,
+ external_net=self.ext_net_name,
+ network_driver='foo')
+
+ with self.assertRaises(BadRequest):
+ self.cluster_template = magnum_utils.create_cluster_template(
+ self.magnum, config)
+
+ def test_create_cluster_template_bad_volume_driver(self):
+ config = ClusterTemplateConfig(
+ name=self.cluster_type_name,
+ image=self.image_creator.image_settings.name,
+ keypair=self.keypair_creator.keypair_settings.name,
+ external_net=self.ext_net_name,
+ volume_driver='foo')
+
+ with self.assertRaises(BadRequest):
+ self.cluster_template = magnum_utils.create_cluster_template(
+ self.magnum, config)
+
+
+def validate_cluster_template(tmplt_config, tmplt_obj):
+ """
+ Returns true if the configuration matches the ClusterTemplate object
+ :param tmplt_config: the ClusterTemplateConfig object
+ :param tmplt_obj: the ClusterTemplate domain object
+ :return: T/F
+ """
+ if not tmplt_config.network_driver:
+ network_driver = 'flannel'
+ else:
+ network_driver = tmplt_config.network_driver
+
+ return (
+ tmplt_config.coe.value == tmplt_obj.coe and
+ tmplt_config.dns_nameserver == tmplt_obj.dns_nameserver and
+ tmplt_config.docker_storage_driver.value
+ == tmplt_obj.docker_storage_driver and
+ tmplt_config.docker_volume_size == tmplt_obj.docker_volume_size and
+ tmplt_config.external_net == tmplt_obj.external_net and
+ tmplt_config.fixed_net == tmplt_obj.fixed_net and
+ tmplt_config.fixed_subnet == tmplt_obj.fixed_subnet and
+ tmplt_config.flavor == tmplt_obj.flavor and
+ tmplt_config.floating_ip_enabled == tmplt_obj.floating_ip_enabled and
+ tmplt_config.http_proxy == tmplt_obj.http_proxy and
+ tmplt_config.https_proxy == tmplt_obj.https_proxy and
+ tmplt_config.no_proxy == tmplt_obj.no_proxy and
+ tmplt_config.image == tmplt_obj.image and
+ tmplt_config.insecure_registry == tmplt_obj.insecure_registry and
+ tmplt_config.keypair == tmplt_obj.keypair and
+ tmplt_config.labels == tmplt_obj.labels and
+ tmplt_config.master_flavor == tmplt_obj.master_flavor and
+ tmplt_config.master_lb_enabled == tmplt_obj.master_lb_enabled and
+ tmplt_config.name == tmplt_obj.name and
+ network_driver == tmplt_obj.network_driver and
+ tmplt_config.no_proxy == tmplt_obj.no_proxy and
+ tmplt_config.public == tmplt_obj.public and
+ tmplt_config.registry_enabled == tmplt_obj.registry_enabled and
+ tmplt_config.server_type.value == tmplt_obj.server_type and
+ tmplt_config.tls_disabled == tmplt_obj.tls_disabled and
+ tmplt_config.volume_driver == tmplt_obj.volume_driver
+ )
# limitations under the License.
import uuid
-from snaps.openstack import create_router
-from snaps.openstack.create_network import NetworkSettings, SubnetSettings, \
- PortSettings
-from snaps.openstack.create_security_group import SecurityGroupSettings, \
- SecurityGroupRuleSettings, Direction
+from neutronclient.common.exceptions import NotFound, BadRequest
+
+from snaps.config.network import NetworkConfig, SubnetConfig, PortConfig
+from snaps.config.security_group import (
+ SecurityGroupConfig, SecurityGroupRuleConfig, Direction)
from snaps.openstack.tests import openstack_tests
from snaps.openstack.tests import validation_utils
from snaps.openstack.tests.os_source_file_test import OSComponentTestCase
def test_create_network(self):
"""
- Tests the neutron_utils.create_neutron_net() function
+ Tests the neutron_utils.create_network() function
"""
self.network = neutron_utils.create_network(
self.neutron, self.os_creds, self.net_config.network_settings)
self.network.name)
self.assertTrue(validate_network(
self.neutron, self.net_config.network_settings.name, True))
+ self.assertEqual(len(self.net_config.network_settings.subnet_settings),
+ len(self.network.subnets))
def test_create_network_empty_name(self):
"""
- Tests the neutron_utils.create_neutron_net() function with an empty
+ Tests the neutron_utils.create_network() function with an empty
network name
"""
with self.assertRaises(Exception):
self.network = neutron_utils.create_network(
self.neutron, self.os_creds,
- network_settings=NetworkSettings(name=''))
+ network_settings=NetworkConfig(name=''))
def test_create_network_null_name(self):
"""
- Tests the neutron_utils.create_neutron_net() function when the network
+ Tests the neutron_utils.create_network() function when the network
name is None
"""
with self.assertRaises(Exception):
self.network = neutron_utils.create_network(
self.neutron, self.os_creds,
- network_settings=NetworkSettings())
+ network_settings=NetworkConfig())
class NeutronUtilsSubnetTests(OSComponentTestCase):
self.port_name = str(guid) + '-port'
self.neutron = neutron_utils.neutron_client(self.os_creds)
self.network = None
- self.subnet = None
self.net_config = openstack_tests.get_pub_net_config(
net_name=guid + '-pub-net', subnet_name=guid + '-pub-subnet',
external_net=self.ext_net_name)
"""
Cleans the remote OpenStack objects
"""
- if self.subnet:
- try:
- neutron_utils.delete_subnet(self.neutron, self.subnet)
- except:
- pass
if self.network:
try:
neutron_utils.delete_network(self.neutron, self.network)
def test_create_subnet(self):
"""
- Tests the neutron_utils.create_neutron_net() function
+ Tests the neutron_utils.create_network() function
"""
self.network = neutron_utils.create_network(
self.neutron, self.os_creds, self.net_config.network_settings)
self.neutron, self.net_config.network_settings.name, True))
subnet_setting = self.net_config.network_settings.subnet_settings[0]
- self.subnet = neutron_utils.create_subnet(
- self.neutron, subnet_setting, self.os_creds, network=self.network)
self.assertTrue(validate_subnet(
self.neutron, subnet_setting.name, subnet_setting.cidr, True))
subnet_query1 = neutron_utils.get_subnet(
self.neutron, subnet_name=subnet_setting.name)
- self.assertEqual(self.subnet, subnet_query1)
+ self.assertEqual(self.network.subnets[0], subnet_query1)
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.subnet, subnet_query2[0])
+ self.assertEqual(self.network.subnets[0], subnet_query2[0])
def test_create_subnet_null_name(self):
"""
self.neutron, self.net_config.network_settings.name, True))
with self.assertRaises(Exception):
- SubnetSettings(cidr=self.net_config.subnet_cidr)
+ SubnetConfig(cidr=self.net_config.subnet_cidr)
def test_create_subnet_empty_name(self):
"""
- Tests the neutron_utils.create_neutron_net() function with an empty
+ Tests the neutron_utils.create_network() function with an empty
name
"""
self.network = neutron_utils.create_network(
self.neutron, self.net_config.network_settings.name, True))
subnet_setting = self.net_config.network_settings.subnet_settings[0]
- self.subnet = neutron_utils.create_subnet(
- self.neutron, subnet_setting, self.os_creds, network=self.network)
self.assertTrue(validate_subnet(
self.neutron, subnet_setting.name, subnet_setting.cidr, True))
self.assertFalse(validate_subnet(
subnet_query1 = neutron_utils.get_subnet(
self.neutron, subnet_name=subnet_setting.name)
- self.assertEqual(self.subnet, subnet_query1)
+ self.assertEqual(self.network.subnets[0], subnet_query1)
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.subnet, subnet_query2[0])
+ self.assertEqual(self.network.subnets[0], subnet_query2[0])
def test_create_subnet_null_cidr(self):
"""
Tests the neutron_utils.create_neutron_subnet() function for an
Exception when the subnet CIDR value is None
"""
- self.network = neutron_utils.create_network(
- self.neutron, self.os_creds, self.net_config.network_settings)
- 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.net_config.network_settings.subnet_settings[0].cidr = None
with self.assertRaises(Exception):
- sub_sets = SubnetSettings(
- cidr=None, name=self.net_config.subnet_name)
- neutron_utils.create_subnet(
- self.neutron, sub_sets, self.os_creds, network=self.network)
+ self.network = neutron_utils.create_network(
+ self.neutron, self.os_creds, self.net_config.network_settings)
def test_create_subnet_empty_cidr(self):
"""
Tests the neutron_utils.create_neutron_subnet() function for an
Exception when the subnet CIDR value is empty
"""
+ self.net_config.network_settings.subnet_settings[0].cidr = ''
+ with self.assertRaises(Exception):
+ self.network = neutron_utils.create_network(
+ self.neutron, self.os_creds, self.net_config.network_settings)
+
+
+class NeutronUtilsIPv6Tests(OSComponentTestCase):
+ """
+ Test for creating IPv6 networks with subnets via neutron_utils.py
+ """
+
+ def setUp(self):
+ self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
+ self.neutron = neutron_utils.neutron_client(self.os_creds)
+ self.network = None
+
+ def tearDown(self):
+ """
+ Cleans the remote OpenStack objects
+ """
+ if self.network:
+ try:
+ neutron_utils.delete_network(self.neutron, self.network)
+ except:
+ pass
+
+ def test_create_network_slaac(self):
+ """
+ Tests the neutron_utils.create_network() with an IPv6 subnet where DHCP
+ is True and IPv6 modes are slaac
+ """
+ sub_setting = SubnetConfig(
+ name=self.guid + '-subnet', cidr='1:1:0:0:0:0:0:0/64',
+ ip_version=6, dns_nameservers=['2620:0:ccc:0:0:0:0:2'],
+ gateway_ip='1:1:0:0:0:0:0:1', start='1:1::ff', end='1:1::ffff',
+ enable_dhcp=True, ipv6_ra_mode='slaac', ipv6_address_mode='slaac')
+ self.network_settings = NetworkConfig(
+ name=self.guid + '-net', subnet_settings=[sub_setting])
+
self.network = neutron_utils.create_network(
- self.neutron, self.os_creds, self.net_config.network_settings)
- 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.os_creds, self.network_settings)
+ self.assertEqual(self.network_settings.name, self.network.name)
+
+ subnet_settings = self.network_settings.subnet_settings[0]
+ self.assertEqual(1, len(self.network.subnets))
+ subnet = self.network.subnets[0]
+
+ self.assertEqual(self.network.id, subnet.network_id)
+ self.assertEqual(subnet_settings.name, subnet.name)
+ self.assertEqual(subnet_settings.start, subnet.start)
+ self.assertEqual(subnet_settings.end, subnet.end)
+ self.assertEqual('1:1::/64', subnet.cidr)
+ self.assertEqual(6, subnet.ip_version)
+ self.assertEqual(1, len(subnet.dns_nameservers))
+ self.assertEqual(
+ sub_setting.dns_nameservers[0], subnet.dns_nameservers[0])
+ self.assertTrue(subnet.enable_dhcp)
+ self.assertEqual(
+ subnet_settings.ipv6_ra_mode.value, subnet.ipv6_ra_mode)
+ self.assertEqual(
+ subnet_settings.ipv6_address_mode.value, subnet.ipv6_address_mode)
- with self.assertRaises(Exception):
- sub_sets = SubnetSettings(
- cidr='', name=self.net_config.subnet_name)
- neutron_utils.create_subnet(self.neutron, sub_sets, self.os_creds,
- network=self.network)
+ def test_create_network_stateful(self):
+ """
+ Tests the neutron_utils.create_network() with an IPv6 subnet where DHCP
+ is True and IPv6 modes are stateful
+ """
+ sub_setting = SubnetConfig(
+ name=self.guid + '-subnet', cidr='1:1:0:0:0:0:0:0/64',
+ ip_version=6, dns_nameservers=['2620:0:ccc:0:0:0:0:2'],
+ gateway_ip='1:1:0:0:0:0:0:1', start='1:1::ff', end='1:1::ffff',
+ enable_dhcp=True, ipv6_ra_mode='dhcpv6-stateful',
+ ipv6_address_mode='dhcpv6-stateful')
+ self.network_settings = NetworkConfig(
+ name=self.guid + '-net', subnet_settings=[sub_setting])
+
+ self.network = neutron_utils.create_network(
+ self.neutron, self.os_creds, self.network_settings)
+
+ self.assertEqual(self.network_settings.name, self.network.name)
+
+ subnet_settings = self.network_settings.subnet_settings[0]
+ self.assertEqual(1, len(self.network.subnets))
+ subnet = self.network.subnets[0]
+
+ self.assertEqual(self.network.id, subnet.network_id)
+ self.assertEqual(subnet_settings.name, subnet.name)
+ self.assertEqual(subnet_settings.start, subnet.start)
+ self.assertEqual(subnet_settings.end, subnet.end)
+ self.assertEqual('1:1::/64', subnet.cidr)
+ self.assertEqual(6, subnet.ip_version)
+ self.assertEqual(1, len(subnet.dns_nameservers))
+ self.assertEqual(
+ sub_setting.dns_nameservers[0], subnet.dns_nameservers[0])
+ self.assertTrue(subnet.enable_dhcp)
+ self.assertEqual(
+ subnet_settings.ipv6_ra_mode.value, subnet.ipv6_ra_mode)
+ self.assertEqual(
+ subnet_settings.ipv6_address_mode.value, subnet.ipv6_address_mode)
+
+ def test_create_network_stateless(self):
+ """
+ Tests the neutron_utils.create_network() when DHCP is enabled and
+ the RA and address modes are both 'slaac'
+ """
+ sub_setting = SubnetConfig(
+ name=self.guid + '-subnet', cidr='1:1:0:0:0:0:0:0/64',
+ ip_version=6, dns_nameservers=['2620:0:ccc:0:0:0:0:2'],
+ gateway_ip='1:1:0:0:0:0:0:1', start='1:1::ff', end='1:1::ffff',
+ enable_dhcp=True, ipv6_ra_mode='dhcpv6-stateless',
+ ipv6_address_mode='dhcpv6-stateless')
+ self.network_settings = NetworkConfig(
+ name=self.guid + '-net', subnet_settings=[sub_setting])
+
+ self.network = neutron_utils.create_network(
+ self.neutron, self.os_creds, self.network_settings)
+
+ self.assertEqual(self.network_settings.name, self.network.name)
+
+ subnet_settings = self.network_settings.subnet_settings[0]
+ self.assertEqual(1, len(self.network.subnets))
+ subnet = self.network.subnets[0]
+
+ self.assertEqual(self.network.id, subnet.network_id)
+ self.assertEqual(subnet_settings.name, subnet.name)
+ self.assertEqual(subnet_settings.start, subnet.start)
+ self.assertEqual(subnet_settings.end, subnet.end)
+ self.assertEqual('1:1::/64', subnet.cidr)
+ self.assertEqual(6, subnet.ip_version)
+ self.assertEqual(1, len(subnet.dns_nameservers))
+ self.assertEqual(
+ sub_setting.dns_nameservers[0], subnet.dns_nameservers[0])
+ self.assertTrue(subnet.enable_dhcp)
+ self.assertEqual(
+ subnet_settings.ipv6_ra_mode.value, subnet.ipv6_ra_mode)
+ self.assertEqual(
+ subnet_settings.ipv6_address_mode.value, subnet.ipv6_address_mode)
+
+ def test_create_network_no_dhcp_slaac(self):
+ """
+ Tests the neutron_utils.create_network() for a BadRequest when
+ DHCP is not enabled and the RA and address modes are both 'slaac'
+ """
+ sub_setting = SubnetConfig(
+ name=self.guid + '-subnet', cidr='1:1:0:0:0:0:0:0/64',
+ ip_version=6, dns_nameservers=['2620:0:ccc:0:0:0:0:2'],
+ gateway_ip='1:1:0:0:0:0:0:1', start='1:1::ff', end='1:1::ffff',
+ enable_dhcp=False, ipv6_ra_mode='slaac', ipv6_address_mode='slaac')
+ self.network_settings = NetworkConfig(
+ name=self.guid + '-net', subnet_settings=[sub_setting])
+
+ with self.assertRaises(BadRequest):
+ self.network = neutron_utils.create_network(
+ self.neutron, self.os_creds, self.network_settings)
+
+ def test_create_network_invalid_start_ip(self):
+ """
+ Tests the neutron_utils.create_network() that contains one IPv6 subnet
+ with an invalid start IP to ensure Neutron assigns it the smallest IP
+ possible
+ """
+ sub_setting = SubnetConfig(
+ name=self.guid + '-subnet', cidr='1:1::/48', ip_version=6,
+ start='foo')
+ self.network_settings = NetworkConfig(
+ name=self.guid + '-net', subnet_settings=[sub_setting])
+
+ self.network = neutron_utils.create_network(
+ self.neutron, self.os_creds, self.network_settings)
+
+ self.assertEqual('1:1::2', self.network.subnets[0].start)
+ self.assertEqual(
+ '1:1:0:ffff:ffff:ffff:ffff:ffff', self.network.subnets[0].end)
+
+ def test_create_network_invalid_end_ip(self):
+ """
+ Tests the neutron_utils.create_network() that contains one IPv6 subnet
+ with an invalid end IP to ensure Neutron assigns it the largest IP
+ possible
+ """
+ sub_setting = SubnetConfig(
+ name=self.guid + '-subnet', cidr='1:1::/48', ip_version=6,
+ end='bar')
+ self.network_settings = NetworkConfig(
+ name=self.guid + '-net', subnet_settings=[sub_setting])
+
+ self.network = neutron_utils.create_network(
+ self.neutron, self.os_creds, self.network_settings)
+
+ self.assertEqual('1:1::2', self.network.subnets[0].start)
+ self.assertEqual(
+ '1:1:0:ffff:ffff:ffff:ffff:ffff', self.network.subnets[0].end)
+
+ def test_create_network_with_bad_cidr(self):
+ """
+ Tests the neutron_utils.create_network() for a BadRequest when
+ the subnet CIDR is invalid
+ """
+ sub_setting = SubnetConfig(
+ name=self.guid + '-subnet', cidr='1:1:1:/48', ip_version=6)
+ self.network_settings = NetworkConfig(
+ name=self.guid + '-net', subnet_settings=[sub_setting])
+
+ with self.assertRaises(BadRequest):
+ self.network = neutron_utils.create_network(
+ self.neutron, self.os_creds, self.network_settings)
+
+ def test_create_network_invalid_gateway_ip(self):
+ """
+ Tests the neutron_utils.create_network() for a BadRequest when
+ the subnet gateway IP is invalid
+ """
+ sub_setting = SubnetConfig(
+ name=self.guid + '-subnet', cidr='1:1::/48', ip_version=6,
+ gateway_ip='1:2::1')
+ self.network_settings = NetworkConfig(
+ name=self.guid + '-net', subnet_settings=[sub_setting])
+
+ with self.assertRaises(BadRequest):
+ self.network = neutron_utils.create_network(
+ self.neutron, self.os_creds, self.network_settings)
+
+ def test_create_network_with_bad_dns(self):
+ """
+ Tests the neutron_utils.create_network() for a BadRequest when
+ the DNS IP is invalid
+ """
+ sub_setting = SubnetConfig(
+ name=self.guid + '-subnet', cidr='1:1::/48', ip_version=6,
+ dns_nameservers=['foo'])
+ self.network_settings = NetworkConfig(
+ name=self.guid + '-net', subnet_settings=[sub_setting])
+
+ with self.assertRaises(BadRequest):
+ self.network = neutron_utils.create_network(
+ self.neutron, self.os_creds, self.network_settings)
class NeutronUtilsRouterTests(OSComponentTestCase):
self.port_name = str(guid) + '-port'
self.neutron = neutron_utils.neutron_client(self.os_creds)
self.network = None
- self.subnet = None
self.port = None
self.router = None
self.interface_router = None
Cleans the remote OpenStack objects
"""
if self.interface_router:
- neutron_utils.remove_interface_router(self.neutron, self.router,
- self.subnet)
+ neutron_utils.remove_interface_router(
+ self.neutron, self.router, self.network.subnets[0])
if self.router:
try:
except:
pass
- if self.subnet:
- try:
- neutron_utils.delete_subnet(self.neutron, self.subnet)
- except:
- pass
-
if self.network:
- try:
- neutron_utils.delete_network(self.neutron, self.network)
- except:
- pass
+ neutron_utils.delete_network(self.neutron, self.network)
def test_create_router_simple(self):
"""
- Tests the neutron_utils.create_neutron_net() function when an external
- gateway is requested
+ Tests the neutron_utils.create_router()
"""
self.router = neutron_utils.create_router(
self.neutron, self.os_creds, self.net_config.router_settings)
def test_create_router_with_public_interface(self):
"""
- Tests the neutron_utils.create_neutron_net() function when an external
- gateway is requested
+ Tests the neutron_utils.create_router() function with a pubic interface
"""
subnet_setting = self.net_config.network_settings.subnet_settings[0]
self.net_config = openstack_tests.OSNetworkConfig(
ext_net = neutron_utils.get_network(
self.neutron, network_name=self.ext_net_name)
- self.assertEqual(
- self.router.external_gateway_info['network_id'], ext_net.id)
-
- def test_create_router_empty_name(self):
- """
- Tests the neutron_utils.create_neutron_net() function
- """
- with self.assertRaises(Exception):
- this_router_settings = create_router.RouterSettings(name='')
- self.router = neutron_utils.create_router(self.neutron,
- self.os_creds,
- this_router_settings)
-
- def test_create_router_null_name(self):
- """
- Tests the neutron_utils.create_neutron_subnet() function when the
- subnet CIDR value is None
- """
- with self.assertRaises(Exception):
- this_router_settings = create_router.RouterSettings()
- self.router = neutron_utils.create_router(self.neutron,
- self.os_creds,
- this_router_settings)
- validate_router(self.neutron, None, True)
+ self.assertEqual(self.router.external_network_id, ext_net.id)
def test_add_interface_router(self):
"""
self.neutron, self.net_config.network_settings.name, True))
subnet_setting = self.net_config.network_settings.subnet_settings[0]
- self.subnet = neutron_utils.create_subnet(
- self.neutron, subnet_setting,
- self.os_creds, self.network)
self.assertTrue(validate_subnet(
self.neutron, subnet_setting.name, subnet_setting.cidr, True))
True)
self.interface_router = neutron_utils.add_interface_router(
- self.neutron, self.router, self.subnet)
+ self.neutron, self.router, self.network.subnets[0])
validate_interface_router(self.interface_router, self.router,
- self.subnet)
+ self.network.subnets[0])
def test_add_interface_router_null_router(self):
"""
self.neutron, self.net_config.network_settings.name, True))
subnet_setting = self.net_config.network_settings.subnet_settings[0]
- self.subnet = neutron_utils.create_subnet(
- self.neutron, subnet_setting,
- self.os_creds, self.network)
self.assertTrue(validate_subnet(
self.neutron, subnet_setting.name, subnet_setting.cidr, True))
with self.assertRaises(NeutronException):
self.interface_router = neutron_utils.add_interface_router(
- self.neutron, self.router, self.subnet)
+ self.neutron, self.router, self.network.subnets[0])
def test_add_interface_router_null_subnet(self):
"""
with self.assertRaises(NeutronException):
self.interface_router = neutron_utils.add_interface_router(
- self.neutron, self.router, self.subnet)
+ self.neutron, self.router, None)
+
+ def test_add_interface_router_missing_subnet(self):
+ """
+ Tests the neutron_utils.add_interface_router() function for an
+ Exception when the subnet object has been deleted
+ """
+ self.network = neutron_utils.create_network(
+ self.neutron, self.os_creds, self.net_config.network_settings)
+ 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.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)
+
+ for subnet in self.network.subnets:
+ neutron_utils.delete_subnet(self.neutron, subnet)
+
+ with self.assertRaises(NotFound):
+ self.interface_router = neutron_utils.add_interface_router(
+ self.neutron, self.router, self.network.subnets[0])
def test_create_port(self):
"""
self.neutron, self.net_config.network_settings.name, True))
subnet_setting = self.net_config.network_settings.subnet_settings[0]
- self.subnet = neutron_utils.create_subnet(
- self.neutron, subnet_setting, self.os_creds, self.network)
self.assertTrue(validate_subnet(
self.neutron, subnet_setting.name, subnet_setting.cidr, True))
self.port = neutron_utils.create_port(
- self.neutron, self.os_creds, PortSettings(
+ self.neutron, self.os_creds, PortConfig(
name=self.port_name,
ip_addrs=[{
'subnet_name': subnet_setting.name,
self.neutron, self.net_config.network_settings.name, True))
subnet_setting = self.net_config.network_settings.subnet_settings[0]
- self.subnet = neutron_utils.create_subnet(
- self.neutron, subnet_setting, self.os_creds, self.network)
self.assertTrue(validate_subnet(self.neutron, subnet_setting.name,
subnet_setting.cidr, True))
self.port = neutron_utils.create_port(
- self.neutron, self.os_creds, PortSettings(
+ self.neutron, self.os_creds, PortConfig(
name=self.port_name,
network_name=self.net_config.network_settings.name,
ip_addrs=[{
self.neutron, self.net_config.network_settings.name, True))
subnet_setting = self.net_config.network_settings.subnet_settings[0]
- self.subnet = neutron_utils.create_subnet(
- self.neutron, subnet_setting,
- self.os_creds, self.network)
self.assertTrue(validate_subnet(
self.neutron, subnet_setting.name, subnet_setting.cidr, True))
self.port = neutron_utils.create_port(
self.neutron, self.os_creds,
- PortSettings(
+ PortConfig(
network_name=self.net_config.network_settings.name,
ip_addrs=[{
'subnet_name': subnet_setting.name,
with self.assertRaises(Exception):
self.port = neutron_utils.create_port(
self.neutron, self.os_creds,
- PortSettings(
+ PortConfig(
name=self.port_name,
network_name=self.net_config.network_settings.name,
ip_addrs=[{
self.neutron, self.net_config.network_settings.name, True))
subnet_setting = self.net_config.network_settings.subnet_settings[0]
- self.subnet = neutron_utils.create_subnet(
- self.neutron, subnet_setting,
- self.os_creds, self.network)
self.assertTrue(validate_subnet(
self.neutron, subnet_setting.name, subnet_setting.cidr, True))
with self.assertRaises(Exception):
self.port = neutron_utils.create_port(
self.neutron, self.os_creds,
- PortSettings(
+ PortConfig(
name=self.port_name,
network_name=self.net_config.network_settings.name,
ip_addrs=[{
self.neutron, self.net_config.network_settings.name, True))
subnet_setting = self.net_config.network_settings.subnet_settings[0]
- self.subnet = neutron_utils.create_subnet(
- self.neutron, subnet_setting, self.os_creds, self.network)
self.assertTrue(validate_subnet(
self.neutron, subnet_setting.name, subnet_setting.cidr, True))
with self.assertRaises(Exception):
self.port = neutron_utils.create_port(
self.neutron, self.os_creds,
- PortSettings(
+ PortConfig(
name=self.port_name,
network_name=self.net_config.network_settings.name,
ip_addrs=[{
self.neutron, self.net_config.network_settings.name, True))
subnet_setting = self.net_config.network_settings.subnet_settings[0]
- self.subnet = neutron_utils.create_subnet(
- self.neutron, subnet_setting, self.os_creds, self.network)
self.assertTrue(validate_subnet(
self.neutron, subnet_setting.name, subnet_setting.cidr, True))
with self.assertRaises(Exception):
self.port = neutron_utils.create_port(
self.neutron, self.os_creds,
- PortSettings(
+ PortConfig(
name=self.port_name,
network_name=self.net_config.network_settings.name,
ip_addrs=[{
"""
Tests the neutron_utils.create_security_group() function
"""
- sec_grp_settings = SecurityGroupSettings(name=self.sec_grp_name)
+ sec_grp_settings = SecurityGroupConfig(name=self.sec_grp_name)
security_group = neutron_utils.create_security_group(self.neutron,
self.keystone,
sec_grp_settings)
def test_create_sec_grp_no_name(self):
"""
- Tests the SecurityGroupSettings constructor and
+ Tests the SecurityGroupConfig constructor and
neutron_utils.create_security_group() function to ensure that
attempting to create a security group without a name will raise an
exception
"""
with self.assertRaises(Exception):
- sec_grp_settings = SecurityGroupSettings()
+ sec_grp_settings = SecurityGroupConfig()
self.security_groups.append(
neutron_utils.create_security_group(self.neutron,
self.keystone,
"""
Tests the neutron_utils.create_security_group() function
"""
- sec_grp_settings = SecurityGroupSettings(name=self.sec_grp_name,
- description='hello group')
+ 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))
Tests the neutron_utils.create_security_group() function
"""
- sec_grp_rule_settings = SecurityGroupRuleSettings(
+ sec_grp_rule_settings = SecurityGroupRuleConfig(
sec_grp_name=self.sec_grp_name, direction=Direction.ingress)
- sec_grp_settings = SecurityGroupSettings(
+ sec_grp_settings = SecurityGroupConfig(
name=self.sec_grp_name, description='hello group',
rule_settings=[sec_grp_rule_settings])
self.security_groups.append(neutron_utils.create_security_group(
self.neutron, self.keystone,
- SecurityGroupSettings(name=self.sec_grp_name + '-1',
- description='hello group')))
+ SecurityGroupConfig(
+ name=self.sec_grp_name + '-1', description='hello group')))
self.security_groups.append(neutron_utils.create_security_group(
self.neutron, self.keystone,
- SecurityGroupSettings(name=self.sec_grp_name + '-2',
- description='hello group')))
+ SecurityGroupConfig(
+ name=self.sec_grp_name + '-2', description='hello group')))
sec_grp_1b = neutron_utils.get_security_group_by_id(
self.neutron, self.security_groups[0].id)
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
+import time
import uuid
import os
-import time
from snaps import file_utils
+from snaps.config.flavor import FlavorConfig
+from snaps.config.network import PortConfig
+from snaps.config.vm_inst import VmInstanceConfig
+from snaps.config.volume import VolumeConfig
from snaps.openstack import create_instance
-from snaps.openstack.create_flavor import FlavorSettings, OpenStackFlavor
+from snaps.openstack.create_flavor import OpenStackFlavor
from snaps.openstack.create_image import OpenStackImage
-from snaps.openstack.create_instance import VmInstanceSettings
-from snaps.openstack.create_network import OpenStackNetwork, PortSettings
+from snaps.openstack.create_instance import OpenStackVmInstance
+from snaps.openstack.create_network import OpenStackNetwork
+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
+from snaps.openstack.utils import (
+ nova_utils, neutron_utils, glance_utils, cinder_utils)
__author__ = 'spisarski'
# This should not throw an exception
nova.flavors.list()
+ def test_nova_get_hypervisor_hosts(self):
+ """
+ Tests to ensure that get_hypervisors() function works.
+ """
+ nova = nova_utils.nova_client(self.os_creds)
+
+ hosts = nova_utils.get_hypervisor_hosts(nova)
+ # This should not throw an exception
+ self.assertGreaterEqual(len(hosts), 1)
+
def test_nova_connect_fail(self):
"""
Tests to ensure that the improper credentials cannot connect.
and creating an OS image file within OpenStack
"""
guid = self.__class__.__name__ + '-' + str(uuid.uuid4())
- self.flavor_settings = FlavorSettings(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.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.flavor = None
self.flavor_creator = OpenStackFlavor(
self.os_creds,
- FlavorSettings(
+ FlavorConfig(
name=guid + '-flavor-name', ram=256, disk=10, vcpus=1))
self.flavor_creator.create()
- port_settings = PortSettings(name=guid + '-port',
- network_name=network_settings.name)
+ port_settings = PortConfig(
+ name=guid + '-port', network_name=network_settings.name)
self.port = neutron_utils.create_port(
self.neutron, self.os_creds, port_settings)
- self.instance_settings = VmInstanceSettings(
+ self.instance_settings = VmInstanceConfig(
name=guid + '-vm_inst',
flavor=self.flavor_creator.flavor_settings.name,
port_settings=[port_settings])
iters += 1
self.assertTrue(active)
- vm_inst = nova_utils.get_latest_server_object(self.nova, self.vm_inst)
+ vm_inst = nova_utils.get_latest_server_object(
+ self.nova, self.neutron, self.vm_inst)
self.assertEqual(self.vm_inst.name, vm_inst.name)
self.assertEqual(self.vm_inst.id, vm_inst.id)
+
+
+class NovaUtilsInstanceVolumeTests(OSComponentTestCase):
+ """
+ Tests the creation of VM instances via nova_utils.py
+ """
+
+ def setUp(self):
+ """
+ Setup objects required by VM instances
+ :return:
+ """
+
+ 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.image_creator = None
+ self.network_creator = None
+ self.flavor_creator = None
+ self.volume_creator = None
+ self.instance_creator = None
+
+ try:
+ image_settings = openstack_tests.cirros_image_settings(
+ name=guid + '-image', image_metadata=self.image_metadata)
+ self.image_creator = OpenStackImage(
+ self.os_creds, image_settings=image_settings)
+ self.image_creator.create()
+
+ network_settings = openstack_tests.get_priv_net_config(
+ guid + '-net', 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))
+ self.flavor_creator.create()
+
+ # Create Volume
+ volume_settings = VolumeConfig(
+ name=self.__class__.__name__ + '-' + str(guid))
+ self.volume_creator = OpenStackVolume(
+ self.os_creds, volume_settings)
+ self.volume_creator.create(block=True)
+
+ port_settings = PortConfig(
+ name=guid + '-port', network_name=network_settings.name)
+ instance_settings = VmInstanceConfig(
+ name=guid + '-vm_inst',
+ flavor=self.flavor_creator.flavor_settings.name,
+ port_settings=[port_settings])
+ self.instance_creator = OpenStackVmInstance(
+ self.os_creds, instance_settings, image_settings)
+ self.instance_creator.create(block=True)
+ except:
+ self.tearDown()
+ raise
+
+ def tearDown(self):
+ """
+ Cleanup deployed resources
+ :return:
+ """
+ if self.instance_creator:
+ try:
+ self.instance_creator.clean()
+ except:
+ pass
+ if self.volume_creator:
+ try:
+ self.volume_creator.clean()
+ except:
+ pass
+ if self.flavor_creator:
+ try:
+ self.flavor_creator.clean()
+ except:
+ pass
+ if self.network_creator:
+ try:
+ self.network_creator.clean()
+ except:
+ pass
+ if self.image_creator:
+ try:
+ self.image_creator.clean()
+ except:
+ pass
+
+ def test_add_remove_volume(self):
+ """
+ Tests the nova_utils.create_server() method
+ :return:
+ """
+
+ 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)
+ 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)
+
+ # Detach volume to VM
+ nova_utils.detach_volume(
+ self.nova, neutron, self.instance_creator.get_vm_inst(),
+ self.volume_creator.get_volume())
+
+ time.sleep(10)
+
+ 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)
+
+ # Validate Attachment
+ self.assertIsNotNone(vol_attach)
+ self.assertEqual(self.volume_creator.get_volume().id, vol_attach.id)
+ self.assertEqual(1, len(vol_attach.attachments))
+ self.assertEqual(vm_attach.volume_ids[0]['id'],
+ vol_attach.attachments[0]['volume_id'])
+
+ # 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))
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
+import unittest
+
import os
import uuid
+from snaps.config.network import SubnetConfig, NetworkConfig, PortConfig
+from snaps.config.flavor import FlavorConfig
+from snaps.config.keypair import KeypairConfig
+from snaps.config.qos import Consumer
+from snaps.config.security_group import (
+ SecurityGroupRuleConfig, Direction, Protocol, SecurityGroupConfig)
+from snaps.config.vm_inst import VmInstanceConfig, FloatingIpConfig
+from snaps.domain.flavor import Flavor
+from snaps.domain.volume import (
+ Volume, VolumeType, VolumeTypeEncryption, QoSSpec)
from snaps.openstack import (
create_image, create_network, create_router, create_flavor,
create_keypairs, create_instance)
-from snaps.openstack.create_network import (
- NetworkSettings, OpenStackNetwork, SubnetSettings)
-from snaps.openstack.create_security_group import (
- SecurityGroupRuleSettings, Direction, Protocol, OpenStackSecurityGroup,
- SecurityGroupSettings)
+from snaps.openstack.create_qos import Consumer
+from snaps.openstack.create_network import OpenStackNetwork
+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 (
class SettingsUtilsNetworkingTests(OSComponentTestCase):
"""
- Tests the ability to reverse engineer NetworkSettings objects from existing
+ Tests the ability to reverse engineer NetworkConfig objects from existing
networks deployed to OpenStack
"""
def test_derive_net_settings_no_subnet(self):
"""
- Validates the utility function settings_utils#create_network_settings
- returns an acceptable NetworkSettings object and ensures that the
+ Validates the utility function settings_utils#create_network_config
+ returns an acceptable NetworkConfig object and ensures that the
new settings object will not cause the new OpenStackNetwork instance
to create another network
"""
- net_settings = NetworkSettings(name=self.network_name)
+ net_settings = NetworkConfig(name=self.network_name)
self.net_creator = OpenStackNetwork(self.os_creds, net_settings)
network = self.net_creator.create()
- derived_settings = settings_utils.create_network_settings(
+ derived_settings = settings_utils.create_network_config(
self.neutron, network)
self.assertIsNotNone(derived_settings)
def test_derive_net_settings_two_subnets(self):
"""
- Validates the utility function settings_utils#create_network_settings
- returns an acceptable NetworkSettings object
+ Validates the utility function settings_utils#create_network_config
+ returns an acceptable NetworkConfig object
"""
subnet_settings = list()
- subnet_settings.append(SubnetSettings(name='sub1', cidr='10.0.0.0/24'))
- subnet_settings.append(SubnetSettings(name='sub2', cidr='10.0.1.0/24'))
- net_settings = NetworkSettings(name=self.network_name,
- subnet_settings=subnet_settings)
+ subnet_settings.append(SubnetConfig(name='sub1', cidr='10.0.0.0/24'))
+ subnet_settings.append(SubnetConfig(name='sub2', cidr='10.0.1.0/24'))
+ net_settings = NetworkConfig(
+ name=self.network_name, subnet_settings=subnet_settings)
self.net_creator = OpenStackNetwork(self.os_creds, net_settings)
network = self.net_creator.create()
- derived_settings = settings_utils.create_network_settings(
+ derived_settings = settings_utils.create_network_config(
self.neutron, network)
self.assertIsNotNone(derived_settings)
class SettingsUtilsVmInstTests(OSComponentTestCase):
"""
- Tests the ability to reverse engineer VmInstanceSettings objects from
+ Tests the ability to reverse engineer VmInstanceConfig objects from
existing VMs/servers deployed to OpenStack
"""
Instantiates the CreateImage object that is responsible for downloading
and creating an OS image file within OpenStack
"""
- # super(self.__class__, self).__start__()
-
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)
# Create Flavor
self.flavor_creator = create_flavor.OpenStackFlavor(
self.os_creds,
- create_flavor.FlavorSettings(name=guid + '-flavor-name',
- ram=256, disk=1, vcpus=1))
+ FlavorConfig(
+ name=guid + '-flavor-name', ram=256, disk=1, vcpus=1))
self.flavor_creator.create()
# Create Key/Pair
self.keypair_creator = create_keypairs.OpenStackKeypair(
- self.os_creds, create_keypairs.KeypairSettings(
+ self.os_creds, KeypairConfig(
name=self.keypair_name,
public_filepath=self.keypair_pub_filepath,
private_filepath=self.keypair_priv_filepath))
# Create Security Group
sec_grp_name = guid + '-sec-grp'
- rule1 = SecurityGroupRuleSettings(sec_grp_name=sec_grp_name,
- direction=Direction.ingress,
- protocol=Protocol.icmp)
- rule2 = SecurityGroupRuleSettings(sec_grp_name=sec_grp_name,
- direction=Direction.ingress,
- protocol=Protocol.tcp,
- port_range_min=22,
- port_range_max=22)
+ rule1 = SecurityGroupRuleConfig(
+ sec_grp_name=sec_grp_name, direction=Direction.ingress,
+ protocol=Protocol.icmp)
+ rule2 = SecurityGroupRuleConfig(
+ sec_grp_name=sec_grp_name, direction=Direction.ingress,
+ protocol=Protocol.tcp, port_range_min=22, port_range_max=22)
self.sec_grp_creator = OpenStackSecurityGroup(
self.os_creds,
- SecurityGroupSettings(name=sec_grp_name,
- rule_settings=[rule1, rule2]))
+ SecurityGroupConfig(
+ name=sec_grp_name, rule_settings=[rule1, rule2]))
self.sec_grp_creator.create()
# Create instance
ports_settings = list()
ports_settings.append(
- create_network.PortSettings(
+ PortConfig(
name=self.port_1_name,
network_name=self.pub_net_config.network_settings.name))
- instance_settings = create_instance.VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=ports_settings,
- floating_ip_settings=[create_instance.FloatingIpSettings(
+ floating_ip_settings=[FloatingIpConfig(
name=self.floating_ip_name, port_name=self.port_1_name,
router_name=self.pub_net_config.router_settings.name)])
# super(self.__class__, self).__clean__()
- def test_derive_vm_inst_settings(self):
+ def test_derive_vm_inst_config(self):
"""
- Validates the utility function settings_utils#create_vm_inst_settings
- returns an acceptable VmInstanceSettings object
+ Validates the utility function settings_utils#create_vm_inst_config
+ returns an acceptable VmInstanceConfig object
"""
self.inst_creator.create(block=True)
server = nova_utils.get_server(
- self.nova, vm_inst_settings=self.inst_creator.instance_settings)
- derived_vm_settings = settings_utils.create_vm_inst_settings(
+ self.nova, self.neutron,
+ vm_inst_settings=self.inst_creator.instance_settings)
+ derived_vm_settings = settings_utils.create_vm_inst_config(
self.nova, self.neutron, server)
self.assertIsNotNone(derived_vm_settings)
self.assertIsNotNone(derived_vm_settings.port_settings)
def test_derive_image_settings(self):
"""
Validates the utility function settings_utils#create_image_settings
- returns an acceptable ImageSettings object
+ returns an acceptable ImageConfig object
"""
self.inst_creator.create(block=True)
server = nova_utils.get_server(
- self.nova, vm_inst_settings=self.inst_creator.instance_settings)
- derived_image_settings = settings_utils.determine_image_settings(
+ self.nova, self.neutron,
+ vm_inst_settings=self.inst_creator.instance_settings)
+ derived_image_settings = settings_utils.determine_image_config(
self.glance, server, [self.image_creator.image_settings])
self.assertIsNotNone(derived_image_settings)
self.assertEqual(self.image_creator.image_settings.name,
derived_image_settings.name)
+
+
+class SettingsUtilsUnitTests(unittest.TestCase):
+ """
+ Exercises the settings_utils.py functions around volumes
+ """
+
+ 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)
+ settings = settings_utils.create_volume_config(volume)
+ self.assertEqual(volume.name, settings.name)
+ self.assertEqual(volume.description, settings.description)
+ self.assertEqual(volume.size, settings.size)
+ self.assertEqual(volume.type, settings.type_name)
+ self.assertEqual(volume.availability_zone, settings.availability_zone)
+ self.assertEqual(volume.multi_attach, settings.multi_attach)
+
+ def test_vol_type_settings_from_vol(self):
+ encryption = VolumeTypeEncryption(
+ volume_encryption_id='vol-encrypt-id', volume_type_id='vol-typ-id',
+ control_location='front-end', provider='FooClass', cipher='1',
+ key_size=1)
+ qos_spec = QoSSpec(name='qos-spec-name', spec_id='qos-spec-id',
+ consumer=Consumer.back_end)
+ volume_type = VolumeType(
+ name='vol-type-name', volume_type_id='vol-type-id', public=True,
+ encryption=encryption, qos_spec=qos_spec)
+
+ settings = settings_utils.create_volume_type_config(volume_type)
+ self.assertEqual(volume_type.name, settings.name)
+ self.assertEqual(volume_type.public, settings.public)
+
+ encrypt_settings = settings.encryption
+ self.assertIsNotNone(encrypt_settings)
+ self.assertEqual(encryption.control_location,
+ encrypt_settings.control_location.value)
+ self.assertEqual(encryption.cipher, encrypt_settings.cipher)
+ self.assertEqual(encryption.key_size, encrypt_settings.key_size)
+
+ self.assertEqual(qos_spec.name, settings.qos_spec_name)
+
+ def test_flavor_settings_from_flavor(self):
+ flavor = Flavor(
+ name='flavor-name', flavor_id='flavor-id', ram=99, disk=101,
+ vcpus=9, ephemeral=3, swap=5, rxtx_factor=7, is_public=False)
+ settings = settings_utils.create_flavor_config(flavor)
+ self.assertEqual(flavor.name, settings.name)
+ self.assertEqual(flavor.id, settings.flavor_id)
+ self.assertEqual(flavor.ram, settings.ram)
+ self.assertEqual(flavor.disk, settings.disk)
+ self.assertEqual(flavor.vcpus, settings.vcpus)
+ self.assertEqual(flavor.ephemeral, settings.ephemeral)
+ self.assertEqual(flavor.swap, settings.swap)
+ self.assertEqual(flavor.rxtx_factor, settings.rxtx_factor)
+ self.assertEqual(flavor.is_public, settings.is_public)
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
+import ast
import logging
import re
if ssh:
ssh.close()
+ vars = dict()
+ if args.vars:
+ vars = ast.literal_eval(args.vars)
+ if not isinstance(vars, dict):
+ vars = dict()
+
retval = ansible_utils.apply_playbook(
parsed_args.playbook, [parsed_args.ip_addr], parsed_args.host_user,
- parsed_args.priv_key, variables={'name': 'Foo'},
+ parsed_args.priv_key, variables=vars,
proxy_setting=proxy_settings)
exit(retval)
required=False, help='<host>:<port>')
parser.add_argument('-s', '--ssh-proxy-cmd', dest='ssh_proxy_cmd',
required=False)
+ parser.add_argument('-v', '--vars', dest='vars',
+ required=False)
args = parser.parse_args()
main(args)
+
+++ /dev/null
-DEVICE={{ nic_name }}
-NAME={{ nic_name }}
-IPADDR={{ nic_ip }}
-
-DEFROUTE=no
-NETMASK=255.255.255.0
-NM_CONTROLLED=no
-IPV6INIT=yes
-IPV6_AUTOCONF=yes
-IPV6_DEFROUTE=yes
-IPV6_PEERDNS=yes
-IPV6_PEERROUTES=yes
-IPV6_FAILURE_FATAL=no
-ONBOOT=yes
\ No newline at end of file
+++ /dev/null
-auto {{ nic_name }}
-iface {{ nic_name }} inet dhcp
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 (
+ Direction, Protocol, SecurityGroupConfig, SecurityGroupRuleConfig)
+from snaps.config.vm_inst import VmInstanceConfig, FloatingIpConfig
+
from snaps.openstack import create_flavor
from snaps.openstack import create_image
from snaps.openstack import create_instance
from snaps.openstack import create_keypairs
from snaps.openstack import create_network
from snaps.openstack import create_router
-from snaps.openstack.create_security_group import (
- SecurityGroupRuleSettings, Direction, Protocol, OpenStackSecurityGroup,
- SecurityGroupSettings)
+from snaps.openstack.create_security_group import OpenStackSecurityGroup
from snaps.openstack.tests import openstack_tests
from snaps.openstack.tests.create_instance_tests import check_dhcp_lease
from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase
# Create Flavor
self.flavor_creator = create_flavor.OpenStackFlavor(
self.admin_os_creds,
- create_flavor.FlavorSettings(name=guid + '-flavor-name',
- ram=2048, disk=10, vcpus=2,
- metadata=self.flavor_metadata))
+ FlavorConfig(
+ name=guid + '-flavor-name', ram=2048, disk=10, vcpus=2,
+ metadata=self.flavor_metadata))
self.flavor_creator.create()
# Create Key/Pair
self.keypair_creator = create_keypairs.OpenStackKeypair(
- self.os_creds, create_keypairs.KeypairSettings(
+ self.os_creds, KeypairConfig(
name=self.keypair_name,
public_filepath=self.keypair_pub_filepath,
private_filepath=self.keypair_priv_filepath))
# Create Security Group
sec_grp_name = guid + '-sec-grp'
- rule1 = SecurityGroupRuleSettings(sec_grp_name=sec_grp_name,
- direction=Direction.ingress,
- protocol=Protocol.icmp)
- rule2 = SecurityGroupRuleSettings(sec_grp_name=sec_grp_name,
- direction=Direction.ingress,
- protocol=Protocol.tcp,
- port_range_min=22,
- port_range_max=22)
+ rule1 = SecurityGroupRuleConfig(
+ sec_grp_name=sec_grp_name, direction=Direction.ingress,
+ protocol=Protocol.icmp)
+ rule2 = SecurityGroupRuleConfig(
+ sec_grp_name=sec_grp_name, direction=Direction.ingress,
+ protocol=Protocol.tcp, port_range_min=22, port_range_max=22)
self.sec_grp_creator = OpenStackSecurityGroup(
self.os_creds,
- SecurityGroupSettings(name=sec_grp_name,
- rule_settings=[rule1, rule2]))
+ SecurityGroupConfig(
+ name=sec_grp_name, rule_settings=[rule1, rule2]))
self.sec_grp_creator.create()
# Create instance
ports_settings = list()
ports_settings.append(
- create_network.PortSettings(
+ PortConfig(
name=self.port_1_name,
network_name=self.pub_net_config.network_settings.name))
- instance_settings = create_instance.VmInstanceSettings(
+ instance_settings = VmInstanceConfig(
name=self.vm_inst_name,
flavor=self.flavor_creator.flavor_settings.name,
port_settings=ports_settings,
- floating_ip_settings=[create_instance.FloatingIpSettings(
+ floating_ip_settings=[FloatingIpConfig(
name=self.floating_ip_name, port_name=self.port_1_name,
router_name=self.pub_net_config.router_settings.name)])
# Block until VM's ssh port has been opened
self.assertTrue(self.inst_creator.vm_ssh_active(block=True))
+ # Block until cloud-init has completed
+ self.assertTrue(self.inst_creator.cloud_init_complete(block=True))
+
ssh_client = self.inst_creator.ssh_client()
self.assertIsNotNone(ssh_client)
ssh = ansible_utils.ssh_client(ip, user, priv_key,
self.os_creds.proxy_settings)
self.assertIsNotNone(ssh)
-
+ scp = None
try:
scp = SCPClient(ssh.get_transport())
scp.get('~/hello.txt', self.test_file_local_path)
finally:
- scp.close()
+ if scp:
+ scp.close()
ssh.close()
self.assertTrue(os.path.isfile(self.test_file_local_path))
# Block until VM's ssh port has been opened
self.assertTrue(self.inst_creator.vm_ssh_active(block=True))
+ # Block until cloud-init has completed
+ self.assertTrue(self.inst_creator.cloud_init_complete(block=True))
+
# Apply Security Group
self.inst_creator.add_security_group(
self.sec_grp_creator.get_security_group())
ssh = ansible_utils.ssh_client(ip, user, priv_key,
self.os_creds.proxy_settings)
self.assertIsNotNone(ssh)
+ scp = None
try:
scp = SCPClient(ssh.get_transport())
scp.get('/tmp/hello.txt', self.test_file_local_path)
finally:
- scp.close()
+ if scp:
+ scp.close()
ssh.close()
self.assertTrue(os.path.isfile(self.test_file_local_path))
import logging
import unittest
+from snaps.config.tests.cluster_template_tests import (
+ ClusterTemplateConfigUnitTests)
+from snaps.config.tests.network_tests import (
+ NetworkConfigUnitTests, SubnetConfigUnitTests, PortConfigUnitTests)
+from snaps.config.tests.security_group_tests import (
+ SecurityGroupConfigUnitTests, SecurityGroupRuleConfigUnitTests)
+from snaps.config.tests.vm_inst_tests import (
+ VmInstanceConfigUnitTests, FloatingIpConfigUnitTests)
+from snaps.config.tests.volume_tests import VolumeConfigUnitTests
+from snaps.config.tests.volume_type_tests import VolumeTypeConfigUnitTests
+from snaps.config.tests.qos_tests import QoSConfigUnitTests
+from snaps.config.tests.stack_tests import StackConfigUnitTests
+from snaps.config.tests.router_tests import RouterConfigUnitTests
+from snaps.config.tests.user_tests import UserConfigUnitTests
+from snaps.config.tests.project_tests import ProjectConfigUnitTests
+from snaps.config.tests.keypair_tests import KeypairConfigUnitTests
+from snaps.config.tests.flavor_tests import FlavorConfigUnitTests
+import snaps.config.tests.image_tests as image_tests
+import snaps.openstack.tests.create_image_tests as creator_tests
+from snaps.domain.test.cluster_template_tests import ClusterTemplateUnitTests
from snaps.domain.test.flavor_tests import FlavorDomainObjectTests
from snaps.domain.test.image_tests import ImageDomainObjectTests
from snaps.domain.test.keypair_tests import KeypairDomainObjectTests
VmInstDomainObjectTests, FloatingIpDomainObjectTests)
from snaps.domain.test.volume_tests import (
QoSSpecDomainObjectTests, VolumeTypeDomainObjectTests,
- VolumeTypeEncryptionObjectTests)
+ VolumeTypeEncryptionObjectTests, VolumeDomainObjectTests)
+from snaps.openstack.tests.cluster_template_tests import (
+ CreateClusterTemplateTests)
from snaps.openstack.tests.conf.os_credentials_tests import (
ProxySettingsUnitTests, OSCredsUnitTests)
from snaps.openstack.tests.create_flavor_tests import (
CreateFlavorTests, FlavorSettingsUnitTests)
from snaps.openstack.tests.create_image_tests import (
- CreateImageSuccessTests, CreateImageNegativeTests, ImageSettingsUnitTests,
+ CreateImageSuccessTests, CreateImageNegativeTests,
CreateMultiPartImageTests)
from snaps.openstack.tests.create_instance_tests import (
- CreateInstanceSingleNetworkTests, CreateInstancePubPrivNetTests,
- CreateInstanceOnComputeHost, CreateInstanceSimpleTests,
- FloatingIpSettingsUnitTests, InstanceSecurityGroupTests,
- VmInstanceSettingsUnitTests, CreateInstancePortManipulationTests,
- SimpleHealthCheck, CreateInstanceFromThreePartImage,
- CreateInstanceMockOfflineTests, CreateInstanceTwoNetTests)
+ CreateInstanceSingleNetworkTests, CreateInstanceOnComputeHost,
+ CreateInstanceSimpleTests, FloatingIpSettingsUnitTests,
+ InstanceSecurityGroupTests, VmInstanceSettingsUnitTests,
+ CreateInstancePortManipulationTests, SimpleHealthCheck,
+ CreateInstanceFromThreePartImage, CreateInstanceMockOfflineTests,
+ CreateInstanceTwoNetTests, CreateInstanceVolumeTests,
+ CreateInstanceIPv6NetworkTests)
from snaps.openstack.tests.create_keypairs_tests import (
CreateKeypairsTests, KeypairSettingsUnitTests, CreateKeypairsCleanupTests)
from snaps.openstack.tests.create_network_tests import (
CreateNetworkSuccessTests, NetworkSettingsUnitTests, PortSettingsUnitTests,
- SubnetSettingsUnitTests, CreateNetworkTypeTests)
+ SubnetSettingsUnitTests, CreateNetworkTypeTests, CreateNetworkIPv6Tests)
from snaps.openstack.tests.create_project_tests import (
CreateProjectSuccessTests, ProjectSettingsUnitTests,
CreateProjectUserTests)
SecurityGroupSettingsUnitTests)
from snaps.openstack.tests.create_stack_tests import (
StackSettingsUnitTests, CreateStackSuccessTests, CreateStackNegativeTests,
- CreateComplexStackTests)
+ CreateStackFlavorTests, CreateStackFloatingIpTests,
+ CreateStackNestedResourceTests, CreateStackKeypairTests,
+ CreateStackVolumeTests, CreateStackSecurityGroupTests)
from snaps.openstack.tests.create_user_tests import (
UserSettingsUnitTests, CreateUserSuccessTests)
+from snaps.openstack.tests.create_volume_tests import (
+ VolumeSettingsUnitTests, CreateSimpleVolumeSuccessTests,
+ CreateVolumeWithTypeTests, CreateVolumeWithImageTests,
+ CreateSimpleVolumeFailureTests)
from snaps.openstack.tests.create_volume_type_tests import (
VolumeTypeSettingsUnitTests, CreateSimpleVolumeTypeSuccessTests,
CreateVolumeTypeComplexTests)
OSComponentTestCase, OSIntegrationTestCase)
from snaps.openstack.utils.tests.cinder_utils_tests import (
CinderSmokeTests, CinderUtilsQoSTests, CinderUtilsSimpleVolumeTypeTests,
- CinderUtilsAddEncryptionTests, CinderUtilsVolumeTypeCompleteTests)
+ CinderUtilsAddEncryptionTests, CinderUtilsVolumeTypeCompleteTests,
+ CinderUtilsVolumeTests)
from snaps.openstack.utils.tests.glance_utils_tests import (
GlanceSmokeTests, GlanceUtilsTests)
from snaps.openstack.utils.tests.heat_utils_tests import (
HeatSmokeTests, HeatUtilsCreateSimpleStackTests,
- HeatUtilsCreateComplexStackTests)
+ HeatUtilsCreateComplexStackTests, HeatUtilsFlavorTests,
+ HeatUtilsKeypairTests, HeatUtilsVolumeTests, HeatUtilsSecurityGroupTests)
from snaps.openstack.utils.tests.keystone_utils_tests import (
KeystoneSmokeTests, KeystoneUtilsTests)
from snaps.openstack.utils.tests.neutron_utils_tests import (
NeutronSmokeTests, NeutronUtilsNetworkTests, NeutronUtilsSubnetTests,
NeutronUtilsRouterTests, NeutronUtilsSecurityGroupTests,
- NeutronUtilsFloatingIpTests)
+ NeutronUtilsFloatingIpTests, NeutronUtilsIPv6Tests)
from snaps.openstack.utils.tests.nova_utils_tests import (
NovaSmokeTests, NovaUtilsKeypairTests, NovaUtilsFlavorTests,
- NovaUtilsInstanceTests)
+ NovaUtilsInstanceTests, NovaUtilsInstanceVolumeTests)
+from snaps.openstack.utils.tests.settings_utils_tests import (
+ SettingsUtilsUnitTests)
+from snaps.openstack.utils.tests.magnum_utils_tests import (
+ MagnumSmokeTests, MagnumUtilsClusterTypeTests)
from snaps.provisioning.tests.ansible_utils_tests import (
AnsibleProvisioningTests)
from snaps.tests.file_utils_tests import FileUtilsTests
:return: None as the tests will be adding to the 'suite' parameter object
"""
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(FileUtilsTests))
- suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
- SecurityGroupRuleSettingsUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
ProxySettingsUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
OSCredsUnitTests))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
+ SecurityGroupConfigUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
SecurityGroupSettingsUnitTests))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
+ SecurityGroupRuleConfigUnitTests))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
+ SecurityGroupRuleSettingsUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
SecurityGroupDomainObjectTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
SecurityGroupRuleDomainObjectTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
- ImageSettingsUnitTests))
+ image_tests.ImageConfigUnitTests))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
+ creator_tests.ImageSettingsUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
ImageDomainObjectTests))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
+ FlavorConfigUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
FlavorSettingsUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
FlavorDomainObjectTests))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
+ KeypairConfigUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
KeypairSettingsUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
KeypairDomainObjectTests))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
+ UserConfigUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
UserSettingsUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
UserDomainObjectTests))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
+ ProjectConfigUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
ProjectSettingsUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
NetworkQuotasDomainObjectTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
RoleDomainObjectTests))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
+ NetworkConfigUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
NetworkSettingsUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
NetworkObjectTests))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
+ SubnetConfigUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
SubnetSettingsUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
SubnetObjectTests))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
+ PortConfigUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
PortSettingsUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
PortDomainObjectTests))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
+ RouterConfigUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
RouterSettingsUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
RouterDomainObjectTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
InterfaceRouterDomainObjectTests))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
+ FloatingIpConfigUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
FloatingIpSettingsUnitTests))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
+ VmInstanceConfigUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
VmInstanceSettingsUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
StackDomainObjectTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
ResourceDomainObjectTests))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
+ StackConfigUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
StackSettingsUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
VolumeTypeDomainObjectTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
VolumeTypeEncryptionObjectTests))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
+ VolumeDomainObjectTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
QoSSpecDomainObjectTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
VmInstDomainObjectTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
FloatingIpDomainObjectTests))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
+ QoSConfigUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
QoSSettingsUnitTests))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
+ VolumeTypeConfigUnitTests))
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
VolumeTypeSettingsUnitTests))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
+ VolumeConfigUnitTests))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
+ VolumeSettingsUnitTests))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
+ ClusterTemplateConfigUnitTests))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
+ ClusterTemplateUnitTests))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
+ SettingsUtilsUnitTests))
def add_openstack_client_tests(suite, os_creds, ext_net_name,
suite.addTest(OSComponentTestCase.parameterize(
NeutronUtilsSubnetTests, os_creds=os_creds, ext_net_name=ext_net_name,
log_level=log_level))
+ suite.addTest(OSComponentTestCase.parameterize(
+ NeutronUtilsIPv6Tests, os_creds=os_creds, ext_net_name=ext_net_name,
+ log_level=log_level))
suite.addTest(OSComponentTestCase.parameterize(
NeutronUtilsRouterTests, os_creds=os_creds, ext_net_name=ext_net_name,
log_level=log_level))
suite.addTest(OSComponentTestCase.parameterize(
NovaUtilsInstanceTests, os_creds=os_creds, ext_net_name=ext_net_name,
log_level=log_level, image_metadata=image_metadata))
+ suite.addTest(OSComponentTestCase.parameterize(
+ NovaUtilsInstanceVolumeTests, os_creds=os_creds,
+ ext_net_name=ext_net_name, log_level=log_level,
+ image_metadata=image_metadata))
suite.addTest(OSComponentTestCase.parameterize(
CreateFlavorTests, os_creds=os_creds, ext_net_name=ext_net_name,
log_level=log_level))
HeatUtilsCreateComplexStackTests, os_creds=os_creds,
ext_net_name=ext_net_name, log_level=log_level,
image_metadata=image_metadata))
+ suite.addTest(OSComponentTestCase.parameterize(
+ HeatUtilsFlavorTests, os_creds=os_creds,
+ ext_net_name=ext_net_name, log_level=log_level,
+ image_metadata=image_metadata))
+ suite.addTest(OSComponentTestCase.parameterize(
+ HeatUtilsKeypairTests, os_creds=os_creds,
+ ext_net_name=ext_net_name, log_level=log_level,
+ image_metadata=image_metadata))
+ suite.addTest(OSComponentTestCase.parameterize(
+ HeatUtilsSecurityGroupTests, os_creds=os_creds,
+ ext_net_name=ext_net_name, log_level=log_level,
+ image_metadata=image_metadata))
+ suite.addTest(OSComponentTestCase.parameterize(
+ HeatUtilsVolumeTests, os_creds=os_creds,
+ ext_net_name=ext_net_name, log_level=log_level,
+ image_metadata=image_metadata))
suite.addTest(OSComponentTestCase.parameterize(
CinderUtilsQoSTests, os_creds=os_creds,
ext_net_name=ext_net_name, log_level=log_level,
image_metadata=image_metadata))
+ suite.addTest(OSComponentTestCase.parameterize(
+ CinderUtilsVolumeTests, os_creds=os_creds,
+ ext_net_name=ext_net_name, log_level=log_level,
+ image_metadata=image_metadata))
suite.addTest(OSComponentTestCase.parameterize(
CinderUtilsSimpleVolumeTypeTests, os_creds=os_creds,
ext_net_name=ext_net_name, log_level=log_level,
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(
CreateRouterSuccessTests, os_creds=os_creds, ext_net_name=ext_net_name,
use_keystone=use_keystone,
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(
+ CreateSimpleVolumeSuccessTests, 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(
+ CreateSimpleVolumeFailureTests, 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(
+ CreateVolumeWithTypeTests, 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(
+ CreateVolumeWithImageTests, 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(
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(
+ CreateInstanceVolumeTests, 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(
+ CreateInstanceIPv6NetworkTests, 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(
CreateStackSuccessTests, 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(
+ CreateStackVolumeTests, 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(
+ CreateStackFlavorTests, 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(
+ CreateStackKeypairTests, 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(
+ CreateStackSecurityGroupTests, 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(
CreateStackNegativeTests, 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(
- CreateComplexStackTests, os_creds=os_creds,
+ CreateStackFloatingIpTests, 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(
+ CreateStackNestedResourceTests, 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))
:return: None as the tests will be adding to the 'suite' parameter object
"""
suite.addTest(OSComponentTestCase.parameterize(
- CreateNetworkTypeTests, os_creds=os_creds, ext_net_name=ext_net_name,
- log_level=log_level))
+ CreateNetworkTypeTests, os_creds=os_creds,
+ ext_net_name=ext_net_name, log_level=log_level))
suite.addTest(OSComponentTestCase.parameterize(
CreateInstanceMockOfflineTests, os_creds=os_creds,
ext_net_name=ext_net_name, log_level=log_level))
- suite.addTest(OSIntegrationTestCase.parameterize(
- CreateInstancePubPrivNetTests, os_creds=os_creds,
- ext_net_name=ext_net_name, log_level=log_level))
+ suite.addTest(OSComponentTestCase.parameterize(
+ MagnumSmokeTests, os_creds=os_creds,
+ ext_net_name=ext_net_name, log_level=log_level))
+ suite.addTest(OSComponentTestCase.parameterize(
+ MagnumUtilsClusterTypeTests, os_creds=os_creds,
+ ext_net_name=ext_net_name, log_level=log_level))
+ suite.addTest(OSComponentTestCase.parameterize(
+ CreateClusterTemplateTests, os_creds=os_creds,
+ ext_net_name=ext_net_name, log_level=log_level))