Add handler for copying of VIPs

New handler triggers copying of VIPs from given original cluster to new
one. The reason for separation of this action from copying of network
configuration is that accordion to [1] copying of existing VIPs/allocation
of new ones will not take effect for new cluster unless it has assigned
nodes. Thus in context of cluster upgrade procedure VIP transfer must be
done after node reassignment, and as long as nodes are being operated on
one by one it would be not efficient to call VIP copying method after
each such reassignment.

Tests updated accordingly.

[1]: https://review.openstack.org/#/c/284841/

Change-Id: I33670e8f2561be6fe18cec75bfc7ecc056ae2f6b
Closes-Bug: #1552744
This commit is contained in:
Artem Roma 2016-03-01 15:38:37 +02:00 committed by Nikita Zubkov
parent ecec1f6d3c
commit c0876c580e
8 changed files with 188 additions and 28 deletions

View File

@ -31,6 +31,8 @@ class ClusterUpgradeExtension(extensions.BaseExtension):
'handler': handlers.ClusterUpgradeCloneHandler},
{'uri': r'/clusters/(?P<cluster_id>\d+)/upgrade/assign/?$',
'handler': handlers.NodeReassignHandler},
{'uri': r'/clusters/(?P<cluster_id>\d+)/upgrade/vips/?$',
'handler': handlers.CopyVIPsHandler},
]
@classmethod

View File

@ -101,3 +101,41 @@ class NodeReassignHandler(base.BaseHandler):
if reprovision:
self.handle_task(cluster_id, [node.node])
class CopyVIPsHandler(base.BaseHandler):
single = objects.Cluster
validator = validators.CopyVIPsValidator
@base.content
def POST(self, cluster_id):
"""Copy VIPs from original cluster to new one
Original cluster object is obtained from existing relation between
clusters that is created on cluster clone operation
:param cluster_id: id of cluster that VIPs must be copied to
:http: * 200 (OK)
* 400 (validation failed)
* 404 (seed cluster is not found)
"""
from .objects import relations
cluster = self.get_object_or_404(self.single, cluster_id)
relation = relations.UpgradeRelationObject.get_cluster_relation(
cluster.id)
self.checked_data(cluster=cluster, relation=relation)
# get original cluster object and create adapter with it
orig_cluster_adapter = \
adapters.NailgunClusterAdapter(
adapters.NailgunClusterAdapter.get_by_uid(
relation.orig_cluster_id)
)
seed_cluster_adapter = adapters.NailgunClusterAdapter(cluster)
upgrade.UpgradeHelper.copy_vips(orig_cluster_adapter,
seed_cluster_adapter)

View File

@ -37,14 +37,18 @@ class BaseCloneClusterTest(nailgun_test_base.BaseIntegrationTest):
version="liberty-9.0",
)
self.src_cluster_db = self.env.create_cluster(
api=False,
release_id=self.src_release.id,
net_provider=consts.CLUSTER_NET_PROVIDERS.neutron,
net_l23_provider=consts.NEUTRON_L23_PROVIDERS.ovs,
self.src_cluster_db = self.env.create(
cluster_kwargs={
'api': False,
'release_id': self.src_release.id,
'net_provider': consts.CLUSTER_NET_PROVIDERS.neutron,
'net_l23_provider': consts.NEUTRON_L23_PROVIDERS.ovs,
},
nodes_kwargs=[{'roles': ['controller']}]
)
self.src_cluster = adapters.NailgunClusterAdapter(
self.src_cluster_db)
self.data = {
"name": "cluster-clone-{0}".format(self.src_cluster.id),
"release_id": self.dst_release.id,

View File

@ -14,7 +14,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from mock import patch
import mock
from oslo_serialization import jsonutils
@ -75,7 +75,7 @@ class TestClusterUpgradeCloneHandler(tests_base.BaseCloneClusterTest):
class TestNodeReassignHandler(base.BaseIntegrationTest):
@patch('nailgun.task.task.rpc.cast')
@mock.patch('nailgun.task.task.rpc.cast')
def test_node_reassign_handler(self, mcast):
self.env.create(
cluster_kwargs={'api': False},
@ -97,7 +97,7 @@ class TestNodeReassignHandler(base.BaseIntegrationTest):
provisioned_uids = [int(n['uid']) for n in nodes]
self.assertEqual([node_id, ], provisioned_uids)
@patch('nailgun.task.task.rpc.cast')
@mock.patch('nailgun.task.task.rpc.cast')
def test_node_reassign_handler_with_roles(self, mcast):
cluster = self.env.create(
cluster_kwargs={'api': False},
@ -122,7 +122,7 @@ class TestNodeReassignHandler(base.BaseIntegrationTest):
self.assertEqual(node.pending_roles, ['compute'])
self.assertTrue(mcast.called)
@patch('nailgun.task.task.rpc.cast')
@mock.patch('nailgun.task.task.rpc.cast')
def test_node_reassign_handler_without_reprovisioning(self, mcast):
cluster = self.env.create(
cluster_kwargs={'api': False},
@ -229,3 +229,28 @@ class TestNodeReassignHandler(base.BaseIntegrationTest):
headers=self.default_headers,
expect_errors=True)
self.assertEqual(400, resp.status_code)
class TestCopyVipsHandler(base.BaseIntegrationTest):
def test_copy_vips_called(self):
from ..objects import relations
orig_cluster = self.env.create_cluster(api=False)
new_cluster = self.env.create_cluster(api=False)
relations.UpgradeRelationObject.create_relation(
orig_cluster.id, new_cluster.id)
with mock.patch('nailgun.extensions.cluster_upgrade.handlers'
'.upgrade.UpgradeHelper.copy_vips') as copy_vips_mc:
resp = self.app.post(
reverse(
'CopyVIPsHandler',
kwargs={'cluster_id': new_cluster.id}
),
headers=self.default_headers,
)
self.assertEqual(resp.status_code, 200)
self.assertTrue(copy_vips_mc.called)

View File

@ -17,13 +17,31 @@
import copy
import six
from nailgun import consts
from nailgun.objects.serializers import network_configuration
from . import base as base_tests
from ..objects import adapters
from ..objects import relations
class TestUpgradeHelperCloneCluster(base_tests.BaseCloneClusterTest):
def setUp(self):
super(TestUpgradeHelperCloneCluster, self).setUp()
self.orig_net_manager = self.src_cluster.get_network_manager()
self.serialize_nets = network_configuration.\
NeutronNetworkConfigurationSerializer.\
serialize_for_cluster
self.public_net_data = {
"cidr": "192.168.42.0/24",
"gateway": "192.168.42.1",
"ip_ranges": [["192.168.42.5", "192.168.42.11"]],
}
def test_create_cluster_clone(self):
new_cluster = self.helper.create_cluster_clone(self.src_cluster,
self.data)
@ -60,28 +78,48 @@ class TestUpgradeHelperCloneCluster(base_tests.BaseCloneClusterTest):
self.assertEqual(editable_attrs[section][key]["value"],
value["value"])
def update_public_net_params(self, networks):
pub_net = self._get_pub_net(networks)
pub_net.update(self.public_net_data)
self.orig_net_manager.update(networks)
def _get_pub_net(self, networks):
return next(net for net in networks['networks'] if
net['name'] == consts.NETWORKS.public)
def test_copy_network_config(self):
new_cluster = self.helper.create_cluster_clone(self.src_cluster,
self.data)
orig_net_manager = self.src_cluster.get_network_manager()
serialize_nets = network_configuration.\
NeutronNetworkConfigurationSerializer.\
serialize_for_cluster
# Do some unordinary changes
nets = serialize_nets(self.src_cluster.cluster)
nets["networks"][0].update({
"cidr": "172.16.42.0/24",
"gateway": "172.16.42.1",
"ip_ranges": [["172.16.42.2", "172.16.42.126"]],
})
orig_net_manager.update(nets)
orig_net_manager.assign_vips_for_net_groups()
# Do some unordinary changes to public network
nets = self.serialize_nets(self.src_cluster.cluster)
self.update_public_net_params(nets)
self.helper.copy_network_config(self.src_cluster, new_cluster)
orig_nets = serialize_nets(self.src_cluster_db)
new_nets = serialize_nets(new_cluster.cluster)
new_nets = self.serialize_nets(new_cluster.cluster)
public_net = self._get_pub_net(new_nets)
self.assertEqual(public_net['cidr'], self.public_net_data['cidr'])
self.assertEqual(public_net['gateway'],
self.public_net_data['gateway'])
self.assertEqual(public_net['ip_ranges'],
self.public_net_data['ip_ranges'])
def test_copy_vips(self):
new_cluster = self.helper.clone_cluster(self.src_cluster, self.data)
# we have to move node to new cluster before VIP assignment
# because there is no point in the operation for a cluster
# w/o nodes
node = adapters.NailgunNodeAdapter(self.src_cluster.cluster.nodes[0])
self.helper.assign_node_to_cluster(node, new_cluster, node.roles, [])
self.helper.copy_vips(self.src_cluster, new_cluster)
orig_nets = self.serialize_nets(self.src_cluster.cluster)
new_nets = self.serialize_nets(new_cluster.cluster)
self.assertEqual(orig_nets["management_vip"],
new_nets["management_vip"])
self.assertEqual(orig_nets["management_vrouter_vip"],
@ -92,8 +130,7 @@ class TestUpgradeHelperCloneCluster(base_tests.BaseCloneClusterTest):
new_nets["public_vrouter_vip"])
def test_clone_cluster(self):
orig_net_manager = self.src_cluster.get_network_manager()
orig_net_manager.assign_vips_for_net_groups()
self.orig_net_manager.assign_vips_for_net_groups()
new_cluster = self.helper.clone_cluster(self.src_cluster, self.data)
relation = relations.UpgradeRelationObject.get_cluster_relation(
self.src_cluster.id)

View File

@ -190,3 +190,37 @@ class TestNodeReassignNoReinstallValidator(tests_base.BaseCloneClusterTest):
msg = '^Role "controller" in conflict with role compute$'
with self.assertRaisesRegexp(errors.InvalidData, msg):
self.validator.validate(data, self.dst_cluster)
class TestCopyVIPsValidator(base.BaseTestCase):
validator = validators.CopyVIPsValidator
def test_non_existing_relation_fail(self):
with self.assertRaises(errors.InvalidData) as cm:
self.validator.validate(data=None, cluster=None, relation=None)
self.assertEqual(
cm.exception.message,
"Relation for given cluster does not exist"
)
def test_cluster_is_not_seed(self):
cluster = self.env.create_cluster(api=False)
seed_cluster = self.env.create_cluster(api=False)
relations.UpgradeRelationObject.create_relation(
orig_cluster_id=cluster.id,
seed_cluster_id=cluster.id,
)
relation = relations.UpgradeRelationObject.get_cluster_relation(
cluster.id)
with self.assertRaises(errors.InvalidData) as cm:
self.validator.validate(data=None, cluster=seed_cluster,
relation=relation)
self.assertEqual(
cm.exception.message,
"Given cluster is not seed cluster"
)

View File

@ -150,10 +150,15 @@ class UpgradeHelper(object):
nets_serializer.serialize_for_cluster(orig_cluster.cluster),
nets_serializer.serialize_for_cluster(new_cluster.cluster))
orig_net_manager = orig_cluster.get_network_manager()
new_net_manager = new_cluster.get_network_manager()
new_net_manager.update(nets)
@classmethod
def copy_vips(cls, orig_cluster, new_cluster):
orig_net_manager = orig_cluster.get_network_manager()
new_net_manager = new_cluster.get_network_manager()
vips = orig_net_manager.get_assigned_vips()
for ng_name in vips:
if ng_name not in (consts.NETWORKS.public,

View File

@ -147,3 +147,18 @@ class NodeReassignValidator(assignment.NodeAssignmentValidator):
raise errors.InvalidData("Node {0} is already assigned to cluster"
" {1}".format(node.id, cluster.id),
log_message=True)
class CopyVIPsValidator(base.BasicValidator):
@classmethod
def validate(cls, data, cluster, relation):
if relation is None:
raise errors.InvalidData(
"Relation for given cluster does not exist"
)
if cluster.id != relation.seed_cluster_id:
raise errors.InvalidData("Given cluster is not seed cluster")
return data