Adds "*" support in openstack.yaml for conflicts

"*" can now be used in openstack.yaml roles conflicts section if
role conflicts with all others

Change-Id: I4628ac55946f1b8d58d708cde0b9f45b3a61eb29
Closes-Bug: #1466420
This commit is contained in:
Nick Bogdanov 2015-07-21 17:40:08 +03:00
parent 8995e80e80
commit 9b1083d135
5 changed files with 106 additions and 32 deletions

View File

@ -122,10 +122,15 @@ class NodeAssignmentValidator(AssignmentValidator):
@classmethod
def check_roles_for_conflicts(cls, roles, roles_metadata):
all_roles = set(roles_metadata.keys())
for role in roles:
if "conflicts" in roles_metadata[role]:
other_roles = roles - set([role])
conflicting_roles = set(roles_metadata[role]["conflicts"])
conflicting_roles = roles_metadata[role]["conflicts"]
if conflicting_roles == "*":
conflicting_roles = all_roles - set([role])
else:
conflicting_roles = set(conflicting_roles)
conflicting_roles &= other_roles
if conflicting_roles:
raise errors.InvalidData(

View File

@ -102,7 +102,7 @@ ROLE_META_INFO = {
"type": "string",
"description": "Short description of role functionality"},
"conflicts": {
"type": "array",
"type": ["array", "string"],
"description": "Specify which roles conflict this one."},
"has_primary": {
"type": "boolean",

View File

@ -14,6 +14,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import yaml
from oslo.serialization import jsonutils
from nailgun.db.sqlalchemy.models import NodeBondInterface
@ -23,6 +25,17 @@ from nailgun.utils import reverse
class TestAssignmentHandlers(BaseIntegrationTest):
def _assign_roles(self, assignment_data, expect_errors=False):
return self.app.post(
reverse(
'NodeAssignmentHandler',
kwargs={'cluster_id': self.cluster.id}
),
jsonutils.dumps(assignment_data),
headers=self.default_headers,
expect_errors=expect_errors
)
def test_assignment(self):
self.env.create(
cluster_kwargs={"api": True},
@ -33,7 +46,7 @@ class TestAssignmentHandlers(BaseIntegrationTest):
}
]
)
cluster = self.env.clusters[0]
self.cluster = self.env.clusters[0]
node = self.env.nodes[0]
assignment_data = [
{
@ -41,30 +54,15 @@ class TestAssignmentHandlers(BaseIntegrationTest):
"roles": ['controller']
}
]
resp = self.app.post(
reverse(
'NodeAssignmentHandler',
kwargs={'cluster_id': cluster.id}
),
jsonutils.dumps(assignment_data),
headers=self.default_headers
)
resp = self._assign_roles(assignment_data)
self.assertEqual(200, resp.status_code)
self.assertEqual(node.cluster, cluster)
self.assertEqual(node.cluster, self.cluster)
self.datadiff(
node.pending_roles,
assignment_data[0]["roles"]
)
resp = self.app.post(
reverse(
'NodeAssignmentHandler',
kwargs={'cluster_id': cluster.id}
),
jsonutils.dumps(assignment_data),
headers=self.default_headers,
expect_errors=True
)
resp = self._assign_roles(assignment_data, True)
self.assertEqual(400, resp.status_code)
def test_unassignment(self):
@ -160,9 +158,71 @@ class TestAssignmentHandlers(BaseIntegrationTest):
headers=self.default_headers,
expect_errors=True
)
self.assertEquals(404, resp.status_code)
def test_assign_conflicting_roles(self):
self.env.create(
cluster_kwargs={"api": True},
nodes_kwargs=[
{
"cluster_id": None,
"api": True
}
]
)
self.cluster = self.env.clusters[0]
node = self.env.nodes[0]
assignment_data = [
{
"id": node.id,
"roles": ['controller', 'compute']
}
]
resp = self._assign_roles(assignment_data, True)
self.assertEquals(400, resp.status_code)
def test_assign_conflicting_all_role(self):
ROLE = yaml.safe_load("""
name: test_role
meta:
name: "Some plugin role"
description: "Some description"
conflicts: "*"
volumes_roles_mapping:
- id: os
allocate_size: all
""")
release = self.env.create_release()
resp = self.env.create_role(release.id, ROLE)
self.env.create(
cluster_kwargs={
"api": True,
"release_id": release.id
},
nodes_kwargs=[
{
"cluster_id": None,
"api": True
}
]
)
self.cluster = self.env.clusters[0]
node = self.env.nodes[0]
assignment_data = [
{
"id": node.id,
"roles": ['controller', 'test_role']
}
]
resp = self._assign_roles(assignment_data, True)
self.assertEquals(400, resp.status_code, resp.body)
assignment_data[0]["roles"] = ['test_role']
resp = self._assign_roles(assignment_data)
self.assertEquals(200, resp.status_code, resp.body)
def test_add_node_with_cluster_network_template(self):
net_template = """
{

View File

@ -276,15 +276,24 @@ define([
model: models.Role,
processConflicts: function() {
this.each(function(role) {
role.conflicts = _.chain(role.conflicts)
.union(role.get('conflicts'))
.uniq()
.compact()
.value();
_.each(role.get('conflicts'), function(conflict) {
var conflictingRole = this.findWhere({name: conflict});
conflictingRole.conflicts = conflictingRole.conflicts || [];
conflictingRole.conflicts.push(role.get('name'));
var roleConflicts = role.get('conflicts'),
roleName = role.get('name');
if (roleConflicts == '*') {
role.conflicts = _.map(this.reject({name: roleName}), function(role) {
return role.get('name');
});
} else {
role.conflicts = _.chain(role.conflicts)
.union(roleConflicts)
.uniq()
.compact()
.value();
}
_.each(role.conflicts, function(conflictRoleName) {
var conflictingRole = this.findWhere({name: conflictRoleName});
conflictingRole.conflicts = _.uniq(_.union(conflictingRole.conflicts || [], [roleName]));
}, this);
}, this);
}

View File

@ -1331,7 +1331,7 @@ function($, _, i18n, Backbone, React, utils, models, dispatcher, controls, dialo
conflicts = _.chain(this.props.selectedRoles)
.union(this.props.indeterminateRoles)
.map(function(role) {
return roles.find({name: role}).get('conflicts');
return roles.find({name: role}).conflicts;
})
.flatten()
.uniq()