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:
parent
3d17d4d4f7
commit
b2539c4466
|
@ -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.
|
||||
|
|
|
@ -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 = {
|
||||
|
|
Loading…
Reference in New Issue