Optimize docker profile for node selection

This patch optimizes the docker container profile for random node
selection. The goal is to avoid loading complete Node object into
cluster's rt property for performance (especially for large clusters).
Instead, this patch uses get_all_by_cluster_id DB call for retrieving
nodes that fits the need. It also saves the cost on filtering nodes by
node status.

Change-Id: I68554c0765b7097878be4c27f3b1bcc3f7b51976
This commit is contained in:
tengqm 2017-04-16 23:15:53 -04:00
parent 3d17d4d4f7
commit b2539c4466
2 changed files with 34 additions and 42 deletions

View File

@ -194,7 +194,6 @@ class DockerProfile(base.Profile):
:param ctx: An instance of the request context.
:param host_cluster: The uuid of the hosting cluster.
"""
self.cluster = None
try:
self.cluster = cluster.Cluster.load(ctx, cluster_id=host_cluster)
@ -202,22 +201,16 @@ class DockerProfile(base.Profile):
msg = ex.enhance_msg('host', ex)
raise exc.InternalError(message=msg)
nodes = self.cluster.rt['nodes']
filters = {consts.NODE_STATUS: consts.NS_ACTIVE}
nodes = no.Node.get_all_by_cluster(ctx, cluster_id=host_cluster,
filters=filters)
if len(nodes) == 0:
msg = _("The cluster (%s) contains no nodes") % host_cluster
msg = _("The cluster (%s) contains no active nodes") % host_cluster
raise exc.InternalError(message=msg)
else:
good_nodes = []
for i in range(len(nodes)):
if nodes[i].status == "ACTIVE":
good_nodes.append(nodes[i])
if len(good_nodes) > 0:
node = good_nodes[random.randrange(len(good_nodes))]
else:
msg = _("There is no active nodes running in the cluster (%s)"
) % host_cluster
raise exc.InternalError(message=msg)
return node
# TODO(anyone): Should pick a node by its load
db_node = nodes[random.randrange(len(nodes))]
return node_mod.Node.load(ctx, db_node=db_node)
def _get_host_ip(self, obj, host_node, host_type):
"""Fetch the ip address of physical node.

View File

@ -215,19 +215,29 @@ class TestContainerDockerProfile(base.SenlinTestCase):
msg = _("The host node 'fake_node' could not be found.")
self.assertEqual(msg, ex.message)
@mock.patch.object(node.Node, 'load')
@mock.patch.object(no.Node, 'get_all_by_cluster')
@mock.patch.object(cluster.Cluster, 'load')
def test__get_random_node(self, mock_cluster):
node1 = mock.Mock(status='ERROR')
node2 = mock.Mock(status='ACTIVE')
node3 = mock.Mock(status='ACTIVE')
cluster = mock.Mock(rt={'nodes': [node1, node2, node3]})
def test__get_random_node(self, mock_cluster, mock_nodes, mock_load):
cluster = mock.Mock()
mock_cluster.return_value = cluster
active_nodes = [node2, node3]
node1 = mock.Mock()
node2 = mock.Mock()
mock_nodes.return_value = [node1, node2]
profile = dp.DockerProfile('container', self.spec)
ctx = mock.Mock()
node = profile._get_random_node(ctx, 'host_cluster')
self.assertIn(node, active_nodes)
x_node = mock.Mock()
mock_load.return_value = x_node
res = profile._get_random_node(ctx, 'host_cluster')
self.assertEqual(x_node, res)
mock_cluster.assert_called_once_with(ctx, cluster_id='host_cluster')
mock_nodes.assert_called_once_with(ctx, cluster_id='host_cluster',
filters={'status': 'ACTIVE'})
mock_load.assert_called_once_with(ctx, db_node=mock.ANY)
n = mock_load.call_args[1]['db_node']
self.assertIn(n, [node1, node2])
@mock.patch.object(cluster.Cluster, 'load')
def test__get_random_node_cluster_not_found(self, mock_load):
@ -243,34 +253,23 @@ class TestContainerDockerProfile(base.SenlinTestCase):
msg = _("The host cluster 'host_cluster' could not be found.")
self.assertEqual(msg, ex.message)
@mock.patch.object(no.Node, 'get_all_by_cluster')
@mock.patch.object(cluster.Cluster, 'load')
def test__get_random_node_empty_cluster(self, mock_cluster):
cluster = mock.Mock(rt={'nodes': []})
def test__get_random_node_empty_cluster(self, mock_cluster, mock_nodes):
cluster = mock.Mock()
mock_cluster.return_value = cluster
mock_nodes.return_value = []
profile = dp.DockerProfile('container', self.spec)
ctx = mock.Mock()
ex = self.assertRaises(exc.InternalError,
profile._get_random_node,
ctx, 'host_cluster')
msg = _('The cluster (host_cluster) contains no nodes')
msg = _('The cluster (host_cluster) contains no active nodes')
self.assertEqual(msg, ex.message)
@mock.patch.object(cluster.Cluster, 'load')
def test__get_random_node_no_active_nodes(self, mock_cluster):
node1 = mock.Mock(status='ERROR')
node2 = mock.Mock(status='ERROR')
node3 = mock.Mock(status='ERROR')
cluster = mock.Mock(rt={'nodes': [node1, node2, node3]})
mock_cluster.return_value = cluster
profile = dp.DockerProfile('container', self.spec)
ctx = mock.Mock()
ex = self.assertRaises(exc.InternalError,
profile._get_random_node,
ctx, 'host_cluster')
msg = _('There is no active nodes running in the cluster '
'(host_cluster)')
self.assertEqual(msg, ex.message)
mock_nodes.assert_called_once_with(ctx, cluster_id='host_cluster',
filters={'status': 'ACTIVE'})
def test_get_host_ip_nova_server(self):
addresses = {