Neutron-mapper uses only DTOs from neutron.yang
[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.Collections;
14 import java.util.List;
15 import java.util.Set;
16
17 import javax.annotation.Nullable;
18
19 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
20 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
21 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
22 import org.opendaylight.groupbasedpolicy.neutron.gbp.util.NeutronGbpIidFactory;
23 import org.opendaylight.groupbasedpolicy.neutron.mapper.EndpointRegistrator;
24 import org.opendaylight.groupbasedpolicy.neutron.mapper.infrastructure.NetworkClient;
25 import org.opendaylight.groupbasedpolicy.neutron.mapper.infrastructure.NetworkService;
26 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.PortUtils;
27 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.SubnetUtils;
28 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
29 import org.opendaylight.groupbasedpolicy.util.IidFactory;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L2BridgeDomainId;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L2FloodDomainId;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L3ContextId;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubnetId;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.UniqueId;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.RegisterEndpointInput;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.RegisterEndpointInputBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.UnregisterEndpointInput;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.UnregisterEndpointInputBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.fields.L3Address;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.fields.L3AddressBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointKey;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.unregister.endpoint.input.L2;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.unregister.endpoint.input.L2Builder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.unregister.endpoint.input.L3;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.unregister.endpoint.input.L3Builder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.gbp.by.neutron.mappings.endpoints.by.ports.EndpointByPort;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.neutron.by.gbp.mappings.ports.by.endpoints.PortByEndpoint;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.L2BridgeDomain;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.L2BridgeDomainBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet;
59 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
62
63 import com.google.common.base.Optional;
64 import com.google.common.collect.ImmutableList;
65
66 public class NeutronPortAware implements NeutronAware<Port> {
67
68     private static final Logger LOG = LoggerFactory.getLogger(NeutronPortAware.class);
69     public static final InstanceIdentifier<Port> PORT_WILDCARD_IID =
70             InstanceIdentifier.builder(Neutron.class).child(Ports.class).child(Port.class).build();
71     private final DataBroker dataProvider;
72     private final EndpointRegistrator epRegistrator;
73
74     public NeutronPortAware(DataBroker dataProvider, EndpointRegistrator epRegistrator) {
75         this.dataProvider = checkNotNull(dataProvider);
76         this.epRegistrator = checkNotNull(epRegistrator);
77     }
78
79     @Override
80     public void onCreated(Port port, Neutron neutron) {
81         LOG.trace("created port - {}", port);
82         if (PortUtils.isRouterInterfacePort(port)) {
83             LOG.trace("Port is router interface port: {}", port.getUuid().getValue());
84             // router interface port can have only one IP
85             Optional<FixedIps> potentialPortIpWithSubnet = PortUtils.resolveFirstFixedIps(port);
86             if (!potentialPortIpWithSubnet.isPresent()) {
87                 LOG.warn("Illegal state - router interface port does not contain fixed IPs {}",
88                         port);
89                 return;
90             }
91             FixedIps portIpWithSubnet = potentialPortIpWithSubnet.get();
92             L3ContextId routerL3Context = new L3ContextId(port.getDeviceId());
93             // change L3Context for all EPs with same subnet as router port
94             changeL3ContextForEpsInSubnet(portIpWithSubnet.getSubnetId(), neutron.getPorts(), routerL3Context);
95             // set L3Context as parent for bridge domain which is parent of subnet
96             TenantId tenantId = new TenantId(port.getTenantId().getValue());
97             Optional<Subnet> potentialRouterPortSubnet = SubnetUtils.findSubnet(portIpWithSubnet.getSubnetId(), neutron.getSubnets());
98             if (!potentialRouterPortSubnet.isPresent()) {
99                 LOG.warn("Illegal state - router interface port is in subnet which does not exist. {}",
100                         port);
101                 return;
102             }
103             ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
104             Subnet routerPortSubnet = potentialRouterPortSubnet.get();
105             L2BridgeDomainId l2BdId = new L2BridgeDomainId(routerPortSubnet.getNetworkId().getValue());
106             L2BridgeDomain l2Bd = new L2BridgeDomainBuilder().setId(l2BdId).setParent(routerL3Context).build();
107             rwTx.merge(LogicalDatastoreType.CONFIGURATION, IidFactory.l2BridgeDomainIid(tenantId, l2BdId), l2Bd);
108             // set virtual router IP for subnet
109             org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.Subnet subnet =
110                     NeutronSubnetAware.createSubnet(routerPortSubnet, portIpWithSubnet.getIpAddress());
111             rwTx.merge(LogicalDatastoreType.CONFIGURATION, IidFactory.subnetIid(tenantId, subnet.getId()), subnet);
112             DataStoreHelper.submitToDs(rwTx);
113         } else if (PortUtils.isDhcpPort(port)) {
114             // process as normal port but put it to DHCP group
115             LOG.trace("Port is DHCP port: {}", port.getUuid().getValue());
116             Optional<FixedIps> firstFixedIps = PortUtils.resolveFirstFixedIps(port);
117             if (!firstFixedIps.isPresent()) {
118                 LOG.warn("DHCP port does not have an IP address. {}", port);
119                 return;
120             }
121             RegisterEndpointInputBuilder epInBuilder = createBasicEndpointInputBuilder(port);
122             // endpoint has only one network containment therefore only first IP is used
123             FixedIps ipWithSubnet = firstFixedIps.get();
124             epInBuilder.setNetworkContainment(new SubnetId(ipWithSubnet.getSubnetId().getValue()));
125             L3Address l3Address = new L3AddressBuilder().setL3Context(new L3ContextId(port.getNetworkId().getValue()))
126                 .setIpAddress(ipWithSubnet.getIpAddress())
127                 .build();
128             epInBuilder.setL3Address(ImmutableList.of(l3Address));
129             List<EndpointGroupId> epgsFromSecGroups = resolveEpgIdsFromSecGroups(port.getSecurityGroups());
130             epgsFromSecGroups.add(NetworkService.EPG_ID);
131             epInBuilder.setEndpointGroups(epgsFromSecGroups);
132             registerEndpointAndStoreMapping(epInBuilder.build(), port);
133         } else if (PortUtils.isNormalPort(port)) {
134             LOG.trace("Port is normal port: {}", port.getUuid().getValue());
135             RegisterEndpointInputBuilder epInBuilder = createBasicEndpointInputBuilder(port);
136             Optional<FixedIps> firstFixedIps = PortUtils.resolveFirstFixedIps(port);
137             if (firstFixedIps.isPresent()) {
138                 // endpoint has only one network containment therefore only first IP is used
139                 FixedIps ipWithSubnet = firstFixedIps.get();
140                 epInBuilder.setNetworkContainment(new SubnetId(ipWithSubnet.getSubnetId().getValue()));
141                 L3Address l3Address = resolveL3AddressFromPort(port, ipWithSubnet, neutron);
142                 epInBuilder.setL3Address(ImmutableList.of(l3Address));
143             } else {
144                 epInBuilder.setNetworkContainment(new L2FloodDomainId(port.getNetworkId().getValue()));
145             }
146             List<EndpointGroupId> epgsFromSecGroups = resolveEpgIdsFromSecGroups(port.getSecurityGroups());
147             epgsFromSecGroups.add(NetworkClient.EPG_ID);
148             epInBuilder.setEndpointGroups(epgsFromSecGroups);
149             registerEndpointAndStoreMapping(epInBuilder.build(), port);
150         } else if (PortUtils.isRouterGatewayPort(port)) {
151             // do nothing because actual trigger is attaching of port to router
152             LOG.trace("Port is router gateway port: {}", port.getUuid().getValue());
153         } else if (PortUtils.isFloatingIpPort(port)) {
154             // do nothing because trigger is floating IP
155             LOG.trace("Port is floating ip: {}", port.getUuid().getValue());
156         } else {
157             LOG.warn("Unknown port: {}", port);
158         }
159     }
160
161     private void changeL3ContextForEpsInSubnet(Uuid subnetUuid, Ports ports, L3ContextId newl3Context) {
162         Set<Port> portsInSameSubnet = PortUtils.findPortsBySubnet(subnetUuid, ports);
163         for (Port portInSameSubnet : portsInSameSubnet) {
164             if (PortUtils.isNormalPort(portInSameSubnet) || PortUtils.isDhcpPort(portInSameSubnet)) {
165                 // endpoints are created only from neutron normal port or DHCP port
166                 Optional<FixedIps> firstFixedIps = PortUtils.resolveFirstFixedIps(portInSameSubnet);
167                 if (firstFixedIps.isPresent()) {
168                     // endpoint has only one network containment therefore only first IP is used
169                     FixedIps ipWithSubnet = firstFixedIps.get();
170                     RegisterEndpointInputBuilder epInBuilder = createBasicEndpointInputBuilder(portInSameSubnet);
171                     epInBuilder.setNetworkContainment(new SubnetId(ipWithSubnet.getSubnetId().getValue()));
172                     L3Address l3Address = new L3AddressBuilder().setL3Context(newl3Context)
173                         .setIpAddress(ipWithSubnet.getIpAddress())
174                         .build();
175                     epInBuilder.setL3Address(ImmutableList.of(l3Address));
176                     List<EndpointGroupId> epgsFromSecGroups =
177                             resolveEpgIdsFromSecGroups(portInSameSubnet.getSecurityGroups());
178                     epgsFromSecGroups.add(NetworkClient.EPG_ID);
179                     epRegistrator.registerEndpoint(epInBuilder.build());
180                     // unregister L3EP
181                     L3ContextId oldL3Context = new L3ContextId(portInSameSubnet.getNetworkId().getValue());
182                     L3 l3 = new L3Builder().setL3Context(oldL3Context).setIpAddress(ipWithSubnet.getIpAddress())
183                         .build();
184                     UnregisterEndpointInput epUnreg = new UnregisterEndpointInputBuilder().setL3(ImmutableList.of(l3)).build();
185                     epRegistrator.unregisterEndpoint(epUnreg);
186                 }
187             }
188         }
189     }
190
191     private static RegisterEndpointInputBuilder createBasicEndpointInputBuilder(Port port) {
192         return new RegisterEndpointInputBuilder().setL2Context(new L2BridgeDomainId(port.getNetworkId().getValue()))
193             .setMacAddress(new MacAddress(port.getMacAddress()))
194             .setTenant(new TenantId(port.getTenantId().getValue()))
195             .setTimestamp(System.currentTimeMillis());
196     }
197
198     private static List<EndpointGroupId> resolveEpgIdsFromSecGroups(@Nullable List<Uuid> securityGroups) {
199         List<EndpointGroupId> epgIds = new ArrayList<>();
200         if ((securityGroups == null || securityGroups.isEmpty())) {
201             return epgIds;
202         }
203         for (Uuid secGrp : securityGroups) {
204             epgIds.add(new EndpointGroupId(secGrp.getValue()));
205         }
206         return epgIds;
207     }
208
209     private void registerEndpointAndStoreMapping(RegisterEndpointInput regEpInput, Port port) {
210         boolean isRegisteredEndpoint = epRegistrator.registerEndpoint(regEpInput);
211         if (isRegisteredEndpoint) {
212             ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
213             UniqueId portId = new UniqueId(port.getUuid().getValue());
214             EndpointKey epKey = new EndpointKey(new L2BridgeDomainId(port.getNetworkId().getValue()),
215                     new MacAddress(port.getMacAddress()));
216             LOG.trace("Adding Port-Endpoint mapping for port {} (device owner {}) and endpoint {}",
217                     port.getUuid().getValue(), port.getDeviceOwner(), epKey);
218             EndpointByPort endpointByPort = MappingFactory.createEndpointByPort(epKey, portId);
219             rwTx.put(LogicalDatastoreType.OPERATIONAL, NeutronGbpIidFactory.endpointByPortIid(portId), endpointByPort,
220                     true);
221             PortByEndpoint portByEndpoint = MappingFactory.createPortByEndpoint(portId, epKey);
222             rwTx.put(LogicalDatastoreType.OPERATIONAL,
223                     NeutronGbpIidFactory.portByEndpointIid(epKey.getL2Context(), epKey.getMacAddress()), portByEndpoint,
224                     true);
225             DataStoreHelper.submitToDs(rwTx);
226         }
227     }
228
229     @Override
230     public void onUpdated(Port oldPort, Port newPort, Neutron oldNeutron, Neutron newNeutron) {
231         LOG.trace("updated port - OLD: {}\nNEW: {}", oldPort, newPort);
232         onDeleted(oldPort, oldNeutron, newNeutron);
233         onCreated(newPort, newNeutron);
234     }
235
236     @Override
237     public void onDeleted(Port port, Neutron oldNeutron, Neutron newNeutron) {
238         LOG.trace("deleted port - {}", port);
239         if (PortUtils.isRouterInterfacePort(port)) {
240             LOG.trace("Port is router interface port: {}", port.getUuid().getValue());
241             // router interface port can have only one IP
242             Optional<FixedIps> potentialPortIpWithSubnet = PortUtils.resolveFirstFixedIps(port);
243             if (!potentialPortIpWithSubnet.isPresent()) {
244                 LOG.warn("Illegal state - router interface port does not contain fixed IPs {}",
245                         port);
246                 return;
247             }
248             FixedIps portIpWithSubnet = potentialPortIpWithSubnet.get();
249             L3ContextId l3Context = new L3ContextId(port.getNetworkId().getValue());
250             // change L3Context for all EPs with same subnet as router port
251             changeL3ContextForEpsInSubnet(portIpWithSubnet.getSubnetId(), oldNeutron.getPorts(), l3Context);
252             // set L3Context as parent for bridge domain which is parent of subnet
253             TenantId tenantId = new TenantId(port.getTenantId().getValue());
254             Optional<Subnet> potentialRouterPortSubnet = SubnetUtils.findSubnet(portIpWithSubnet.getSubnetId(), oldNeutron.getSubnets());
255             if (!potentialRouterPortSubnet.isPresent()) {
256                 LOG.warn("Illegal state - router interface port is in subnet which does not exist. {}",
257                         port);
258                 return;
259             }
260             ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
261             Subnet routerPortSubnet = potentialRouterPortSubnet.get();
262             L2BridgeDomainId l2BdId = new L2BridgeDomainId(routerPortSubnet.getNetworkId().getValue());
263             L2BridgeDomain l2Bd = new L2BridgeDomainBuilder().setId(l2BdId).setParent(l3Context).build();
264             rwTx.merge(LogicalDatastoreType.CONFIGURATION, IidFactory.l2BridgeDomainIid(tenantId, l2BdId), l2Bd);
265             // remove virtual router IP for subnet
266             org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.Subnet subnet =
267                     NeutronSubnetAware.createSubnet(routerPortSubnet, null);
268             rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.subnetIid(tenantId, subnet.getId()), subnet);
269             DataStoreHelper.submitToDs(rwTx);
270         } else if (PortUtils.isDhcpPort(port)) {
271             LOG.trace("Port is DHCP port: {}", port.getUuid().getValue());
272             UnregisterEndpointInput unregEpInput = createUnregisterEndpointInput(port, oldNeutron);
273             unregisterEndpointAndRemoveMapping(unregEpInput, port);
274         } else if (PortUtils.isNormalPort(port)) {
275             LOG.trace("Port is normal port: {}", port.getUuid().getValue());
276             UnregisterEndpointInput unregEpInput = createUnregisterEndpointInput(port, oldNeutron);
277             unregisterEndpointAndRemoveMapping(unregEpInput, port);
278         } else if (PortUtils.isRouterGatewayPort(port)) {
279             // do nothing because actual trigger is detaching of port from router
280             LOG.trace("Port is router gateway port: {}", port.getUuid().getValue());
281         } else if (PortUtils.isFloatingIpPort(port)) {
282             // do nothing because trigger is floating IP
283             LOG.trace("Port is floating ip: {}", port.getUuid().getValue());
284         } else {
285             LOG.warn("Unknown port: {}", port);
286         }
287     }
288
289     private UnregisterEndpointInput createUnregisterEndpointInput(Port port, Neutron neutron) {
290         UnregisterEndpointInputBuilder inputBuilder = new UnregisterEndpointInputBuilder();
291         L2 l2Ep = new L2Builder().setL2Context(new L2BridgeDomainId(port.getNetworkId().getValue()))
292             .setMacAddress(new MacAddress(port.getMacAddress()))
293             .build();
294         inputBuilder.setL2(ImmutableList.of(l2Ep));
295         // we've registered EP with only first IP so remove only EP with first IP
296         Optional<FixedIps> potentialFirstIp = PortUtils.resolveFirstFixedIps(port);
297         if (!potentialFirstIp.isPresent()) {
298             FixedIps firstIp = potentialFirstIp.get();
299             L3Address l3Address = resolveL3AddressFromPort(port, firstIp, neutron);
300             L3 l3 = new L3Builder().setIpAddress(l3Address.getIpAddress())
301                 .setL3Context(l3Address.getL3Context())
302                 .build();
303             inputBuilder.setL3(ImmutableList.of(l3));
304         }
305         return inputBuilder.build();
306     }
307
308     private void unregisterEndpointAndRemoveMapping(UnregisterEndpointInput unregEpInput, Port port) {
309         boolean isUnregisteredEndpoint = epRegistrator.unregisterEndpoint(unregEpInput);
310         if (isUnregisteredEndpoint) {
311             ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
312             UniqueId portId = new UniqueId(port.getUuid().getValue());
313             EndpointKey epKey = new EndpointKey(new L2BridgeDomainId(port.getNetworkId().getValue()),
314                     new MacAddress(port.getMacAddress()));
315             LOG.trace("Removing Port-Endpoint mapping for port {} (device owner {}) and endpoint {}",
316                     port.getUuid().getValue(), port.getDeviceOwner(), epKey);
317             DataStoreHelper.removeIfExists(LogicalDatastoreType.OPERATIONAL,
318                     NeutronGbpIidFactory.endpointByPortIid(portId), rwTx);
319             DataStoreHelper.removeIfExists(LogicalDatastoreType.OPERATIONAL,
320                     NeutronGbpIidFactory.portByEndpointIid(epKey.getL2Context(), epKey.getMacAddress()), rwTx);
321             DataStoreHelper.submitToDs(rwTx);
322         }
323     }
324
325     private static L3Address resolveL3AddressFromPort(Port port, FixedIps portFixedIPs, Neutron neutron) {
326         Set<Port> routerIfacePorts = PortUtils.findRouterInterfacePorts(neutron.getPorts());
327         for (Port routerIfacePort : routerIfacePorts) {
328             Uuid routerIfacePortSubnet = routerIfacePort.getFixedIps().get(0).getSubnetId();
329             // if port is in the same subnet as router interface then we want to use L3Context of
330             // router
331             if (portFixedIPs.getSubnetId().equals(routerIfacePortSubnet)) {
332                 L3ContextId epL3ContextId = new L3ContextId(routerIfacePort.getDeviceId());
333                 LOG.trace("Router interface port was found in the same subnet as port have {}", port);
334                 return new L3AddressBuilder().setL3Context(epL3ContextId)
335                     .setIpAddress(portFixedIPs.getIpAddress())
336                     .build();
337             }
338         }
339         return new L3AddressBuilder().setL3Context(new L3ContextId(port.getNetworkId().getValue()))
340             .setIpAddress(portFixedIPs.getIpAddress())
341             .build();
342     }
343
344 }