From 24bf38f0247eccf700b1428a9ec10c6e452ddea2 Mon Sep 17 00:00:00 2001 From: samu4924 Date: Sat, 30 Mar 2013 14:50:01 -0500 Subject: [PATCH] Initial Commit --- AUTHORS.md | 0 HISTORY.md | 0 HISTORY.rst | 0 LICENSE | 13 + MANIFEST.in | 1 + NOTICE | 0 README.md | 68 ++++ pip-requires | 0 setup.py | 94 +++++ test_repo/__init__.py | 22 ++ test_repo/__init__.pyc | Bin 0 -> 324 bytes test_repo/compute/__init__.py | 16 + test_repo/compute/__init__.pyc | Bin 0 -> 132 bytes test_repo/compute/fixtures.py | 214 ++++++++++++ test_repo/compute/fixtures.pyc | Bin 0 -> 7425 bytes test_repo/compute/functional/__init__.py | 16 + test_repo/compute/functional/__init__.pyc | Bin 0 -> 143 bytes .../compute/functional/flavors/__init__.py | 17 + .../compute/functional/flavors/__init__.pyc | Bin 0 -> 185 bytes .../functional/flavors/test_flavors.py | 236 +++++++++++++ .../functional/flavors/test_flavors.pyc | Bin 0 -> 15511 bytes .../compute/functional/images/__init__.py | 16 + .../compute/functional/images/__init__.pyc | Bin 0 -> 154 bytes .../compute/functional/images/test_images.py | 56 +++ .../compute/functional/images/test_images.pyc | Bin 0 -> 2354 bytes .../functional/images/test_images_metadata.py | 100 ++++++ .../images/test_images_metadata_negative.py | 51 +++ .../functional/images/test_images_negative.py | 59 ++++ .../images/test_images_negative.pyc | Bin 0 -> 2750 bytes .../functional/images/test_list_images.py | 257 ++++++++++++++ .../functional/images/test_list_images.pyc | Bin 0 -> 12876 bytes .../images/test_list_images_negative.py | 83 +++++ .../compute/functional/servers/__init__.py | 16 + .../compute/functional/servers/__init__.pyc | Bin 0 -> 155 bytes .../functional/servers/actions/__init__.py | 16 + .../servers/actions/test_change_password.py | 53 +++ .../actions/test_reboot_server_hard.py | 50 +++ .../actions/test_reboot_server_soft.py | 49 +++ .../servers/actions/test_rebuild_server.py | 115 ++++++ .../servers/actions/test_rescue_server.py | 68 ++++ .../actions/test_resize_server_confirm.py | 131 +++++++ .../actions/test_resize_server_revert.py | 101 ++++++ .../functional/servers/test_create_server.py | 96 +++++ .../functional/servers/test_list_servers.py | 282 +++++++++++++++ .../functional/servers/test_list_servers.pyc | Bin 0 -> 12704 bytes .../servers/test_server_metadata.py | 194 ++++++++++ .../functional/servers/test_servers.py | 330 ++++++++++++++++++ .../servers/test_servers_negative.py | 163 +++++++++ test_repo/compute/functional/test_links.py | 118 +++++++ test_repo/identity/__init__.py | 16 + test_repo/identity/quickauthtest.py | 23 ++ 51 files changed, 3140 insertions(+) create mode 100644 AUTHORS.md create mode 100644 HISTORY.md create mode 100644 HISTORY.rst create mode 100644 LICENSE create mode 100644 MANIFEST.in create mode 100644 NOTICE create mode 100644 README.md create mode 100644 pip-requires create mode 100644 setup.py create mode 100644 test_repo/__init__.py create mode 100644 test_repo/__init__.pyc create mode 100644 test_repo/compute/__init__.py create mode 100644 test_repo/compute/__init__.pyc create mode 100644 test_repo/compute/fixtures.py create mode 100644 test_repo/compute/fixtures.pyc create mode 100644 test_repo/compute/functional/__init__.py create mode 100644 test_repo/compute/functional/__init__.pyc create mode 100644 test_repo/compute/functional/flavors/__init__.py create mode 100644 test_repo/compute/functional/flavors/__init__.pyc create mode 100644 test_repo/compute/functional/flavors/test_flavors.py create mode 100644 test_repo/compute/functional/flavors/test_flavors.pyc create mode 100644 test_repo/compute/functional/images/__init__.py create mode 100644 test_repo/compute/functional/images/__init__.pyc create mode 100644 test_repo/compute/functional/images/test_images.py create mode 100644 test_repo/compute/functional/images/test_images.pyc create mode 100644 test_repo/compute/functional/images/test_images_metadata.py create mode 100644 test_repo/compute/functional/images/test_images_metadata_negative.py create mode 100644 test_repo/compute/functional/images/test_images_negative.py create mode 100644 test_repo/compute/functional/images/test_images_negative.pyc create mode 100644 test_repo/compute/functional/images/test_list_images.py create mode 100644 test_repo/compute/functional/images/test_list_images.pyc create mode 100644 test_repo/compute/functional/images/test_list_images_negative.py create mode 100644 test_repo/compute/functional/servers/__init__.py create mode 100644 test_repo/compute/functional/servers/__init__.pyc create mode 100644 test_repo/compute/functional/servers/actions/__init__.py create mode 100644 test_repo/compute/functional/servers/actions/test_change_password.py create mode 100644 test_repo/compute/functional/servers/actions/test_reboot_server_hard.py create mode 100644 test_repo/compute/functional/servers/actions/test_reboot_server_soft.py create mode 100644 test_repo/compute/functional/servers/actions/test_rebuild_server.py create mode 100644 test_repo/compute/functional/servers/actions/test_rescue_server.py create mode 100644 test_repo/compute/functional/servers/actions/test_resize_server_confirm.py create mode 100644 test_repo/compute/functional/servers/actions/test_resize_server_revert.py create mode 100644 test_repo/compute/functional/servers/test_create_server.py create mode 100644 test_repo/compute/functional/servers/test_list_servers.py create mode 100644 test_repo/compute/functional/servers/test_list_servers.pyc create mode 100644 test_repo/compute/functional/servers/test_server_metadata.py create mode 100644 test_repo/compute/functional/servers/test_servers.py create mode 100644 test_repo/compute/functional/servers/test_servers_negative.py create mode 100644 test_repo/compute/functional/test_links.py create mode 100644 test_repo/identity/__init__.py create mode 100644 test_repo/identity/quickauthtest.py diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 00000000..e69de29b diff --git a/HISTORY.md b/HISTORY.md new file mode 100644 index 00000000..e69de29b diff --git a/HISTORY.rst b/HISTORY.rst new file mode 100644 index 00000000..e69de29b diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..0d72c9b2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +# Copyright 2013 Rackspace +# +# 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. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 00000000..261a72d1 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1 @@ +include README.md LICENSE NOTICE HISTORY.md pip-requires \ No newline at end of file diff --git a/NOTICE b/NOTICE new file mode 100644 index 00000000..e69de29b diff --git a/README.md b/README.md new file mode 100644 index 00000000..d38974c1 --- /dev/null +++ b/README.md @@ -0,0 +1,68 @@ +CloudRoast, CloudCAFE Test Repo +================================ +
+ (----) (----)--)
+  (--(----)  (----) --)
+(----) (--(----) (----)
+-----------------------
+\                     /
+ \                   /
+  \_________________/    
+     )\ `   `(` `
+     ( ) ` )' )  \_
+    (   )  _)  \   )
+  ) )   (_  )   ,  (
+  (  ,  )   (   (
+    (  (    )    )
+  === CloudRoast ===
+= A CloudCAFE Test Repository =
+
+ +CloudRoast is a rich, full bodied blend of premium roasted automated test cases. CloudRoast tests are based on the expanded unittest driver in the +[Open CAFE Core](https://github.com/stackforge) and built using the [CloudCAFE Framework](https://github.com/stackforge). + +CloudRoast tests support smoke, functional, integration, scenario and reliability based test cases for OpenStack. It is meant to be highly flexible +and leave the logic of the testing in the hands of the test case developer while leaving the interactions with OpenStack, various resources and +support infrastructure to CloudCAFE. + +Installation +------------ +CloudRoast can be [installed with pip](https://pypi.python.org/pypi/pip) from the git repository after it is cloned to a local machine. + +* First follow the README instructions to install the [CloudCAFE Framework](https://github.com/stackforge) +* Clone this repository to your local machine +* CD to the root directory in your cloned repository. +* Run "pip install . --upgrade" and pip will auto install all other dependencies. + +Configuration +-------------- +CloudRoast runs on the [CloudCAFE Framework](https://github.com/stackforge) using the cafe-runner. It relies on the configurations installed to: +/.cloudcafe/configs/ by CloudCAFE. + +At this stage you will have the Open CAFE Core engine, the CloudCAFE Framework implementation and the Open Source automated test cases. You are now +ready to: +1) Execute the test cases against a deployed Open Stack. + or +2) Write entirely new tests in this repository using the CloudCAFE Framework. + +Logging +------- +If tests are executed with the built-in cafe-runner, runtime logs will be output to +/.cloudcafe/logs///. + +In addition, tests built from the built-in CAFE unittest driver will generate +csv statistics files in /.cloudcafe/logs///statistics for each and ever execution of each and every test case that +provides metrics of execution over time for elapsed time, pass/fail rates, etc... + +Basic CloudRoast Package Anatomy +------------------------------- +Below is a short description of the top level CloudRoast Packages. + +##test_repo +This is the root package for all automated tests. This is namespace is currently **required** by the cafe-runner for any Test Repository plug-in. + +##identity +OpenStack Identity Service cafe-runner plug-in test cases. + +##compute +OpenStack Compute Service cafe-runner plug-in test cases. diff --git a/pip-requires b/pip-requires new file mode 100644 index 00000000..e69de29b diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..ba989e06 --- /dev/null +++ b/setup.py @@ -0,0 +1,94 @@ +""" +Copyright 2013 Rackspace + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import os +import sys + +import test_repo + +try: + from setuptools import setup, find_packages +except ImportError: + from distutils.core import setup, find_packages + +if sys.argv[-1] == 'publish': + os.system('python setup.py sdist upload') + sys.exit() + +requires = open('pip-requires').readlines() + +setup( + name='test_repo', + version=test_repo.__version__, + description='CloudCAFE based automated test repository for OpenStack', + long_description='{0}\n\n{1}'.format( + open('README.md').read(), + open('HISTORY.md').read()), + author='Rackspace Cloud QE', + author_email='cloud-cafe@lists.rackspace.com', + url='http://rackspace.com', + packages=find_packages(exclude=[]), + package_data={'': ['LICENSE', 'NOTICE']}, + package_dir={'test_repo': 'test_repo'}, + include_package_data=True, + install_requires=requires, + license=open('LICENSE').read(), + zip_safe=False, + #https://the-hitchhikers-guide-to-packaging.readthedocs.org/en/latest/specification.html + classifiers=( + 'Development Status :: 1 - Planning', + 'Intended Audience :: Developers', + 'Natural Language :: English', + 'License :: Other/Proprietary License', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + ) +) + +''' @todo: need to clean this up or do it with puppet/chef ''' +# Default Config Options +root_dir = "{0}/.cloudcafe".format(os.path.expanduser("~")) +config_dir = "{0}/configs".format(root_dir) + +# Build Default directories +if(os.path.exists("{0}/engine.config".format(config_dir)) == False): + raise Exception("Core CAFE Engine configuration not found") +else: + # Copy over the default configurations + if(os.path.exists("~install")): + os.remove("~install") + # Report + print('\n'.join(["\t\t (----) (----)--)", + "\t\t (--(----) (----) --)", + "\t\t(----) (--(----) (----)", + "\t\t-----------------------", + "\t\t\ /", + "\t\t \ /", + "\t\t \_________________/", + "\t\t )\ ` `(` `", + "\t\t ( ) ` )' ) \_", + "\t\t ( ) _) \ )", + "\t\t ) ) (_ ) , (", + "\t\t ( , ) ( (", + "\t\t ( ( ) )", + "\t\t === CloudRoast ===", + "\t\t= A CloudCAFE Test Repository ="])) + else: + # State file + temp = open("~install", "w") + temp.close() diff --git a/test_repo/__init__.py b/test_repo/__init__.py new file mode 100644 index 00000000..66a76b1c --- /dev/null +++ b/test_repo/__init__.py @@ -0,0 +1,22 @@ +""" +Copyright 2013 Rackspace + +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. +""" + +__title__ = 'test_repo' +__version__ = '0.0.1' +#__build__ = 0x010100 +__author__ = 'Rackspace Cloud QE' +__license__ = 'Internal Only' +__copyright__ = 'Copyright 2013 Rackspace Inc.' diff --git a/test_repo/__init__.pyc b/test_repo/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7f414ed018e874214c14ecd2ea6484ffbf7e5552 GIT binary patch literal 324 zcmYLFO-lnY5S{F{OIzth@F*;v_Ox#GDk2mQy;Q2Vy$nkdY%nHECM)cp^N;v5`~yyk zZ3FWr@6E>}$$yuN@1N~X!v7JRpE&4-K>@Uk0$KqogEj+|Lz{z|Ksy07g}18Uwm46q zDPqvQrzx|hZt4|X^6j;gAJlhJmuu?>Rqmc?$@0cU9b9M27iUMhW@YXBF_`y#Ebp4t zeR-bSI9YG2wDn+w7)`XA(*;Y2UpkQSj``xiyCLpt D2uT~D literal 0 HcmV?d00001 diff --git a/test_repo/compute/fixtures.py b/test_repo/compute/fixtures.py new file mode 100644 index 00000000..bcaf91bb --- /dev/null +++ b/test_repo/compute/fixtures.py @@ -0,0 +1,214 @@ +""" +Copyright 2013 Rackspace + +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. +""" + +''' +@summary: Base Classes for Compute Test Suites (Collections of Test Cases) +@note: Correspondes DIRECTLY TO A unittest.TestCase +@see: http://docs.python.org/library/unittest.html#unittest.TestCase +@copyright: Copyright (c) 2012 Rackspace US, Inc. +''' +import os + +from cafe.drivers.unittest.fixtures import BaseTestFixture +from cloudcafe.common.resources import ResourcePool +from cloudcafe.compute.common.exceptions import TimeoutException, \ + BuildErrorException +from cloudcafe.compute.common.types import NovaServerStatusTypes as ServerStates +from cloudcafe.compute.common.datagen import rand_name +from cloudcafe.compute.common.exception_handler import ExceptionHandler +from cloudcafe.compute.flavors_api.client import FlavorsClient +from cloudcafe.compute.servers_api.client import ServersClient +from cloudcafe.compute.images_api.client import ImagesClient +from cloudcafe.compute.servers_api.behaviors import ServerBehaviors +from cloudcafe.compute.images_api.behaviors import ImageBehaviors +from cloudcafe.compute.config import ComputeConfig +from cloudcafe.compute.flavors_api.config import FlavorsConfig +from cloudcafe.compute.images_api.config import ImagesConfig +from cloudcafe.compute.servers_api.config import ServersConfig +from cloudcafe.identity.v2_0.tokens_api.client import TokenAPI_Client +from cloudcafe.identity.v2_0.tokens_api.behaviors import TokenAPI_Behaviors +from cloudcafe.identity.v2_0.tokens_api.config import TokenAPI_Config + + +class ComputeFixture(BaseTestFixture): + ''' + @summary: Fixture for an Compute test. + ''' + + @classmethod + def setUpClass(cls): + super(ComputeFixture, cls).setUpClass() + cls.flavors_config = FlavorsConfig() + cls.images_config = ImagesConfig() + cls.servers_config = ServersConfig() + cls.compute_config = ComputeConfig() + + cls.flavor_ref = cls.flavors_config.primary_flavor + cls.flavor_ref_alt = cls.flavors_config.secondary_flavor + cls.image_ref = cls.images_config.primary_image + cls.image_ref_alt = cls.images_config.secondary_image + cls.disk_path = cls.servers_config.instance_disk_path + + identity_config = TokenAPI_Config() + token_client = TokenAPI_Client(identity_config.authentication_endpoint, + 'json', 'json') + token_behaviors = TokenAPI_Behaviors(token_client) + access_data = token_behaviors.get_access_data(identity_config.username, + identity_config.password, + identity_config.tenant_name) + + compute_service = access_data.get_service( + cls.compute_config.compute_endpoint_name) + url = compute_service.get_endpoint( + cls.compute_config.region).public_url + cls.flavors_client = FlavorsClient(url, access_data.token.id_, + 'json', 'json') + cls.servers_client = ServersClient(url, access_data.token.id_, + 'json', 'json') + cls.images_client = ImagesClient(url, access_data.token.id_, + 'json', 'json') + cls.server_behaviors = ServerBehaviors(cls.servers_client, + cls.servers_config, + cls.images_config, + cls.flavors_config) + cls.image_behaviors = ImageBehaviors(cls.images_client, + cls.images_config) + cls.flavors_client.add_exception_handler(ExceptionHandler()) + cls.resources = ResourcePool() + + + @classmethod + def tearDownClass(cls): + super(ComputeFixture, cls).tearDownClass() + cls.flavors_client.delete_exception_handler(ExceptionHandler()) + cls.resources.release() + + @classmethod + def parse_image_id(self, image_response): + """ + @summary: Extract Image Id from Image response + @param image_response: Image response + @type image_ref: string + @return: Image id + @rtype: string + """ + image_ref = image_response.headers['location'] + return image_ref.rsplit('/')[-1] + + def verify_server_event_details(self, server, image, flavor, event): + ''' + @summary: Verifies the common attributes for all compute events + @param event: Contains event details (actual data) to be verified. + @type event: Dictionary + ''' + failure = 'Expected {0} field in event to be {1}, was {2}' + image_id = event.payload.image_ref_url.rsplit('/')[-1] + + self.assertEqual(server.tenant_id, event.payload.tenant_id, + msg=failure.format('tenant id', server.tenant_id, + event.payload.tenant_id)) + self.assertEqual(event.payload.user_id, server.user_id, + msg=failure.format('user id', server.user_id, + event.payload.user_id)) + self.assertEqual(event.payload.instance_type_id, int(server.flavor.id), + msg=failure.format('flavor id', server.flavor.id, + event.payload.instance_type_id)) + self.assertEqual(event.payload.instance_type, flavor.name, + msg=failure.format('flavor name', flavor.name, + event.payload.instance_type)) + self.assertEqual(event.payload.memory_mb, flavor.ram, + msg=failure.format('RAM size', flavor.ram, + event.payload.memory_mb)) + self.assertEqual(event.payload.disk_gb, flavor.disk, + msg=failure.format('disk size', flavor.disk, + event.payload.disk_gb)) + self.assertEqual(event.payload.instance_id, server.id, + msg=failure.format('server id', server.id, + event.payload.instance_id)) + self.assertEqual(event.payload.display_name, server.name, + msg=failure.format('server name', server.name, + event.payload.display_name)) + self.assertEqual(image_id, server.image.id, + msg=failure.format('image id', server.image.id, + image_id)) + +class CreateServerFixture(ComputeFixture): + ''' + @summary: Creates a server using defaults from the test data, + waits for active state. + ''' + + @classmethod + def setUpClass(cls, name=None, + imageRef=None, flavorRef=None, + personality=None, metadata=None, + diskConfig=None, networks=None): + + ''' + @summary:Creates a server and waits for server to reach active status + @param name: The name of the server. + @type name: String + @param image_ref: The reference to the image used to build the server. + @type image_ref: String + @param flavor_ref: The flavor used to build the server. + @type flavor_ref: String + @param meta: A dictionary of values to be used as metadata. + @type meta: Dictionary. The limit is 5 key/values. + @param personality: A list of dictionaries for files to be + injected into the server. + @type personality: List + @param disk_config: MANUAL/AUTO/None + @type disk_config: String + @param networks:The networks to which you want to attach the server + @type networks: String + ''' + + super(CreateServerFixture, cls).setUpClass() + if name is None: + name = rand_name('testserver') + if imageRef is None: + imageRef = cls.image_ref + if flavorRef is None: + flavorRef = cls.flavor_ref + cls.flavor_ref = flavorRef + cls.image_ref = imageRef + resp = cls.servers_client.create_server(name, imageRef, + flavorRef, + personality=personality, + metadata=metadata, + disk_config=diskConfig, + networks=networks) + cls.created_server = resp.entity + try: + wait_response = cls.server_behaviors.wait_for_server_status( + cls.created_server.id, + ServerStates.ACTIVE) + wait_response.entity.admin_pass = cls.created_server.admin_pass + except TimeoutException as exception: + cls.assertClassSetupFailure(exception.message) + except BuildErrorException as exception: + cls.assertClassSetupFailure(exception.message) + finally: + cls.resources.add(cls.created_server.id, + cls.servers_client.delete_server) + cls.server_response = wait_response + if cls.server_response.entity.status != ServerStates.ACTIVE: + cls.assertClassSetupFailure('Server %s did not reach active state', + cls.created_server.id) + + @classmethod + def tearDownClass(cls): + super(CreateServerFixture, cls).tearDownClass() diff --git a/test_repo/compute/fixtures.pyc b/test_repo/compute/fixtures.pyc new file mode 100644 index 0000000000000000000000000000000000000000..de07b6fb762cc01fbd0a3189dd7d3cea31fc9a01 GIT binary patch literal 7425 zcmb_hTT>jz74F%E1yW5BVke3#rO`9`Ymd7xLi8;-UD%8Fo`?$hU-zMVebIj#D?=bHcg z$L^OMRsO5v?_GTQUnoMQ4p2HOtf|CN2dLn;u9BKc>*}Dc9JVJ^GNIB*buek$lPYPb zbV?me*>*!E(<+@&2Q#)krIMyfuc?D;wmq$qS(VPIgE`xtQOUeY7u3OmZ8uf2sM71| z;JR&JQ^}G_m({_tZO^LYhDvX$gPUm2si>|FF{^p?C#7B}_3gne6)vdgmQWW5)K|Eu zz1DTE^#>HqySksIL2-V1c(ZY6=%k#-OB!rjW#Uf)FSex!}N*N+Y8t*tysqK=94 zOuPA^?c0K+ey4di%T2Tav7(4{FV8~o-{0}JxAq@?>+U~tH{E^~8)z|Ys-zT#k05ev zOmAatEzCQ*-8(bKdDhO0qqQV{QebLp1F>V1CbtKzp^{GCJ1gR&W5dzQs@v+k%=X>$IEkfADX&K??;OuvY1$09nmpLL=hSNmCz zg-KM{o_X}_gi&T@YsMJi-WhmJ>>Yqex$5uErH+!-reaD;oU32fo6p;bNF+u!}v-+HwBVCPf+!Op{N!@jP@ZT<}^yoVYWu~$UB zA`fv1!?w1D+YWkhyQpqbC&~L^yOXDDr|WBXK3>1`$@+(_+t7eRz;X3^QQMSnRqYH%O+rV{m#WKfn#~W=N%R zm$NOoh14scp*ZucewmT2`bBhTc*%8yF~boaY8vwRL1JVkl_r@H462+P^>|#F;hZ@i zzh5NgM%kd7=meZ;FZNHOGa0f{)yIUhAdQ53lsMq#f~}{Ky*2p>S{yA7y*mDk@0~a! zkB2(G9hT0+DOXHy0hEywPk0{S+GAf`*%fJGtR8jsI55s5hugNar`ZO`dt)P zI2rl|@iW-}7bCZdvb!xFk4H1L_RgA#g5rMuG!v8e-X>)QMHnR!tl&p>nA$wf{-yST zJNGMmnijx)=+IKQLjILizJUt-WLF}b)zWRj*TajUtgBV)FZAERC*FA6d2c^6@B+pa zv~hPr_pr#*vSnc(s7?|7yW0zjAa&)w;Fd5b(13d@TeXMW=ByVDm=8BxZHhQM8uk|v zz&Wer<8Y*hqL&2ZJ|;OX9{?QK0jm<=Z*eok2^~j42vjya#X|Rz*tED=V!1j>4&@S! zPv4`Rz04t5rxX-A@~!4L{1__jk;D zgJA`NExlARb)^b`F>cuG$b_L$D5co7E5!iUV45Eh1IP=d6q~zJ46qKS#SyV)Nh!q^ zt`r0OW0B1fF^M5SDaEc|DYhhHqiaLB0Yxb7wl7@}vzxv=J`|P;86hH6D31Zr{$7hw zr2ZR<;cXa%^j}3qd>A7PH^-3+znSKl8yMWGCqPacan+1s0^$# z6U2!6rR|0hS_u*YID>wWxb*DrxF&a>L@uC(!-nmlD!JOFru(tv55U_&ziwjz+s}H) zETYi;{)2ND@=55%Svj0l{QZaL@3~I{?S8+0?lH2I>x?K+sZ<|ni2|Z5$c!6@npcSM z$~t>2ptE&t*dq37UNmp>b63aTMZy}!`efL$F-0X=y=9)!c_ zg%JW{0&krx9G+QLVYdH(trK)KGHpK&#T6m@N^wrr3P@(0g55Vk)`@)XCW%~loEa-4 z;gk0kM<5D~F^fZ!ID-JO^4Z8lx_B^pfDr7hQBz zvV4gTJOYnQ#cq-J8QR|ONF<{iz6^8-!$FcI-nsICFXd2QgL<*KGv5yF%V2(KqxaFR zH&CcX!&wFVFF1<`09d}|EDh?T^wIiF@U&1EX?X_Q!J7oUO?(<5GQj$*d+G3KxZ^ZWfy=)D z>93080G17PGOda>0Um&gH6d@z5Dx)OZPx=q(n+Rsq+>HI{r5s|Fp;x#ASaSc2(kir z1>jDpt^gs45JZVlkoWg*%Ya7|zVs*qdGF@o;` zijX}DCyB^KfwD8oUsPXb+iC(y-APjw(~fiARD$M!8}Kc2ZBBL@95;mH-5=p_)Hh#a ziJbmT)g2_Qk_dA_lD#_LU{ua^MHp8E>gUV(ozzwF=W^%}fM3lX^LDURP`)R(N8f|D z8XWP!P%haC(hj)z>by(if%}I^caBHTzkdm-=#)0xeHayMe4e6#Srr$t$nx%8h%m?F zRfM2a2In0W5&SOUjT)`X2eyO)c>Nei_2VO17Y8L9?$yH$Gfg{Q2~_{9{!#I(2Y?$2 zHr!1&8~`u(<}^t93=IUbWiS9MSqbGAx67&+B6nMMCW+G+SgYMTE)v@{t8qlsZYLfC z@W2otxW+5vrkljL@ECtE8wM!6+e{|Q`3&r2#0dt~VkCGOafC6-j0+}KLCS8ZXU`N$Q|+#lZ?BPosSqIgDUCy|2&=i73y>ig{tk^ zn06O+?-7eHSbWLi8y4IPiRp$r`cor0Ua1(Lrq0GhqgHq7vW-h0xA<^mhvs49;TI>~ z!Gl;kED#43x;^;ckm@;%+LYl-5*}K?SSEM;i}bHOu!OM(KEqcb%U?a>wW~_`nrB}) znRJcoweX|)UfH(qe!5-JY}-ClUY4}iVqCJKm6D7u%}1BUthhMl;0?2UsUALRdQ3ay zVRd}Q*5P|}9mc(+yPCVK$6hx21KIjl literal 0 HcmV?d00001 diff --git a/test_repo/compute/functional/flavors/test_flavors.py b/test_repo/compute/functional/flavors/test_flavors.py new file mode 100644 index 00000000..7681ca39 --- /dev/null +++ b/test_repo/compute/functional/flavors/test_flavors.py @@ -0,0 +1,236 @@ +""" +Copyright 2013 Rackspace + +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 cafe.drivers.unittest.decorators import tags +from cloudcafe.compute.common.exceptions import BadRequest, ItemNotFound +from test_repo.compute.fixtures import ComputeFixture + + +class FlavorsTest(ComputeFixture): + + def test_list_flavors(self): + """ List of all flavors should contain the expected flavor """ + response = self.flavors_client.list_flavors() + flavors = response.entity + self.assertTrue(len(flavors) > 0) + response = self.flavors_client.get_flavor_details(self.flavor_ref) + flavor = response.entity + for each in flavors: + if flavor.id == each.id: + return + self.fail("The expected flavor: %s not found in the flavor list." % flavor.id) + + def test_list_flavors_with_detail(self): + """ Detailed list of all flavors should contain the expected flavor """ + response = self.flavors_client.list_flavors_with_detail() + flavors = response.entity + self.assertTrue(len(flavors) > 0) + response = self.flavors_client.get_flavor_details(self.flavor_ref) + flavor = response.entity + self.assertIn(flavor, flavors, "The expected flavor: %s not found in the flavor list." % flavor.id) + + def test_get_flavor(self): + """ The expected flavor details should be returned """ + response = self.flavors_client.get_flavor_details(self.flavor_ref) + flavor = response.entity + self.assertEqual(self.flavor_ref, flavor.id, "Could not retrieve the expected flavor.") + + def test_get_non_existent_flavor(self): + """flavor details are not returned for non existent flavors""" + try: + self.flavors_client.get_flavor_details(999) + self.fail('No exception thrown for a non-existent flavor id') + except ItemNotFound: + pass + + def test_list_flavors_limit_results(self): + """Only the expected number of flavors should be returned""" + response = self.flavors_client.list_flavors(limit=1) + flavors = response.entity + self.assertEqual(1, len(flavors), + "The length of flavor list was %s instead of 1" % len(flavors)) + + def test_list_flavors_detailed_limit_results(self): + """Only the expected number of flavors (detailed) should be returned""" + response = self.flavors_client.list_flavors_with_detail(limit=1) + flavors = response.entity + self.assertEqual(1, len(flavors), + "The length of flavor list was %s instead of 1" % len(flavors)) + + def test_list_flavors_using_marker(self): + """The list of flavors should start from the provided marker""" + response = self.flavors_client.list_flavors() + flavors = response.entity + flavors.sort(key=lambda k: k.id) + + # Filter out any flavors of the same size + filter_criteria = lambda x: x.id > flavors[1].id + expected_flavors = filter(filter_criteria, flavors) + + response = self.flavors_client.list_flavors(marker=flavors[1].id) + actual_flavors = response.entity + actual_flavors.sort(key=lambda k: k.id) + expected_flavors.sort(key=lambda k: k.id) + self.assertEqual(actual_flavors, expected_flavors, + msg='Filtered flavor was incorrectly \ + included in the list of returned flavors') + + def test_list_flavors_detailed_using_marker(self): + """The list of flavors should start from the provided marker""" + response = self.flavors_client.list_flavors_with_detail() + flavors = response.entity + flavors.sort(key=lambda k: k.id) + + # Filter out any flavors of the same size + filter_criteria = lambda x: x.id > flavors[1].id + expected_flavors = filter(filter_criteria, flavors) + response = self.flavors_client.list_flavors_with_detail(marker=flavors[1].id) + actual_flavors = response.entity + actual_flavors.sort(key=lambda k: k.id) + expected_flavors.sort(key=lambda k: k.id) + self.assertEqual(actual_flavors, expected_flavors, + msg='Filtered flavors list does not begin at provided marker') + + def test_list_flavors_detailed_filter_by_min_disk(self): + """The detailed list of flavors should be filtered by disk space""" + response = self.flavors_client.list_flavors_with_detail() + flavors = response.entity + flavors.sort(key=lambda k: int(k.disk)) + + # Filter out any flavors of the same size + filter_criteria = lambda x: int(x.disk) >= int(flavors[1].disk) + expected_flavors = filter(filter_criteria, flavors) + response = self.flavors_client.list_flavors_with_detail(min_disk=flavors[1].disk) + actual_flavors = response.entity + actual_flavors.sort(key=lambda k: k.id) + expected_flavors.sort(key=lambda k: k.id) + self.assertEqual(actual_flavors, expected_flavors, + msg="A flavor with min_disk lower than %s was returned" % (flavors[1].disk)) + + def test_list_flavors_detailed_filter_by_min_ram(self): + """The detailed list of flavors should be filtered by RAM""" + response = self.flavors_client.list_flavors_with_detail() + flavors = response.entity + flavors.sort(key=lambda k: int(k.ram)) + # Filter out any flavors of the same size + filter_criteria = lambda x: int(x.ram) >= int(flavors[1].ram) + expected_flavors = filter(filter_criteria, flavors) + response = self.flavors_client.list_flavors_with_detail(min_ram=flavors[1].ram) + actual_flavors = response.entity + actual_flavors.sort(key=lambda k: k.id) + expected_flavors.sort(key=lambda k: k.id) + self.assertEqual(actual_flavors, expected_flavors, + msg="A flavor with min_ram lower than %s was returned" % (flavors[1].ram)) + + def test_list_flavors_filter_by_min_disk(self): + """The list of flavors should be filtered by disk space""" + response = self.flavors_client.list_flavors_with_detail() + flavors = response.entity + flavors.sort(key=lambda k: int(k.disk)) + + # Filter out any flavors of the same size + filter_criteria = lambda x: int(x.disk) >= int(flavors[1].disk) + expected_flavors = filter(filter_criteria, flavors) + response = self.flavors_client.list_flavors(min_disk=flavors[1].disk) + actual_flavors = response.entity + actual_flavors.sort(key=lambda k: k.id) + expected_flavors.sort(key=lambda k: k.id) + self.assertEqual(actual_flavors, expected_flavors, + msg="A flavor with min_disk lower than %s was returned" % (flavors[1].disk)) + + def test_list_flavors_filter_by_min_ram(self): + """The list of flavors should be filtered by RAM""" + response = self.flavors_client.list_flavors_with_detail() + flavors = response.entity + flavors.sort(key=lambda k: int(k.ram)) + + # Filter out any flavors of the same size + filter_criteria = lambda x: int(x.ram) >= int(flavors[1].ram) + expected_flavors = filter(filter_criteria, flavors) + response = self.flavors_client.list_flavors(min_ram=flavors[1].ram) + actual_flavors = response.entity + actual_flavors.sort(key=lambda k: k.id) + expected_flavors.sort(key=lambda k: k.id) + self.assertEqual(actual_flavors, expected_flavors, + msg="A flavor with min_disk lower than %s was returned" % flavors[1].ram) + + def test_list_flavors_detailed_filter_by_invalid_min_disk(self): + """The detailed list of flavors should be filtered by disk space""" + with self.assertRaises(BadRequest): + response = self.flavors_client.list_flavors_with_detail(min_disk='invalid_disk') + flavors = response.entity + self.assertTrue(len(flavors) == 0, + msg="The list of flavors is not empty for \ + an invalid min disk value") + + def test_list_flavors_detailed_filter_by_invalid_min_ram(self): + """The detailed list of flavors should be filtered by RAM""" + with self.assertRaises(BadRequest): + response = self.flavors_client.list_flavors_with_detail(min_ram='invalid_ram') + flavors = response.entity + self.assertTrue(len(flavors) == 0, + msg="The list of flavors is not empty for \ + an invalid min RAM value") + + def test_list_flavors_filter_by_invalid_min_disk(self): + """The detailed list of flavors should be filtered by disk space""" + with self.assertRaises(BadRequest): + response = self.flavors_client.list_flavors(min_disk='invalid_disk') + flavors = response.entity + self.assertTrue(len(flavors) == 0, + msg="The list of flavors is not empty for an \ + invalid min disk value") + + def test_list_flavors_filter_by_invalid_min_ram(self): + """The detailed list of flavors should be filtered by RAM""" + with self.assertRaises(BadRequest): + response = self.flavors_client.list_flavors(min_ram='invalid_ram') + flavors = response.entity + self.assertTrue(len(flavors) == 0, + msg="The list of flavors is not empty for \ + an invalid min RAM value") + + def test_list_flavors_detailed_filter_min_disk_value_greater_than_max_flavor_disk(self): + """The detailed list of flavors should be filtered by disk space""" + response = self.flavors_client.list_flavors_with_detail(min_disk='99999') + flavors = response.entity + self.assertTrue(len(flavors) == 0, + msg="The list of flavors is not empty for the value \ + of min disk greater then max flavor disk size.") + + def test_list_flavors_detailed_filter_min_ram_value_greater_than_max_flavor_ram(self): + """The detailed list of flavors should be filtered by RAM""" + response = self.flavors_client.list_flavors_with_detail(min_ram='99999') + flavors = response.entity + self.assertTrue(len(flavors) == 0, + msg="The list of flavors is not empty for the value \ + of min RAM greater then max flavor RAM size.") + + def test_list_flavors_filter_min_disk_value_greater_than_max_flavor_disk(self): + """The detailed list of flavors should be filtered by disk space""" + response = self.flavors_client.list_flavors(min_disk='99999') + flavors = response.entity + self.assertTrue(len(flavors) == 0, + msg="The list of flavors is not empty for the value \ + of min disk greater then max flavor disk size.") + + def test_list_flavors_filter_min_disk_value_greater_than_max_flavor_ram(self): + """The detailed list of flavors should be filtered by RAM""" + response = self.flavors_client.list_flavors(min_ram='99999') + flavors = response.entity + self.assertTrue(len(flavors) == 0, + msg="The list of flavors is not empty for the value \ + of min RAM greater then max flavor RAM size.") diff --git a/test_repo/compute/functional/flavors/test_flavors.pyc b/test_repo/compute/functional/flavors/test_flavors.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9b063958f2bc20450469504a886ebfa5d69d96f3 GIT binary patch literal 15511 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 W5)Pm_Ho5sJr8%i~Alr(8m;nGmNF-kX literal 0 HcmV?d00001 diff --git a/test_repo/compute/functional/images/test_images.py b/test_repo/compute/functional/images/test_images.py new file mode 100644 index 00000000..a3498b79 --- /dev/null +++ b/test_repo/compute/functional/images/test_images.py @@ -0,0 +1,56 @@ +""" +Copyright 2013 Rackspace + +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 cafe.drivers.unittest.decorators import tags +from cloudcafe.compute.common.datagen import rand_name +from cloudcafe.compute.common.types import NovaImageStatusTypes +from test_repo.compute.fixtures import CreateServerFixture + + +class ImagesTest(CreateServerFixture): + + @classmethod + def setUpClass(cls): + super(ImagesTest, cls).setUpClass() + cls.name = rand_name('testserver') + cls.server = cls.server_response.entity + + @classmethod + def tearDownClass(cls): + super(ImagesTest, cls).tearDownClass() + + @tags(type='smoke', net='no') + def test_create_delete_image(self): + """An image for the provided server should be created""" + + name = rand_name('testimage') + server_id = self.server.id + image_response = self.servers_client.create_image(server_id, name) + image_id = self.parse_image_id(image_response) + self.image_behaviors.wait_for_image_status(image_id, + NovaImageStatusTypes.ACTIVE) + + # Delete image and wait for image to be deleted + self.image_behaviors.wait_for_image_to_be_deleted(image_id) + + @tags(type='smoke', net='no') + def test_get_image(self): + '''The expected image should be returned''' + image_response = self.images_client.get_image(self.image_ref) + image = image_response.entity + self.assertEqual(self.image_ref, image.id, + "Could not retrieve the expected image with id %s" % + (image.id)) diff --git a/test_repo/compute/functional/images/test_images.pyc b/test_repo/compute/functional/images/test_images.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2e92b7e5a7a4f9fb584d6367b8f3c61fcdf4c9a7 GIT binary patch literal 2354 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 literal 0 HcmV?d00001 diff --git a/test_repo/compute/functional/images/test_list_images_negative.py b/test_repo/compute/functional/images/test_list_images_negative.py new file mode 100644 index 00000000..0209e4a7 --- /dev/null +++ b/test_repo/compute/functional/images/test_list_images_negative.py @@ -0,0 +1,83 @@ +""" +Copyright 2013 Rackspace + +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 cafe.drivers.unittest.decorators import tags +from cloudcafe.compute.common.exceptions import BadRequest +from test_repo.compute.fixtures import ComputeFixture + + +class ImagesListTestNegative(ComputeFixture): + + @tags(type='negative', net='no') + def test_list_images_filter_by_nonexistent_server_id(self): + """Negative Test: Images should not get listed with invalid server ID""" + server_id = 'sjlfdlkjfldjlkdjfldjf' + images = self.images_client.list_images(server_ref=server_id) + self.assertEqual(200, images.status_code, + "The response code is not 200") + self.assertEqual(0, len(images.entity), + "The list of images is not empty.") + + @tags(type='negative', net='no') + def test_list_images_filter_by_nonexistent_image_name(self): + """Images should not get listed when filtered with invalid image name""" + image_name = 'aljsdjfsjkljlkjdfkjs999' + images = self.images_client.list_images(image_name=image_name) + self.assertEqual(200, images.status_code, + "The response code is not 200.") + self.assertEqual(0, len(images.entity), + "The list of images is not empty.") + + @tags(type='negative', net='no') + def test_list_images_filter_by_invalid_image_status(self): + """Images should not get listed when filtered with invalid status""" + image_status = 'INVALID' + images = self.images_client.list_images(status=image_status) + self.assertEqual(200, images.status_code, + "The response code is not 200.") + self.assertEqual(0, len(images.entity), + "The list of images is not empty.") + + @tags(type='negative', net='no') + def test_list_images_filter_by_invalid_marker(self): + """Images should not get listed when filtered with invalid marker""" + marker = 999 + with self.assertRaises(BadRequest): + self.images_client.list_images(marker=marker) + + @tags(type='negative', net='no') + def test_list_images_filter_by_invalid_type(self): + """Images should not get listed when filtered with invalid type""" + type = 'INVALID' + images = self.images_client.list_images(image_type=type) + self.assertEqual(200, images.status_code, + "The response code is not 200.") + self.assertEqual(0, len(images.entity), + "The list of images is not empty") + + @tags(type='negative', net='no') + def test_list_images_filter_by_invalid_changes_since(self): + """Images should not get listed with invalid changes since""" + changes_since = '2012-02-22T' + with self.assertRaises(BadRequest): + self.images_client.list_images(changes_since=changes_since) + + @tags(type='negative', net='no') + def test_list_images_filter_by_invalid_limit(self): + """Images should not get listed with invalid limit""" + limit = -3 + with self.assertRaises(BadRequest): + self.images_client.list_images(limit=limit) diff --git a/test_repo/compute/functional/servers/__init__.py b/test_repo/compute/functional/servers/__init__.py new file mode 100644 index 00000000..dd8b1e4e --- /dev/null +++ b/test_repo/compute/functional/servers/__init__.py @@ -0,0 +1,16 @@ +""" +Copyright 2013 Rackspace + +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. +""" + diff --git a/test_repo/compute/functional/servers/__init__.pyc b/test_repo/compute/functional/servers/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..32a3d502cb713685330e8bec4266d1b6a3fd8256 GIT binary patch literal 155 zcmZSn%*(ZVl5b!#0~9a@ll2rY)(!Au7%>2B>9R1?dqO#PYV*U8|%)HE!_;|g7 X$`THsK{mPhDWy57b|CADftUdRa$h9$ literal 0 HcmV?d00001 diff --git a/test_repo/compute/functional/servers/actions/__init__.py b/test_repo/compute/functional/servers/actions/__init__.py new file mode 100644 index 00000000..dd8b1e4e --- /dev/null +++ b/test_repo/compute/functional/servers/actions/__init__.py @@ -0,0 +1,16 @@ +""" +Copyright 2013 Rackspace + +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. +""" + diff --git a/test_repo/compute/functional/servers/actions/test_change_password.py b/test_repo/compute/functional/servers/actions/test_change_password.py new file mode 100644 index 00000000..83356b30 --- /dev/null +++ b/test_repo/compute/functional/servers/actions/test_change_password.py @@ -0,0 +1,53 @@ +""" +Copyright 2013 Rackspace + +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 cafe.drivers.unittest.decorators import tags +from test_repo.compute.fixtures import CreateServerFixture + + +class ChangeServerPasswordTests(CreateServerFixture): + + @classmethod + def setUpClass(cls): + super(ChangeServerPasswordTests, cls).setUpClass() + cls.server = cls.server_response.entity + cls.new_password = "newslice129690TuG72Bgj2" + + # Change password and wait for server to return to active state + cls.compute_provider.change_password_and_await(cls.server.id, + cls.new_password) + + @classmethod + def tearDownClass(cls): + super(ChangeServerPasswordTests, cls).tearDownClass() + + @tags(type='smoke', net='yes') + def test_can_log_in_with_new_password(self): + '''Verify the admin user can log in with the new password''' + + '''Get server details ''' + response = self.servers_client.get_server(self.server.id) + self.server = response.entity + '''Set the server's adminPass attribute to the new password,vas this field is not set in getServer''' + self.server.adminPass = self.new_password + + public_address = self.compute_provider.get_public_ip_address(self.server) + '''Get an instance of the remote client ''' + remote_client = self.compute_provider.get_remote_instance_client(self.server, public_address) + + self.assertTrue(remote_client.can_connect_to_public_ip(), + "Could not connect to server (%s) using new admin password %s" % + (public_address, self.new_password)) diff --git a/test_repo/compute/functional/servers/actions/test_reboot_server_hard.py b/test_repo/compute/functional/servers/actions/test_reboot_server_hard.py new file mode 100644 index 00000000..723e127c --- /dev/null +++ b/test_repo/compute/functional/servers/actions/test_reboot_server_hard.py @@ -0,0 +1,50 @@ +""" +Copyright 2013 Rackspace + +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 time + +from cafe.drivers.unittest.decorators import tags +from cloudcafe.compute.common.types import NovaServerRebootTypes +from test_repo.compute.fixtures import ComputeFixture + + +class RebootServerHardTests(ComputeFixture): + + @classmethod + def setUpClass(cls): + super(RebootServerHardTests, cls).setUpClass() + response = cls.compute_provider.create_active_server() + cls.server = response.entity + cls.resources.add(cls.server.id, cls.servers_client.delete_server) + + @classmethod + def tearDownClass(cls): + super(RebootServerHardTests, cls).tearDownClass() + + @tags(type='smoke', net='yes') + def test_reboot_server_hard(self): + """ The server should be power cycled """ + public_address = self.compute_provider.get_public_ip_address(self.server) + remote_instance = self.compute_provider.get_remote_instance_client(self.server, public_address) + uptime_start = remote_instance.get_uptime() + start = time.time() + + self.compute_provider.reboot_and_await(self.server.id, NovaServerRebootTypes.HARD) + remote_client = self.compute_provider.get_remote_instance_client(self.server, public_address) + finish = time.time() + uptime_post_reboot = remote_client.get_uptime() + self.assertLess(uptime_post_reboot, (uptime_start + (finish - start))) + diff --git a/test_repo/compute/functional/servers/actions/test_reboot_server_soft.py b/test_repo/compute/functional/servers/actions/test_reboot_server_soft.py new file mode 100644 index 00000000..22bdfc3c --- /dev/null +++ b/test_repo/compute/functional/servers/actions/test_reboot_server_soft.py @@ -0,0 +1,49 @@ +""" +Copyright 2013 Rackspace + +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 time + +from cafe.drivers.unittest.decorators import tags +from cloudcafe.compute.common.types import NovaServerRebootTypes +from test_repo.compute.fixtures import ComputeFixture + + +class RebootServerSoftTests(ComputeFixture): + + @classmethod + def setUpClass(cls): + super(RebootServerSoftTests, cls).setUpClass() + response = cls.compute_provider.create_active_server() + cls.server = response.entity + cls.resources.add(cls.server.id, cls.servers_client.delete_server) + + @classmethod + def tearDownClass(cls): + super(RebootServerSoftTests, cls).tearDownClass() + + @tags(type='smoke', net='yes') + def test_reboot_server_soft(self): + """ The server should be signaled to reboot gracefully """ + public_address = self.compute_provider.get_public_ip_address(self.server) + remote_instance = self.compute_provider.get_remote_instance_client(self.server, public_address) + uptime_start = remote_instance.get_uptime() + start = time.time() + + self.compute_provider.reboot_and_await(self.server.id, NovaServerRebootTypes.SOFT) + remote_client = self.compute_provider.get_remote_instance_client(self.server, public_address) + finish = time.time() + uptime_post_reboot = remote_client.get_uptime() + self.assertLess(uptime_post_reboot, (uptime_start + (finish - start))) diff --git a/test_repo/compute/functional/servers/actions/test_rebuild_server.py b/test_repo/compute/functional/servers/actions/test_rebuild_server.py new file mode 100644 index 00000000..37d6eb0c --- /dev/null +++ b/test_repo/compute/functional/servers/actions/test_rebuild_server.py @@ -0,0 +1,115 @@ +""" +Copyright 2013 Rackspace + +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 base64 +import unittest2 as unittest + +from cafe.drivers.unittest.decorators import tags +from cloudcafe.compute.common.types import NovaServerStatusTypes +from cloudcafe.compute.common.datagen import rand_name +from test_repo.compute.fixtures import ComputeFixture + + + +class RebuildServerTests(ComputeFixture): + + @classmethod + def setUpClass(cls): + super(RebuildServerTests, cls).setUpClass() + response = cls.compute_provider.create_active_server() + cls.server = response.entity + response = cls.flavors_client.get_flavor_details(cls.flavor_ref) + cls.flavor = response.entity + cls.resources.add(cls.server.id, cls.servers_client.delete_server) + cls.metadata = {'key': 'value'} + cls.name = rand_name('testserver') + file_contents = 'Test server rebuild.' + personality = [{'path': '/etc/rebuild.txt', + 'contents': base64.b64encode(file_contents)}] + cls.password = 'rebuild' + + rebuilt_server_response = cls.servers_client.rebuild(cls.server.id, + cls.image_ref_alt, + name=cls.name, + metadata=cls.metadata, + personality=personality, + admin_pass=cls.password) + cls.rebuilt_server_response = cls.compute_provider.wait_for_server_status(cls.server.id, + NovaServerStatusTypes.ACTIVE) + + @classmethod + def tearDownClass(cls): + super(RebuildServerTests, cls).tearDownClass() + + @tags(type='smoke', net='no') + def test_verify_rebuild_server_response(self): + #Verify the properties in the initial response are correct + rebuilt_server = self.rebuilt_server_response.entity + + if rebuilt_server.addresses.public is not None: + v4_address = rebuilt_server.addresses.public.ipv4 + v6_address = rebuilt_server.addresses.public.ipv6 + self.assertEqual(v4_address, self.server.accessIPv4, + msg="AccessIPv4 did not match") + self.assertEqual(v6_address, self.server.accessIPv6, + msg="AccessIPv6 did not match") + + self.assertEquals(rebuilt_server.tenant_id, self.config.compute_api.tenant_id, + msg="Tenant id did not match") + self.assertEqual(rebuilt_server.name, self.name, + msg="Server name did not match") + self.assertTrue(rebuilt_server.hostId is not None, + msg="Host id was not set") + self.assertEqual(rebuilt_server.image.id, self.image_ref_alt, + msg="Image id did not match") + self.assertEqual(rebuilt_server.flavor.id, self.flavor_ref, + msg="Flavor id did not match") + self.assertEqual(rebuilt_server.id, self.server.id, msg="Server id did not match") + + self.assertEqual(rebuilt_server.links.bookmark, self.server.links.bookmark, msg="Bookmark links do not match") + self.assertEqual(self.server.links.self, self.rebuilt_server_response['location'], + msg="Location url did not match a valid link for the server") + self.assertEqual(rebuilt_server.metadata.key, 'value') + self.assertEqual(rebuilt_server.created, self.server.created, + msg="Server Created date changed after rebuild") + self.assertTrue(rebuilt_server.updated != self.server.updated, + msg="Server Updated date not changed after rebuild") + self.assertEquals(rebuilt_server.addresses, self.server.addresses, + msg="Server IP addresses changed after rebuild") + + @tags(type='positive', net='yes') + @unittest.skip('V1 Bug:I-04125') + def test_server_hostname_after_rebuild(self): + server = self.rebuilt_server_response.entity + rebuilt_server = self.rebuilt_server_response.entity + public_address = self.compute_provider.get_public_ip_address(rebuilt_server) + server.adminPass = self.password + remote_instance = self.compute_provider.get_remote_instance_client(server, public_address) + + # Verify that the server hostname is set to the new server name + hostname = remote_instance.get_hostname() + self.assertEqual(hostname, server.name, + msg="The hostname was not same as the server name after rebuild") + + @tags(type='smoke', net='yes') + def test_can_log_into_server_after_rebuild(self): + server = self.rebuilt_server_response.entity + rebuilt_server = self.rebuilt_server_response.entity + public_address = self.compute_provider.get_public_ip_address(rebuilt_server) + server.adminPass = self.password + remote_instance = self.compute_provider.get_remote_instance_client(server, public_address) + self.assertTrue(remote_instance.can_connect_to_public_ip(), + msg="Could not connect to server (%s) using new admin password %s" % (public_address, server.adminPass)) diff --git a/test_repo/compute/functional/servers/actions/test_rescue_server.py b/test_repo/compute/functional/servers/actions/test_rescue_server.py new file mode 100644 index 00000000..a39cc76e --- /dev/null +++ b/test_repo/compute/functional/servers/actions/test_rescue_server.py @@ -0,0 +1,68 @@ +""" +Copyright 2013 Rackspace + +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 cafe.drivers.unittest.decorators import tags +from test_repo.compute.fixtures import ComputeFixture + + +class ServerRescueTests(ComputeFixture): + + @classmethod + def setUpClass(cls): + super(ServerRescueTests, cls).setUpClass() + server_response = cls.compute_provider.create_active_server() + cls.server = server_response.entity + cls.resources.add(cls.server.id, cls.servers_client.delete_server) + flavor_response = cls.flavors_client.get_flavor_details(cls.flavor_ref) + cls.flavor = flavor_response.entity + + @classmethod + def tearDownClass(cls): + super(ServerRescueTests, cls).tearDownClass() + + @tags(type='smoke', net='yes') + def test_rescue_and_unrescue_server_test(self): + """Verify that a server can enter and exit rescue mode""" + rescue_response = self.servers_client.rescue(self.server.id) + changed_password = rescue_response.entity.adminPass + self.assertTrue(rescue_response.status_code is 200, + msg="The response code while rescuing a server is %s instead of 200" % rescue_response.status_code) + self.assertTrue(self.server.adminPass is not changed_password, + msg="The password did not change after Rescue.") + + #Enter rescue mode + rescue_server_response = self.compute_provider.wait_for_server_status(self.server.id, 'RESCUE') + rescue_server = rescue_server_response.entity + rescue_server.adminPass = changed_password + + remote_client = self.compute_provider.get_remote_instance_client(rescue_server) + + #Verify if hard drives are attached + remote_client = self.compute_provider.get_remote_instance_client(rescue_server) + partitions = remote_client.get_partition_details() + self.assertEqual(3, len(partitions)) + + #Exit rescue mode + unrescue_response = self.servers_client.unrescue(self.server.id) + self.assertTrue(unrescue_response.status_code == 202, + msg="The response code while unrescuing a server is %s instead of 202" % rescue_response.status_code) + + self.compute_provider.wait_for_server_status(self.server.id, 'ACTIVE') + remote_client = self.compute_provider.get_remote_instance_client(self.server) + partitions = remote_client.get_partition_details() + self.assertEqual(2, len(partitions), msg="The number of partitions after unrescue were not two.") + result, message = remote_client.verify_partitions(self.flavor.disk, self.flavor.swap, 'active', partitions) + self.assertTrue(result, msg=message) diff --git a/test_repo/compute/functional/servers/actions/test_resize_server_confirm.py b/test_repo/compute/functional/servers/actions/test_resize_server_confirm.py new file mode 100644 index 00000000..2c6eb8e8 --- /dev/null +++ b/test_repo/compute/functional/servers/actions/test_resize_server_confirm.py @@ -0,0 +1,131 @@ +""" +Copyright 2013 Rackspace + +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 cafe.drivers.unittest.decorators import tags +from cloudcafe.compute.common.types import NovaServerStatusTypes +from cloudcafe.compute.common.equality_tools import EqualityTools +from test_repo.compute.fixtures import ComputeFixture + + +class ResizeServerUpConfirmTests(ComputeFixture): + + @classmethod + def setUpClass(cls): + super(ResizeServerUpConfirmTests, cls).setUpClass() + server_response = cls.compute_provider.create_active_server() + server_to_resize = server_response.entity + cls.resources.add(server_to_resize.id, cls.servers_client.delete_server) + + # resize server and confirm + cls.servers_client.resize(server_to_resize.id, cls.flavor_ref_alt) + cls.compute_provider.wait_for_server_status(server_to_resize.id, + NovaServerStatusTypes.VERIFY_RESIZE) + + cls.servers_client.confirm_resize(server_to_resize.id) + cls.compute_provider.wait_for_server_status(server_to_resize.id, + NovaServerStatusTypes.ACTIVE) + resized_server_response = cls.servers_client.get_server(server_to_resize.id) + cls.resized_server = resized_server_response.entity + cls.resized_server.adminPass = server_to_resize.adminPass + + @classmethod + def tearDownClass(cls): + super(ResizeServerUpConfirmTests, cls).tearDownClass() + + @tags(type='smoke', net='no') + def test_verify_confirm_resize_response(self): + pass + + @tags(type='smoke', net='no') + def test_server_properties_after_resize(self): + self.assertEqual(self.flavor_ref_alt, self.resized_server.flavor.id) + + @tags(type='smoke', net='yes') + def test_ram_and_disk_size_on_resize_up_server_confirm_test(self): + """ + The server's RAM and disk space should be modified to that of + the provided flavor + """ + + new_flavor = self.flavors_client.get_flavor_details(self.flavor_ref_alt).entity + public_address = self.compute_provider.get_public_ip_address(self.resized_server) + + remote_instance = self.compute_provider.get_remote_instance_client(self.resized_server, public_address) + + lower_limit = int(new_flavor.ram) - (int(new_flavor.ram) * .1) + server_ram_size = int(remote_instance.get_ram_size_in_mb()) + server_swap_size = int(remote_instance.get_swap_size_in_mb()) + self.assertTrue(int(new_flavor.ram) == server_ram_size or lower_limit <= server_ram_size, + msg="Ram size after confirm-resize did not match. Expected ram size : %s, Actual ram size : %s" % (new_flavor.ram, server_ram_size)) + self.assertEquals(int(new_flavor.swap), server_swap_size, + msg="Swap size after confirm-resize did not match. Expected swap size : %s, Actual swap size : %s" % (new_flavor.swap, server_swap_size)) + self.assertTrue(EqualityTools.are_sizes_equal(new_flavor.disk, remote_instance.get_disk_size_in_gb(), 0.5), + msg="Disk size %s after confirm-resize did not match size %s" % (remote_instance.get_disk_size_in_gb(), new_flavor.disk)) + + +class ResizeServerDownConfirmTests(ComputeFixture): + + @classmethod + def setUpClass(cls): + super(ResizeServerDownConfirmTests, cls).setUpClass() + server_response = cls.compute_provider.create_active_server(flavor_ref=cls.flavor_ref_alt) + server_to_resize = server_response.entity + cls.resources.add(server_to_resize.id, cls.servers_client.delete_server) + + # resize server and confirm + cls.servers_client.resize(server_to_resize.id, cls.flavor_ref) + cls.compute_provider.wait_for_server_status(server_to_resize.id, + NovaServerStatusTypes.VERIFY_RESIZE) + + cls.servers_client.confirm_resize(server_to_resize.id) + cls.compute_provider.wait_for_server_status(server_to_resize.id, + NovaServerStatusTypes.ACTIVE) + resized_server_response = cls.servers_client.get_server(server_to_resize.id) + cls.resized_server = resized_server_response.entity + cls.resized_server.adminPass = server_to_resize.adminPass + + @classmethod + def tearDownClass(cls): + super(ResizeServerDownConfirmTests, cls).tearDownClass() + + @tags(type='smoke', net='no') + def test_verify_confirm_resize_response(self): + pass + + @tags(type='smoke', net='no') + def test_server_properties_after_resize(self): + self.assertEqual(self.flavor_ref, self.resized_server.flavor.id) + + @tags(type='smoke', net='yes') + def test_ram_and_disk_size_on_resize_up_server_confirm_test(self): + """ + The server's RAM and disk space should be modified to that of + the provided flavor + """ + + new_flavor = self.flavors_client.get_flavor_details(self.flavor_ref).entity + public_address = self.compute_provider.get_public_ip_address(self.resized_server) + remote_instance = self.compute_provider.get_remote_instance_client(self.resized_server, public_address) + + lower_limit = int(new_flavor.ram) - (int(new_flavor.ram) * .1) + server_ram_size = int(remote_instance.get_ram_size_in_mb()) + server_swap_size = int(remote_instance.get_swap_size_in_mb()) + self.assertTrue(int(new_flavor.ram) == server_ram_size or lower_limit <= server_ram_size, + msg="Ram size after confirm-resize did not match. Expected ram size : %s, Actual ram size : %s" % (new_flavor.ram, server_ram_size)) + self.assertEquals(int(new_flavor.swap), server_swap_size, + msg="Swap size after confirm-resize did not match. Expected swap size: %s, Actual swap size: %s" % (new_flavor.swap, server_swap_size)) + self.assertTrue(EqualityTools.are_sizes_equal(new_flavor.disk, remote_instance.get_disk_size_in_gb(), 0.5), + msg="Disk size %s after confirm-resize did not match size %s" % (remote_instance.get_disk_size_in_gb(), new_flavor.disk)) diff --git a/test_repo/compute/functional/servers/actions/test_resize_server_revert.py b/test_repo/compute/functional/servers/actions/test_resize_server_revert.py new file mode 100644 index 00000000..b97561ef --- /dev/null +++ b/test_repo/compute/functional/servers/actions/test_resize_server_revert.py @@ -0,0 +1,101 @@ +""" +Copyright 2013 Rackspace + +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 cafe.drivers.unittest.decorators import tags +from cloudcafe.compute.common.types import NovaServerStatusTypes +from cloudcafe.compute.common.datagen import rand_name +from cloudcafe.compute.common.equality_tools import EqualityTools +from test_repo.compute.fixtures import ComputeFixture + + +class ResizeServerUpRevertTests(ComputeFixture): + + @classmethod + def setUpClass(cls): + super(ResizeServerUpRevertTests, cls).setUpClass() + response = cls.compute_provider.create_active_server() + cls.server = response.entity + cls.remote_instance = cls.compute_provider.get_remote_instance_client(cls.server) + file_name = rand_name('file') + '.txt' + file_content = 'This is a test file' + cls.file_details = cls. remote_instance.create_file(file_name, file_content) + response = cls.flavors_client.get_flavor_details(cls.flavor_ref) + cls.flavor = response.entity + cls.resources.add(cls.server.id, cls.servers_client.delete_server) + + @classmethod + def tearDownClass(cls): + super(ResizeServerUpRevertTests, cls).tearDownClass() + + @tags(type='smoke', net='yes') + def test_ram_and_disk_size_on_resize_up_server_revert(self): + """ + The server's RAM and disk space should return to its original + values after a resize is reverted + """ + server_response = self.compute_provider.create_active_server() + server_to_resize = server_response.entity + self.resources.add(server_to_resize.id, self.servers_client.delete_server) + remote_instance = self.compute_provider.get_remote_instance_client(server_to_resize) + file_name = rand_name('file') + '.txt' + file_content = 'This is a test file' + file_details = remote_instance.create_file(file_name, file_content) + + #resize server and revert + self.servers_client.resize(server_to_resize.id, self.flavor_ref_alt) + self.compute_provider.wait_for_server_status(server_to_resize.id, NovaServerStatusTypes.VERIFY_RESIZE) + + self.servers_client.revert_resize(server_to_resize.id) + reverted_server_response = self.compute_provider.wait_for_server_status(server_to_resize.id, NovaServerStatusTypes.ACTIVE) + reverted_server = reverted_server_response.entity + flavor_response = self.flavors_client.get_flavor_details(self.flavor_ref) + flavor = flavor_response.entity + + '''Verify that the server resize was reverted ''' + public_address = self.compute_provider.get_public_ip_address(reverted_server) + reverted_server.adminPass = server_to_resize.adminPass + remote_instance = self.compute_provider.get_remote_instance_client(reverted_server, public_address) + actual_file_content = remote_instance.get_file_details(file_details.name) + + '''Verify that the file content does not change after resize revert''' + self.assertEqual(actual_file_content, file_details, msg="file changed after resize revert") + + self.assertEqual(self.flavor_ref, reverted_server.flavor.id, + msg="Flavor id not reverted") + lower_limit = int(flavor.ram) - (int(flavor.ram) * .1) + server_ram_size = int(remote_instance.get_ram_size_in_mb()) + self.assertTrue(int(flavor.ram) == server_ram_size or lower_limit <= server_ram_size, + msg="Ram size after revert did not match.Expected ram size : %s, Actual ram size : %s" % (flavor.ram, server_ram_size)) + + self.assertTrue(EqualityTools.are_sizes_equal(flavor.disk, remote_instance.get_disk_size_in_gb(), 0.5), + msg="Disk size %s after revert did not match %s" % (remote_instance.get_disk_size_in_gb(), flavor.disk)) + + def _assert_server_details(self, server, expected_name, expected_accessIPv4, expected_accessIPv6, expected_id, expected_image_ref): + self.assertEqual(expected_accessIPv4, server.accessIPv4, + msg="AccessIPv4 did not match") + self.assertEqual(expected_accessIPv6, server.accessIPv6, + msg="AccessIPv6 did not match") + self.assertEquals(self.config.nova.tenant_id, server.tenant_id, + msg="Tenant id did not match") + self.assertEqual(expected_name, server.name, + msg="Server name did not match") + self.assertTrue(server.host_id is not None, + msg="Host id was not set") + self.assertEqual(expected_image_ref, server.image.id, + msg="Image id did not match") + self.assertEqual(self.flavor_ref, server.flavor.id, + msg="Flavor id did not match") + self.assertEqual(expected_id, server.id, msg="Server id did not match") diff --git a/test_repo/compute/functional/servers/test_create_server.py b/test_repo/compute/functional/servers/test_create_server.py new file mode 100644 index 00000000..44975b24 --- /dev/null +++ b/test_repo/compute/functional/servers/test_create_server.py @@ -0,0 +1,96 @@ +""" +Copyright 2013 Rackspace + +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 base64 + +from cafe.drivers.unittest.decorators import tags +from cloudcafe.compute.common.types import NovaServerStatusTypes +from cloudcafe.compute.common.datagen import rand_name +from test_repo.compute.fixtures import ComputeFixture +from cafe.engine.clients.remote_instance.instance_client import InstanceClientFactory + + +class CreateServerTest(ComputeFixture): + + @classmethod + def setUpClass(cls): + super(CreateServerTest, cls).setUpClass() + cls.name = rand_name("cctestserver") + cls.metadata = {'meta_key_1': 'meta_value_1', + 'meta_key_2': 'meta_value_2'} + cls.create_resp = cls.servers_client.create_server(cls.name, cls.image_ref, cls.flavor_ref, + metadata=cls.metadata) + created_server = cls.create_resp.entity + wait_response = cls.server_behaviors.wait_for_server_status(created_server.id, + NovaServerStatusTypes.ACTIVE) + wait_response.entity.admin_pass = created_server.admin_pass + cls.image = cls.images_client.get_image(cls.image_ref).entity + cls.flavor = cls.flavors_client.get_flavor_details(cls.flavor_ref).entity + cls.server = wait_response.entity + + @classmethod + def tearDownClass(cls): + super(CreateServerTest, cls).tearDownClass() + + @tags(type='smoke', net='no') + def test_create_server_response(self): + """Verify the parameters are correct in the initial response""" + + self.assertTrue(self.server.id is not None, + msg="Server id was not set in the response") + self.assertTrue(self.server.admin_pass is not None, + msg="Admin password was not set in the response") + self.assertTrue(self.server.links is not None, + msg="Server links were not set in the response") + + @tags(type='smoke', net='no') + def test_created_server_fields(self): + """Verify that a created server has all expected fields""" + message = "Expected {0} to be {1}, was {2}." + + self.assertEqual(self.server.name, self.name, + msg=message.format('server name', self.server.name, + self.name)) + self.assertEqual(self.image_ref, self.server.image.id, + msg=message.format('image id', self.image_ref, + self.server.image.id)) + self.assertEqual(self.server.flavor.id, self.flavor_ref, + msg=message.format('flavor id', self.flavor_ref, + self.server.flavor.id)) + self.assertTrue(self.server.created is not None, + msg="Expected server created date to be set, was null.") + self.assertTrue(self.server.updated is not None, + msg="Expected server updated date to be set, was null.") + self.assertGreaterEqual(self.server.updated, self.server.created, + msg="Expected server updated date to be before the created date.") + + @tags(type='smoke', net='no') + def test_server_access_addresses(self): + """If the server has public addresses, the access IP addresses should be same as the public addresses""" + addresses = self.server.addresses + if addresses.public is not None: + self.assertTrue(addresses.public.ipv4 is not None, + msg="Expected server to have a public IPv4 address set.") + self.assertTrue(addresses.public.ipv6 is not None, + msg="Expected server to have a public IPv6 address set.") + self.assertTrue(addresses.private.ipv4 is not None, + msg="Expected server to have a private IPv4 address set.") + self.assertEqual(addresses.public.ipv4, self.server.accessIPv4, + msg="Expected access IPv4 address to be {0}, was {1}.".format( + addresses.public.ipv4, self.server.accessIPv4)) + self.assertEqual(addresses.public.ipv6, self.server.accessIPv6, + msg="Expected access IPv6 address to be {0}, was {1}.".format( + addresses.public.ipv6, self.server.accessIPv6)) diff --git a/test_repo/compute/functional/servers/test_list_servers.py b/test_repo/compute/functional/servers/test_list_servers.py new file mode 100644 index 00000000..788259a9 --- /dev/null +++ b/test_repo/compute/functional/servers/test_list_servers.py @@ -0,0 +1,282 @@ +""" +Copyright 2013 Rackspace + +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 cafe.drivers.unittest.decorators import tags +from test_repo.compute.fixtures import ComputeFixture + + +class ServerListTest(ComputeFixture): + + @classmethod + def setUpClass(cls): + super(ServerListTest, cls).setUpClass() + # Creation of 3 servers needed for the tests + active_server_response = cls.server_behaviors.create_active_server() + cls.server = active_server_response.entity + active_server_response = cls.server_behaviors.create_active_server() + cls.server_second = active_server_response.entity + active_server_response = cls.server_behaviors.create_active_server( + image_ref=cls.image_ref_alt, + flavor_ref=cls.flavor_ref_alt) + + cls.server_third = active_server_response.entity + cls.resources.add(cls.server.id, + cls.servers_client.delete_server) + cls.resources.add(cls.server_second.id, + cls.servers_client.delete_server) + cls.resources.add(cls.server_third.id, + cls.servers_client.delete_server) + + @classmethod + def tearDownClass(cls): + super(ServerListTest, cls).tearDownClass() + + @tags(type='smoke', net='no') + def test_get_server(self): + """Return the full details of a single server""" + server_info_response = self.servers_client.get_server(self.server.id) + server_info = server_info_response.entity + self.assertEqual(200, server_info_response.status_code) + self.assertEqual(self.server.name, server_info.name, + msg="Server name did not match") + self.assertEqual(self.image_ref, server_info.image.id, + msg="Image id did not match") + self.assertEqual(self.flavor_ref, server_info.flavor.id, + msg="Flavor id did not match") + + @tags(type='smoke', net='no') + def test_list_servers(self): + """All servers should be returned""" + list_servers_response = self.servers_client.list_servers() + list_servers = list_servers_response.entity + self.assertEqual(200, list_servers_response.status_code) + self.assertTrue(self.server.min_details() in + list_servers, msg="Server with id %s was not found in the list" % self.server.id) + self.assertTrue(self.server_second.min_details() in + list_servers, msg="Server with id %s was not found in the list" % self.server_second.id) + self.assertTrue(self.server_third.min_details() in + list_servers, msg="Server with id %s was not found in the list" % self.server_third.id) + + @tags(type='smoke', net='no') + def test_list_servers_with_detail(self): + """Return a detailed list of all servers""" + list_servers_detail_response = self.servers_client.list_servers_with_detail() + list_servers_detail = list_servers_detail_response.entity + self.assertEqual(200, list_servers_detail_response.status_code) + servers_lists = [] + for i in list_servers_detail: + servers_lists.append(i.id) + self.assertTrue(self.server.id in servers_lists, + msg="Server with id %s was not found in the list" % self.server.id) + self.assertTrue(self.server_second.id in servers_lists, + msg="Server with id %s was not found in the list" % self.server_second.id) + self.assertTrue(self.server_third.id in servers_lists, + msg="Server with id %s was not found in the list" % self.server_third.id) + + @tags(type='positive', net='no') + def test_list_server_details_using_marker(self): + """The list of servers should start from the provided marker""" + list_server_detail_response = self.servers_client.list_servers_with_detail() + list_server_detail = list_server_detail_response.entity + first_server = list_server_detail[0] + + # Verify the list of servers doesn't contain the server used as a marker + params = first_server.id + filtered_servers = self.servers_client.list_servers_with_detail(marker=params) + self.assertEqual(200, filtered_servers.status_code) + self.assertTrue(first_server not in filtered_servers.entity, + msg="The server id used as marker found in the server list") + + @tags(type='positive', net='no') + def test_list_servers_using_marker(self): + """The list of servers should start from the provided marker""" + list_server_info_response = self.servers_client.list_servers() + list_server_info = list_server_info_response.entity + first_server = list_server_info[0] + + # Verify the list of servers doesn't contain the server used as a marker + params = first_server.id + filtered_servers = self.servers_client.list_servers(marker=params) + self.assertEqual(200, filtered_servers.status_code) + self.assertTrue(first_server not in filtered_servers.entity, + msg="The server id used as marker found in the server list") + + @tags(type='positive', net='no') + def test_list_server_with_detail_limit_results(self): + """Verify only the expected number of results (with full details) are returned""" + limit = 1 + params = limit + server_with_limit = self.servers_client.list_servers_with_detail(limit=params) + self.assertEqual(limit, len(server_with_limit.entity), + msg="The number of servers returned (%s) was more than the limit (%s)" % (len(server_with_limit.entity), limit)) + + @tags(type='positive', net='no') + def test_list_servers_filter_by_image(self): + """Filter the list of servers by image""" + params = self.image_ref + list_servers_response = self.servers_client.list_servers(image=params) + list_servers = list_servers_response.entity + self.assertEqual(200, list_servers_response.status_code) + + self.assertTrue(self.server.min_details() in + list_servers, msg="Server with id %s was not found in the list" % self.server.image.id) + self.assertTrue(self.server_second.min_details() in + list_servers, msg="Server with id %s was not found in the list" % self.server_second.image.id) + self.assertTrue(self.server_third.min_details() not in + list_servers, msg="Server with id %s and image id %s was found in the list filtered by image id %s" % (self.server_third.id, self.server_third.image.id, self.image_ref)) + + @tags(type='positive', net='no') + def test_list_servers_filter_by_flavor(self): + """Filter the list of servers by flavor""" + params = self.flavor_ref_alt + list_servers_response = self.servers_client.list_servers(flavor=params) + list_servers = list_servers_response.entity + self.assertEqual(200, list_servers_response.status_code) + + self.assertTrue(self.server.min_details() not in list_servers, + msg="Server with id %s and flavor id %s was found in the list filtered by flavor id %s" % (self.server.id, self.server.flavor.id, self.flavor_ref_alt)) + self.assertTrue(self.server_second.min_details() not in list_servers, + msg="Server with id %s and flavor id %s was found in the list filtered by flavor id %s" % (self.server_second.id, self.server_second.flavor.id, self.flavor_ref_alt)) + self.assertTrue(self.server_third.min_details() in list_servers, + msg="Server with id %s was not found in the list" % self.server_third.id) + + @tags(type='positive', net='no') + def test_list_servers_filter_by_server_name(self): + """ Filter the list of servers by name """ + params = self.server.name + list_servers_response = self.servers_client.list_servers(name=params) + list_servers = list_servers_response.entity + self.assertEqual(200, list_servers_response.status_code) + self.assertTrue(self.server.min_details() in list_servers, + msg="Server with id %s was not found in the list" % self.server.id) + self.assertTrue(self.server_second.min_details() not in list_servers, + msg="Server with id %s and name %s was found in the list filtered by name %s" % (self.server.id, self.server_second.name, self.server.name)) + self.assertTrue(self.server_third.min_details() not in list_servers, + msg="Server with id %s and name %s was found in the list filtered by name %s" % (self.server_third.id, self.server_third.name, self.server.name)) + + @tags(type='positive', net='no') + def test_list_servers_filter_by_server_status(self): + """ Filter the list of servers by server status """ + params = 'active' + list_servers_response = self.servers_client.list_servers(status=params) + list_servers = list_servers_response.entity + self.assertEqual(200, list_servers_response.status_code) + self.assertTrue(self.server.min_details() in + list_servers, msg="Server with id %s was not found in the list" % self.server.id) + self.assertTrue(self.server_second.min_details() in + list_servers, msg="Server with id %s was not found in the list" % self.server_second.id) + self.assertTrue(self.server_third.min_details() in + list_servers, msg="Server with id %s was not found in the list" % self.server_third.id) + + @tags(type='positive', net='no') + def test_list_servers_filter_by_changes_since(self): + """Filter the list of servers by changes-since""" + change_time = self.server_second.created + params = change_time + servers = self.servers_client.list_servers(changes_since=params) + self.assertEqual(200, servers.status_code) + servers_ids_list = [] + for i in servers.entity: + servers_ids_list.append(i.id) + self.assertTrue(self.server.id not in servers_ids_list, + msg="Server with id %s was found in the list" % self.server.id) + self.assertTrue(self.server_second.id in servers_ids_list, + msg="Server with id %s was not found in the list" % self.server_second.id) + self.assertTrue(self.server_third.id in servers_ids_list, + msg="Server with id %s was not found in the list" % self.server_third.id) + + @tags(type='positive', net='no') + def test_list_servers_detailed_filter_by_image(self): + """Filter the detailed list of servers by image""" + params = self.image_ref + servers_response = self.servers_client.list_servers_with_detail(image=params) + self.assertEqual(200, servers_response.status_code) + servers_list = [] + for i in servers_response.entity: + servers_list.append(i.id) + self.assertTrue(self.server.id in servers_list, + msg="Server with id %s was not found in the list" % self.server.id) + self.assertTrue(self.server_second.id in servers_list, + msg="Server with id %s was not found in the list" % self.server_second.id) + self.assertTrue(self.server_third.id not in servers_list, + msg="Server with id %s and image id %s was found in the list filtered by image id %s" % (self.server_third.id, self.server_third.image.id, self.image_ref)) + + @tags(type='positive', net='no') + def test_list_servers_detailed_filter_by_flavor(self): + """Filter the detailed list of servers by flavor""" + params = self.flavor_ref_alt + filtered_servers_response = self.servers_client.list_servers_with_detail(flavor=params) + filtered_servers = filtered_servers_response.entity + self.assertEqual(200, filtered_servers_response.status_code) + + self.assertTrue(self.server not in filtered_servers, + msg="Server with id %s and flavor id %s was found in the list filtered by flavor id %s" % (self.server.id, self.server.flavor.id, self.flavor_ref_alt)) + self.assertTrue(self.server_second not in filtered_servers, + msg="Server with id %s and flavor id %s was found in the list filtered by flavor id %s" % (self.server_second.id, self.server.flavor.id, self.flavor_ref_alt)) + self.assertTrue(self.server_third in filtered_servers, + msg="Server with id %s was not found in the list" % self.server_third.id,) + + @tags(type='positive', net='no') + def test_list_servers_detailed_filter_by_server_name(self): + """Filter the detailed list of servers by server name""" + params = self.server.name + filtered_servers_response = self.servers_client.list_servers_with_detail(name=params) + filtered_servers = filtered_servers_response.entity + self.assertEqual(200, filtered_servers_response.status_code) + + self.assertTrue(self.server in filtered_servers, + msg="Server with id %s was not found in the list" % self.server.id) + self.assertTrue(self.server_second not in filtered_servers, + msg="Server with id %s and name %s was found in the list filtered by name %s" % (self.server_second.id, self.server_second.name, self.server.name)) + self.assertTrue(self.server_third not in filtered_servers, + msg="Server with id %s and name %s was found in the list filtered by name %s" % (self.server_third.id, self.server_third.name, self.server.name)) + + @tags(type='positive', net='no') + def test_list_servers_detailed_filter_by_server_status(self): + """Filter the detailed list of servers by server status""" + params = 'active' + filtered_servers_response = self.servers_client.list_servers_with_detail(status=params) + filtered_servers = filtered_servers_response.entity + self.assertEqual(200, filtered_servers_response.status_code) + servers_list = [] + for i in filtered_servers: + servers_list.append(i.id) + self.assertTrue(self.server.id in servers_list, + msg="Server with id %s was not found in the list" % self.server.id) + self.assertTrue(self.server_second.id in servers_list, + msg="Server with id %s was not found in the list" % self.server_second.id) + self.assertTrue(self.server_third.id in servers_list, + msg="Server with id %s was not found in the list" % self.server_third.id) + + @tags(type='positive', net='no') + def test_list_servers_detailed_filter_by_changes_since(self): + """Create a filter for the server with the second server created date""" + change_time = self.server_second.created + params = change_time + + # Filter the detailed list of servers by changes-since + filtered_servers_response = self.servers_client.list_servers_with_detail(changes_since=params) + filtered_servers = filtered_servers_response.entity + self.assertEqual(200, filtered_servers_response.status_code) + servers_list = [] + for i in filtered_servers: + servers_list.append(i.id) + self.assertTrue(self.server.id not in servers_list, + msg="Server with id %s was found in the list" % self.server.id) + self.assertTrue(self.server_second.id in servers_list, + msg="Server with id %s was not found in the list" % self.server_second.id) + self.assertTrue(self.server_third.id in servers_list, + msg="Server with id %s was not found in the list" % self.server_third.id) diff --git a/test_repo/compute/functional/servers/test_list_servers.pyc b/test_repo/compute/functional/servers/test_list_servers.pyc new file mode 100644 index 0000000000000000000000000000000000000000..608f5ea4300f733396a1773c2d26687d8614a4ba GIT binary patch literal 12704 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 literal 0 HcmV?d00001 diff --git a/test_repo/compute/functional/servers/test_server_metadata.py b/test_repo/compute/functional/servers/test_server_metadata.py new file mode 100644 index 00000000..eaa5dea6 --- /dev/null +++ b/test_repo/compute/functional/servers/test_server_metadata.py @@ -0,0 +1,194 @@ +""" +Copyright 2013 Rackspace + +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 unittest2 as unittest + +from cafe.drivers.unittest.decorators import tags +from cloudcafe.compute.common.datagen import rand_name +from cloudcafe.compute.common.exceptions import ItemNotFound +from test_repo.compute.fixtures import ComputeFixture + + +class ServerMetadataTest(ComputeFixture): + + @classmethod + def setUpClass(cls): + super(ServerMetadataTest, cls).setUpClass() + server_response = cls.server_behaviors.create_active_server() + cls.server = server_response.entity + cls.resources.add(cls.server.id, cls.servers_client.delete_server) + + @classmethod + def tearDownClass(cls): + super(ServerMetadataTest, cls).tearDownClass() + + def setUp(self): + super(ServerMetadataTest, self).setUp() + self.meta = {'meta_key_1': 'meta_value_1', 'meta_key_2': 'meta_value_2'} + self.servers_client.set_server_metadata(self.server.id, self.meta) + + @tags(type='positive', net='no') + def test_list_server_metadata(self): + """All metadata key/value pairs for a server should be returned""" + metadata_response = self.servers_client.list_server_metadata(self.server.id) + metadata = metadata_response.entity + self.assertEqual(200, metadata_response.status_code, + "List server metadata call response was: %s" % (metadata_response.status_code)) + self.assertEqual('meta_value_1', metadata.meta_key_1, + "Metadata Item not found on server %s. Expected Item Key : meta_key_1, Value : meta_value_1" % self.server.id) + self.assertEqual('meta_value_2', metadata.meta_key_2, + "Metadata Item not found on server %s. Expected Item Key : meta_key_2, Value : meta_value_2" % self.server.id) + + @tags(type='positive', net='no') + def test_set_server_metadata(self): + """The server's metadata should be replaced with the provided values""" + meta = {'meta1': 'data1'} + server_response = self.server_behaviors.create_active_server(metadata=meta) + server = server_response.entity + self.resources.add(server.id, self.servers_client.delete_server) + + new_meta = {'meta2': 'data2', 'meta3': 'data3'} + metadata_response = self.servers_client.set_server_metadata(server.id, + new_meta) + metadata = metadata_response.entity + self.assertEqual(200, metadata_response.status_code, + "Set server metadata call response was: %s" % (metadata_response.status_code)) + self.assertEqual('data2', metadata.meta2, + "Metadata Item not found on server %s. Expected Item Key : meta2, Value : data2" % server.id) + self.assertEqual('data3', metadata.meta3, + "Metadata Item not found on server %s. Expected Item Key : meta3, Value : data3" % server.id) + self.assertFalse(hasattr(metadata, 'meta1'), + "The already existing metadata(Key : meta1) is not removed during Set metadata") + + actual_metadata_response = self.servers_client.list_server_metadata(server.id) + actual_metadata = actual_metadata_response.entity + self.assertEqual('data2', actual_metadata.meta2, + "Metadata Item not found on server %s. Expected Item Key : meta2, Value : data2" % server.id) + self.assertEqual('data3', actual_metadata.meta3, + "Metadata Item not found on server %s. Expected Item Key : meta3, Value : data3" % server.id) + self.assertFalse(hasattr(actual_metadata, 'meta1'), + "The already existing metadata(Key : meta1) is not removed during Set metadata") + + @tags(type='positive', net='no') + def test_update_server_metadata(self): + """The server's metadata values should be updated to the provided values""" + meta = {'key1': 'alt1', 'key2': 'alt2', 'meta_key_1': 'alt3'} + metadata_response = self.servers_client.update_server_metadata(self.server.id, meta) + metadata = metadata_response.entity + self.assertEqual(200, metadata_response.status_code, + "Update server metadata call response was: %s" % (metadata_response.status_code)) + self.assertEqual('alt1', metadata.key1, + "Metadata Item not found on server %s. Expected Item Key : key1, Value : alt1" % self.server.id) + self.assertEqual('alt2', metadata.key2, + "Metadata Item not found on server %s. Expected Item Key : key2, Value : alt2" % self.server.id) + self.assertEqual('alt3', metadata.meta_key_1, + "Metadata Item not found on server %s. Expected Item Key : meta_key_1, Value : alt3" % self.server.id) + self.assertEqual('meta_value_2', metadata.meta_key_2, + "Metadata Item not found on server %s. Expected Item Key : meta_key_2, Value : meta_value_2" % self.server.id) + + #Verify the values have been updated to the proper values + actual_metadata_response = self.servers_client.list_server_metadata(self.server.id) + actual_metadata = actual_metadata_response.entity + self.assertEqual('alt1', actual_metadata.key1, + "Metadata Item not found on server %s. Expected Item Key : key1, Value : alt1" % self.server.id) + self.assertEqual('alt2', actual_metadata.key2, + "Metadata Item not found on server %s. Expected Item Key : key2, Value : alt2" % self.server.id) + self.assertEqual('alt3', actual_metadata.meta_key_1, + "Metadata Item not found on server %s. Expected Item Key : meta_key_1, Value : alt3" % self.server.id) + self.assertEqual('meta_value_2', actual_metadata.meta_key_2, + "Metadata Item not found on server %s. Expected Item Key : meta_key_2, Value : meta_value_2" % self.server.id) + + @tags(type='positive', net='no') + def test_get_server_metadata_item(self): + """The value for a specific metadata key should be returned""" + metadata_response = self.servers_client.get_server_metadata_item(self.server.id, + 'meta_key_1') + metadata = metadata_response.entity + self.assertEqual('meta_value_1', metadata.meta_key_1, + msg="Metadata Item not found on server %s. Expected Item Key : meta_key_1, Value : meta_value_1" % self.server.id) + + @tags(type='positive', net='no') + def test_set_server_metadata_item(self): + """The value provided for the given meta item should be set for the server""" + meta = {'meta_key_2': 'nova'} + metadata_response = self.servers_client.set_server_metadata_item(self.server.id, + 'meta_key_2', 'nova') + metadata = metadata_response.entity + self.assertEqual(200, metadata_response.status_code, + "Set server metadata item call response was: %s" % (metadata_response.status_code)) + self.assertEqual('nova', metadata.meta_key_2, + "Metadata Item not found on server %s. Expected Item Key : meta_key_2, Value : nova" % self.server.id) + + actual_metadata_response = self.servers_client.list_server_metadata(self.server.id) + actual_metadata = actual_metadata_response.entity + self.assertEqual('nova', actual_metadata.meta_key_2, + "Metadata Item not found on server %s. Expected Item Key : meta_key_2, Value : nova" % self.server.id) + self.assertEqual('meta_value_1', actual_metadata.meta_key_1, + "Metadata Item not found on server %s. Expected Item Key : meta_key_1, Value : meta_value_1" % self.server.id) + + @tags(type='positive', net='no') + def test_add_new_server_metadata_item(self): + """ The metadata item should be added to the server""" + meta = {'meta_key_3': 'meta_value_3'} + metadata_response = self.servers_client.set_server_metadata_item(self.server.id, + 'meta_key_3', 'meta_value_3') + metadata = metadata_response.entity + self.assertEqual(200, metadata_response.status_code, "Add server metadata item call response was: %s" % (metadata_response.status_code)) + self.assertEqual('meta_value_3', metadata.meta_key_3, + "Metadata Item not found on server %s. Expected Item Key : meta_key_3, Value : meta_value_3" % self.server.id) + + actual_metadata_response = self.servers_client.list_server_metadata(self.server.id) + actual_metadata = actual_metadata_response.entity + self.assertEqual('meta_value_3', actual_metadata.meta_key_3, + "Metadata Item not found on server %s. Expected Item Key : meta_key_3, Value : meta_value_3" % self.server.id) + self.assertEqual('meta_value_2', actual_metadata.meta_key_2, + "Metadata Item not found on server %s. Expected Item Key : meta_key_2, Value : meta_value_2" % self.server.id) + self.assertEqual('meta_value_1', actual_metadata.meta_key_1, + "Metadata Item not found on server %s. Expected Item Key : meta_key_1, Value : meta_value_1" % self.server.id) + + @tags(type='positive', net='no') + def test_delete_server_metadata_item(self): + """The metadata value/key pair should be deleted from the server""" + + response = self.servers_client.delete_server_metadata_item(self.server.id, + 'meta_key_1') + self.assertEqual(204, response.status_code, "Delete server metadata item call response was: %s" % (response.status_code)) + metadata_response = self.servers_client.list_server_metadata(self.server.id) + metadata = metadata_response.entity + self.assertFalse(hasattr(metadata, 'meta_key_1'), "The server\ + metadata item (Key: meta_key_1) is not deleted") + + @tags(type='negative', net='no') + def test_delete_nonexistent_server_metadata_item(self): + with self.assertRaises(ItemNotFound): + self.servers_client.delete_server_metadata_item(self.server.id, + 'meta_key_5') + + @tags(type='negative', net='no') + def test_get_nonexistent_server_metadata_item(self): + with self.assertRaises(ItemNotFound): + self.servers_client.get_server_metadata_item(self.server.id, + 'meta_key_5') + + @tags(type='negative', net='no') + @unittest.skip('Failing, under review') + def test_set_blank_metadata_dict(self): + blank_meta = {'': ''} + create_server_response = self.servers_client.create_server(rand_name('testserver'), self.image_ref, self.flavor_ref, metadata=blank_meta) + server_response = self.servers_client.get_server(create_server_response.entity.id) + server = server_response.entity + self.assertEqual("", server.metadata['']) + diff --git a/test_repo/compute/functional/servers/test_servers.py b/test_repo/compute/functional/servers/test_servers.py new file mode 100644 index 00000000..98520099 --- /dev/null +++ b/test_repo/compute/functional/servers/test_servers.py @@ -0,0 +1,330 @@ +""" +Copyright 2013 Rackspace + +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 base64 +import time + +from cafe.drivers.unittest.decorators import tags +from cloudcafe.compute.common.models.metadata import Metadata +from cloudcafe.compute.common.datagen import rand_name +from cloudcafe.compute.common.types import NovaServerStatusTypes +from test_repo.compute.fixtures import ComputeFixture + + +class ServersTest(ComputeFixture): + + @classmethod + def setUpClass(cls): + super(ServersTest, cls).setUpClass() + cls.file_contents = 'This is a test file.' + cls.personality = [{'path': '/root/.csivh', + 'contents': base64.b64encode(cls.file_contents)}] + cls.meta = {'meta_key_1': 'meta_value_1', 'meta_key_2': 'meta_value_2'} + cls.accessIPv4 = '192.168.32.16' + cls.accessIPv6 = '3ffe:1900:4545:3:200:f8ff:fe21:67cf' + + @classmethod + def tearDownClass(cls): + super(ServersTest, cls).tearDownClass() + + @tags(type='smoke', net='no') + def test_update_server_details(self): + """The server name and access ip addresses should be changed to the provided values""" + active_server_response = self.server_behaviors.create_active_server() + active_server = active_server_response.entity + updated_server_response = self.servers_client.update_server(active_server.id, + name='newname', + accessIPv4=self.accessIPv4, + accessIPv6=self.accessIPv6) + updated_server = updated_server_response.entity + updated_server_details_response = self.servers_client.get_server(active_server.id) + updated_server_details = updated_server_details_response.entity + self.server_behaviors.wait_for_server_status(active_server.id, + NovaServerStatusTypes.ACTIVE) + # Verify the name and access IPs of the server have changed + self.assertEqual('newname', updated_server.name, + msg="The name was not updated") + self.assertEqual(self.accessIPv4, updated_server.accessIPv4, + msg="AccessIPv4 was not updated") + self.assertEqual(self.accessIPv6, updated_server.accessIPv6, + msg="AccessIPv6 was not updated") + self.assertEqual(active_server.created, updated_server.created, + msg="The creation date was updated") + + # Verify details changed on get updated server call + self.assertEqual('newname', updated_server_details.name, + msg="The name was not updated") + self.assertEqual(self.accessIPv4, updated_server_details.accessIPv4, + msg="AccessIPv4 was not updated") + self.assertEqual(self.accessIPv6, updated_server_details.accessIPv6, + msg="AccessIPv6 was not updated") + self.assertEqual(active_server.created, updated_server_details.created, + msg="The creation date was updated") + self.assertNotEqual(active_server.updated, updated_server_details.updated, + msg="Server %s updated time did not change after a modification to the server." % updated_server_details.id) + + #Teardown + self.servers_client.delete_server(updated_server_details.id) + + def _assert_server_details(self, server, expected_name, + expected_accessIPv4, expected_accessIPv6, + expected_flavor): + self.assertEqual(expected_accessIPv4, server.accessIPv4, + msg="AccessIPv4 did not match") + self.assertEqual(expected_accessIPv6, server.accessIPv6, + msg="AccessIPv6 did not match") + self.assertEqual(expected_name, server.name, + msg="Server name did not match") + self.assertEqual(self.image_ref, server.image.id, + msg="Image id did not match") + self.assertEqual(expected_flavor, server.flavor.id, + msg="Flavor id did not match") + self.assertTrue(server.created is not None, + msg="Server created date was not set") + self.assertTrue(server.updated is not None, + msg="Server updated date was not set") + self.assertGreaterEqual(server.updated, server.created, + msg="Server updated date was before the created date") + self.assertEqual(self.meta, Metadata._obj_to_dict(server.metadata), + msg="Metadata did not match") + + def _assert_public_address_is_valid(self, addresses): + + self.assertTrue(len(addresses.public.addresses) == 2, + msg="Server does not have a Public IPs set") + ipv4_public_address = None + ipv6_public_address = None + if self.config.misc.serializer == "xml": + for address in addresses.public.addresses: + if address.version == '4': + ipv4_public_address = address + else: + ipv6_public_address = address + self.assertTrue(ipv4_public_address.version == '4' and + ipv4_public_address.addr is not None, + msg="Server does not have a Public IPv4 set") + self.assertTrue(ipv6_public_address.version == '6' and + ipv6_public_address.addr is not None, + msg="Server does not have a Public IPv6 set") + else: + for address in addresses.public.addresses: + if address.version == 4: + ipv4_public_address = address + else: + ipv6_public_address = address + self.assertTrue(ipv4_public_address.version == 4 and + ipv4_public_address.addr is not None, + msg="Server does not have a Public IPv4 set") + self.assertTrue(ipv6_public_address.version == 6 and + ipv6_public_address.addr is not None, + msg="Server does not have a Public IPv6 set") + + @tags(type='positive', net='no') + def test_delete_server(self): + """A server should be built using the specified image and flavor""" + active_server_response = self.server_behaviors.create_active_server() + active_server = active_server_response.entity + deleted_server_response = self.servers_client.delete_server(active_server.id) + self.assertEqual(204, deleted_server_response.status_code, + msg='The delete call response was: %s' + % (deleted_server_response.status_code)) + self.server_behaviors.wait_for_server_status(active_server.id, + NovaServerStatusTypes.DELETED) + # Verify the server is now in deleted status + parameter = str(active_server.created) + list_servers = self.servers_client.list_servers_with_detail(changes_since=parameter) + found = False + for server in list_servers.entity: + if server.id == active_server.id: + deleted_server = server + found = True + + self.assertTrue(found, + msg="The server which was deleted was not found in the server list") + self.assertEqual('DELETED', deleted_server.status, + msg="The server which was deleted was not in DELETED status") + + @tags(type='positive', net='yes') + def test_create_server_with_admin_password(self): + """ + If an admin password is provided on server creation, the server's root + password should be set to that password. + """ + name = rand_name("testserver") + admin_password = 'oldslice129690TuG72Bgj2' + create_server_response = self.servers_client.create_server(name, self.image_ref, self.flavor_ref, admin_pass=admin_password) + created_server = create_server_response.entity + + self.assertEqual(admin_password, created_server.admin_pass, + msg='Verify that given adminPass equals with actual one') + active_server = self.server_behaviors.wait_for_server_status(created_server.id, NovaServerStatusTypes.ACTIVE) + + + @tags(type='positive', net='no') + def test_create_server_with_image_and_flavor_self_link(self): + """Create a server using image and flavor self links""" + name = rand_name("testserver") + image = self.images_client.get_image(self.image_ref) + image_self_link = image.entity.id + flavor = self.flavors_client.get_flavor_details(self.flavor_ref) + flavor_self_link = flavor.entity.id + + create_server_response = self.servers_client.create_server(name, + image_self_link, + flavor_self_link) + created_server = create_server_response.entity + #Verify the parameters are correct in the initial response + self.assertTrue(created_server.id is not None, + msg="The server id was not set in response") + self.assertTrue(created_server.admin_pass is not None, + msg="Admin password was not set in response") + self.assertTrue(created_server.links is not None, + msg="Server links were not set in response") + + '''Wait for the server to become active''' + active_server_response = self.server_behaviors.wait_for_server_status(created_server.id, + NovaServerStatusTypes.ACTIVE) + active_server = active_server_response.entity + get_server_info_response = self.servers_client.get_server(created_server.id) + get_server_info = get_server_info_response.entity + + '''Verify that the image Id of the image ref link which is used to create server + is same as the image id of the created server''' + self.assertEqual(get_server_info.image.id, self._parse_link_to_retrieve_id(image_self_link), + msg="The image does not match to the image mentioned during create") + + '''Verify that the flavor Id of the flavor ref link which is used to create server + is same as the flavor id of the created server''' + self.assertEqual(get_server_info.flavor.id, self._parse_link_to_retrieve_id(flavor_self_link), + msg="The flavor does not match to the flavor mentioned during create") + + self.servers_client.delete_server(active_server.id) + + def _parse_link_to_retrieve_id(self, ref): + temp = ref.rsplit('/') + #Return the last item, which is the image id + return temp[len(temp) - 1] + + @tags(type='positive', net='no') + def test_create_server_with_image_and_flavor_bookmark_link(self): + """Create a server using image and flavor bookmark links""" + name = rand_name("testserver") + image = self.images_client.get_image(self.image_ref) + image_bookmark_link = image.entity.links.links.get('bookmark') + flavor = self.flavors_client.get_flavor_details(self.flavor_ref) + flavor_bookmark_link = flavor.entity.links.links.get('bookmark') + create_server_response = self.servers_client.create_server(name, + image_bookmark_link, + flavor_bookmark_link) + created_server = create_server_response.entity + + '''Verify the parameters are correct in the initial response''' + self.assertTrue(created_server.id is not None, + msg="The server id was not set in the response") + self.assertTrue(created_server.admin_pass is not None, + msg="Admin password was not set in the response") + self.assertTrue(created_server.links is not None, + msg="Server links were not set in the response") + + '''Wait for the server to become active''' + active_server_response = self.server_behaviors.wait_for_server_status(created_server.id, + NovaServerStatusTypes.ACTIVE) + active_server = active_server_response.entity + get_server_info_response = self.servers_client.get_server(created_server.id) + get_server_info = get_server_info_response.entity + '''Verify that the correct image and flavor refs were used''' + self.assertEqual(get_server_info.image.id, self._parse_link_to_retrieve_id(image_bookmark_link), + msg="The image does not match to the image mentioned during create") + self.assertEqual(get_server_info.flavor.id, self._parse_link_to_retrieve_id(flavor_bookmark_link), + msg="The flavor does not match to the flavor mentioned during create") + + self.servers_client.delete_server(active_server.id) + + @tags(type='positive', net='no') + def test_update_server_using_server_self_link(self): + """Update a server using the server self link""" + name = rand_name("testserver") + stored_name = name + '''Create an active server''' + active_server_response = self.server_behaviors.create_active_server() + active_server = active_server_response.entity + '''Need to ensure there is atleast one second gap between creating and + updating a server. The test failed once without the sleep.''' + time.sleep(1) + '''Some processing''' + link = str(active_server.links.self) + link_list = link.split('/') + server_id = link_list[6] + '''Use server self link to update the server''' + updated_server_response = self.servers_client.update_server(server_id, + name, + accessIPv4=self.accessIPv4, + accessIPv6=self.accessIPv6) + updated_server = updated_server_response.entity + self.server_behaviors.wait_for_server_status(updated_server.id, NovaServerStatusTypes.ACTIVE) + + '''Verify the name and access ips of the server have changed''' + get_server_info = self.servers_client.get_server(updated_server.id) + self.assertEqual(stored_name, get_server_info.entity.name, + msg="The name was not updated") + self.assertEqual(self.accessIPv4, get_server_info.entity.accessIPv4, + msg="AccessIPv4 was not updated") + self.assertEqual(self.accessIPv6, get_server_info.entity.accessIPv6, + msg="AccessIPv6 was not updated") + self.assertEqual(active_server.created, get_server_info.entity.created, + msg="The creation date was updated") + self.assertTrue(active_server.updated != get_server_info.entity.updated, + msg="Server %s updated time did not change after a modification to the server." % updated_server.id) + + self.servers_client.delete_server(get_server_info.entity.id) + + @tags(type='positive', net='no') + def test_update_server_using_server_bookmark_link(self): + """Update a server using the server bookmark link""" + name = rand_name("testserver") + stored_name = name + # Create an active server + active_server_response = self.server_behaviors.create_active_server() + active_server = active_server_response.entity + '''Need to ensure there is atleast one second gap between creating + and updating a server. The test failed once without the sleep.''' + time.sleep(1) + #Some processing + link = str(active_server.links.bookmark) + link_list = link.split('/') + server_id = link_list[5] + '''Use server bookmark's link to update the server''' + updated_server_response = self.servers_client.update_server(server_id, + name, + accessIPv4=self.accessIPv4, + accessIPv6=self.accessIPv6) + updated_server = updated_server_response.entity + self.server_behaviors.wait_for_server_status(updated_server.id, NovaServerStatusTypes.ACTIVE) + + '''Verify the name and access ips of the server have changed''' + get_server_info = self.servers_client.get_server(updated_server.id) + self.assertEqual(stored_name, get_server_info.entity.name, + msg="The name was not updated") + self.assertEqual(self.accessIPv4, get_server_info.entity.accessIPv4, + msg="AccessIPv4 was not updated") + self.assertEqual(self.accessIPv6, get_server_info.entity.accessIPv6, + msg="AccessIPv6 was not updated") + self.assertEqual(active_server.created, get_server_info.entity.created, + msg="The creation date was updated") + self.assertTrue(active_server.updated != get_server_info.entity.updated, + msg="Server %s updated time did not change after a modification to the server." % updated_server.id) + + self.servers_client.delete_server(get_server_info.entity.id) diff --git a/test_repo/compute/functional/servers/test_servers_negative.py b/test_repo/compute/functional/servers/test_servers_negative.py new file mode 100644 index 00000000..81c8c82c --- /dev/null +++ b/test_repo/compute/functional/servers/test_servers_negative.py @@ -0,0 +1,163 @@ +""" +Copyright 2013 Rackspace + +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 cafe.drivers.unittest.decorators import tags +from cloudcafe.compute.common.exceptions import BadRequest, ItemNotFound +from cloudcafe.compute.common.datagen import rand_name +from test_repo.compute.fixtures import ComputeFixture + + +class ServersNegativeTest(ComputeFixture): + + @classmethod + def setUpClass(cls): + super(ServersNegativeTest, cls).setUpClass() + + + @classmethod + def tearDownClass(cls): + super(ServersNegativeTest, cls).tearDownClass() + + @tags(type='negative', net='no') + def test_server_name_blank(self): + with self.assertRaises(BadRequest): + self.servers_client.create_server('', self.image_ref, self.flavor_ref) + + @tags(type='negative', net='no') + def test_personality_file_contents_not_encoded(self): + """Server should not get created with a personality file whose content is not encoded""" + file_contents = 'This is a test file.' + personality = [{'path': '/etc/testfile.txt', + 'contents': file_contents}] + with self.assertRaises(BadRequest): + server = self.servers_client.create_server('blankfile', + self.image_ref, self.flavor_ref, + personality=personality) + + @tags(type='negative', net='no') + def test_invalid_ip_v4_access_address(self): + """Negative test: Server should not get created with invalid ipv4 address""" + accessIPv4 = '1.1.1.1.1.1' + name = rand_name("testserver") + with self.assertRaises(BadRequest): + server_response = self.servers_client.create_server(name, + self.image_ref, + self.flavor_ref, + accessIPv4=accessIPv4) + + @tags(type='negative', net='no') + def test_invalid_ip_v6_access_address(self): + """Negative test: Server should not get created with invalid ipv6 address""" + accessIPv6 = '2.2.2.2' + name = rand_name("testserver") + with self.assertRaises(BadRequest): + server_response = self.servers_client.create_server(name, + self.image_ref, + self.flavor_ref, + accessIPv6=accessIPv6) + + @tags(type='negative', net='no') + def test_server_metadata_item_nonexistent_server(self): + """Negative test: GET on nonexistent server should not succeed""" + with self.assertRaises(ItemNotFound): + self.servers_client.get_server_metadata_item(999, 'test2') + + @tags(type='negative', net='no') + def test_list_server_metadata_nonexistent_server(self): + """List metadata on a non existent server should not succeed""" + with self.assertRaises(ItemNotFound): + self.servers_client.list_server_metadata(999) + + @tags(type='negative', net='no') + def test_set_server_metadata_nonexistent_server(self): + """Set metadata on a non existent server should not succeed""" + meta = {'meta1': 'data1'} + + with self.assertRaises(ItemNotFound): + metadata_response = self.servers_client.set_server_metadata(999, meta) + + @tags(type='negative', net='no') + def test_update_server_metadata_nonexistent_server(self): + """An update should not happen for a non existent image""" + meta = {'key1': 'value1', 'key2': 'value2'} + with self.assertRaises(ItemNotFound): + self.servers_client.update_server_metadata(999, meta) + + @tags(type='negative', net='no') + def test_delete_server_metadata_item_nonexistent_server(self): + """Should not be able to delete metadata item from a non existent server""" + with self.assertRaises(ItemNotFound): + self.servers_client.delete_server_metadata_item(999, 'delkey') + + @tags(type='negative', net='no') + def test_create_server_with_unknown_flavor(self): + """Server creation with a flavor ID which does not exist, should not be allowed""" + with self.assertRaises(BadRequest): + self.servers_client.create_server('testserver', self.image_ref, 999) + + @tags(type='negative', net='no') + def test_create_server_with_unknown_image(self): + """Server creation with an image ID which does not exist,should not be allowed""" + with self.assertRaises(BadRequest): + self.servers_client.create_server('testserver', 999, self.flavor_ref) + + @tags(type='negative', net='no') + def test_get_nonexistent_server_fails(self): + """Making a get request for a server that does not exist should cause a 404""" + with self.assertRaises(ItemNotFound): + self.servers_client.get_server(999) + + @tags(type='negative', net='no') + def test_delete_nonexistent_server_fails(self): + """Making a delete request for a server that does not exist should cause a 404""" + with self.assertRaises(ItemNotFound): + self.servers_client.delete_server(999) + + @tags(type='negative', net='no') + def test_list_addresses_for_nonexistant_server_fails(self): + """Making a list address request for a server that does not exist should cause a 404""" + with self.assertRaises(ItemNotFound): + self.servers_client.list_addresses(999) + + @tags(type='negative', net='no') + def test_list_addresses_for_invalid_network_id_fails(self): + """Making a list address request for a server that does not exist should cause a 404""" + server_response = self.server_behaviors.create_active_server() + server = server_response.entity + with self.assertRaises(ItemNotFound): + self.servers_client.list_addresses_by_network(server.id, 999) + + @tags(type='negative', net='no') + def test_list_addresses_by_network_for_nonexistant_server_fails(self): + """Making a list address by network request for a server that does not exist should cause a 404""" + with self.assertRaises(ItemNotFound): + self.servers_client.list_addresses_by_network(999, 'public') + + @tags(type='negative', net='no') + def test_cannot_get_deleted_server(self): + """A 400 response should be returned when you get a server which is deleted""" + server = self.server_behaviors.create_active_server() + delete_resp = self.servers_client.delete_server(server.entity.id) + self.assertEqual(204, delete_resp.status_code) + self.server_behaviors.wait_for_server_to_be_deleted(server.entity.id) + with self.assertRaises(ItemNotFound): + self.servers_client.get_server(server.entity.id) + + @tags(type='negative', net='no') + def test_create_server_with_invalid_name(self): + """Server creation with a blank/invalid name should not be allowed""" + with self.assertRaises(BadRequest): + self.servers_client.create_server('', self.image_ref, self.flavor_ref) diff --git a/test_repo/compute/functional/test_links.py b/test_repo/compute/functional/test_links.py new file mode 100644 index 00000000..6a16b22d --- /dev/null +++ b/test_repo/compute/functional/test_links.py @@ -0,0 +1,118 @@ +""" +Copyright 2013 Rackspace + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import re +import unittest2 as unittest +from urlparse import urlparse + +from cafe.drivers.unittest.decorators import tags +from test_repo.compute.fixtures import ComputeFixture + + +class TestLinks(ComputeFixture): + + @classmethod + def setUpClass(cls): + super(TestLinks, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(TestLinks, cls).tearDownClass() + + @tags(type='positive', net='no') + def test_verify_server_self_link(self): + """Verify that a server self link has the correct format""" + self.server_resp = self.server_behaviors.create_active_server() + self.server_id = self.server_resp.entity.id + self.resources.add(self.server_id, self.servers_client.delete_server) + server_self_link = self.server_resp.entity.links.self + self.assertTrue(self._has_version(server_self_link)) + + get_server_resp = self.servers_client.get_server(server_self_link) + self.assertEqual(self.server_id, get_server_resp.entity.id) + + @tags(type='positive', net='no') + @unittest.skip('V1 Bug:D-03447') + def test_verify_server_bookmark_link(self): + """Verify that server bookmark link is a link with no version number""" + self.server_resp = self.server_behaviors.create_active_server() + self.server_id = self.server_resp.entity.id + self.resources.add(self.server_id, self.servers_client.delete_server) + server_bookmark_link = self.server_resp.entity.links.bookmark + + self.assertFalse(self._has_version(server_bookmark_link)) + + retrieved_server = self.servers_client.get_server(server_bookmark_link) + self.assertEqual(self.server_id, retrieved_server.entity.id) + + @tags(type='positive', net='no') + def test_verify_flavor_self_link(self): + """Verify that flavor self link is a full url with a version number""" + flavor_resp = self.flavors_client.get_flavor_details(self.flavor_ref) + flavor_self_link = flavor_resp.entity.links.self + + self.assertTrue(self._has_version(flavor_self_link)) + # Verify that the same flavor can be retrieved using the flavor self link + retrieved_flavor_resp = self.flavors_client.get_flavor_details(str(flavor_self_link)) + self.assertEqual(retrieved_flavor_resp.entity.id, flavor_resp.entity.id) + self.assertEqual(retrieved_flavor_resp.entity.ram, flavor_resp.entity.ram) + self.assertEqual(retrieved_flavor_resp.entity.swap, flavor_resp.entity.swap) + + @tags(type='positive', net='no') + @unittest.skip('V1 Bug:D-03447') + def test_verify_flavor_bookmark_link(self): + """Verify that flavor bookmark link is a permanent link with no version number""" + flavor_resp = self.flavors_client.get_flavor_details(self.flavor_ref) + flavor_bookmark_link = flavor_resp.entity.links.bookmark + + self.assertFalse(self._has_version(flavor_bookmark_link)) + retrieved_flavor_resp = self.flavors_client.get_flavor_details(flavor_bookmark_link) + self.assertEqual(retrieved_flavor_resp.entity.id, flavor_resp.entity.id) + self.assertEqual(retrieved_flavor_resp.entity.ram, flavor_resp.entity.ram) + self.assertEqual(retrieved_flavor_resp.entity.swap, flavor_resp.entity.swap) + + @tags(type='positive', net='no') + def test_image_self_link_during_get_image(self): + """Verify that image self link is a full url with a version number""" + image_resp = self.images_client.get_image(self.image_ref) + image_self_link = image_resp.entity.links.self + + self.assertTrue(self._has_version(image_self_link)) + + # Verify that the same image can be retrieved using the image self link + retrieved_image_with_self_link = self.images_client.get_image(image_self_link) + self.assertEqual(retrieved_image_with_self_link.entity.id, image_resp.entity.id) + + @tags(type='positive', net='no') + @unittest.skip('V1 Bug:D-03447') + def test_image_bookmark_link_during_get_image(self): + """Verify that image bookmark link is a permanent link with no version number""" + image_resp = self.images_client.get_image(self.image_ref) + image_bookmark_link = image_resp.entity.links.bookmark + + self.assertFalse(self._has_version(image_bookmark_link)) + + # Verify that the same image can be retrieved using the image bookmark link + retrieved_image_with_bookmark_link = self.images_client.get_image(image_bookmark_link) + self.assertEqual(retrieved_image_with_bookmark_link.entity.id, image_resp.entity.id) + + def _has_version(self, link): + return re.search('^/v+\d', urlparse(link).path) is not None + + def _parse_ref_to_retrieve_id(self, ref): + temp = ref.rsplit('/') + #Return the last item, which is the image id + return temp[len(temp) - 1] diff --git a/test_repo/identity/__init__.py b/test_repo/identity/__init__.py new file mode 100644 index 00000000..dd8b1e4e --- /dev/null +++ b/test_repo/identity/__init__.py @@ -0,0 +1,16 @@ +""" +Copyright 2013 Rackspace + +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. +""" + diff --git a/test_repo/identity/quickauthtest.py b/test_repo/identity/quickauthtest.py new file mode 100644 index 00000000..08fb6e60 --- /dev/null +++ b/test_repo/identity/quickauthtest.py @@ -0,0 +1,23 @@ +""" +Copyright 2013 Rackspace + +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 cloudcafe.identity.v2_0.tokens_api.provider import TokenAPI_Provider + +tokens_api = TokenAPI_Provider() +auth_token = tokens_api.behaviors.get_token_by_password() + +assert auth_token is not None, 'Auth token returned as None' +assert auth_token != '', 'Auth token returned empty'