Files
kuryr-kubernetes/kuryr_kubernetes/controller/handlers/ingress_lbaas.py
Yash Gupta 4c3e338273 Reuse utils.get_lbaas_spec in lb handler
LoadBalancerHandler._get_lbaas_spec is identical to
utils.get_lbaas_spec, which is used by LBaaSSpecHandler, so we can reuse the
function from utils instead of duplication of code.

Note that the passed k8s object is endpoint in case of
LoadBalancerHandler and service in case of LBaaSSpecHandler (but same
annotation used in both cases, so a common function should be enough)

Change-Id: I124109f79bcdefcc4948eb35b4bbb4a9ca87c43b
Signed-off-by: Yash Gupta <y.gupta@samsung.com>
2019-09-03 14:30:03 +09:00

214 lines
8.9 KiB
Python

# Copyright (c) 2018 RedHat, Inc.
# All Rights Reserved.
#
# 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 oslo_serialization import jsonutils
from kuryr_kubernetes import clients
from kuryr_kubernetes import config
from kuryr_kubernetes import constants as k_const
from kuryr_kubernetes.controller.drivers import base as drv_base
from kuryr_kubernetes.controller.handlers import lbaas as h_lbaas
from kuryr_kubernetes.controller.ingress import ingress_ctl
from kuryr_kubernetes.objects import lbaas as obj_lbaas
from kuryr_kubernetes import utils
LOG = logging.getLogger(__name__)
class IngressLoadBalancerHandler(h_lbaas.LoadBalancerHandler):
"""IngressLoadBalancerHandler handles K8s Endpoints events.
IngressLoadBalancerHandler handles K8s Endpoints events and tracks
changes in LBaaSServiceSpec to update Ingress Controller
L7 router accordingly.
"""
OBJECT_KIND = k_const.K8S_OBJ_ENDPOINTS
OBJECT_WATCH_PATH = "%s/%s" % (k_const.K8S_API_BASE, "endpoints")
def __init__(self):
super(IngressLoadBalancerHandler, self).__init__()
self._drv_lbaas = drv_base.LBaaSDriver.get_instance()
self._l7_router = None
def _should_ignore(self, endpoints, lbaas_spec):
return not(lbaas_spec and
self._has_pods(endpoints))
def on_present(self, endpoints):
if not self._l7_router:
ing_ctl = ingress_ctl.IngressCtrlr.get_instance()
self._l7_router, listener = ing_ctl.get_router_and_listener()
if not self._l7_router:
LOG.info("No L7 router found - do nothing")
return
lbaas_spec = utils.get_lbaas_spec(endpoints)
if self._should_ignore(endpoints, lbaas_spec):
return
pool_name = self._drv_lbaas.get_loadbalancer_pool_name(
self._l7_router, endpoints['metadata']['namespace'],
endpoints['metadata']['name'])
pool = self._drv_lbaas.get_pool_by_name(pool_name,
self._l7_router.project_id)
if not pool:
if self._get_lbaas_route_state(endpoints):
self._set_lbaas_route_state(endpoints, None)
LOG.debug("L7 routing: no route defined for service "
":%s - do nothing", endpoints['metadata']['name'])
else:
# pool was found in L7 router LB ,verify that members are up2date
lbaas_route_state = self._get_lbaas_route_state(endpoints)
if not lbaas_route_state:
lbaas_route_state = obj_lbaas.LBaaSRouteState()
lbaas_route_state.pool = pool
if self._sync_lbaas_route_members(endpoints,
lbaas_route_state, lbaas_spec):
self._set_lbaas_route_state(endpoints, lbaas_route_state)
self._clear_route_notification(endpoints)
def on_deleted(self, endpoints):
if not self._l7_router:
LOG.info("No L7 router found - do nothing")
return
lbaas_route_state = self._get_lbaas_route_state(endpoints)
if not lbaas_route_state:
return
self._remove_unused_route_members(endpoints, lbaas_route_state,
obj_lbaas.LBaaSServiceSpec())
def _sync_lbaas_route_members(self, endpoints,
lbaas_route_state, lbaas_spec):
changed = False
if self._remove_unused_route_members(
endpoints, lbaas_route_state, lbaas_spec):
changed = True
if self._add_new_route_members(endpoints, lbaas_route_state):
changed = True
return changed
def _add_new_route_members(self, endpoints, lbaas_route_state):
changed = False
current_targets = {(str(m.ip), m.port)
for m in lbaas_route_state.members}
for subset in endpoints.get('subsets', []):
subset_ports = subset.get('ports', [])
for subset_address in subset.get('addresses', []):
try:
target_ip = subset_address['ip']
target_ref = subset_address['targetRef']
if target_ref['kind'] != k_const.K8S_OBJ_POD:
continue
except KeyError:
continue
for subset_port in subset_ports:
target_port = subset_port['port']
if (target_ip, target_port) in current_targets:
continue
# TODO(apuimedo): Do not pass subnet_id at all when in
# L3 mode once old neutron-lbaasv2 is not supported, as
# octavia does not require it
if (config.CONF.octavia_defaults.member_mode ==
k_const.OCTAVIA_L2_MEMBER_MODE):
member_subnet_id = self._get_pod_subnet(target_ref,
target_ip)
else:
# We use the service subnet id so that the connectivity
# from VIP to pods happens in layer 3 mode, i.e.,
# routed.
member_subnet_id = self._l7_router.subnet_id
member = self._drv_lbaas.ensure_member(
loadbalancer=self._l7_router,
pool=lbaas_route_state.pool,
subnet_id=member_subnet_id,
ip=target_ip,
port=target_port,
target_ref_namespace=target_ref['namespace'],
target_ref_name=target_ref['name'])
lbaas_route_state.members.append(member)
changed = True
return changed
def _remove_unused_route_members(
self, endpoints, lbaas_route_state, lbaas_spec):
spec_port_names = {p.name for p in lbaas_spec.ports}
current_targets = {(a['ip'], p['port'])
for s in endpoints['subsets']
for a in s['addresses']
for p in s['ports']
if p.get('name') in spec_port_names}
removed_ids = set()
for member in lbaas_route_state.members:
if (str(member.ip), member.port) in current_targets:
continue
self._drv_lbaas.release_member(self._l7_router, member)
removed_ids.add(member.id)
if removed_ids:
lbaas_route_state.members = [
m for m in lbaas_route_state.members
if m.id not in removed_ids]
return bool(removed_ids)
def _set_lbaas_route_state(self, endpoints, route_state):
if route_state is None:
LOG.debug("Removing LBaaSRouteState annotation: %r", route_state)
annotation = None
else:
route_state.obj_reset_changes(recursive=True)
LOG.debug("Setting LBaaSRouteState annotation: %r", route_state)
annotation = jsonutils.dumps(route_state.obj_to_primitive(),
sort_keys=True)
k8s = clients.get_kubernetes_client()
k8s.annotate(endpoints['metadata']['selfLink'],
{k_const.K8S_ANNOTATION_LBAAS_RT_STATE: annotation},
resource_version=endpoints['metadata']['resourceVersion'])
def _get_lbaas_route_state(self, endpoints):
try:
annotations = endpoints['metadata']['annotations']
annotation = annotations[k_const.K8S_ANNOTATION_LBAAS_RT_STATE]
except KeyError:
return None
obj_dict = jsonutils.loads(annotation)
obj = obj_lbaas.LBaaSRouteState.obj_from_primitive(obj_dict)
LOG.debug("Got LBaaSRouteState from annotation: %r", obj)
return obj
def _clear_route_notification(self, endpoints):
try:
annotations = endpoints['metadata']['annotations']
annotation = annotations[
k_const.K8S_ANNOTATION_LBAAS_RT_NOTIF]
except KeyError:
return
LOG.debug("Removing LBaaSRouteNotifier annotation")
annotation = None
k8s = clients.get_kubernetes_client()
k8s.annotate(
endpoints['metadata']['selfLink'],
{k_const.K8S_ANNOTATION_LBAAS_RT_NOTIF: annotation},
resource_version=endpoints['metadata']['resourceVersion'])