diff --git a/kuryr_kubernetes/clients.py b/kuryr_kubernetes/clients.py index 3672c31e1..b2cbd6e5e 100644 --- a/kuryr_kubernetes/clients.py +++ b/kuryr_kubernetes/clients.py @@ -29,6 +29,10 @@ _OPENSTACKSDK = 'openstacksdk' _POD_RESOURCES_CLIENT = 'pod-resources-client' +def get_network_client(): + return _clients[_OPENSTACKSDK].network + + def get_neutron_client(): return _clients[_NEUTRON_CLIENT] diff --git a/kuryr_kubernetes/controller/handlers/namespace.py b/kuryr_kubernetes/controller/handlers/namespace.py index d402c2fe1..0953028ce 100644 --- a/kuryr_kubernetes/controller/handlers/namespace.py +++ b/kuryr_kubernetes/controller/handlers/namespace.py @@ -188,17 +188,16 @@ class NamespaceHandler(k8s_base.ResourceEventHandler): @MEMOIZE def _check_quota(self, quota): - neutron = clients.get_neutron_client() - resources = {'subnet': neutron.list_subnets, - 'network': neutron.list_networks, - 'security_group': neutron.list_security_groups} + os_net = clients.get_network_client() + resources = {'subnets': os_net.subnets, + 'networks': os_net.networks, + 'security_groups': os_net.security_groups} - for resource, neutron_func in resources.items(): + for resource, network_func in resources.items(): resource_quota = quota[resource] - resource_name = resource + 's' if utils.has_limit(resource_quota): - if not utils.is_available(resource_name, resource_quota, - neutron_func): + if not utils.is_available(resource, resource_quota, + network_func): return False return True diff --git a/kuryr_kubernetes/controller/handlers/policy.py b/kuryr_kubernetes/controller/handlers/policy.py index 42bf20e50..f6dbfcc95 100644 --- a/kuryr_kubernetes/controller/handlers/policy.py +++ b/kuryr_kubernetes/controller/handlers/policy.py @@ -141,11 +141,10 @@ class NetworkPolicyHandler(k8s_base.ResourceEventHandler): @MEMOIZE def _check_quota(self, quota): - neutron = clients.get_neutron_client() - sg_quota = quota['security_group'] - sg_func = neutron.list_security_groups - if utils.has_limit(sg_quota): - return utils.is_available('security_groups', sg_quota, sg_func) + os_net = clients.get_network_client() + if utils.has_limit(quota.security_groups): + return utils.is_available('security_groups', quota.security_groups, + os_net.security_groups) return True def _is_service_affected(self, service, affected_pods): diff --git a/kuryr_kubernetes/controller/handlers/vif.py b/kuryr_kubernetes/controller/handlers/vif.py index 6739c529b..133900140 100644 --- a/kuryr_kubernetes/controller/handlers/vif.py +++ b/kuryr_kubernetes/controller/handlers/vif.py @@ -212,11 +212,9 @@ class VIFHandler(k8s_base.ResourceEventHandler): @MEMOIZE def is_ready(self, quota): - neutron = clients.get_neutron_client() - port_quota = quota['port'] - port_func = neutron.list_ports - if utils.has_limit(port_quota): - return utils.is_available('ports', port_quota, port_func) + os_net = clients.get_network_client() + if utils.has_limit(quota.ports): + return utils.is_available('ports', quota.ports, os_net.ports) return True @staticmethod diff --git a/kuryr_kubernetes/controller/managers/health.py b/kuryr_kubernetes/controller/managers/health.py index e6becd93c..6a09c25df 100644 --- a/kuryr_kubernetes/controller/managers/health.py +++ b/kuryr_kubernetes/controller/managers/health.py @@ -59,9 +59,9 @@ class HealthServer(object): self.headers = {'Connection': 'close'} def _components_ready(self): - neutron = clients.get_neutron_client() + os_net = clients.get_network_client() project_id = config.CONF.neutron_defaults.project - quota = neutron.show_quota(project_id).get('quota') + quota = os_net.get_quota(project_id) for component in self._registry: if not component.is_ready(quota): diff --git a/kuryr_kubernetes/tests/unit/controller/drivers/test_vif_pool.py b/kuryr_kubernetes/tests/unit/controller/drivers/test_vif_pool.py index dd1d57324..80e75410b 100644 --- a/kuryr_kubernetes/tests/unit/controller/drivers/test_vif_pool.py +++ b/kuryr_kubernetes/tests/unit/controller/drivers/test_vif_pool.py @@ -342,6 +342,9 @@ class BaseVIFPool(test_base.TestCase): oslo_cfg.CONF.set_override('resource_tags', tags, group='neutron_defaults') + self.addCleanup(oslo_cfg.CONF.clear_override, 'resource_tags', + group='neutron_defaults') + neutron.list_networks.return_value = {'networks': [{'id': net_id}]} cls._cleanup_leftover_ports(m_driver) @@ -362,6 +365,8 @@ class BaseVIFPool(test_base.TestCase): oslo_cfg.CONF.set_override('resource_tags', tags, group='neutron_defaults') + self.addCleanup(oslo_cfg.CONF.clear_override, 'resource_tags', + group='neutron_defaults') neutron.list_networks.return_value = {'networks': []} cls._cleanup_leftover_ports(m_driver) @@ -384,6 +389,9 @@ class BaseVIFPool(test_base.TestCase): oslo_cfg.CONF.set_override('resource_tags', tags, group='neutron_defaults') + self.addCleanup(oslo_cfg.CONF.clear_override, 'resource_tags', + group='neutron_defaults') + neutron.list_networks.return_value = {'networks': [{'id': net_id}]} cls._cleanup_leftover_ports(m_driver) @@ -404,6 +412,9 @@ class BaseVIFPool(test_base.TestCase): oslo_cfg.CONF.set_override('resource_tags', tags, group='neutron_defaults') + self.addCleanup(oslo_cfg.CONF.clear_override, 'resource_tags', + group='neutron_defaults') + neutron.list_networks.return_value = {'networks': [{'id': net_id}]} cls._cleanup_leftover_ports(m_driver) @@ -419,9 +430,6 @@ class BaseVIFPool(test_base.TestCase): port_id = str(uuid.uuid4()) port = get_port_obj(port_id=port_id) m_get_ports.return_value = [port] - oslo_cfg.CONF.set_override('resource_tags', - [], - group='neutron_defaults') cls._cleanup_leftover_ports(m_driver) neutron.list_networks.assert_not_called() @@ -437,9 +445,6 @@ class BaseVIFPool(test_base.TestCase): port = get_port_obj(port_id=port_id) port['binding:host_id'] = None m_get_ports.return_value = [port] - oslo_cfg.CONF.set_override('resource_tags', - [], - group='neutron_defaults') cls._cleanup_leftover_ports(m_driver) neutron.list_networks.assert_not_called() diff --git a/kuryr_kubernetes/tests/unit/controller/managers/test_health.py b/kuryr_kubernetes/tests/unit/controller/managers/test_health.py index 089bfea95..2160d0a3b 100644 --- a/kuryr_kubernetes/tests/unit/controller/managers/test_health.py +++ b/kuryr_kubernetes/tests/unit/controller/managers/test_health.py @@ -144,29 +144,29 @@ class TestHealthServer(base.TestCase): @mock.patch.object(_TestHandler, 'is_ready') def test__components_ready(self, m_status): - neutron = self.useFixture(k_fix.MockNeutronClient()).client - neutron.show_quota.return_value = get_quota_obj() + os_net = self.useFixture(k_fix.MockNetworkClient()).client + os_net.get_quota.return_value = get_quota_obj() self.srv._registry = [_TestHandler()] m_status.return_value = True resp = self.srv._components_ready() m_status.assert_called_once() - self.assertEqual(resp, True) - neutron.show_quota.assert_called_once() + self.assertIs(resp, True) + os_net.get_quota.assert_called_once() @mock.patch.object(_TestHandler, 'is_ready') def test__components_ready_error(self, m_status): - neutron = self.useFixture(k_fix.MockNeutronClient()).client - neutron.show_quota.return_value = get_quota_obj() + os_net = self.useFixture(k_fix.MockNetworkClient()).client + os_net.get_quota.return_value = get_quota_obj() self.srv._registry = [_TestHandler()] m_status.return_value = False resp = self.srv._components_ready() m_status.assert_called_once() - self.assertEqual(resp, False) - neutron.show_quota.assert_called_once() + self.assertIs(resp, False) + os_net.get_quota.assert_called_once() @mock.patch.object(_TestHandler, 'is_alive') def test_liveness(self, m_status): diff --git a/kuryr_kubernetes/tests/unit/kuryr_fixtures.py b/kuryr_kubernetes/tests/unit/kuryr_fixtures.py index c9a40c2da..20de3e37f 100644 --- a/kuryr_kubernetes/tests/unit/kuryr_fixtures.py +++ b/kuryr_kubernetes/tests/unit/kuryr_fixtures.py @@ -41,3 +41,11 @@ class MockLBaaSClient(fixtures.Fixture): self.useFixture(fixtures.MockPatch( 'kuryr_kubernetes.clients.get_loadbalancer_client', lambda: self.client)) + + +class MockNetworkClient(fixtures.Fixture): + def _setUp(self): + self.client = mock.Mock() + self.useFixture(fixtures.MockPatch( + 'kuryr_kubernetes.clients.get_network_client', + lambda: self.client)) diff --git a/kuryr_kubernetes/tests/unit/test_clients.py b/kuryr_kubernetes/tests/unit/test_clients.py index df55bc5d6..a1e0eea5f 100644 --- a/kuryr_kubernetes/tests/unit/test_clients.py +++ b/kuryr_kubernetes/tests/unit/test_clients.py @@ -31,6 +31,7 @@ class TestK8sClient(test_base.TestCase): neutron_mock = mock.Mock() openstacksdk_mock = mock.Mock() openstacksdk_mock.load_balancer = mock.Mock() + openstacksdk_mock.network = mock.Mock() k8s_dummy = object() m_cfg.kubernetes.api_root = k8s_api_root @@ -46,3 +47,5 @@ class TestK8sClient(test_base.TestCase): self.assertIs(openstacksdk_mock, clients.get_openstacksdk()) self.assertIs(openstacksdk_mock.load_balancer, clients.get_loadbalancer_client()) + self.assertIs(openstacksdk_mock.network, + clients.get_network_client()) diff --git a/kuryr_kubernetes/tests/unit/test_utils.py b/kuryr_kubernetes/tests/unit/test_utils.py index 2653791ec..623140e7f 100644 --- a/kuryr_kubernetes/tests/unit/test_utils.py +++ b/kuryr_kubernetes/tests/unit/test_utils.py @@ -13,6 +13,8 @@ # limitations under the License. import mock +import munch +from openstack import exceptions as os_exc from os_vif import objects from oslo_config import cfg @@ -28,6 +30,7 @@ CONF = cfg.CONF class TestUtils(test_base.TestCase): + @mock.patch('socket.gethostname') def test_get_node_name(self, m_gethostname): m_gethostname.return_value = 'foo' @@ -64,18 +67,18 @@ class TestUtils(test_base.TestCase): @mock.patch('kuryr_kubernetes.os_vif_util.neutron_to_osvif_network') @mock.patch('kuryr_kubernetes.os_vif_util.neutron_to_osvif_subnet') def test_get_subnet(self, m_osv_subnet, m_osv_network): - neutron = self.useFixture(k_fix.MockNeutronClient()).client + os_net = self.useFixture(k_fix.MockNetworkClient()).client subnet = mock.MagicMock() network = mock.MagicMock() subnet_id = mock.sentinel.subnet_id network_id = mock.sentinel.network_id - neutron_subnet = {'network_id': network_id} + neutron_subnet = munch.Munch({'network_id': network_id}) neutron_network = mock.sentinel.neutron_network - neutron.show_subnet.return_value = {'subnet': neutron_subnet} - neutron.show_network.return_value = {'network': neutron_network} + os_net.get_subnet.return_value = neutron_subnet + os_net.get_network.return_value = neutron_network m_osv_subnet.return_value = subnet m_osv_network.return_value = network @@ -83,8 +86,8 @@ class TestUtils(test_base.TestCase): ret = utils.get_subnet(subnet_id) self.assertEqual(network, ret) - neutron.show_subnet.assert_called_once_with(subnet_id) - neutron.show_network.assert_called_once_with(network_id) + os_net.get_subnet.assert_called_once_with(subnet_id) + os_net.get_network.assert_called_once_with(network_id) m_osv_subnet.assert_called_once_with(neutron_subnet) m_osv_network.assert_called_once_with(neutron_network) network.subnets.objects.append.assert_called_once_with(subnet) @@ -217,3 +220,55 @@ class TestUtils(test_base.TestCase): ret = utils.has_port_changes(service, lbaas_spec) self.assertFalse(ret) + + def test_get_nodes_ips(self): + os_net = self.useFixture(k_fix.MockNetworkClient()).client + ip1 = munch.Munch({'fixed_ips': [{'ip_address': '10.0.0.1'}], + 'trunk_details': True}) + ip2 = munch.Munch({'fixed_ips': [{'ip_address': '10.0.0.2'}], + 'trunk_details': True}) + ip3 = munch.Munch({'fixed_ips': [{'ip_address': '10.0.0.3'}], + 'trunk_details': None}) + ports = (p for p in [ip1, ip2, ip3]) + + os_net.ports.return_value = ports + trunk_ips = utils.get_nodes_ips() + os_net.ports.assert_called_once_with(status='ACTIVE') + self.assertEqual(trunk_ips, [ip1.fixed_ips[0]['ip_address'], + ip2.fixed_ips[0]['ip_address']]) + + def test_get_nodes_ips_tagged(self): + CONF.set_override('resource_tags', ['foo'], group='neutron_defaults') + self.addCleanup(CONF.clear_override, 'resource_tags', + group='neutron_defaults') + + os_net = self.useFixture(k_fix.MockNetworkClient()).client + ip1 = munch.Munch({'fixed_ips': [{'ip_address': '10.0.0.1'}], + 'trunk_details': True}) + ip2 = munch.Munch({'fixed_ips': [{'ip_address': '10.0.0.2'}], + 'trunk_details': False}) + ports = (p for p in [ip1, ip2]) + + os_net.ports.return_value = ports + trunk_ips = utils.get_nodes_ips() + os_net.ports.assert_called_once_with(status='ACTIVE', tags=['foo']) + self.assertEqual(trunk_ips, [ip1.fixed_ips[0]['ip_address']]) + + def test_get_subnet_cidr(self): + os_net = self.useFixture(k_fix.MockNetworkClient()).client + subnet_id = mock.sentinel.subnet_id + subnet = munch.Munch(cidr='10.0.0.0/24') + os_net.get_subnet.return_value = subnet + + result = utils.get_subnet_cidr(subnet_id) + os_net.get_subnet.assert_called_once_with(subnet_id) + self.assertEqual(result, '10.0.0.0/24') + + def test_get_subnet_cidr_no_such_subnet(self): + os_net = self.useFixture(k_fix.MockNetworkClient()).client + subnet_id = mock.sentinel.subnet_id + os_net.get_subnet.side_effect = os_exc.ResourceNotFound + + self.assertRaises(os_exc.ResourceNotFound, utils.get_subnet_cidr, + subnet_id) + os_net.get_subnet.assert_called_once_with(subnet_id) diff --git a/kuryr_kubernetes/utils.py b/kuryr_kubernetes/utils.py index 2e26e9ac3..10644a5fa 100644 --- a/kuryr_kubernetes/utils.py +++ b/kuryr_kubernetes/utils.py @@ -16,7 +16,7 @@ import time import requests -from neutronclient.common import exceptions as n_exc +from openstack import exceptions as os_exc from os_vif import objects from oslo_cache import core as cache from oslo_config import cfg @@ -162,45 +162,42 @@ def get_leader_name(): def get_nodes_ips(): """Get the IPs of the trunk ports associated to the deployment.""" trunk_ips = [] - neutron = clients.get_neutron_client() + os_net = clients.get_network_client() tags = CONF.neutron_defaults.resource_tags if tags: - ports = neutron.list_ports(status='ACTIVE', - tags=CONF.neutron_defaults.resource_tags) + ports = os_net.ports(status='ACTIVE', tags=tags) else: # NOTE(ltomasbo: if tags are not used, assume all the trunk ports are # part of the kuryr deployment - ports = neutron.list_ports(status='ACTIVE') - for port in ports.get('ports'): - if port.get('trunk_details'): - trunk_ips.append(port['fixed_ips'][0]['ip_address']) + ports = os_net.ports(status='ACTIVE') + for port in ports: + if port.trunk_details: + trunk_ips.append(port.fixed_ips[0]['ip_address']) return trunk_ips @MEMOIZE def get_subnet(subnet_id): - neutron = clients.get_neutron_client() + os_net = clients.get_network_client() - n_subnet = neutron.show_subnet(subnet_id).get('subnet') - network_id = n_subnet['network_id'] - n_network = neutron.show_network(network_id).get('network') + n_subnet = os_net.get_subnet(subnet_id) + n_network = os_net.get_network(n_subnet.network_id) subnet = os_vif_util.neutron_to_osvif_subnet(n_subnet) network = os_vif_util.neutron_to_osvif_network(n_network) network.subnets.objects.append(subnet) - return network @MEMOIZE def get_subnet_cidr(subnet_id): - neutron = clients.get_neutron_client() + os_net = clients.get_network_client() try: - subnet_obj = neutron.show_subnet(subnet_id) - except n_exc.NeutronClientException: + subnet_obj = os_net.get_subnet(subnet_id) + except os_exc.ResourceNotFound: LOG.exception("Subnet %s CIDR not found!", subnet_id) raise - return subnet_obj.get('subnet')['cidr'] + return subnet_obj.cidr def extract_pod_annotation(annotation): @@ -222,8 +219,8 @@ def has_limit(quota): return quota != NO_LIMIT -def is_available(resource, resource_quota, neutron_func): - qnt_resources = len(neutron_func().get(resource)) +def is_available(resource, resource_quota, network_func): + qnt_resources = len(list(network_func())) availability = resource_quota - qnt_resources if availability <= 0: LOG.error("Quota exceeded for resource: %s", resource)