Allow to update roles for deployed nodes.
Closes-bug: #1641189 Change-Id: Ibe55b6011922f6a0c8cabfcaa1b4ced3ccd328be
This commit is contained in:
parent
5e6da80a44
commit
3622556a6a
|
@ -41,13 +41,13 @@ class NodeAssignmentHandler(BaseHandler):
|
|||
* 400 (invalid nodes data specified)
|
||||
* 404 (cluster/node not found in db)
|
||||
"""
|
||||
self.get_object_or_404(
|
||||
cluster = self.get_object_or_404(
|
||||
objects.Cluster,
|
||||
cluster_id
|
||||
)
|
||||
data = self.checked_data(
|
||||
self.validator.validate_collection_update,
|
||||
cluster_id=cluster_id
|
||||
cluster_id=cluster.id
|
||||
)
|
||||
nodes = self.get_objects_list_or_404(
|
||||
objects.NodeCollection,
|
||||
|
@ -55,9 +55,15 @@ class NodeAssignmentHandler(BaseHandler):
|
|||
)
|
||||
|
||||
for node in nodes:
|
||||
objects.Node.update(node, {"cluster_id": cluster_id,
|
||||
"pending_roles": data[node.id],
|
||||
"pending_addition": True})
|
||||
update = {"cluster_id": cluster.id, "pending_roles": data[node.id]}
|
||||
# NOTE(el): don't update pending_addition flag
|
||||
# if node is already assigned to the cluster
|
||||
# otherwise it would create problems for roles
|
||||
# update
|
||||
if not node.cluster:
|
||||
update["pending_addition"] = True
|
||||
objects.Node.update(node, update)
|
||||
|
||||
# fuel-client expects valid json for all put and post request
|
||||
raise self.http(200, None)
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
# 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 operator import attrgetter
|
||||
import sqlalchemy as sa
|
||||
|
||||
from nailgun.api.v1.validators.base import BasicValidator
|
||||
|
@ -30,9 +29,6 @@ from nailgun.utils.restrictions import RestrictionBase
|
|||
|
||||
class AssignmentValidator(BasicValidator):
|
||||
|
||||
predicate = None
|
||||
done_error_msg_template = None
|
||||
|
||||
@staticmethod
|
||||
def check_all_nodes(nodes, node_ids):
|
||||
not_found_node_ids = set(node_ids) - set(n.id for n in nodes)
|
||||
|
@ -44,24 +40,15 @@ class AssignmentValidator(BasicValidator):
|
|||
), log_message=True
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def check_if_already_done(cls, nodes):
|
||||
already_done_nodes = [n.id for n in nodes if cls.predicate(n)]
|
||||
already_done_nodes.sort()
|
||||
if already_done_nodes:
|
||||
raise errors.InvalidData(
|
||||
cls.done_error_msg_template
|
||||
.format(", ".join(map(str, already_done_nodes))),
|
||||
log_message=True
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def check_unique_hostnames(cls, nodes, cluster_id):
|
||||
hostnames = [node.hostname for node in nodes]
|
||||
node_ids = [node.id for node in nodes]
|
||||
conflicting_hostnames = [
|
||||
x[0] for x in
|
||||
db.query(
|
||||
Node.hostname).filter(sa.and_(
|
||||
~Node.id.in_(node_ids),
|
||||
Node.hostname.in_(hostnames),
|
||||
Node.cluster_id == cluster_id,
|
||||
)
|
||||
|
@ -76,11 +63,6 @@ class AssignmentValidator(BasicValidator):
|
|||
|
||||
class NodeAssignmentValidator(AssignmentValidator):
|
||||
|
||||
predicate = attrgetter('cluster')
|
||||
done_error_msg_template = "Nodes with ids {0} already assigned to " \
|
||||
"environments. Nodes must be unassigned " \
|
||||
"before they can be assigned again."
|
||||
|
||||
@classmethod
|
||||
def validate_collection_update(cls, data, cluster_id=None):
|
||||
data = cls.validate_json(data)
|
||||
|
@ -89,7 +71,6 @@ class NodeAssignmentValidator(AssignmentValidator):
|
|||
received_node_ids = dict_data.keys()
|
||||
nodes = db.query(Node).filter(Node.id.in_(received_node_ids))
|
||||
cls.check_all_nodes(nodes, received_node_ids)
|
||||
cls.check_if_already_done(nodes)
|
||||
cluster = objects.Cluster.get_by_uid(
|
||||
cluster_id, fail_if_not_found=True
|
||||
)
|
||||
|
@ -160,13 +141,6 @@ class NodeAssignmentValidator(AssignmentValidator):
|
|||
|
||||
class NodeUnassignmentValidator(AssignmentValidator):
|
||||
|
||||
done_error_msg_template = "Can't unassign nodes with ids {0} " \
|
||||
"if they not assigned."
|
||||
|
||||
@staticmethod
|
||||
def predicate(node):
|
||||
return not node.cluster or node.pending_deletion
|
||||
|
||||
@classmethod
|
||||
def validate_collection_update(cls, data, cluster_id=None):
|
||||
list_data = cls.validate_json(data)
|
||||
|
@ -190,5 +164,4 @@ class NodeUnassignmentValidator(AssignmentValidator):
|
|||
), cluster_id), log_message=True
|
||||
)
|
||||
cls.check_all_nodes(nodes, node_ids_set)
|
||||
cls.check_if_already_done(nodes)
|
||||
return nodes
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2016 Mirantis, Inc.
|
||||
#
|
||||
# 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 mock
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from nailgun import consts
|
||||
from nailgun import rpc
|
||||
from nailgun.test.base import BaseIntegrationTest
|
||||
from nailgun.test.base import fake_tasks
|
||||
from nailgun.utils import reverse
|
||||
|
||||
|
||||
class TestNodeAssignment(BaseIntegrationTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestNodeAssignment, self).setUp()
|
||||
self.env.create(
|
||||
api=False,
|
||||
nodes_kwargs=[
|
||||
{"name": "First",
|
||||
"pending_roles": ["controller"],
|
||||
"pending_addition": True},
|
||||
{"name": "Second",
|
||||
"pending_roles": ["compute"],
|
||||
"pending_addition": True}
|
||||
]
|
||||
)
|
||||
self.cluster = self.env.clusters[-1]
|
||||
|
||||
@fake_tasks(fake_rpc=False, mock_rpc=False)
|
||||
@mock.patch('nailgun.rpc.cast')
|
||||
def test_reassign_role_to_deployed_node(self, _):
|
||||
# First deploy
|
||||
task = self.env.launch_deployment(cluster_id=self.cluster.id)
|
||||
|
||||
for t in task.subtasks:
|
||||
rpc.receiver.NailgunReceiver().deploy_resp(
|
||||
task_uuid=t.uuid,
|
||||
status=consts.TASK_STATUSES.ready,
|
||||
progress=100,
|
||||
nodes=[{'uid': n.uid, 'status': consts.NODE_STATUSES.ready}
|
||||
for n in self.cluster.nodes])
|
||||
|
||||
# Update roles
|
||||
first_node = filter(
|
||||
lambda n: n.name == 'First', self.cluster.nodes)[0]
|
||||
second_node = filter(
|
||||
lambda n: n.name == 'Second', self.cluster.nodes)[0]
|
||||
|
||||
assignment_data = [{
|
||||
"id": first_node.id,
|
||||
"roles": ['controller', 'cinder']}]
|
||||
self.app.post(
|
||||
reverse('NodeAssignmentHandler',
|
||||
kwargs={'cluster_id': self.cluster.id}),
|
||||
jsonutils.dumps(assignment_data),
|
||||
headers=self.default_headers)
|
||||
|
||||
unassignment_data = [{"id": second_node.id}]
|
||||
self.app.post(
|
||||
reverse('NodeUnassignmentHandler',
|
||||
kwargs={'cluster_id': self.cluster.id}),
|
||||
jsonutils.dumps(unassignment_data),
|
||||
headers=self.default_headers)
|
||||
|
||||
# Second deploy
|
||||
# In case of problems will raise an error
|
||||
self.app.put(reverse(
|
||||
'DeploySelectedNodes', kwargs={'cluster_id': self.cluster.id}),
|
||||
'{}', headers=self.default_headers)
|
|
@ -62,9 +62,9 @@ class TestAssignmentHandlers(BaseIntegrationTest):
|
|||
node.pending_roles,
|
||||
assignment_data[0]["roles"]
|
||||
)
|
||||
|
||||
# NOTE(el): Role can be reassigned after initial assigment
|
||||
resp = self._assign_roles(assignment_data, True)
|
||||
self.assertEqual(400, resp.status_code)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
|
||||
def test_unassignment(self):
|
||||
cluster = self.env.create(
|
||||
|
@ -145,24 +145,6 @@ class TestAssignmentHandlers(BaseIntegrationTest):
|
|||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertEqual(node.pending_deletion, True)
|
||||
|
||||
def test_assigment_with_already_assigned_node(self):
|
||||
cluster = self.env.create_cluster(api=False)
|
||||
node = self.env.create_node(cluster_id=cluster.id)
|
||||
resp = self.app.post(
|
||||
reverse(
|
||||
'NodeAssignmentHandler',
|
||||
kwargs={'cluster_id': cluster.id}
|
||||
),
|
||||
jsonutils.dumps([{'id': node.id, 'roles': ['controller']}]),
|
||||
headers=self.default_headers,
|
||||
expect_errors=True
|
||||
)
|
||||
msg = 'Nodes with ids {} already assigned ' \
|
||||
'to environments. Nodes must be unassigned ' \
|
||||
'before they can be assigned again.'.format(node.id)
|
||||
self.assertEquals(400, resp.status_code)
|
||||
self.assertEquals(msg, resp.json_body["message"])
|
||||
|
||||
def test_assigment_with_invalid_cluster(self):
|
||||
node = self.env.create_node(api=False)
|
||||
|
||||
|
|
Loading…
Reference in New Issue