Merge "Slight simplification in OvsdbConnectionInstance"
[ovsdb.git] / openstack / net-virt / src / main / java / org / opendaylight / ovsdb / openstack / netvirt / LBaaSHandler.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
11 package org.opendaylight.ovsdb.openstack.netvirt;
12
13 import org.opendaylight.neutron.spi.INeutronLoadBalancerAware;
14 import org.opendaylight.neutron.spi.INeutronLoadBalancerCRUD;
15 import org.opendaylight.neutron.spi.INeutronLoadBalancerPoolCRUD;
16 import org.opendaylight.neutron.spi.INeutronNetworkCRUD;
17 import org.opendaylight.neutron.spi.INeutronPortCRUD;
18 import org.opendaylight.neutron.spi.INeutronSubnetCRUD;
19 import org.opendaylight.neutron.spi.NeutronLoadBalancer;
20 import org.opendaylight.neutron.spi.NeutronLoadBalancerPool;
21 import org.opendaylight.neutron.spi.NeutronLoadBalancerPoolMember;
22 import org.opendaylight.controller.sal.core.Node;
23 import org.opendaylight.controller.sal.core.NodeConnector;
24 import org.opendaylight.controller.sal.core.Property;
25 import org.opendaylight.controller.sal.core.UpdateType;
26 import org.opendaylight.controller.switchmanager.IInventoryListener;
27 import org.opendaylight.controller.switchmanager.ISwitchManager;
28 import org.opendaylight.ovsdb.openstack.netvirt.api.Action;
29 import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerConfiguration;
30 import org.opendaylight.ovsdb.openstack.netvirt.api.LoadBalancerProvider;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 import com.google.common.base.Preconditions;
35
36 import java.net.HttpURLConnection;
37 import java.util.List;
38 import java.util.Map;
39
40 /**
41  * Handle requests for OpenStack Neutron v2.0 LBaaS API calls for /v2.0/loadbalancers.
42  */
43
44 //TODO: Implement INeutronLoadBalancerHealthMonitorAware, INeutronLoadBalancerListenerAware, INeutronLoadBalancerPoolMemberAware,
45
46 public class LBaaSHandler extends AbstractHandler
47         implements INeutronLoadBalancerAware, IInventoryListener {
48
49     private static final Logger logger = LoggerFactory.getLogger(LBaaSHandler.class);
50
51     // The implementation for each of these services is resolved by the OSGi Service Manager
52     private volatile INeutronLoadBalancerCRUD neutronLBCache;
53     private volatile INeutronLoadBalancerPoolCRUD neutronLBPoolCache;
54     private volatile INeutronPortCRUD neutronPortsCache;
55     private volatile INeutronNetworkCRUD neutronNetworkCache;
56     private volatile INeutronSubnetCRUD neutronSubnetCache;
57     private volatile LoadBalancerProvider loadBalancerProvider;
58     private volatile ISwitchManager switchManager;
59
60     @Override
61     public int canCreateNeutronLoadBalancer(NeutronLoadBalancer neutronLB) {
62         //Always allowed and not wait for pool and members to be created
63         return HttpURLConnection.HTTP_OK;
64     }
65
66     @Override
67     public void neutronLoadBalancerCreated(NeutronLoadBalancer neutronLB) {
68         logger.debug("Neutron LB Creation : {}", neutronLB.toString());
69         enqueueEvent(new NorthboundEvent(neutronLB, 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 doNeutronLoadBalancerCreate(NeutronLoadBalancer neutronLB) {
78         Preconditions.checkNotNull(loadBalancerProvider);
79         LoadBalancerConfiguration lbConfig = extractLBConfiguration(neutronLB);
80
81         if (!lbConfig.isValid()) {
82             logger.debug("Neutron LB pool configuration invalid for {} ", lbConfig.getName());
83         } else if (this.switchManager.getNodes().size() == 0) {
84             logger.debug("Noop with LB {} creation because no nodes available.", lbConfig.getName());
85         } else {
86             for (Node node: this.switchManager.getNodes()) {
87                 loadBalancerProvider.programLoadBalancerRules(node, lbConfig, Action.ADD);
88             }
89         }
90     }
91
92     @Override
93     public int canUpdateNeutronLoadBalancer(NeutronLoadBalancer delta, NeutronLoadBalancer original) {
94         //Update allowed anytime, even when the LB has no active pool yet
95         return HttpURLConnection.HTTP_OK;
96     }
97
98     @Override
99     public void neutronLoadBalancerUpdated(NeutronLoadBalancer neutronLB) {
100         logger.debug("Neutron LB Update : {}", neutronLB.toString());
101         enqueueEvent(new NorthboundEvent(neutronLB, Action.UPDATE));
102     }
103
104     @Override
105     public int canDeleteNeutronLoadBalancer(NeutronLoadBalancer neutronLB) {
106         //Always allowed and not wait for pool to stop using it
107         return HttpURLConnection.HTTP_OK;
108     }
109
110     @Override
111     public void neutronLoadBalancerDeleted(NeutronLoadBalancer neutronLB) {
112         logger.debug("Neutron LB Deletion : {}", neutronLB.toString());
113         enqueueEvent(new NorthboundEvent(neutronLB, Action.DELETE));
114     }
115
116     private void doNeutronLoadBalancerDelete(NeutronLoadBalancer neutronLB) {
117         Preconditions.checkNotNull(loadBalancerProvider);
118         LoadBalancerConfiguration lbConfig = extractLBConfiguration(neutronLB);
119
120         if (!lbConfig.isValid()) {
121             logger.debug("Neutron LB pool configuration invalid for {} ", lbConfig.getName());
122         } else if (this.switchManager.getNodes().size() == 0) {
123             logger.debug("Noop with LB {} deletion because no nodes available.", lbConfig.getName());
124         } else {
125             for (Node node: this.switchManager.getNodes()) {
126                 loadBalancerProvider.programLoadBalancerRules(node, lbConfig, Action.DELETE);
127             }
128         }
129     }
130
131     /**
132      * Process the event.
133      *
134      * @param abstractEvent the {@link org.opendaylight.ovsdb.openstack.netvirt.AbstractEvent} event to be handled.
135      * @see org.opendaylight.ovsdb.openstack.netvirt.api.EventDispatcher
136      */
137     @Override
138     public void processEvent(AbstractEvent abstractEvent) {
139         logger.debug("Processing Loadbalancer event " + abstractEvent);
140         if (!(abstractEvent instanceof NorthboundEvent)) {
141             logger.error("Unable to process abstract event " + abstractEvent);
142             return;
143         }
144         NorthboundEvent ev = (NorthboundEvent) abstractEvent;
145         switch (ev.getAction()) {
146             case ADD:
147                 doNeutronLoadBalancerCreate(ev.getLoadBalancer());
148                 break;
149             case DELETE:
150                 doNeutronLoadBalancerDelete(ev.getLoadBalancer());
151                 break;
152             case UPDATE:
153                 /**
154                  * Currently member update requires delete and re-adding
155                  * Also, weights and weight updates are not supported
156                  */
157                 doNeutronLoadBalancerDelete(ev.getLoadBalancer());
158                 doNeutronLoadBalancerCreate(ev.getLoadBalancer());
159                 break;
160             default:
161                 logger.warn("Unable to process event action " + ev.getAction());
162                 break;
163         }
164     }
165
166     /**
167      * Useful utility for extracting the loadbalancer instance
168      * configuration from the neutron LB cache
169      */
170     public LoadBalancerConfiguration extractLBConfiguration(NeutronLoadBalancer neutronLB) {
171         String loadBalancerName = neutronLB.getLoadBalancerName();
172         String loadBalancerVip = neutronLB.getLoadBalancerVipAddress();
173         String loadBalancerSubnetID = neutronLB.getLoadBalancerVipSubnetID();
174
175         LoadBalancerConfiguration lbConfig = new LoadBalancerConfiguration(loadBalancerName, loadBalancerVip);
176         Map.Entry<String,String> providerInfo = NeutronCacheUtils.getProviderInformation(neutronNetworkCache, neutronSubnetCache, loadBalancerSubnetID);
177         if (providerInfo != null) {
178             lbConfig.setProviderNetworkType(providerInfo.getKey());
179             lbConfig.setProviderSegmentationId(providerInfo.getValue());
180         }
181         lbConfig.setVmac(NeutronCacheUtils.getMacAddress(neutronPortsCache, loadBalancerSubnetID, loadBalancerVip));
182
183         String memberID, memberIP, memberMAC, memberProtocol, memberSubnetID;
184         Integer memberPort;
185         Boolean memberAdminStateIsUp;
186
187         for (NeutronLoadBalancerPool neutronLBPool: neutronLBPoolCache.getAllNeutronLoadBalancerPools()) {
188             List<NeutronLoadBalancerPoolMember> members = neutronLBPool.getLoadBalancerPoolMembers();
189             memberProtocol = neutronLBPool.getLoadBalancerPoolProtocol();
190             if (memberProtocol == null) {
191                 continue;
192             }
193
194             if (!(memberProtocol.equalsIgnoreCase(LoadBalancerConfiguration.PROTOCOL_TCP) ||
195                   memberProtocol.equalsIgnoreCase(LoadBalancerConfiguration.PROTOCOL_HTTP) ||
196                   memberProtocol.equalsIgnoreCase(LoadBalancerConfiguration.PROTOCOL_HTTPS))) {
197                 continue;
198             }
199             for (NeutronLoadBalancerPoolMember neutronLBPoolMember: members) {
200                 memberAdminStateIsUp = neutronLBPoolMember.getPoolMemberAdminStateIsUp();
201                 memberSubnetID = neutronLBPoolMember.getPoolMemberSubnetID();
202                 if (memberSubnetID == null || memberAdminStateIsUp == null) {
203                     continue;
204                 }
205                 else if (memberSubnetID.equals(loadBalancerSubnetID) && memberAdminStateIsUp.booleanValue()) {
206                     memberID = neutronLBPoolMember.getPoolMemberID();
207                     memberIP = neutronLBPoolMember.getPoolMemberAddress();
208                     memberPort = neutronLBPoolMember.getPoolMemberProtoPort();
209                     if (memberSubnetID == null || memberID == null || memberIP == null || memberPort == null) {
210                         logger.debug("Neutron LB pool member details incomplete: {}", neutronLBPoolMember);
211                         continue;
212                     }
213                     memberMAC = NeutronCacheUtils.getMacAddress(neutronPortsCache, memberSubnetID, memberIP);
214                     if (memberMAC == null) {
215                         continue;
216                     }
217                     lbConfig.addMember(memberID, memberIP, memberMAC, memberProtocol, memberPort);
218                 }
219             }
220         }
221         return lbConfig;
222     }
223
224     /**
225      * On the addition of a new node, we iterate through all existing loadbalancer
226      * instances and program the node for all of them. It is sufficient to do that only
227      * when a node is added, and only for the LB instances (and not individual members).
228      */
229     @Override
230     public void notifyNode(Node node, UpdateType type, Map<String, Property> propMap) {
231         logger.debug("notifyNode: Node {} update {} from Controller's inventory Service", node, type);
232         Preconditions.checkNotNull(loadBalancerProvider);
233
234         for (NeutronLoadBalancer neutronLB: neutronLBCache.getAllNeutronLoadBalancers()) {
235             LoadBalancerConfiguration lbConfig = extractLBConfiguration(neutronLB);
236             if (!lbConfig.isValid()) {
237                 logger.debug("Neutron LB configuration invalid for {} ", lbConfig.getName());
238             } else {
239                if (type.equals(UpdateType.ADDED)) {
240                    loadBalancerProvider.programLoadBalancerRules(node, lbConfig, Action.ADD);
241
242                /* When node disappears, we do nothing for now. Making a call to
243                 * loadBalancerProvider.programLoadBalancerRules(node, lbConfig, Action.DELETE)
244                 * can lead to TransactionCommitFailedException. Similarly when node is changed,
245                 * because of remove followed by add, we do nothing.
246                 */
247
248                  //(type.equals(UpdateType.REMOVED) || type.equals(UpdateType.CHANGED))
249                } else {
250                    continue;
251                }
252             }
253         }
254     }
255
256     @Override
257     public void notifyNodeConnector(NodeConnector arg0, UpdateType arg1,
258             Map<String, Property> arg2) {
259         //NOOP
260     }
261 }