Bug 3269 Implementation of floating ip in neutron-mapper
[groupbasedpolicy.git] / neutron-mapper / src / main / java / org / opendaylight / groupbasedpolicy / neutron / mapper / mapping / NeutronPortAware.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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 package org.opendaylight.groupbasedpolicy.neutron.mapper.mapping;
9
10 import static com.google.common.base.Preconditions.checkNotNull;
11
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.HashMap;
15 import java.util.HashSet;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Set;
19 import java.util.concurrent.ExecutionException;
20
21 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
22 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
23 import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
24 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
25 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
26 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.DataStoreHelper;
27 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.IidFactory;
28 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.MappingUtils;
29 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.MappingUtils.ForwardingCtx;
30 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.NeutronUtils;
31 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.Utils;
32 import org.opendaylight.neutron.spi.INeutronPortAware;
33 import org.opendaylight.neutron.spi.NeutronPort;
34 import org.opendaylight.neutron.spi.NeutronSecurityGroup;
35 import org.opendaylight.neutron.spi.NeutronSecurityRule;
36 import org.opendaylight.neutron.spi.Neutron_IPs;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpPrefix;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L2FloodDomainId;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L3ContextId;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Name;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubnetId;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.UniqueId;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.EndpointService;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.RegisterEndpointInput;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.RegisterEndpointInputBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.RegisterL3PrefixEndpointInput;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.RegisterL3PrefixEndpointInputBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.UnregisterEndpointInput;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.UnregisterEndpointInputBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.fields.L3Address;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.fields.L3AddressBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.l3.prefix.fields.EndpointL3Gateways;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.l3.prefix.fields.EndpointL3GatewaysBuilder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointKey;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3Key;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3PrefixKey;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.unregister.endpoint.input.L2;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.unregister.endpoint.input.L2Builder;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.unregister.endpoint.input.L3;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.unregister.endpoint.input.L3Builder;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.endpoints.by.floating.ip.ports.EndpointByFloatingIpPort;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.endpoints.by.ports.EndpointByPort;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.endpoints.by.router._interface.ports.EndpointByRouterInterfacePort;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.endpoints.by.router.gateway.ports.EndpointByRouterGatewayPort;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.neutron.by.gbp.mappings.floating.ip.ports.by.endpoints.FloatingIpPortByEndpoint;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.neutron.by.gbp.mappings.ports.by.endpoints.PortByEndpoint;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.neutron.by.gbp.mappings.router._interface.ports.by.endpoints.RouterInterfacePortByEndpoint;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.neutron.by.gbp.mappings.router.gateway.ports.by.endpoints.RouterGatewayPortByEndpoint;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.EndpointLocation.LocationType;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContextInput;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContextInputBuilder;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Subnet;
79 import org.opendaylight.yangtools.yang.common.RpcResult;
80 import org.slf4j.Logger;
81 import org.slf4j.LoggerFactory;
82
83 import com.google.common.base.Function;
84 import com.google.common.base.Optional;
85 import com.google.common.base.Strings;
86 import com.google.common.collect.Collections2;
87 import com.google.common.collect.ImmutableList;
88
89 public class NeutronPortAware implements INeutronPortAware {
90
91     public static final Logger LOG = LoggerFactory.getLogger(NeutronPortAware.class);
92     private static final String DEVICE_OWNER_DHCP = "network:dhcp";
93     private static final String DEVICE_OWNER_ROUTER_IFACE = "network:router_interface";
94     private static final String DEVICE_OWNER_ROUTER_GATEWAY = "network:router_gateway";
95     private static final String DEVICE_OWNER_FLOATING_IP = "network:floatingip";
96     private static final int DHCP_CLIENT_PORT = 68;
97     private static final int DHCP_SERVER_PORT = 67;
98     private final DataBroker dataProvider;
99     private final EndpointService epService;
100     private final static Map<String, UniqueId> floatingIpPortByDeviceId = new HashMap<>();
101
102     public NeutronPortAware(DataBroker dataProvider, EndpointService epService) {
103         this.dataProvider = checkNotNull(dataProvider);
104         this.epService = checkNotNull(epService);
105     }
106
107     /**
108      * @see org.opendaylight.neutron.spi.INeutronPortAware#canCreatePort(org.opendaylight.neutron.spi.NeutronPort)
109      */
110     @Override
111     public int canCreatePort(NeutronPort port) {
112         LOG.trace("canCreatePort - {}", port);
113         // TODO Li msunal this has to be rewrite when OFOverlay renderer will support l3-endpoints.
114         List<Neutron_IPs> fixedIPs = port.getFixedIPs();
115         if (fixedIPs != null && fixedIPs.size() > 1) {
116             LOG.warn("Neutron mapper does not support multiple IPs on the same port.");
117             return StatusCode.BAD_REQUEST;
118         }
119         return StatusCode.OK;
120     }
121
122     /**
123      * @see org.opendaylight.neutron.spi.INeutronPortAware#neutronPortCreated(org.opendaylight.neutron.spi.NeutronPort)
124      */
125     @Override
126     public void neutronPortCreated(NeutronPort port) {
127         LOG.trace("neutronPortCreated - {}", port);
128         if (isRouterInterfacePort(port)) {
129             LOG.trace("Port is router interface - {} does nothing. {} handles router iface.",
130                     NeutronPortAware.class.getSimpleName(), NeutronRouterAware.class.getSimpleName());
131             return;
132         }
133         if (isRouterGatewayPort(port)) {
134             LOG.trace("Port is router gateway - {} does nothing. {} handles router iface.",
135                     NeutronPortAware.class.getSimpleName(), NeutronRouterAware.class.getSimpleName());
136             return;
137         }
138         if (isFloatingIpPort(port)) {
139             LOG.trace("Port is floating ip - {} device id - {}", port.getID(), port.getDeviceID());
140             floatingIpPortByDeviceId.put(port.getDeviceID(), new UniqueId(port.getID()));
141             return;
142         }
143         ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
144         TenantId tenantId = new TenantId(Utils.normalizeUuid(port.getTenantID()));
145         if (isDhcpPort(port)) {
146             LOG.trace("Port is DHCP port. - {}", port.getID());
147             List<NeutronSecurityRule> dhcpSecRules = createDhcpSecRules(port, null, rwTx);
148             if (dhcpSecRules == null) {
149                 rwTx.cancel();
150                 return;
151             }
152
153             for (NeutronSecurityRule dhcpSecRule : dhcpSecRules) {
154                 boolean isDhcpSecRuleAdded = NeutronSecurityRuleAware.addNeutronSecurityRule(dhcpSecRule, rwTx);
155                 if (!isDhcpSecRuleAdded) {
156                     rwTx.cancel();
157                     return;
158                 }
159             }
160         } else {
161             List<NeutronSecurityGroup> secGroups = port.getSecurityGroups();
162             if (secGroups != null) {
163                 for (NeutronSecurityGroup secGroup : secGroups) {
164                     EndpointGroupId epgId = new EndpointGroupId(secGroup.getSecurityGroupUUID());
165                     Optional<EndpointGroup> potentialEpg = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,
166                             IidFactory.endpointGroupIid(tenantId, epgId), rwTx);
167                     if (!potentialEpg.isPresent()) {
168                         boolean isSecGroupCreated = NeutronSecurityGroupAware.addNeutronSecurityGroup(secGroup, rwTx);
169                         if (!isSecGroupCreated) {
170                             rwTx.cancel();
171                             return;
172                         }
173                         if (containsSecRuleWithRemoteSecGroup(secGroup)) {
174                             List<NeutronSecurityRule> dhcpSecRules = createDhcpSecRules(port, epgId, rwTx);
175                             if (dhcpSecRules == null) {
176                                 rwTx.cancel();
177                                 return;
178                             }
179                             List<NeutronSecurityRule> routerSecRules = NeutronRouterAware.createRouterSecRules(port, epgId, rwTx);
180                             if (routerSecRules == null) {
181                                 rwTx.cancel();
182                                 return;
183                             }
184                         }
185                     } else {
186                         List<NeutronSecurityRule> secRules = secGroup.getSecurityRules();
187                         if (secRules != null) {
188                             for (NeutronSecurityRule secRule : secRules) {
189                                 NeutronSecurityRuleAware.addNeutronSecurityRule(secRule, rwTx);
190                             }
191                         }
192                     }
193                 }
194             }
195         }
196         boolean isNeutronPortCreated = addNeutronPort(port, rwTx, epService);
197         if (!isNeutronPortCreated) {
198             rwTx.cancel();
199             return;
200         }
201
202         DataStoreHelper.submitToDs(rwTx);
203     }
204
205     public static boolean addNeutronPort(NeutronPort port, ReadWriteTransaction rwTx, EndpointService epService) {
206         TenantId tenantId = new TenantId(Utils.normalizeUuid(port.getTenantID()));
207         L2FloodDomainId l2FdId = new L2FloodDomainId(port.getNetworkUUID());
208         ForwardingCtx fwCtx = MappingUtils.createForwardingContext(tenantId, l2FdId, rwTx);
209         boolean isFwCtxValid = validateForwardingCtx(fwCtx);
210         if (!isFwCtxValid) {
211             return false;
212         }
213         EndpointKey epKey = new EndpointKey(fwCtx.getL2BridgeDomain().getId(), new MacAddress(port.getMacAddress()));
214         addNeutronGbpMapping(port, epKey, rwTx);
215
216         try {
217             RegisterEndpointInput registerEpRpcInput = createRegisterEndpointInput(port, fwCtx);
218
219             RpcResult<Void> rpcResult = epService.registerEndpoint(registerEpRpcInput).get();
220             if (!rpcResult.isSuccessful()) {
221                 LOG.warn("Illegal state - RPC registerEndpoint failed. Input of RPC: {}", registerEpRpcInput);
222                 return false;
223             }
224         } catch (InterruptedException | ExecutionException e) {
225             LOG.error("addPort - RPC invocation failed.", e);
226             return false;
227         }
228         return true;
229     }
230
231     private static void addNeutronGbpMapping(NeutronPort port, EndpointKey epKey, ReadWriteTransaction rwTx) {
232         UniqueId portId = new UniqueId(port.getID());
233         if (isRouterInterfacePort(port)) {
234             LOG.trace("Adding RouterInterfacePort-Endpoint mapping for port {} and endpoint {}", port.getID(), epKey);
235             EndpointByRouterInterfacePort endpointByPort = MappingFactory.createEndpointByRouterInterfacePort(epKey,
236                     portId);
237             rwTx.put(LogicalDatastoreType.OPERATIONAL, IidFactory.endpointByRouterInterfacePortIid(portId),
238                     endpointByPort, true);
239             RouterInterfacePortByEndpoint portByEndpoint = MappingFactory.createRouterInterfacePortByEndpoint(portId,
240                     epKey);
241             rwTx.put(LogicalDatastoreType.OPERATIONAL,
242                     IidFactory.routerInterfacePortByEndpointIid(epKey.getL2Context(), epKey.getMacAddress()),
243                     portByEndpoint, true);
244         } else if (isRouterGatewayPort(port)) {
245             LOG.trace("Adding RouterGatewayPort-Endpoint mapping for port {} and endpoint {}", port.getID(), epKey);
246             EndpointByRouterGatewayPort endpointByPort = MappingFactory.createEndpointByRouterGatewayPort(epKey, portId);
247             rwTx.put(LogicalDatastoreType.OPERATIONAL, IidFactory.endpointByRouterGatewayPortIid(portId),
248                     endpointByPort, true);
249             RouterGatewayPortByEndpoint portByEndpoint = MappingFactory.createRouterGatewayPortByEndpoint(portId, epKey);
250             rwTx.put(LogicalDatastoreType.OPERATIONAL,
251                     IidFactory.routerGatewayPortByEndpointIid(epKey.getL2Context(), epKey.getMacAddress()),
252                     portByEndpoint, true);
253         } else if (isFloatingIpPort(port)) {
254             LOG.trace("Adding FloatingIpPort-Endpoint mapping for port {} and endpoint {}", port.getID(), epKey);
255             EndpointByFloatingIpPort endpointByPort = MappingFactory.createEndpointByFloatingIpPort(epKey, portId);
256             rwTx.put(LogicalDatastoreType.OPERATIONAL, IidFactory.endpointByFloatingIpPortIid(portId),
257                     endpointByPort, true);
258             FloatingIpPortByEndpoint portByEndpoint = MappingFactory.createFloatingIpPortByEndpoint(portId, epKey);
259             rwTx.put(LogicalDatastoreType.OPERATIONAL,
260                     IidFactory.floatingIpPortByEndpointIid(epKey.getL2Context(), epKey.getMacAddress()),
261                     portByEndpoint, true);
262         } else {
263             LOG.trace("Adding Port-Endpoint mapping for port {} (device owner {}) and endpoint {}", port.getID(),
264                     port.getDeviceOwner(), epKey);
265             EndpointByPort endpointByPort = MappingFactory.createEndpointByPort(epKey, portId);
266             rwTx.put(LogicalDatastoreType.OPERATIONAL, IidFactory.endpointByPortIid(portId), endpointByPort, true);
267             PortByEndpoint portByEndpoint = MappingFactory.createPortByEndpoint(portId, epKey);
268             rwTx.put(LogicalDatastoreType.OPERATIONAL,
269                     IidFactory.portByEndpointIid(epKey.getL2Context(), epKey.getMacAddress()), portByEndpoint, true);
270         }
271     }
272
273     public static boolean addL3PrefixEndpoint(L3ContextId l3ContextId, IpPrefix ipPrefix, IpAddress ipAddress, TenantId tenantId,
274             ReadWriteTransaction rwTx, EndpointService epService) {
275
276         EndpointL3PrefixKey epL3PrefixKey = new EndpointL3PrefixKey( ipPrefix, l3ContextId);
277
278         EndpointL3Key epL3Key = null;
279         List<EndpointL3Key> l3Gateways = new ArrayList<>();
280         if (ipAddress != null) {
281             epL3Key = new EndpointL3Key(ipAddress, l3ContextId);
282             l3Gateways.add(epL3Key);
283         }
284
285
286         try {
287             RegisterL3PrefixEndpointInput registerL3PrefixEpRpcInput = createRegisterL3PrefixEndpointInput(epL3PrefixKey, l3Gateways,tenantId);
288
289             RpcResult<Void> rpcResult = epService.registerL3PrefixEndpoint(registerL3PrefixEpRpcInput).get();
290             if (!rpcResult.isSuccessful()) {
291                 LOG.warn("Illegal state - RPC registerEndpoint failed. Input of RPC: {}", registerL3PrefixEpRpcInput);
292                 return false;
293             }
294         } catch (InterruptedException | ExecutionException e) {
295             LOG.error("addPort - RPC invocation failed.", e);
296             return false;
297         }
298         return true;
299
300     }
301
302     private static boolean validateForwardingCtx(ForwardingCtx fwCtx) {
303         if (fwCtx.getL2FloodDomain() == null) {
304             LOG.warn("Illegal state - l2-flood-domain does not exist.");
305             return false;
306         }
307         if (fwCtx.getL2BridgeDomain() == null) {
308             LOG.warn("Illegal state - l2-bridge-domain does not exist.");
309             return false;
310         }
311         if (fwCtx.getL3Context() == null) {
312             LOG.warn("Illegal state - l3-context does not exist.");
313             return false;
314         }
315         return true;
316     }
317
318     private List<NeutronSecurityRule> createDhcpSecRules(NeutronPort port, EndpointGroupId consumerEpgId, ReadTransaction rTx) {
319         TenantId tenantId = new TenantId(Utils.normalizeUuid(port.getTenantID()));
320         Neutron_IPs firstIp = MappingUtils.getFirstIp(port.getFixedIPs());
321         if (firstIp == null) {
322             LOG.warn("Illegal state - DHCP port does not have an IP address.");
323             return null;
324         }
325         SubnetId dhcpSubnetId = new SubnetId(firstIp.getSubnetUUID());
326         Optional<Subnet> potentialSubnet = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,
327                 IidFactory.subnetIid(tenantId, dhcpSubnetId), rTx);
328         if (!potentialSubnet.isPresent()) {
329             LOG.warn("Illegal state - Subnet {} where is DHCP port does not exist.", dhcpSubnetId.getValue());
330             return null;
331         }
332         IpPrefix ipSubnet = potentialSubnet.get().getIpPrefix();
333         NeutronSecurityRule dhcpRuleEgress = createDhcpSecRule(port.getID(), tenantId, ipSubnet, consumerEpgId, true);
334         NeutronSecurityRule dhcpRuleIngress = createDhcpSecRule(port.getID(), tenantId, ipSubnet, consumerEpgId, false);
335         return ImmutableList.of(dhcpRuleEgress, dhcpRuleIngress);
336     }
337
338     private NeutronSecurityRule createDhcpSecRule(String ruleUuid, TenantId tenantId, IpPrefix ipSubnet, EndpointGroupId consumerEpgId,
339             boolean isEgress) {
340         NeutronSecurityRule dhcpSecRule = new NeutronSecurityRule();
341         dhcpSecRule.setSecurityRuleGroupID(MappingUtils.EPG_DHCP_ID.getValue());
342         dhcpSecRule.setSecurityRuleTenantID(tenantId.getValue());
343         dhcpSecRule.setSecurityRuleRemoteIpPrefix(Utils.getStringIpPrefix(ipSubnet));
344         if (consumerEpgId != null) {
345             dhcpSecRule.setSecurityRemoteGroupID(consumerEpgId.getValue());
346         }
347         if (isEgress) {
348             dhcpSecRule.setSecurityRuleUUID(NeutronUtils.EGRESS + "__" + ruleUuid);
349             dhcpSecRule.setSecurityRuleDirection(NeutronUtils.EGRESS);
350             dhcpSecRule.setSecurityRulePortMin(DHCP_CLIENT_PORT);
351             dhcpSecRule.setSecurityRulePortMax(DHCP_CLIENT_PORT);
352         } else {
353             dhcpSecRule.setSecurityRuleUUID(NeutronUtils.INGRESS + "__" + ruleUuid);
354             dhcpSecRule.setSecurityRuleDirection(NeutronUtils.INGRESS);
355             dhcpSecRule.setSecurityRulePortMin(DHCP_SERVER_PORT);
356             dhcpSecRule.setSecurityRulePortMax(DHCP_SERVER_PORT);
357         }
358         dhcpSecRule.setSecurityRuleProtocol(NeutronUtils.UDP);
359         if (ipSubnet.getIpv4Prefix() != null) {
360             dhcpSecRule.setSecurityRuleEthertype(NeutronUtils.IPv4);
361         } else {
362             dhcpSecRule.setSecurityRuleEthertype(NeutronUtils.IPv6);
363         }
364         return dhcpSecRule;
365     }
366
367     /**
368      * @see org.opendaylight.neutron.spi.INeutronPortAware#canUpdatePort(org.opendaylight.neutron.spi.NeutronPort,
369      *      org.opendaylight.neutron.spi.NeutronPort)
370      */
371     @Override
372     public int canUpdatePort(NeutronPort delta, NeutronPort original) {
373         LOG.trace("canUpdatePort - delta: {} original: {}", delta, original);
374         if (delta.getFixedIPs() == null || delta.getFixedIPs().isEmpty()) {
375             return StatusCode.OK;
376         }
377         // TODO Li msunal this has to be rewrite when OFOverlay renderer will support l3-endpoints.
378         List<Neutron_IPs> fixedIPs = delta.getFixedIPs();
379         if (fixedIPs != null && fixedIPs.size() > 1) {
380             LOG.warn("Neutron mapper does not support multiple IPs on the same port.");
381             return StatusCode.BAD_REQUEST;
382         }
383         return StatusCode.OK;
384     }
385
386     /**
387      * @see org.opendaylight.neutron.spi.INeutronPortAware#neutronPortUpdated(org.opendaylight.neutron.spi.NeutronPort)
388      */
389     @Override
390     public void neutronPortUpdated(NeutronPort port) {
391         LOG.trace("neutronPortUpdated - {}", port);
392         if (isRouterInterfacePort(port)) {
393             LOG.trace("Port is router interface - {} does nothing. {} handles router iface.",
394                     NeutronPortAware.class.getSimpleName(), NeutronRouterAware.class.getSimpleName());
395             return;
396         }
397         if (isRouterGatewayPort(port)) {
398             LOG.trace("Port is router gateway - {}", port.getID());
399             return;
400         }
401         if (isFloatingIpPort(port)) {
402             LOG.trace("Port is floating ip - {}", port.getID());
403             return;
404         }
405         if (Strings.isNullOrEmpty(port.getTenantID())) {
406             LOG.trace("REMOVE ME: Tenant is null - {}", port.getID());
407             return;
408         }
409
410         ReadOnlyTransaction rTx = dataProvider.newReadOnlyTransaction();
411         TenantId tenantId = new TenantId(Utils.normalizeUuid(port.getTenantID()));
412         MacAddress macAddress = new MacAddress(port.getMacAddress());
413         L2FloodDomainId l2FdId = new L2FloodDomainId(port.getNetworkUUID());
414         ForwardingCtx fwCtx = MappingUtils.createForwardingContext(tenantId, l2FdId, rTx);
415         boolean isFwCtxValid = validateForwardingCtx(fwCtx);
416         if (!isFwCtxValid) {
417             rTx.close();
418             return;
419         }
420
421         Optional<Endpoint> potentionalEp = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL,
422                 IidFactory.endpointIid(fwCtx.getL2BridgeDomain().getId(), macAddress), rTx);
423         if (!potentionalEp.isPresent()) {
424             LOG.warn("Illegal state - endpoint {} does not exist.", new EndpointKey(fwCtx.getL2BridgeDomain().getId(),
425                     macAddress));
426             rTx.close();
427             return;
428         }
429
430         Endpoint ep = potentionalEp.get();
431         if (isEpIpDifferentThanPortFixedIp(ep, port) || isEpgDifferentThanSecGrp(ep, port)) {
432             UnregisterEndpointInput unregisterEpRpcInput = createUnregisterEndpointInput(ep);
433             RegisterEndpointInput registerEpRpcInput = createRegisterEndpointInput(port, fwCtx);
434             try {
435                 RpcResult<Void> rpcResult = epService.unregisterEndpoint(unregisterEpRpcInput).get();
436                 if (!rpcResult.isSuccessful()) {
437                     LOG.warn("Illegal state - RPC unregisterEndpoint failed. Input of RPC: {}", unregisterEpRpcInput);
438                     rTx.close();
439                     return;
440                 }
441                 rpcResult = epService.registerEndpoint(registerEpRpcInput).get();
442                 if (!rpcResult.isSuccessful()) {
443                     LOG.warn("Illegal state - RPC registerEndpoint failed. Input of RPC: {}", registerEpRpcInput);
444                     rTx.close();
445                     return;
446                 }
447             } catch (InterruptedException | ExecutionException e) {
448                 LOG.error("addPort - RPC invocation failed.", e);
449                 rTx.close();
450                 return;
451             }
452         }
453         rTx.close();
454     }
455
456     private boolean isEpIpDifferentThanPortFixedIp(Endpoint ep, NeutronPort port) {
457         List<L3Address> l3Addresses = ep.getL3Address();
458         List<Neutron_IPs> fixedIPs = port.getFixedIPs();
459         if ((l3Addresses == null || l3Addresses.isEmpty()) && (fixedIPs == null || fixedIPs.isEmpty())) {
460             return false;
461         }
462         if (l3Addresses != null && !l3Addresses.isEmpty() && fixedIPs != null && !fixedIPs.isEmpty()) {
463             if (fixedIPs.get(0).getIpAddress().equals(Utils.getStringIpAddress(l3Addresses.get(0).getIpAddress()))) {
464                 return false;
465             }
466         }
467         return true;
468     }
469
470     private boolean isEpgDifferentThanSecGrp(Endpoint ep, NeutronPort port) {
471         List<EndpointGroupId> epgIds = ep.getEndpointGroups();
472         List<NeutronSecurityGroup> secGroups = port.getSecurityGroups();
473         if ((epgIds == null || epgIds.isEmpty()) && (secGroups == null || secGroups.isEmpty())) {
474             return false;
475         }
476         if (epgIds != null && !epgIds.isEmpty() && secGroups != null && !secGroups.isEmpty()) {
477             if (epgIds.size() != secGroups.size()) {
478                 return true;
479             }
480             Collection<EndpointGroupId> epgIdsFromSecGroups = Collections2.transform(secGroups,
481                     new Function<NeutronSecurityGroup, EndpointGroupId>() {
482
483                         @Override
484                         public EndpointGroupId apply(NeutronSecurityGroup input) {
485                             return new EndpointGroupId(input.getSecurityGroupUUID());
486                         }
487                     });
488             // order independent equals
489             Set<EndpointGroupId> one = new HashSet<>(epgIds);
490             Set<EndpointGroupId> two = new HashSet<>(epgIdsFromSecGroups);
491             if (one.equals(two)) {
492                 return false;
493             }
494         }
495         return true;
496     }
497
498     /**
499      * @see org.opendaylight.neutron.spi.INeutronPortAware#canDeletePort(org.opendaylight.neutron.spi.NeutronPort)
500      */
501     @Override
502     public int canDeletePort(NeutronPort port) {
503         LOG.trace("canDeletePort - {}", port);
504         // nothing to consider
505         return StatusCode.OK;
506     }
507
508     /**
509      * @see org.opendaylight.neutron.spi.INeutronPortAware#neutronPortDeleted(org.opendaylight.neutron.spi.NeutronPort)
510      */
511     @Override
512     public void neutronPortDeleted(NeutronPort port) {
513         LOG.trace("neutronPortDeleted - {}", port);
514         if (isRouterInterfacePort(port)) {
515             LOG.trace("Port is router interface - {} does nothing. {} handles router iface.",
516                     NeutronPortAware.class.getSimpleName(), NeutronRouterAware.class.getSimpleName());
517             return;
518         }
519         if (isRouterGatewayPort(port)) {
520             LOG.trace("Port is router gateway - {} does nothing. {} handles router iface.",
521                     NeutronPortAware.class.getSimpleName(), NeutronRouterAware.class.getSimpleName());
522             return;
523         }
524         if (isFloatingIpPort(port)) {
525             LOG.trace("Port is floating ip - {} device id - {}", port.getID(), port.getDeviceID());
526             floatingIpPortByDeviceId.remove(port.getDeviceID());
527         }
528         ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
529         TenantId tenantId = new TenantId(Utils.normalizeUuid(port.getTenantID()));
530         L2FloodDomainId l2FdId = new L2FloodDomainId(port.getNetworkUUID());
531         ForwardingCtx fwCtx = MappingUtils.createForwardingContext(tenantId, l2FdId, rwTx);
532         boolean isFwCtxValid = validateForwardingCtx(fwCtx);
533         if (!isFwCtxValid) {
534             rwTx.cancel();
535             return;
536         }
537
538         EndpointKey epKey = new EndpointKey(fwCtx.getL2BridgeDomain().getId(), new MacAddress(port.getMacAddress()));
539         deleteNeutronGbpMapping(port, epKey, rwTx);
540         UnregisterEndpointInput unregisterEpRpcInput = createUnregisterEndpointInput(port, fwCtx);
541         try {
542             RpcResult<Void> rpcResult = epService.unregisterEndpoint(unregisterEpRpcInput).get();
543             if (!rpcResult.isSuccessful()) {
544                 LOG.warn("Illegal state - RPC unregisterEndpoint failed. Input of RPC: {}", unregisterEpRpcInput);
545             }
546         } catch (InterruptedException | ExecutionException e) {
547             LOG.error("addPort - RPC invocation failed.", e);
548             rwTx.cancel();
549         }
550     }
551
552     private static void deleteNeutronGbpMapping(NeutronPort port, EndpointKey epKey, ReadWriteTransaction rwTx) {
553         UniqueId portId = new UniqueId(port.getID());
554         if (isRouterInterfacePort(port)) {
555             LOG.trace("Adding RouterInterfacePort-Endpoint mapping for port {} and endpoint {}", port.getID(), epKey);
556             DataStoreHelper.removeIfExists(LogicalDatastoreType.OPERATIONAL,
557                     IidFactory.endpointByRouterInterfacePortIid(portId), rwTx);
558             DataStoreHelper.removeIfExists(LogicalDatastoreType.OPERATIONAL,
559                     IidFactory.routerInterfacePortByEndpointIid(epKey.getL2Context(), epKey.getMacAddress()), rwTx);
560         } else if (isRouterGatewayPort(port)) {
561             LOG.trace("Adding RouterGatewayPort-Endpoint mapping for port {} and endpoint {}", port.getID(), epKey);
562             DataStoreHelper.removeIfExists(LogicalDatastoreType.OPERATIONAL,
563                     IidFactory.endpointByRouterGatewayPortIid(portId), rwTx);
564             DataStoreHelper.removeIfExists(LogicalDatastoreType.OPERATIONAL,
565                     IidFactory.routerGatewayPortByEndpointIid(epKey.getL2Context(), epKey.getMacAddress()), rwTx);
566         } else if (isFloatingIpPort(port)) {
567             LOG.trace("Adding FloatingIpPort-Endpoint mapping for port {} and endpoint {}", port.getID(), epKey);
568             DataStoreHelper.removeIfExists(LogicalDatastoreType.OPERATIONAL,
569                     IidFactory.endpointByFloatingIpPortIid(portId), rwTx);
570             DataStoreHelper.removeIfExists(LogicalDatastoreType.OPERATIONAL,
571                     IidFactory.floatingIpPortByEndpointIid(epKey.getL2Context(), epKey.getMacAddress()), rwTx);
572         } else {
573             LOG.trace("Adding Port-Endpoint mapping for port {} (device owner {}) and endpoint {}", port.getID(),
574                     port.getDeviceOwner(), epKey);
575             DataStoreHelper.removeIfExists(LogicalDatastoreType.OPERATIONAL, IidFactory.endpointByPortIid(portId), rwTx);
576             DataStoreHelper.removeIfExists(LogicalDatastoreType.OPERATIONAL,
577                     IidFactory.portByEndpointIid(epKey.getL2Context(), epKey.getMacAddress()), rwTx);
578         }
579     }
580
581     private static RegisterL3PrefixEndpointInput createRegisterL3PrefixEndpointInput(EndpointL3PrefixKey key, List<EndpointL3Key> endpointL3Keys, TenantId tenantId) {
582         List<EndpointGroupId> epgIds = new ArrayList<>();
583         // each EP has to be in EPG ANY, except dhcp and router
584         epgIds.add(MappingUtils.EPG_ANY_ID);
585
586         List<EndpointL3Gateways> l3Gateways = new ArrayList<EndpointL3Gateways>();
587         for (EndpointL3Key epL3Key : endpointL3Keys) {
588             EndpointL3Gateways l3Gateway = new EndpointL3GatewaysBuilder().setIpAddress(epL3Key.getIpAddress())
589                 .setL3Context(epL3Key.getL3Context())
590                 .build();
591             l3Gateways.add(l3Gateway);
592         }
593         RegisterL3PrefixEndpointInputBuilder inputBuilder = new RegisterL3PrefixEndpointInputBuilder()
594                                                 .setL3Context(key.getL3Context())
595                                                 .setIpPrefix(key.getIpPrefix())
596                                                 .setEndpointGroups(epgIds)
597                                                 .setTenant(tenantId)
598                                                 .setEndpointL3Gateways(l3Gateways)
599                                                 .setTimestamp(System.currentTimeMillis());
600         return inputBuilder.build();
601     }
602
603     private static RegisterEndpointInput createRegisterEndpointInput(NeutronPort port, ForwardingCtx fwCtx) {
604         List<EndpointGroupId> epgIds = new ArrayList<>();
605         // each EP has to be in EPG ANY, except dhcp and router
606         if (isDhcpPort(port)) {
607             epgIds.add(MappingUtils.EPG_DHCP_ID);
608         } else if (!containsSecRuleWithRemoteSecGroup(port.getSecurityGroups())) {
609             epgIds.add(MappingUtils.EPG_ANY_ID);
610         }
611
612         List<NeutronSecurityGroup> securityGroups = port.getSecurityGroups();
613         if ((securityGroups == null || securityGroups.isEmpty())) {
614             if (!isDhcpPort(port)) {
615                 LOG.warn(
616                         "Port {} does not contain any security group. The port should belong to 'default' security group at least.",
617                         port.getPortUUID());
618             }
619         } else {
620             for (NeutronSecurityGroup secGrp : securityGroups) {
621                 epgIds.add(new EndpointGroupId(secGrp.getSecurityGroupUUID()));
622             }
623         }
624         LocationType locationType = LocationType.Internal;
625         if(isRouterGatewayPort(port)) {
626             locationType = LocationType.External;
627         }
628         RegisterEndpointInputBuilder inputBuilder = new RegisterEndpointInputBuilder().setL2Context(
629                 fwCtx.getL2BridgeDomain().getId())
630             .setMacAddress(new MacAddress(port.getMacAddress()))
631             .setTenant(new TenantId(Utils.normalizeUuid(port.getTenantID())))
632             .setEndpointGroups(epgIds)
633             .addAugmentation(OfOverlayContextInput.class,
634                     new OfOverlayContextInputBuilder()
635                         .setPortName(createTapPortName(port))
636                         .setLocationType(locationType)
637                     .build())
638             .setTimestamp(System.currentTimeMillis());
639         List<Neutron_IPs> fixedIPs = port.getFixedIPs();
640         // TODO Li msunal this getting of just first IP has to be rewrite when OFOverlay renderer
641         // will support l3-endpoints. Then we will register L2 and L3 endpoints separately.
642         Neutron_IPs firstIp = MappingUtils.getFirstIp(fixedIPs);
643         if (firstIp != null) {
644             inputBuilder.setNetworkContainment(new SubnetId(firstIp.getSubnetUUID()));
645             L3Address l3Address = new L3AddressBuilder().setIpAddress(Utils.createIpAddress(firstIp.getIpAddress()))
646                 .setL3Context(fwCtx.getL3Context().getId())
647                 .build();
648             inputBuilder.setL3Address(ImmutableList.of(l3Address));
649         }
650         if (!Strings.isNullOrEmpty(port.getName())) {
651
652         }
653         return inputBuilder.build();
654     }
655
656     private static boolean containsSecRuleWithRemoteSecGroup(List<NeutronSecurityGroup> secGroups) {
657         if (secGroups == null) {
658             return false;
659         }
660         for (NeutronSecurityGroup secGroup : secGroups) {
661             boolean containsSecRuleWithRemoteSecGroup = containsSecRuleWithRemoteSecGroup(secGroup);
662             if (containsSecRuleWithRemoteSecGroup) {
663                 return true;
664             }
665         }
666         return false;
667     }
668
669     private static boolean containsSecRuleWithRemoteSecGroup(NeutronSecurityGroup secGroup) {
670         List<NeutronSecurityRule> secRules = secGroup.getSecurityRules();
671         if (secRules == null) {
672             return false;
673         }
674         for (NeutronSecurityRule secRule : secRules) {
675             if (!Strings.isNullOrEmpty(secRule.getSecurityRemoteGroupID())) {
676                 return true;
677             }
678         }
679         return false;
680     }
681
682     private static Name createTapPortName(NeutronPort port) {
683         return new Name("tap" + port.getID().substring(0, 11));
684     }
685
686     private static boolean isDhcpPort(NeutronPort port) {
687         return DEVICE_OWNER_DHCP.equals(port.getDeviceOwner());
688     }
689
690     private static boolean isRouterInterfacePort(NeutronPort port) {
691         return DEVICE_OWNER_ROUTER_IFACE.equals(port.getDeviceOwner());
692     }
693
694     private static boolean isRouterGatewayPort(NeutronPort port) {
695         return DEVICE_OWNER_ROUTER_GATEWAY.equals(port.getDeviceOwner());
696     }
697
698     private static boolean isFloatingIpPort(NeutronPort port) {
699         return DEVICE_OWNER_FLOATING_IP.equals(port.getDeviceOwner());
700     }
701
702     private UnregisterEndpointInput createUnregisterEndpointInput(Endpoint ep) {
703         UnregisterEndpointInputBuilder inputBuilder = new UnregisterEndpointInputBuilder();
704         L2 l2Ep = new L2Builder().setL2Context(ep.getL2Context()).setMacAddress(ep.getMacAddress()).build();
705         inputBuilder.setL2(ImmutableList.of(l2Ep));
706         // TODO Li msunal this has to be rewrite when OFOverlay renderer will support l3-endpoints.
707         // Endpoint probably will not have l3-addresses anymore, because L2 and L3 endpoints should
708         // be registered separately.
709         if (ep.getL3Address() != null && !ep.getL3Address().isEmpty()) {
710             List<L3> l3Eps = new ArrayList<>();
711             for (L3Address ip : ep.getL3Address()) {
712                 l3Eps.add(new L3Builder().setL3Context(ip.getL3Context()).setIpAddress(ip.getIpAddress()).build());
713             }
714             inputBuilder.setL3(l3Eps);
715         }
716         return inputBuilder.build();
717     }
718
719     private UnregisterEndpointInput createUnregisterEndpointInput(NeutronPort port, ForwardingCtx fwCtx) {
720         UnregisterEndpointInputBuilder inputBuilder = new UnregisterEndpointInputBuilder();
721         L2 l2Ep = new L2Builder().setL2Context(fwCtx.getL2BridgeDomain().getId())
722             .setMacAddress(new MacAddress(port.getMacAddress()))
723             .build();
724         inputBuilder.setL2(ImmutableList.of(l2Ep));
725         // TODO Li msunal this has to be rewrite when OFOverlay renderer will support l3-endpoints.
726         // Endpoint probably will not have l3-addresses anymore, because L2 and L3 endpoints should
727         // be registered separately.
728         if (port.getFixedIPs() != null && !port.getFixedIPs().isEmpty()) {
729             inputBuilder.setL3(createL3s(port.getFixedIPs(), fwCtx.getL3Context().getId()));
730         }
731         return inputBuilder.build();
732     }
733
734     private List<L3> createL3s(List<Neutron_IPs> neutronIps, L3ContextId l3ContextId) {
735         List<L3> l3s = new ArrayList<>();
736         for (Neutron_IPs fixedIp : neutronIps) {
737             String ip = fixedIp.getIpAddress();
738             L3 l3 = new L3Builder().setIpAddress(Utils.createIpAddress(ip)).setL3Context(l3ContextId).build();
739             l3s.add(l3);
740         }
741         return l3s;
742     }
743
744     public static UniqueId getFloatingIpPortIdByDeviceId(String deviceId) {
745         return floatingIpPortByDeviceId.get(deviceId);
746     }
747
748 }