/* * Copyright (c) 2014, 2015 SDN Hub, LLC. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.ovsdb.openstack.netvirt; import java.net.HttpURLConnection; import java.util.List; import java.util.Map; import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronLoadBalancer; import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronLoadBalancerPool; import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronLoadBalancerPoolMember; import org.opendaylight.ovsdb.openstack.netvirt.translator.crud.INeutronLoadBalancerCRUD; import org.opendaylight.ovsdb.openstack.netvirt.translator.crud.INeutronLoadBalancerPoolCRUD; import org.opendaylight.ovsdb.openstack.netvirt.translator.crud.INeutronNetworkCRUD; import org.opendaylight.ovsdb.openstack.netvirt.translator.crud.INeutronPortCRUD; import org.opendaylight.ovsdb.openstack.netvirt.translator.crud.INeutronSubnetCRUD; import org.opendaylight.ovsdb.openstack.netvirt.translator.iaware.INeutronLoadBalancerPoolMemberAware; import org.opendaylight.ovsdb.openstack.netvirt.api.Action; import org.opendaylight.ovsdb.openstack.netvirt.api.EventDispatcher; import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerConfiguration; import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerProvider; import org.opendaylight.ovsdb.openstack.netvirt.api.NodeCacheManager; import org.opendaylight.ovsdb.utils.servicehelper.ServiceHelper; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; import org.osgi.framework.ServiceReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Preconditions; /** * Handle requests for OpenStack Neutron v2.0 LBaaS API calls for * /v2.0/pools/{pool_id}/members */ public class LBaaSPoolMemberHandler extends AbstractHandler implements INeutronLoadBalancerPoolMemberAware, ConfigInterface { private static final Logger LOG = LoggerFactory.getLogger(LBaaSPoolMemberHandler.class); // The implementation for each of these services is resolved by the OSGi Service Manager private volatile INeutronLoadBalancerPoolCRUD neutronLBPoolCache; private volatile INeutronLoadBalancerCRUD neutronLBCache; private volatile INeutronPortCRUD neutronPortCache; private volatile INeutronNetworkCRUD neutronNetworkCache; private volatile INeutronSubnetCRUD neutronSubnetCache; private volatile LoadBalancerProvider loadBalancerProvider; private volatile NodeCacheManager nodeCacheManager; @Override public int canCreateNeutronLoadBalancerPoolMember(NeutronLoadBalancerPoolMember neutronLBPoolMember) { LoadBalancerConfiguration lbConfig = extractLBConfiguration(neutronLBPoolMember); if (lbConfig == null) { return HttpURLConnection.HTTP_BAD_REQUEST; } else if (!lbConfig.isValid()) { return HttpURLConnection.HTTP_NOT_ACCEPTABLE; } else { return HttpURLConnection.HTTP_OK; } } @Override public void neutronLoadBalancerPoolMemberCreated(NeutronLoadBalancerPoolMember neutronLBPoolMember) { LOG.debug("Neutron LB Pool Member Creation : {}", neutronLBPoolMember.toString()); enqueueEvent(new NorthboundEvent(neutronLBPoolMember, Action.ADD)); } /** * Assuming that the pool information is fully populated before this call is made, * we go with creating the LoadBalancerConfiguration object for this call with * all information that is necessary to insert flow_mods */ private void doNeutronLoadBalancerPoolMemberCreate(NeutronLoadBalancerPoolMember neutronLBPoolMember) { Preconditions.checkNotNull(loadBalancerProvider); LoadBalancerConfiguration lbConfig = extractLBConfiguration(neutronLBPoolMember); final List nodes = nodeCacheManager.getBridgeNodes(); if (lbConfig == null) { LOG.debug("Neutron LB configuration invalid for member {} ", neutronLBPoolMember.getPoolMemberAddress()); } else if (lbConfig.getVip() == null) { LOG.debug("Neutron LB VIP not created yet for member {} ", neutronLBPoolMember.getID()); } else if (!lbConfig.isValid()) { LOG.debug("Neutron LB pool configuration invalid for {} ", lbConfig.getName()); } else if (nodes.isEmpty()) { LOG.debug("Noop with LB pool member {} creation because no nodes available.", neutronLBPoolMember.getID()); } else { for (Node node : nodes) { loadBalancerProvider.programLoadBalancerPoolMemberRules(node, lbConfig, lbConfig.getMembers().get(neutronLBPoolMember.getID()), Action.ADD); } } } @Override public int canUpdateNeutronLoadBalancerPoolMember(NeutronLoadBalancerPoolMember delta, NeutronLoadBalancerPoolMember original) { return HttpURLConnection.HTTP_NOT_IMPLEMENTED; } @Override public void neutronLoadBalancerPoolMemberUpdated(NeutronLoadBalancerPoolMember neutronLBPoolMember) { LOG.debug("Neutron LB Pool Member Update : {}", neutronLBPoolMember.toString()); enqueueEvent(new NorthboundEvent(neutronLBPoolMember, Action.UPDATE)); } @Override public int canDeleteNeutronLoadBalancerPoolMember(NeutronLoadBalancerPoolMember neutronLBPoolMember) { LoadBalancerConfiguration lbConfig = extractLBConfiguration(neutronLBPoolMember); if (lbConfig == null) { return HttpURLConnection.HTTP_BAD_REQUEST; } else if (!lbConfig.isValid()) { return HttpURLConnection.HTTP_NOT_ACCEPTABLE; } else { return HttpURLConnection.HTTP_OK; } } @Override public void neutronLoadBalancerPoolMemberDeleted(NeutronLoadBalancerPoolMember neutronLBPoolMember) { LOG.debug("Neutron LB Pool Member Deletion : {}", neutronLBPoolMember.toString()); enqueueEvent(new NorthboundEvent(neutronLBPoolMember, Action.DELETE)); } private void doNeutronLoadBalancerPoolMemberDelete(NeutronLoadBalancerPoolMember neutronLBPoolMember) { Preconditions.checkNotNull(loadBalancerProvider); LoadBalancerConfiguration lbConfig = extractLBConfiguration(neutronLBPoolMember); final List nodes = nodeCacheManager.getBridgeNodes(); if (lbConfig == null) { LOG.debug("Neutron LB configuration invalid for member {} ", neutronLBPoolMember.getPoolMemberAddress()); } else if (lbConfig.getVip() == null) { LOG.debug("Neutron LB VIP not created yet for member {} ", neutronLBPoolMember.getID()); } else if (!lbConfig.isValid()) { LOG.debug("Neutron LB pool configuration invalid for {} ", lbConfig.getName()); } else if (nodes.isEmpty()) { LOG.debug("Noop with LB pool member {} deletion because no nodes available.", neutronLBPoolMember.getID()); } else { /* As of now, deleting a member involves recomputing member indices. * This is best done through a complete update of the load balancer instance. */ LoadBalancerConfiguration newLBConfig = new LoadBalancerConfiguration(lbConfig); newLBConfig.removeMember(neutronLBPoolMember.getID()); for (Node node : nodes) { loadBalancerProvider.programLoadBalancerRules(node, lbConfig, Action.DELETE); loadBalancerProvider.programLoadBalancerRules(node, newLBConfig, Action.ADD); } } } /** * Process the event. * * @param abstractEvent the {@link org.opendaylight.ovsdb.openstack.netvirt.AbstractEvent} event to be handled. * @see org.opendaylight.ovsdb.openstack.netvirt.api.EventDispatcher */ @Override public void processEvent(AbstractEvent abstractEvent) { LOG.debug("Processing Loadbalancer member event {}", abstractEvent); if (!(abstractEvent instanceof NorthboundEvent)) { LOG.error("Unable to process abstract event {}", abstractEvent); return; } NorthboundEvent ev = (NorthboundEvent) abstractEvent; switch (ev.getAction()) { case ADD: doNeutronLoadBalancerPoolMemberCreate(ev.getLoadBalancerPoolMember()); break; case DELETE: doNeutronLoadBalancerPoolMemberDelete(ev.getLoadBalancerPoolMember()); break; case UPDATE: /** * Typical upgrade involves changing weights. Since weights are not * supported yet, updates are not supported either. TODO */ LOG.warn("Load balancer pool member update is not supported"); break; default: LOG.warn("Unable to process event action {}", ev.getAction()); break; } } /** * Useful utility for extracting the loadbalancer instance * configuration from the neutron LB cache based on member info * @param neutronLBPoolMember Neutron LB pool member object * @return load balancer configuration of the pool member */ public LoadBalancerConfiguration extractLBConfiguration(NeutronLoadBalancerPoolMember neutronLBPoolMember) { String memberID = neutronLBPoolMember.getID(); String memberIP = neutronLBPoolMember.getPoolMemberAddress(); String memberSubnetID = neutronLBPoolMember.getPoolMemberSubnetID(); Integer memberPort = neutronLBPoolMember.getPoolMemberProtoPort(); String memberPoolID = neutronLBPoolMember.getPoolID(); if (memberSubnetID == null || memberID == null || memberPoolID == null) { LOG.debug("Neutron LB pool member details incomplete [id={}, pool_id={},subnet_id={}", memberID, memberPoolID, memberSubnetID); return null; } String memberMAC = NeutronCacheUtils.getMacAddress(neutronPortCache, memberSubnetID, memberIP); if (memberMAC == null) { LOG.debug("Neutron LB pool member {} MAC address unavailable", memberID); return null; } NeutronLoadBalancerPool neutronLBPool = neutronLBPoolCache.getNeutronLoadBalancerPool(memberPoolID); if (neutronLBPool == null) { LOG.debug("Neutron LB pool {} unavailable", memberPoolID); return null; } String memberProtocol = neutronLBPool.getLoadBalancerPoolProtocol(); if (!(memberProtocol.equalsIgnoreCase(LoadBalancerConfiguration.PROTOCOL_TCP) || memberProtocol.equalsIgnoreCase(LoadBalancerConfiguration.PROTOCOL_HTTP) || memberProtocol.equalsIgnoreCase(LoadBalancerConfiguration.PROTOCOL_HTTPS))) { return null; } String loadBalancerSubnetID=null, loadBalancerVip=null, loadBalancerName=null; for (NeutronLoadBalancer neutronLB: neutronLBCache.getAllNeutronLoadBalancers()) { loadBalancerSubnetID = neutronLB.getLoadBalancerVipSubnetID(); if (memberSubnetID.equals(loadBalancerSubnetID)) { loadBalancerName = neutronLB.getLoadBalancerName(); loadBalancerVip = neutronLB.getLoadBalancerVipAddress(); break; } } /** * It is possible that the VIP has not been created yet. * In that case, we create dummy configuration that will not program rules. */ LoadBalancerConfiguration lbConfig = new LoadBalancerConfiguration(loadBalancerName, loadBalancerVip); Map.Entry providerInfo = NeutronCacheUtils.getProviderInformation(neutronNetworkCache, neutronSubnetCache, memberSubnetID); if (providerInfo != null) { lbConfig.setProviderNetworkType(providerInfo.getKey()); lbConfig.setProviderSegmentationId(providerInfo.getValue()); } lbConfig.setVmac(NeutronCacheUtils.getMacAddress(neutronPortCache, loadBalancerSubnetID, loadBalancerVip)); /* Extract all other active members and include in LB config */ String otherMemberID, otherMemberSubnetID, otherMemberIP, otherMemberMAC, otherMemberProtocol; Boolean otherMemberAdminStateIsUp; Integer otherMemberPort; for (NeutronLoadBalancerPoolMember otherMember: neutronLBPool.getLoadBalancerPoolMembers()) { otherMemberID = otherMember.getID(); if (otherMemberID.equals(memberID)) { continue; //skip } otherMemberIP = otherMember.getPoolMemberAddress(); otherMemberAdminStateIsUp = otherMember.getPoolMemberAdminStateIsUp(); otherMemberSubnetID = otherMember.getPoolMemberSubnetID(); otherMemberPort = otherMember.getPoolMemberProtoPort(); otherMemberProtocol = memberProtocol; if (otherMemberIP != null && otherMemberSubnetID != null && otherMemberAdminStateIsUp != null && otherMemberAdminStateIsUp) { otherMemberMAC = NeutronCacheUtils.getMacAddress(neutronPortCache, otherMemberSubnetID, otherMemberIP); if (otherMemberMAC == null) { continue; } lbConfig.addMember(otherMemberID, otherMemberIP, otherMemberMAC, otherMemberProtocol, otherMemberPort); } } lbConfig.addMember(memberID, memberIP, memberMAC, memberProtocol, memberPort); return lbConfig; } @Override public void setDependencies(ServiceReference serviceReference) { loadBalancerProvider = (LoadBalancerProvider) ServiceHelper.getGlobalInstance(LoadBalancerProvider.class, this); nodeCacheManager = (NodeCacheManager) ServiceHelper.getGlobalInstance(NodeCacheManager.class, this); eventDispatcher = (EventDispatcher) ServiceHelper.getGlobalInstance(EventDispatcher.class, this); eventDispatcher.eventHandlerAdded(serviceReference, this); } @Override public void setDependencies(Object impl) { if (impl instanceof INeutronNetworkCRUD) { neutronNetworkCache = (INeutronNetworkCRUD)impl; } else if (impl instanceof INeutronPortCRUD) { neutronPortCache = (INeutronPortCRUD)impl; } else if (impl instanceof INeutronSubnetCRUD) { neutronSubnetCache = (INeutronSubnetCRUD)impl; } else if (impl instanceof INeutronLoadBalancerCRUD) { neutronLBCache = (INeutronLoadBalancerCRUD)impl; } else if (impl instanceof INeutronLoadBalancerPoolCRUD) { neutronLBPoolCache = (INeutronLoadBalancerPoolCRUD)impl; } else if (impl instanceof LoadBalancerProvider) { loadBalancerProvider = (LoadBalancerProvider)impl; } } }