Merge "Add dummy API definitions for extension subresources"

This commit is contained in:
Jenkins
2017-01-12 21:57:17 +00:00
committed by Gerrit Code Review
6 changed files with 98 additions and 30 deletions

View File

@@ -12,6 +12,10 @@
# License for the specific language governing permissions and limitations
# under the License.
# A declaration of a dummy neutron extension: this may have a number of
# constants being defined, and their aim is to document as much about
# the extension as possible.
# The alias of the extension.
ALIAS = 'dummy'
@@ -20,38 +24,47 @@ ALIAS = 'dummy'
LABEL = 'DUMMY'
# Whether or not this extension is simply signaling behavior to the user
# or it actively modifies the attribute map.
# or it actively modifies the attribute map (mandatory).
IS_SHIM_EXTENSION = False
# Whether the extension is marking the adoption of standardattr model for
# legacy resources, or introducing new standardattr attributes. False or
# None if the standardattr model is adopted since the introduction of
# resource extension.
# resource extension (mandatory).
# If this is True, the alias for the extension should be prefixed with
# 'standard-attr-'.
IS_STANDARD_ATTR_EXTENSION = False
# The name of the extension.
# The name of the extension (mandatory).
NAME = 'Foo Extension'
# A prefix for API resources. An empty prefix means that the API is going
# to be exposed at the v2/ level as any other core resource.
# to be exposed at the v2/ level as any other core resource (mandatory).
API_PREFIX = ''
# The description of the extension.
# The description of the extension (mandatory).
DESCRIPTION = "Provides support for foo"
# A timestamp of when the extension was introduced.
# A timestamp of when the extension was introduced (mandatory).
UPDATED_TIMESTAMP = "2000-00-01T00:00:00-00:00"
# The name of the resource.
# The specific resources and/or attributes for the extension (optional).
# In case of simple extensions, with single resource, the string constants
# RESOURCE_NAME and COLLECTION_NAME can be used, otherwise string literals
# can be used instead.
# The name of the resource introduced or being extended
# (in case it is defined by another extension, or it is
# a core resource).
RESOURCE_NAME = 'foo'
# The plural for the resource.
# The plural for the resource introduced or being extended
# (in case it is defined by another extension, or it is a
# core resource).
COLLECTION_NAME = 'fooes'
# The resource attribute map for the extension. It is effectively the
# bulk of the API contract alongside ACTION_MAP
# bulk of the API contract alongside ACTION_MAP (mandatory).
RESOURCE_ATTRIBUTE_MAP = {
COLLECTION_NAME: {
'id': {'allow_post': False, 'allow_put': False,
@@ -60,8 +73,26 @@ RESOURCE_ATTRIBUTE_MAP = {
}
}
# The subresource attribute map for the extension. It adds child resources
# to main extension's resource. The subresource map must have a parent and
# a parameters entry. If an extension does not need such a map, None can
# be specified (mandatory). For example:
SUB_RESOURCE_ATTRIBUTE_MAP = {
'subfoo': {
'parent': {
'collection_name': COLLECTION_NAME,
'member_name': RESOURCE_NAME},
'parameters': {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True,
'primary_key': True},
},
},
}
# The action map: it associates verbs with methods to be performed on
# the API resource. For example:
# the API resource (mandatory). For example:
#
# ACTION_MAP = {
# RESOURCE_NAME: {
@@ -73,11 +104,11 @@ RESOURCE_ATTRIBUTE_MAP = {
ACTION_MAP = {
}
# The list of required extensions.
# The list of required extensions (mandatory).
REQUIRED_EXTENSIONS = [
]
# The list of optional extensions.
# The list of optional extensions (mandatory).
OPTIONAL_EXTENSIONS = [
]

View File

@@ -77,6 +77,9 @@ RESOURCE_ATTRIBUTE_MAP = {
},
}
# The subresource attribute map for the extension.
SUB_RESOURCE_ATTRIBUTE_MAP = None
# The action map.
ACTION_MAP = {
TRUNK: {

View File

@@ -40,18 +40,27 @@ DESCRIPTION = "Expose trunk port details"
# A timestamp of when the extension was introduced.
TIMESTAMP = "2016-01-01T10:00:00-00:00"
# The name of the resource introduced or being extended.
RESOURCE_NAME = 'port'
# The plural for the resource introduced or being extended.
COLLECTION_NAME = 'ports'
# The specific resources and/or attributes for the extension (optional).
TRUNK_DETAILS = 'trunk_details'
# The resource attribute map for the extension.
RESOURCE_ATTRIBUTE_MAP = {
'ports': {TRUNK_DETAILS: {'allow_post': False, 'allow_put': False,
'default': constants.ATTR_NOT_SPECIFIED,
'is_visible': True,
'enforce_policy': True,
'required_by_policy': True}},
COLLECTION_NAME: {TRUNK_DETAILS: {'allow_post': False, 'allow_put': False,
'default': constants.ATTR_NOT_SPECIFIED,
'is_visible': True,
'enforce_policy': True,
'required_by_policy': True}},
}
# The subresource attribute map for the extension.
SUB_RESOURCE_ATTRIBUTE_MAP = None
# The action map.
ACTION_MAP = None

View File

@@ -60,6 +60,7 @@ class DefinitionBaseTestCase(test_base.BaseTestCase):
extension_module = None
extension_resources = ()
extension_subresources = ()
extension_attributes = ()
def setUp(self):
@@ -73,6 +74,7 @@ class DefinitionBaseTestCase(test_base.BaseTestCase):
self.name = self.extension_module.NAME
self.description = self.extension_module.DESCRIPTION
self.resource_map = self.extension_module.RESOURCE_ATTRIBUTE_MAP
self.subresource_map = self.extension_module.SUB_RESOURCE_ATTRIBUTE_MAP
self.action_map = self.extension_module.ACTION_MAP
self.required_extensions = self.extension_module.REQUIRED_EXTENSIONS
self.optional_extensions = self.extension_module.OPTIONAL_EXTENSIONS
@@ -100,19 +102,40 @@ class DefinitionBaseTestCase(test_base.BaseTestCase):
self.assertIn(
resource, base.KNOWN_RESOURCES + self.extension_resources,
'Resource is unknown, check for typos.')
for attribute in self.resource_map[resource].keys():
self.assertIn(
attribute,
base.KNOWN_ATTRIBUTES + self.extension_attributes,
'Attribute is unknown, check for typos.')
for keyword in self.resource_map[resource][attribute]:
self.assertIn(keyword, base.KNOWN_KEYWORDS,
'Keyword is unknown, check for typos.')
value = self.resource_map[resource][attribute][keyword]
assert_f = ASSERT_FUNCTIONS[keyword]
assert_f(self, attribute,
self.resource_map[resource][attribute],
keyword, value)
self.assertParams(self.resource_map[resource])
def assertParams(self, resource):
for attribute in resource.keys():
self.assertIn(
attribute,
base.KNOWN_ATTRIBUTES + self.extension_attributes,
'Attribute is unknown, check for typos.')
for keyword in resource[attribute]:
self.assertIn(keyword, base.KNOWN_KEYWORDS,
'Keyword is unknown, check for typos.')
value = resource[attribute][keyword]
assert_f = ASSERT_FUNCTIONS[keyword]
assert_f(self, attribute,
resource[attribute],
keyword, value)
def test_subresource_map(self):
if not self.subresource_map:
self.skipTest('API extension has no subresource map.')
for subresource in self.subresource_map:
self.assertIn(
subresource, self.extension_subresources,
'Sub-resource is unknown, check for typos.')
for attribute in self.subresource_map[subresource]:
self.assertIn(attribute, ('parent', 'parameters'))
self.assertIn(
self.subresource_map[subresource]['parent']['collection_name'],
base.KNOWN_RESOURCES + self.extension_resources,
'Sub-resource parent is unknown, check for typos.')
self.assertIn('member_name',
self.subresource_map[subresource]['parent'],
'Incorrect parent definition, check for typos.')
self.assertParams(self.subresource_map[subresource]['parameters'])
def test_action_map(self):
if not self.action_map:

View File

@@ -17,3 +17,4 @@ from neutron_lib.tests.unit.api.definitions import base
class _DummyDefinitionTestCase(base.DefinitionBaseTestCase):
extension_module = _dummy
extension_resources = (_dummy.COLLECTION_NAME,)
extension_subresources = ('subfoo',)

View File

@@ -16,4 +16,5 @@ from neutron_lib.tests.unit.api.definitions import base
class TrunkDetailsDefinitionTestCase(base.DefinitionBaseTestCase):
extension_module = trunk_details
extension_resource = (trunk_details.COLLECTION_NAME,)
extension_attributes = (trunk_details.TRUNK_DETAILS,)