diff --git a/magnum/conductor/scale_manager.py b/magnum/conductor/scale_manager.py index b2056d4d86..d78c0354a8 100644 --- a/magnum/conductor/scale_manager.py +++ b/magnum/conductor/scale_manager.py @@ -13,11 +13,10 @@ # under the License. import abc -from marathon import MarathonClient from oslo_log import log as logging from magnum.common import exception -from magnum.conductor import k8s_api as k8s +from magnum.drivers.common.driver import Driver from magnum.i18n import _ from magnum.i18n import _LI from magnum.i18n import _LW @@ -28,13 +27,9 @@ LOG = logging.getLogger(__name__) def get_scale_manager(context, osclient, cluster): - manager = None - coe = cluster.cluster_template.coe - if coe == 'kubernetes': - manager = K8sScaleManager(context, osclient, cluster) - elif coe == 'mesos': - manager = MesosScaleManager(context, osclient, cluster) - else: + cluster_driver = Driver.get_driver_for_cluster(context, cluster) + manager = cluster_driver.get_scale_manager(context, osclient, cluster) + if not manager: LOG.warning(_LW( "Currently only kubernetes and mesos cluster scale manager " "are available")) @@ -94,41 +89,3 @@ class ScaleManager(object): def _get_hosts_with_container(self, context, cluster): """Return the hosts with container running on them.""" pass - - -class K8sScaleManager(ScaleManager): - - def __init__(self, context, osclient, cluster): - super(K8sScaleManager, self).__init__(context, osclient, cluster) - - def _get_hosts_with_container(self, context, cluster): - k8s_api = k8s.create_k8s_api(self.context, cluster) - hosts = set() - for pod in k8s_api.list_namespaced_pod(namespace='default').items: - hosts.add(pod.spec.node_name) - - return hosts - - -class MesosScaleManager(ScaleManager): - """When scaling a mesos cluster, MesosScaleManager will inspect the - - nodes and find out those with containers on them. Thus we can - ask Heat to delete the nodes without containers. Note that this - is a best effort basis -- Magnum doesn't have any synchronization - with Marathon, so while Magnum is checking for the containers to - choose nodes to remove, new containers can be deployed on the - nodes to be removed. - """ - - def __init__(self, context, osclient, cluster): - super(MesosScaleManager, self).__init__(context, osclient, cluster) - - def _get_hosts_with_container(self, context, cluster): - marathon_client = MarathonClient( - 'http://' + cluster.api_address + ':8080') - hosts = set() - for task in marathon_client.list_tasks(): - hosts.add(task.host) - - return hosts diff --git a/magnum/drivers/common/driver.py b/magnum/drivers/common/driver.py index a182a69fab..b28804936c 100644 --- a/magnum/drivers/common/driver.py +++ b/magnum/drivers/common/driver.py @@ -179,3 +179,8 @@ class Driver(object): """return the monitor with container data for this driver.""" return None + + def get_scale_manager(self, context, osclient, cluster): + """return the scale manager for this driver.""" + + return None diff --git a/magnum/drivers/common/k8s_scale_manager.py b/magnum/drivers/common/k8s_scale_manager.py new file mode 100644 index 0000000000..854f574d64 --- /dev/null +++ b/magnum/drivers/common/k8s_scale_manager.py @@ -0,0 +1,33 @@ +# 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. + +from oslo_log import log as logging + +from magnum.conductor import k8s_api as k8s +from magnum.conductor.scale_manager import ScaleManager + + +LOG = logging.getLogger(__name__) + + +class K8sScaleManager(ScaleManager): + + def __init__(self, context, osclient, cluster): + super(K8sScaleManager, self).__init__(context, osclient, cluster) + + def _get_hosts_with_container(self, context, cluster): + k8s_api = k8s.create_k8s_api(self.context, cluster) + hosts = set() + for pod in k8s_api.list_namespaced_pod(namespace='default').items: + hosts.add(pod.spec.node_name) + + return hosts diff --git a/magnum/drivers/k8s_coreos_v1/driver.py b/magnum/drivers/k8s_coreos_v1/driver.py index 52927f0fc7..8b8cc2c68f 100644 --- a/magnum/drivers/k8s_coreos_v1/driver.py +++ b/magnum/drivers/k8s_coreos_v1/driver.py @@ -13,6 +13,7 @@ # under the License. from magnum.drivers.common import k8s_monitor +from magnum.drivers.common.k8s_scale_manager import K8sScaleManager from magnum.drivers.heat import driver from magnum.drivers.k8s_coreos_v1 import template_def @@ -32,3 +33,6 @@ class Driver(driver.HeatDriver): def get_monitor(self, context, cluster): return k8s_monitor.K8sMonitor(context, cluster) + + def get_scale_manager(self, context, osclient, cluster): + return K8sScaleManager(context, osclient, cluster) diff --git a/magnum/drivers/k8s_fedora_atomic_v1/driver.py b/magnum/drivers/k8s_fedora_atomic_v1/driver.py index 29f41abef2..161cad47e9 100644 --- a/magnum/drivers/k8s_fedora_atomic_v1/driver.py +++ b/magnum/drivers/k8s_fedora_atomic_v1/driver.py @@ -13,6 +13,7 @@ # under the License. from magnum.drivers.common import k8s_monitor +from magnum.drivers.common.k8s_scale_manager import K8sScaleManager from magnum.drivers.heat import driver from magnum.drivers.k8s_fedora_atomic_v1 import template_def @@ -32,3 +33,6 @@ class Driver(driver.HeatDriver): def get_monitor(self, context, cluster): return k8s_monitor.K8sMonitor(context, cluster) + + def get_scale_manager(self, context, osclient, cluster): + return K8sScaleManager(context, osclient, cluster) diff --git a/magnum/drivers/k8s_fedora_ironic_v1/driver.py b/magnum/drivers/k8s_fedora_ironic_v1/driver.py index d698961198..7c018ea180 100644 --- a/magnum/drivers/k8s_fedora_ironic_v1/driver.py +++ b/magnum/drivers/k8s_fedora_ironic_v1/driver.py @@ -13,6 +13,7 @@ # under the License. from magnum.drivers.common import k8s_monitor +from magnum.drivers.common.k8s_scale_manager import K8sScaleManager from magnum.drivers.heat import driver from magnum.drivers.k8s_fedora_ironic_v1 import template_def @@ -32,3 +33,6 @@ class Driver(driver.HeatDriver): def get_monitor(self, context, cluster): return k8s_monitor.K8sMonitor(context, cluster) + + def get_scale_manager(self, context, osclient, cluster): + return K8sScaleManager(context, osclient, cluster) diff --git a/magnum/drivers/mesos_ubuntu_v1/driver.py b/magnum/drivers/mesos_ubuntu_v1/driver.py index 879aa6309c..c40f2395f3 100644 --- a/magnum/drivers/mesos_ubuntu_v1/driver.py +++ b/magnum/drivers/mesos_ubuntu_v1/driver.py @@ -14,6 +14,7 @@ from magnum.drivers.heat import driver from magnum.drivers.mesos_ubuntu_v1 import monitor +from magnum.drivers.mesos_ubuntu_v1.scale_manager import MesosScaleManager from magnum.drivers.mesos_ubuntu_v1 import template_def @@ -32,3 +33,6 @@ class Driver(driver.HeatDriver): def get_monitor(self, context, cluster): return monitor.MesosMonitor(context, cluster) + + def get_scale_manager(self, context, osclient, cluster): + return MesosScaleManager(context, osclient, cluster) diff --git a/magnum/drivers/mesos_ubuntu_v1/scale_manager.py b/magnum/drivers/mesos_ubuntu_v1/scale_manager.py new file mode 100644 index 0000000000..829d9a1750 --- /dev/null +++ b/magnum/drivers/mesos_ubuntu_v1/scale_manager.py @@ -0,0 +1,43 @@ +# 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. + +from marathon import MarathonClient +from oslo_log import log as logging + +from magnum.conductor.scale_manager import ScaleManager + + +LOG = logging.getLogger(__name__) + + +class MesosScaleManager(ScaleManager): + """When scaling a mesos cluster, MesosScaleManager will inspect the + + nodes and find out those with containers on them. Thus we can + ask Heat to delete the nodes without containers. Note that this + is a best effort basis -- Magnum doesn't have any synchronization + with Marathon, so while Magnum is checking for the containers to + choose nodes to remove, new containers can be deployed on the + nodes to be removed. + """ + + def __init__(self, context, osclient, cluster): + super(MesosScaleManager, self).__init__(context, osclient, cluster) + + def _get_hosts_with_container(self, context, cluster): + marathon_client = MarathonClient( + 'http://' + cluster.api_address + ':8080') + hosts = set() + for task in marathon_client.list_tasks(): + hosts.add(task.host) + + return hosts diff --git a/magnum/tests/unit/conductor/test_scale_manager.py b/magnum/tests/unit/conductor/test_scale_manager.py index db69745cc5..cd647d99c2 100644 --- a/magnum/tests/unit/conductor/test_scale_manager.py +++ b/magnum/tests/unit/conductor/test_scale_manager.py @@ -16,34 +16,13 @@ import mock from magnum.common import exception from magnum.conductor import scale_manager +from magnum.drivers.common.k8s_scale_manager import K8sScaleManager +from magnum.drivers.mesos_ubuntu_v1.scale_manager import MesosScaleManager from magnum.tests import base class TestScaleManager(base.TestCase): - @mock.patch('magnum.objects.Cluster.get_by_uuid') - def test_get_scale_manager(self, mock_cluster_get): - mock_context = mock.MagicMock() - mock_osc = mock.MagicMock() - k8s_cluster = mock.MagicMock() - k8s_cluster.cluster_template.coe = 'kubernetes' - mesos_cluster = mock.MagicMock() - mesos_cluster.cluster_template.coe = 'mesos' - invalid_cluster = mock.MagicMock() - invalid_cluster.cluster_template.coe = 'fake' - - mgr = scale_manager.get_scale_manager( - mock_context, mock_osc, k8s_cluster) - self.assertIsInstance(mgr, scale_manager.K8sScaleManager) - - mgr = scale_manager.get_scale_manager( - mock_context, mock_osc, mesos_cluster) - self.assertIsInstance(mgr, scale_manager.MesosScaleManager) - - mgr = scale_manager.get_scale_manager( - mock_context, mock_osc, invalid_cluster) - self.assertIsNone(mgr) - def _test_get_removal_nodes( self, mock_get_hosts, mock_get_num_of_removal, mock_is_scale_down, mock_get_by_uuid, is_scale_down, @@ -215,7 +194,7 @@ class TestK8sScaleManager(base.TestCase): mock_api.list_namespaced_pod.return_value = pods mock_create_api.return_value = mock_api - mgr = scale_manager.K8sScaleManager( + mgr = K8sScaleManager( mock.MagicMock(), mock.MagicMock(), mock.MagicMock()) hosts = mgr._get_hosts_with_container( mock.MagicMock(), mock.MagicMock()) @@ -236,7 +215,7 @@ class TestMesosScaleManager(base.TestCase): tasks = [task_1, task_2] mock_list_tasks.return_value = tasks - mgr = scale_manager.MesosScaleManager( + mgr = MesosScaleManager( mock.MagicMock(), mock.MagicMock(), mock.MagicMock()) hosts = mgr._get_hosts_with_container( mock.MagicMock(), mock.MagicMock())