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