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