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