Merge "Create duplex relations for component"

This commit is contained in:
Jenkins 2016-02-01 09:21:26 +00:00 committed by Gerrit Code Review
commit 294a2998e5
5 changed files with 106 additions and 35 deletions

View File

@ -18,7 +18,9 @@
Release object and collection
"""
import copy
from distutils.version import StrictVersion
import itertools
import yaml
from nailgun import consts
@ -160,12 +162,48 @@ class Release(NailgunObject):
def get_all_components(cls, instance):
"""Get all components related to release
Due to components architecture compatible/incompatible are duplex
relations. So if some component is compatible/incompatible with another
the last one also should have such relation.
:param instance: Release instance
:type instance: Release DB instance
:returns: list -- list of all components
"""
plugin_components = PluginManager.get_components_metadata(instance)
return instance.components_metadata + plugin_components
components = copy.deepcopy(
instance.components_metadata + plugin_components)
# we should provide commutative property for compatible/incompatible
# relations between components
for comp_i, comp_j in itertools.permutations(components, 2):
if cls._check_relation(comp_j, comp_i, 'incompatible'):
comp_i.setdefault('incompatible', []).append({
'name': comp_j['name'],
'message': "Not compatible with {0}".format(
comp_j.get('label') or comp_j.get('name'))})
if cls._check_relation(comp_j, comp_i, 'compatible'):
comp_i.setdefault('compatible', []).append({
'name': comp_j['name']})
return components
@classmethod
def _check_relation(cls, a, b, relation):
"""Helper function to check commutative property for relations"""
return (cls._contain(a.get(relation, []), b['name']) and not
cls._contain(b.get(relation, []), a['name']))
@staticmethod
def _contain(components, name):
"""Check if component with given name exists in components list
:param components: list of components objects(dicts)
:type components: list
:param name: component name or wildcard
:type name: string
"""
prefixes = (comp['name'].split('*', 1)[0] for comp in components)
return any(name.startswith(x) for x in prefixes)
class ReleaseCollection(NailgunCollection):

View File

@ -809,12 +809,12 @@ class EnvironmentManager(object):
{
'name': 'hypervisor:test_hypervisor',
'compatible': [
{'name': 'hypervisors:*'},
{'name': 'storages:object:block:swift'}
{'name': 'hypervisor:*'},
{'name': 'storage:object:block:swift'}
],
'incompatible': [
{'name': 'networks:*'},
{'name': 'additional_services:*'}
{'name': 'network:*'},
{'name': 'additional_service:*'}
]
}
]

View File

@ -361,8 +361,8 @@ class TestClusterComponents(BaseIntegrationTest):
self.assertEqual(resp.status_code, 400)
self.assertEqual(
u"Incompatible components were found: "
"'network:core:test_network_1' incompatible with "
"[u'hypervisor:test_hypervisor'].",
"'hypervisor:test_hypervisor' incompatible with "
"[u'network:core:test_network_1'].",
resp.json_body['message']
)

View File

@ -50,23 +50,24 @@ class TestComponentHandler(base.BaseIntegrationTest):
headers=self.default_headers
)
self.assertEqual(200, resp.status_code)
self.assertEqual(resp.json_body, [
self.assertItemsEqual(resp.json_body, [
{
'name': 'hypervisor:test_component_1',
'compatible': [
{'name': 'hypervisors:*'},
{'name': 'storages:object:block:swift'}],
{'name': 'hypervisor:*'},
{'name': 'storage:object:block:swift'},
{'name': 'storage:test_component_2'}],
'incompatible': [
{'name': 'networks:*'},
{'name': 'additional_services:*'}]},
{'name': 'network:*'},
{'name': 'additional_service:*'}]},
{
'name': 'storage:test_component_2',
'compatible': [
{'name': 'hypervisors:*'},
{'name': 'storages:object:block:swift'}],
{'name': 'hypervisor:*'},
{'name': 'storage:object:block:swift'}],
'incompatible': [
{'name': 'networks:*'},
{'name': 'additional_services:*'}]}])
{'name': 'network:*'},
{'name': 'additional_service:*'}]}])
def test_404_for_get_components_with_none_release_id(self):
resp = self.app.get(

View File

@ -1640,29 +1640,61 @@ class TestRelease(BaseTestCase):
'os': 'ubuntu',
'mode': ['ha'],
'deployment_scripts_path': 'deployment_scripts/'}],
components_metadata=self.env.get_default_components(
name='storage:test_component_2')
components_metadata=[dict(
name='storage:test_component_2',
label='Test storage',
incompatible=[{
'name': 'hypervisor:test_component_1',
'message': 'component_2 not compatible with component_1'}]
), dict(
name='network:test_component_3',
label='Test network',
compatible=[{
'name': 'storage:test_component_2'}])]
)
components = objects.Release.get_all_components(release)
self.assertListEqual(components, [
{
'name': 'hypervisor:test_component_1',
'compatible': [
{'name': 'hypervisors:*'},
{'name': 'storages:object:block:swift'}],
'incompatible': [
{'name': 'networks:*'},
{'name': 'additional_services:*'}]},
{
'name': 'storage:test_component_2',
'compatible': [
{'name': 'hypervisors:*'},
{'name': 'storages:object:block:swift'}],
'incompatible': [
{'name': 'networks:*'},
{'name': 'additional_services:*'}]}])
self.assertItemsEqual(components, [{
'name': 'hypervisor:test_component_1',
'compatible': [
{'name': 'hypervisor:*'},
{'name': 'storage:object:block:swift'}],
'incompatible': [
{'name': 'network:*'},
{'name': 'additional_service:*'},
{'name': 'storage:test_component_2',
'message': 'Not compatible with Test storage'}]}, {
'name': 'storage:test_component_2',
'label': 'Test storage',
'compatible': [
{'name': 'network:test_component_3'}],
'incompatible': [
{'name': 'hypervisor:test_component_1',
'message': 'component_2 not compatible with component_1'}]}, {
'name': 'network:test_component_3',
'label': 'Test network',
'compatible': [
{'name': 'storage:test_component_2'}],
'incompatible': [
{'name': 'hypervisor:test_component_1',
'message': 'Not compatible with hypervisor:test_component_1'}]
}])
def test_contain_component(self):
components = [
{'name': 'test_component_type_1:test_component_1'},
{'name': 'test_component_type_2:*'}
]
self.assertTrue(objects.Release._contain(
components, 'test_component_type_1:test_component_1'))
self.assertTrue(objects.Release._contain(
components, 'test_component_type_2:test_component_3'))
self.assertFalse(objects.Release._contain(
components, 'test_component_type_1:test_component_4'))
class TestOpenstackConfig(BaseTestCase):