cb71f389cdf51ea13868e638f16111dd5a949a59
[ovsdb.git] / openstack / net-virt / src / main / java / org / opendaylight / ovsdb / openstack / netvirt / LBaaSPoolMemberHandler.java
1 /*
2  * Copyright (C) 2014 SDN Hub, LLC.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Authors : Srini Seetharaman
9  */
10 package org.opendaylight.ovsdb.openstack.netvirt;
11
12 import org.opendaylight.neutron.spi.INeutronLoadBalancerCRUD;
13 import org.opendaylight.neutron.spi.INeutronLoadBalancerPoolCRUD;
14 import org.opendaylight.neutron.spi.INeutronLoadBalancerPoolMemberAware;
15 import org.opendaylight.neutron.spi.INeutronNetworkCRUD;
16 import org.opendaylight.neutron.spi.INeutronPortCRUD;
17 import org.opendaylight.neutron.spi.INeutronSubnetCRUD;
18 import org.opendaylight.neutron.spi.NeutronLoadBalancer;
19 import org.opendaylight.neutron.spi.NeutronLoadBalancerPool;
20 import org.opendaylight.neutron.spi.NeutronLoadBalancerPoolMember;
21 import org.opendaylight.ovsdb.openstack.netvirt.api.Action;
22 import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerConfiguration;
23 import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerProvider;
24 import org.opendaylight.ovsdb.openstack.netvirt.api.NodeCacheManager;
25 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28
29 import com.google.common.base.Preconditions;
30
31 import java.net.HttpURLConnection;
32 import java.util.List;
33 import java.util.Map;
34
35 /**
36  * Handle requests for OpenStack Neutron v2.0 LBaaS API calls for
37  * /v2.0/pools/{pool_id}/members
38  */
39
40 public class LBaaSPoolMemberHandler extends AbstractHandler
41         implements INeutronLoadBalancerPoolMemberAware {
42
43     private static final Logger logger = LoggerFactory.getLogger(LBaaSPoolMemberHandler.class);
44
45     // The implementation for each of these services is resolved by the OSGi Service Manager
46     private volatile INeutronLoadBalancerPoolCRUD neutronLBPoolCache;
47     private volatile INeutronLoadBalancerCRUD neutronLBCache;
48     private volatile INeutronPortCRUD neutronPortsCache;
49     private volatile INeutronNetworkCRUD neutronNetworkCache;
50     private volatile INeutronSubnetCRUD neutronSubnetCache;
51     private volatile LoadBalancerProvider loadBalancerProvider;
52     private volatile NodeCacheManager nodeCacheManager;
53
54     @Override
55     public int canCreateNeutronLoadBalancerPoolMember(NeutronLoadBalancerPoolMember neutronLBPoolMember) {
56         LoadBalancerConfiguration lbConfig = extractLBConfiguration(neutronLBPoolMember);
57         if (lbConfig == null) {
58             return HttpURLConnection.HTTP_BAD_REQUEST;
59         } else if (!lbConfig.isValid()) {
60             return HttpURLConnection.HTTP_NOT_ACCEPTABLE;
61         } else {
62             return HttpURLConnection.HTTP_OK;
63         }
64     }
65
66     @Override
67     public void neutronLoadBalancerPoolMemberCreated(NeutronLoadBalancerPoolMember neutronLBPoolMember) {
68         logger.debug("Neutron LB Pool Member Creation : {}", neutronLBPoolMember.toString());
69         enqueueEvent(new NorthboundEvent(neutronLBPoolMember, Action.ADD));
70     }
71
72     /**
73      * Assuming that the pool information is fully populated before this call is made,
74      * we go with creating the LoadBalancerConfiguration object for this call with
75      * all information that is necessary to insert flow_mods
76      */
77     private void doNeutronLoadBalancerPoolMemberCreate(NeutronLoadBalancerPoolMember neutronLBPoolMember) {
78         Preconditions.checkNotNull(loadBalancerProvider);
79         LoadBalancerConfiguration lbConfig = extractLBConfiguration(neutronLBPoolMember);
80         final List<Node> nodes =
81                 nodeCacheManager.getBridgeNodes();
82         if (lbConfig == null) {
83             logger.debug("Neutron LB configuration invalid for member {} ", neutronLBPoolMember.getPoolMemberAddress());
84         } else if (lbConfig.getVip() == null) {
85             logger.debug("Neutron LB VIP not created yet for member {} ", neutronLBPoolMember.getPoolMemberID());
86         } else if (!lbConfig.isValid()) {
87             logger.debug("Neutron LB pool configuration invalid for {} ", lbConfig.getName());
88         } else if (nodes.isEmpty()) {
89             logger.debug("Noop with LB pool member {} creation because no nodes available.", neutronLBPoolMember.getPoolMemberID());
90         } else {
91             for (Node node : nodes) {
92                 loadBalancerProvider.programLoadBalancerPoolMemberRules(node,
93                         lbConfig,
94                         lbConfig.getMembers().get(neutronLBPoolMember.getPoolMemberID()), Action.ADD);
95             }
96         }
97     }
98
99     @Override
100     public int canUpdateNeutronLoadBalancerPoolMember(NeutronLoadBalancerPoolMember delta, NeutronLoadBalancerPoolMember original) {
101         return HttpURLConnection.HTTP_NOT_IMPLEMENTED;
102     }
103
104     @Override
105     public void neutronLoadBalancerPoolMemberUpdated(NeutronLoadBalancerPoolMember neutronLBPoolMember) {
106         logger.debug("Neutron LB Pool Member Update : {}", neutronLBPoolMember.toString());
107         enqueueEvent(new NorthboundEvent(neutronLBPoolMember, Action.UPDATE));
108     }
109
110     @Override
111     public int canDeleteNeutronLoadBalancerPoolMember(NeutronLoadBalancerPoolMember neutronLBPoolMember) {
112         LoadBalancerConfiguration lbConfig = extractLBConfiguration(neutronLBPoolMember);
113         if (lbConfig == null) {
114             return HttpURLConnection.HTTP_BAD_REQUEST;
115         } else if (!lbConfig.isValid()) {
116             return HttpURLConnection.HTTP_NOT_ACCEPTABLE;
117         } else {
118             return HttpURLConnection.HTTP_OK;
119         }
120     }
121
122     @Override
123     public void neutronLoadBalancerPoolMemberDeleted(NeutronLoadBalancerPoolMember neutronLBPoolMember) {
124         logger.debug("Neutron LB Pool Member Deletion : {}", neutronLBPoolMember.toString());
125         enqueueEvent(new NorthboundEvent(neutronLBPoolMember, Action.DELETE));
126     }
127
128     private void doNeutronLoadBalancerPoolMemberDelete(NeutronLoadBalancerPoolMember neutronLBPoolMember) {
129         Preconditions.checkNotNull(loadBalancerProvider);
130
131         LoadBalancerConfiguration lbConfig = extractLBConfiguration(neutronLBPoolMember);
132         final List<Node> nodes = nodeCacheManager.getBridgeNodes();
133         if (lbConfig == null) {
134             logger.debug("Neutron LB configuration invalid for member {} ", neutronLBPoolMember.getPoolMemberAddress());
135         } else if (lbConfig.getVip() == null) {
136             logger.debug("Neutron LB VIP not created yet for member {} ", neutronLBPoolMember.getPoolMemberID());
137         } else if (!lbConfig.isValid()) {
138             logger.debug("Neutron LB pool configuration invalid for {} ", lbConfig.getName());
139         } else if (nodes.isEmpty()) {
140             logger.debug("Noop with LB pool member {} deletion because no nodes available.", neutronLBPoolMember.getPoolMemberID());
141         } else {
142             /* As of now, deleting a member involves recomputing member indices.
143              * This is best done through a complete update of the load balancer instance.
144              */
145             LoadBalancerConfiguration newLBConfig = new LoadBalancerConfiguration(lbConfig);
146             newLBConfig.removeMember(neutronLBPoolMember.getPoolMemberID());
147
148             for (Node node : nodes) {
149                 loadBalancerProvider.programLoadBalancerRules(node, lbConfig, Action.DELETE);
150                 loadBalancerProvider.programLoadBalancerRules(node, newLBConfig, Action.ADD);
151             }
152         }
153     }
154
155     /**
156      * Process the event.
157      *
158      * @param abstractEvent the {@link org.opendaylight.ovsdb.openstack.netvirt.AbstractEvent} event to be handled.
159      * @see org.opendaylight.ovsdb.openstack.netvirt.api.EventDispatcher
160      */
161     @Override
162     public void processEvent(AbstractEvent abstractEvent) {
163         logger.debug("Processing Loadbalancer member event " + abstractEvent);
164         if (!(abstractEvent instanceof NorthboundEvent)) {
165             logger.error("Unable to process abstract event " + abstractEvent);
166             return;
167         }
168         NorthboundEvent ev = (NorthboundEvent) abstractEvent;
169         switch (ev.getAction()) {
170             case ADD:
171                 doNeutronLoadBalancerPoolMemberCreate(ev.getLoadBalancerPoolMember());
172                 break;
173             case DELETE:
174                 doNeutronLoadBalancerPoolMemberDelete(ev.getLoadBalancerPoolMember());
175                 break;
176             case UPDATE:
177                 /**
178                  * Typical upgrade involves changing weights. Since weights are not
179                  * supported yet, updates are not supported either. TODO
180                  */
181                 logger.warn("Load balancer pool member update is not supported");
182                 break;
183             default:
184                 logger.warn("Unable to process event action " + ev.getAction());
185                 break;
186         }
187     }
188
189     /**
190      * Useful utility for extracting the loadbalancer instance
191      * configuration from the neutron LB cache based on member info
192      */
193     public LoadBalancerConfiguration extractLBConfiguration(NeutronLoadBalancerPoolMember neutronLBPoolMember) {
194         String memberID = neutronLBPoolMember.getPoolMemberID();
195         String memberIP = neutronLBPoolMember.getPoolMemberAddress();
196         String memberSubnetID = neutronLBPoolMember.getPoolMemberSubnetID();
197         Integer memberPort = neutronLBPoolMember.getPoolMemberProtoPort();
198         String memberPoolID = neutronLBPoolMember.getPoolID();
199         String memberProtocol = null;
200
201         if (memberSubnetID == null || memberID == null || memberPoolID == null) {
202             logger.debug("Neutron LB pool member details incomplete [id={}, pool_id={},subnet_id={}",
203                     memberID, memberPoolID, memberSubnetID);
204             return null;
205         }
206         String memberMAC = NeutronCacheUtils.getMacAddress(neutronPortsCache, memberSubnetID, memberIP);
207         if (memberMAC == null) {
208             logger.debug("Neutron LB pool member {} MAC address unavailable", memberID);
209             return null;
210         }
211         NeutronLoadBalancerPool neutronLBPool = neutronLBPoolCache.getNeutronLoadBalancerPool(memberPoolID);
212         memberProtocol = neutronLBPool.getLoadBalancerPoolProtocol();
213         if (!(memberProtocol.equalsIgnoreCase(LoadBalancerConfiguration.PROTOCOL_TCP) ||
214                 memberProtocol.equalsIgnoreCase(LoadBalancerConfiguration.PROTOCOL_HTTP) ||
215                 memberProtocol.equalsIgnoreCase(LoadBalancerConfiguration.PROTOCOL_HTTPS))) {
216             return null;
217         }
218
219         String loadBalancerSubnetID=null, loadBalancerVip=null, loadBalancerName=null;
220         for (NeutronLoadBalancer neutronLB: neutronLBCache.getAllNeutronLoadBalancers()) {
221             loadBalancerSubnetID = neutronLB.getLoadBalancerVipSubnetID();
222             if (memberSubnetID.equals(loadBalancerSubnetID)) {
223                 loadBalancerName = neutronLB.getLoadBalancerName();
224                 loadBalancerVip = neutronLB.getLoadBalancerVipAddress();
225                 break;
226             }
227         }
228
229         /**
230          * It is possible that the VIP has not been created yet.
231          * In that case, we create dummy configuration that will not program rules.
232          */
233         LoadBalancerConfiguration lbConfig = new LoadBalancerConfiguration(loadBalancerName, loadBalancerVip);
234         Map.Entry<String,String> providerInfo = NeutronCacheUtils.getProviderInformation(neutronNetworkCache, neutronSubnetCache, memberSubnetID);
235         if (providerInfo != null) {
236             lbConfig.setProviderNetworkType(providerInfo.getKey());
237             lbConfig.setProviderSegmentationId(providerInfo.getValue());
238         }
239         lbConfig.setVmac(NeutronCacheUtils.getMacAddress(neutronPortsCache, loadBalancerSubnetID, loadBalancerVip));
240
241         /* Extract all other active members and include in LB config
242          */
243         String otherMemberID, otherMemberSubnetID, otherMemberIP, otherMemberMAC, otherMemberProtocol;
244         Boolean otherMemberAdminStateIsUp;
245         Integer otherMemberPort;
246
247         for (NeutronLoadBalancerPoolMember otherMember: neutronLBPool.getLoadBalancerPoolMembers()) {
248             otherMemberID = otherMember.getPoolMemberID();
249             if (otherMemberID.equals(memberID)) {
250                 continue; //skip
251             }
252
253             otherMemberIP = otherMember.getPoolMemberAddress();
254             otherMemberAdminStateIsUp = otherMember.getPoolMemberAdminStateIsUp();
255             otherMemberSubnetID = otherMember.getPoolMemberSubnetID();
256             otherMemberPort = otherMember.getPoolMemberProtoPort();
257             otherMemberProtocol = memberProtocol;
258
259             if (otherMemberIP == null || otherMemberSubnetID == null || otherMemberAdminStateIsUp == null) {
260                 continue;
261             } else if (otherMemberAdminStateIsUp.booleanValue()) {
262                 otherMemberMAC = NeutronCacheUtils.getMacAddress(neutronPortsCache, otherMemberSubnetID, otherMemberIP);
263                 if (otherMemberMAC == null) {
264                     continue;
265                 }
266                 lbConfig.addMember(otherMemberID, otherMemberIP, otherMemberMAC, otherMemberProtocol, otherMemberPort);
267             }
268         }
269
270         lbConfig.addMember(memberID, memberIP, memberMAC, memberProtocol, memberPort);
271         return lbConfig;
272     }
273 }