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:
parent
ecec1f6d3c
commit
c0876c580e
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue