Merge "add tests for show and update of Flavor Extra Spec API extension"
diff --git a/tempest/api/compute/admin/test_flavors.py b/tempest/api/compute/admin/test_flavors.py
index 75b8dad..05bb457 100644
--- a/tempest/api/compute/admin/test_flavors.py
+++ b/tempest/api/compute/admin/test_flavors.py
@@ -80,13 +80,7 @@
self.assertEqual(flavor['rxtx_factor'], self.rxtx)
self.assertEqual(flavor['OS-FLV-EXT-DATA:ephemeral'],
self.ephemeral)
- if self._interface == "xml":
- XMLNS_OS_FLV_ACCESS = "http://docs.openstack.org/compute/ext/"\
- "flavor_access/api/v2"
- key = "{" + XMLNS_OS_FLV_ACCESS + "}is_public"
- self.assertEqual(flavor[key], "True")
- if self._interface == "json":
- self.assertEqual(flavor['os-flavor-access:is_public'], True)
+ self.assertEqual(flavor['os-flavor-access:is_public'], True)
# Verify flavor is retrieved
resp, flavor = self.client.get_flavor_details(new_flavor_id)
@@ -156,6 +150,14 @@
def test_create_list_flavor_without_extra_data(self):
# Create a flavor and ensure it is listed
# This operation requires the user to have 'admin' role
+
+ def verify_flavor_response_extension(flavor):
+ # check some extensions for the flavor create/show/detail response
+ self.assertEqual(flavor['swap'], '')
+ self.assertEqual(int(flavor['rxtx_factor']), 1)
+ self.assertEqual(int(flavor['OS-FLV-EXT-DATA:ephemeral']), 0)
+ self.assertEqual(flavor['os-flavor-access:is_public'], True)
+
flavor_name = rand_name(self.flavor_name_prefix)
new_flavor_id = rand_int_id(start=1000)
@@ -171,26 +173,20 @@
self.assertEqual(flavor['vcpus'], self.vcpus)
self.assertEqual(flavor['disk'], self.disk)
self.assertEqual(int(flavor['id']), new_flavor_id)
- self.assertEqual(flavor['swap'], '')
- self.assertEqual(int(flavor['rxtx_factor']), 1)
- self.assertEqual(int(flavor['OS-FLV-EXT-DATA:ephemeral']), 0)
- if self._interface == "xml":
- XMLNS_OS_FLV_ACCESS = "http://docs.openstack.org/compute/ext/"\
- "flavor_access/api/v2"
- key = "{" + XMLNS_OS_FLV_ACCESS + "}is_public"
- self.assertEqual(flavor[key], "True")
- if self._interface == "json":
- self.assertEqual(flavor['os-flavor-access:is_public'], True)
+ verify_flavor_response_extension(flavor)
# Verify flavor is retrieved
resp, flavor = self.client.get_flavor_details(new_flavor_id)
self.assertEqual(resp.status, 200)
self.assertEqual(flavor['name'], flavor_name)
+ verify_flavor_response_extension(flavor)
+
# Check if flavor is present in list
- resp, flavors = self.client.list_flavors_with_detail()
+ resp, flavors = self.user_client.list_flavors_with_detail()
self.assertEqual(resp.status, 200)
for flavor in flavors:
if flavor['name'] == flavor_name:
+ verify_flavor_response_extension(flavor)
flag = True
self.assertTrue(flag)
diff --git a/tempest/api/compute/admin/test_flavors_extra_specs.py b/tempest/api/compute/admin/test_flavors_extra_specs.py
index 0fd4d11..403a946 100644
--- a/tempest/api/compute/admin/test_flavors_extra_specs.py
+++ b/tempest/api/compute/admin/test_flavors_extra_specs.py
@@ -19,7 +19,6 @@
from tempest.api.compute import base
from tempest.common.utils.data_utils import rand_int_id
from tempest.common.utils.data_utils import rand_name
-from tempest import exceptions
from tempest.test import attr
@@ -64,9 +63,9 @@
super(FlavorsExtraSpecsTestJSON, cls).tearDownClass()
@attr(type='gate')
- def test_flavor_set_get_unset_keys(self):
- # Test to SET, GET UNSET flavor extra spec as a user
- # with admin privileges.
+ def test_flavor_set_get_update_show_unset_keys(self):
+ # Test to SET, GET, UPDATE, SHOW, UNSET flavor extra
+ # spec as a user with admin privileges.
# Assigning extra specs values that are to be set
specs = {"key1": "value1", "key2": "value2"}
# SET extra specs to the flavor created in setUp
@@ -79,55 +78,55 @@
self.client.get_flavor_extra_spec(self.flavor['id'])
self.assertEqual(get_resp.status, 200)
self.assertEqual(get_body, specs)
+
+ # UPDATE the value of the extra specs key1
+ update_resp, update_body = \
+ self.client.update_flavor_extra_spec(self.flavor['id'],
+ "key1",
+ key1="value")
+ self.assertEqual(update_resp.status, 200)
+ self.assertEqual({"key1": "value"}, update_body)
+
# GET a key value and verify
- get_resp, get_body = \
+ show_resp, get_body = \
self.client.get_flavor_extra_spec_with_key(self.flavor['id'],
- "key2")
+ "key1")
+ self.assertEqual(show_resp.status, 200)
+ self.assertEqual(get_body, 'value')
+
+ # GET extra specs and verify the value of the key2
+ # is the same as before
+ get_resp, get_body = \
+ self.client.get_flavor_extra_spec(self.flavor['id'])
self.assertEqual(get_resp.status, 200)
- self.assertEqual(get_body, specs['key2'])
+ self.assertEqual(get_body, {"key1": "value", "key2": "value2"})
+
# UNSET extra specs that were set in this test
unset_resp, _ = \
self.client.unset_flavor_extra_spec(self.flavor['id'], "key1")
self.assertEqual(unset_resp.status, 200)
-
- @attr(type=['negative', 'gate'])
- def test_flavor_non_admin_set_keys(self):
- # Test to SET flavor extra spec as a user without admin privileges.
- specs = {"key1": "value1", "key2": "value2"}
- self.assertRaises(exceptions.Unauthorized,
- self.flavors_client.set_flavor_extra_spec,
- self.flavor['id'],
- specs)
+ unset_resp, _ = \
+ self.client.unset_flavor_extra_spec(self.flavor['id'], "key2")
+ self.assertEqual(unset_resp.status, 200)
@attr(type='gate')
- def test_flavor_non_admin_get_keys(self):
+ def test_flavor_non_admin_get_all_keys_and_specified_key(self):
specs = {"key1": "value1", "key2": "value2"}
set_resp, set_body = self.client.set_flavor_extra_spec(
self.flavor['id'], specs)
resp, body = self.flavors_client.get_flavor_extra_spec(
self.flavor['id'])
self.assertEqual(resp.status, 200)
+
for key in specs:
self.assertEqual(body[key], specs[key])
- @attr(type=['negative', 'gate'])
- def test_flavor_non_admin_unset_keys(self):
- specs = {"key1": "value1", "key2": "value2"}
- set_resp, set_body = self.client.set_flavor_extra_spec(
- self.flavor['id'], specs)
-
- self.assertRaises(exceptions.Unauthorized,
- self.flavors_client.unset_flavor_extra_spec,
- self.flavor['id'],
- 'key1')
-
- @attr(type=['negative', 'gate'])
- def test_flavor_unset_nonexistent_key(self):
- nonexistent_key = rand_name('flavor_key')
- self.assertRaises(exceptions.NotFound,
- self.client.unset_flavor_extra_spec,
- self.flavor['id'],
- nonexistent_key)
+ get_resp, get_body = \
+ self.flavors_client.get_flavor_extra_spec_with_key(
+ self.flavor['id'],
+ "key1")
+ self.assertEqual(get_resp.status, 200)
+ self.assertEqual("value1", get_body)
class FlavorsExtraSpecsTestXML(FlavorsExtraSpecsTestJSON):
diff --git a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
new file mode 100644
index 0000000..8d62a2a
--- /dev/null
+++ b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
@@ -0,0 +1,118 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+# Copyright 2013 IBM Corp.
+#
+# 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 tempest.api import compute
+from tempest.api.compute import base
+from tempest.common.utils.data_utils import rand_int_id
+from tempest.common.utils.data_utils import rand_name
+from tempest import exceptions
+from tempest.test import attr
+
+
+class FlavorsExtraSpecsNegativeTestJSON(base.BaseV2ComputeAdminTest):
+ """the Negative tests for FlavorsExtraSpecs."""
+
+ _interface = 'json'
+
+ @classmethod
+ def setUpClass(cls):
+ super(FlavorsExtraSpecsNegativeTestJSON, cls).setUpClass()
+ if not compute.FLAVOR_EXTRA_DATA_ENABLED:
+ msg = "FlavorExtraData extension not enabled."
+ raise cls.skipException(msg)
+
+ cls.client = cls.os_adm.flavors_client
+ flavor_name = rand_name('test_flavor')
+ ram = 512
+ vcpus = 1
+ disk = 10
+ ephemeral = 10
+ cls.new_flavor_id = rand_int_id(start=1000)
+ swap = 1024
+ rxtx = 1
+ # Create a flavor
+ resp, cls.flavor = cls.client.create_flavor(flavor_name,
+ ram, vcpus,
+ disk,
+ cls.new_flavor_id,
+ ephemeral=ephemeral,
+ swap=swap, rxtx=rxtx)
+
+ @classmethod
+ def tearDownClass(cls):
+ resp, body = cls.client.delete_flavor(cls.flavor['id'])
+ cls.client.wait_for_resource_deletion(cls.flavor['id'])
+ super(FlavorsExtraSpecsNegativeTestJSON, cls).tearDownClass()
+
+ @attr(type=['negative', 'gate'])
+ def test_flavor_non_admin_set_keys(self):
+ # Test to SET flavor extra spec as a user without admin privileges.
+ specs = {"key1": "value1", "key2": "value2"}
+ self.assertRaises(exceptions.Unauthorized,
+ self.flavors_client.set_flavor_extra_spec,
+ self.flavor['id'],
+ specs)
+
+ @attr(type=['negative', 'gate'])
+ def test_flavor_non_admin_unset_keys(self):
+ specs = {"key1": "value1", "key2": "value2"}
+ set_resp, set_body = self.client.set_flavor_extra_spec(
+ self.flavor['id'], specs)
+
+ self.assertRaises(exceptions.Unauthorized,
+ self.flavors_client.unset_flavor_extra_spec,
+ self.flavor['id'],
+ 'key1')
+
+ @attr(type=['negative', 'gate'])
+ def test_flavor_unset_nonexistent_key(self):
+ nonexistent_key = rand_name('flavor_key')
+ self.assertRaises(exceptions.NotFound,
+ self.client.unset_flavor_extra_spec,
+ self.flavor['id'],
+ nonexistent_key)
+
+ @attr(type=['negative', 'gate'])
+ def test_flavor_get_nonexistent_key(self):
+ self.assertRaises(exceptions.NotFound,
+ self.flavors_client.get_flavor_extra_spec_with_key,
+ self.flavor['id'],
+ "nonexistent_key")
+
+ @attr(type=['negative', 'gate'])
+ def test_flavor_update_mismatch_key(self):
+ # the key will be updated should be match the key in the body
+ self.assertRaises(exceptions.BadRequest,
+ self.client.update_flavor_extra_spec,
+ self.flavor['id'],
+ "key2",
+ key1="value")
+
+ @attr(type=['negative', 'gate'])
+ def test_flavor_update_more_key(self):
+ # there should be just one item in the request body
+ self.assertRaises(exceptions.BadRequest,
+ self.client.update_flavor_extra_spec,
+ self.flavor['id'],
+ "key1",
+ key1="value",
+ key2="value")
+
+
+class FlavorsExtraSpecsNegativeTestXML(FlavorsExtraSpecsNegativeTestJSON):
+ _interface = 'xml'
diff --git a/tempest/services/compute/json/flavors_client.py b/tempest/services/compute/json/flavors_client.py
index dc05e3e..305a77b 100644
--- a/tempest/services/compute/json/flavors_client.py
+++ b/tempest/services/compute/json/flavors_client.py
@@ -109,6 +109,14 @@
body = json.loads(body)
return resp, body[key]
+ def update_flavor_extra_spec(self, flavor_id, key, **kwargs):
+ """Gets specified extra Specs details of the mentioned flavor."""
+ resp, body = self.put('flavors/%s/os-extra_specs/%s' %
+ (flavor_id, key),
+ json.dumps(kwargs), self.headers)
+ body = json.loads(body)
+ return resp, body
+
def unset_flavor_extra_spec(self, flavor_id, key):
"""Unsets extra Specs from the mentioned flavor."""
return self.delete('flavors/%s/os-extra_specs/%s' % (str(flavor_id),
diff --git a/tempest/services/compute/xml/flavors_client.py b/tempest/services/compute/xml/flavors_client.py
index c7ed044..12e24d0 100644
--- a/tempest/services/compute/xml/flavors_client.py
+++ b/tempest/services/compute/xml/flavors_client.py
@@ -22,6 +22,7 @@
from tempest.common.rest_client import RestClientXML
from tempest.services.compute.xml.common import Document
from tempest.services.compute.xml.common import Element
+from tempest.services.compute.xml.common import Text
from tempest.services.compute.xml.common import xml_to_json
from tempest.services.compute.xml.common import XMLNS_11
@@ -29,7 +30,7 @@
XMLNS_OS_FLV_EXT_DATA = \
"http://docs.openstack.org/compute/ext/flavor_extra_data/api/v1.1"
XMLNS_OS_FLV_ACCESS = \
- "http://docs.openstack.org/compute/ext/flavor_access/api/v1.1"
+ "http://docs.openstack.org/compute/ext/flavor_access/api/v2"
class FlavorsClientXML(RestClientXML):
@@ -49,6 +50,10 @@
if k == '{%s}ephemeral' % XMLNS_OS_FLV_EXT_DATA:
k = 'OS-FLV-EXT-DATA:ephemeral'
+ if k == '{%s}is_public' % XMLNS_OS_FLV_ACCESS:
+ k = 'os-flavor-access:is_public'
+ v = True if v == 'True' else False
+
if k == 'extra_specs':
k = 'OS-FLV-WITH-EXT-SPECS:extra_specs'
flavor[k] = dict(v)
@@ -155,6 +160,21 @@
body = xml_to_json(etree.fromstring(body))
return resp, body
+ def update_flavor_extra_spec(self, flavor_id, key, **kwargs):
+ """Gets specified extra Specs details of the mentioned flavor."""
+ doc = Document()
+ for (k, v) in kwargs.items():
+ element = Element(k)
+ doc.append(element)
+ value = Text(v)
+ element.append(value)
+
+ resp, body = self.put('flavors/%s/os-extra_specs/%s' %
+ (flavor_id, key),
+ str(doc), self.headers)
+ body = xml_to_json(etree.fromstring(body))
+ return resp, {key: body}
+
def unset_flavor_extra_spec(self, flavor_id, key):
"""Unsets an extra spec based on the mentioned flavor and key."""
return self.delete('flavors/%s/os-extra_specs/%s' % (str(flavor_id),