2227224946b31a4c017a611883b79bb79d3a84a7
[groupbasedpolicy.git] / neutron-mapper / src / main / java / org / opendaylight / groupbasedpolicy / neutron / mapper / mapping / NeutronRouterAware.java
1 /*
2  * Copyright (c) 2015 Intel, 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
9 package org.opendaylight.groupbasedpolicy.neutron.mapper.mapping;
10
11 import static com.google.common.base.Preconditions.checkNotNull;
12
13 import java.util.List;
14
15 import javax.annotation.Nonnull;
16
17 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
18 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
19 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
20 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
21 import org.opendaylight.groupbasedpolicy.domain_extension.l2_l3.util.L2L3IidFactory;
22 import org.opendaylight.groupbasedpolicy.neutron.gbp.util.NeutronGbpIidFactory;
23 import org.opendaylight.groupbasedpolicy.neutron.mapper.EndpointRegistrator;
24 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.MappingUtils;
25 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.PortUtils;
26 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.SubnetUtils;
27 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
28 import org.opendaylight.groupbasedpolicy.util.IidFactory;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpPrefix;
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.base_endpoint.rev160427.common.endpoint.fields.NetworkContainmentBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.common.endpoint.fields.network.containment.containment.NetworkDomainContainmentBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.register.endpoint.input.AddressEndpointRegBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContextId;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Description;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L2BridgeDomainId;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L3ContextId;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Name;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.NetworkDomainId;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubnetId;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3Key;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.IpPrefixType;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.SubnetAugmentForwarding;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.SubnetAugmentForwardingBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.has.subnet.Subnet;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.has.subnet.SubnetBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.rev160427.forwarding.forwarding.by.tenant.ForwardingContext;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.rev160427.forwarding.forwarding.by.tenant.ForwardingContextBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.rev160427.forwarding.forwarding.by.tenant.NetworkDomain;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.rev160427.forwarding.forwarding.by.tenant.NetworkDomainBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.rev160427.forwarding.forwarding.by.tenant.NetworkDomainKey;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.neutron.by.gbp.mappings.external.gateways.as.endpoints.ExternalGatewayAsEndpoint;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.gbp.mapper.rev150513.mappings.neutron.by.gbp.mappings.external.gateways.as.l3.endpoints.ExternalGatewayAsL3Endpoint;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.L2BridgeDomain;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.L2BridgeDomainBuilder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.L3Context;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.L3ContextBuilder;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.Routers;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
65 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68
69 import com.google.common.base.Optional;
70 import com.google.common.base.Strings;
71 import com.google.common.collect.ImmutableList;
72
73 public class NeutronRouterAware implements NeutronAware<Router> {
74
75     private static final Logger LOG = LoggerFactory.getLogger(NeutronRouterAware.class);
76     public static final InstanceIdentifier<Router> ROUTER_WILDCARD_IID =
77             InstanceIdentifier.builder(Neutron.class).child(Routers.class).child(Router.class).build();
78     private final DataBroker dataProvider;
79     private final EndpointRegistrator epRegistrator;
80
81     public NeutronRouterAware(DataBroker dataProvider, EndpointRegistrator epRegistrator) {
82         this.dataProvider = checkNotNull(dataProvider);
83         this.epRegistrator = checkNotNull(epRegistrator);
84     }
85
86     @Override
87     public void onCreated(Router router, Neutron neutron) {
88         LOG.trace("created router - {}", router);
89
90         ContextId routerl3ContextId = new ContextId(router.getUuid().getValue());
91         TenantId tenantId = new TenantId(router.getTenantId().getValue());
92         InstanceIdentifier<ForwardingContext> routerL3CtxIid = L2L3IidFactory.l3ContextIid(tenantId, routerl3ContextId);
93         ForwardingContextBuilder fwdCtxBuilder = new ForwardingContextBuilder();
94         Name routerName = null;
95         if (!Strings.isNullOrEmpty(router.getName())) {
96             try {
97                 routerName = new Name(router.getName());
98                 fwdCtxBuilder.setName(routerName);
99             } catch (Exception e) {
100                 LOG.info("Name '{}' of Neutron Subnet '{}' is ignored.", router.getName(),
101                         router.getUuid().getValue());
102                 LOG.debug("Name exception", e);
103             }
104         }
105         ForwardingContext routerl3Context = fwdCtxBuilder.setContextId(routerl3ContextId)
106             .setContextType(MappingUtils.L3_CONTEXT)
107             .build();
108         WriteTransaction wTx = dataProvider.newWriteOnlyTransaction();
109         wTx.put(LogicalDatastoreType.CONFIGURATION, routerL3CtxIid, routerl3Context, true);
110         createTenantL3Context(new L3ContextId(routerl3ContextId), tenantId, routerName, wTx);
111         DataStoreHelper.submitToDs(wTx);
112     }
113
114     @Deprecated
115     private void createTenantL3Context(L3ContextId l3ContextId, TenantId tenantId, Name name, WriteTransaction wTx) {
116         L3ContextBuilder l3ContextBuilder = new L3ContextBuilder();
117         if (name != null) {
118             l3ContextBuilder.setName(name);
119         }
120         L3Context l3Context = l3ContextBuilder.setId(l3ContextId).build();
121         wTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.l3ContextIid(tenantId, l3ContextId), l3Context);
122     }
123
124     @Override
125     public void onUpdated(Router oldRouter, Router newRouter, Neutron oldNeutron, Neutron newNeutron) {
126         LOG.trace("updated router - OLD: {}\nNEW: {}", oldRouter, newRouter);
127
128         ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
129         TenantId tenantId = new TenantId(newRouter.getTenantId().getValue());
130         ContextId routerL3CtxId = new ContextId(newRouter.getUuid().getValue());
131
132         if (newRouter.getGatewayPortId() != null && oldRouter.getGatewayPortId() == null) {
133             // external network is attached to router
134             Uuid gatewayPortId = newRouter.getGatewayPortId();
135             Optional<Port> potentialGwPort = PortUtils.findPort(gatewayPortId, newNeutron.getPorts());
136             if (!potentialGwPort.isPresent()) {
137                 LOG.warn("Illegal state - router gateway port {} does not exist for router {}.",
138                         gatewayPortId.getValue(), newRouter);
139                 rwTx.cancel();
140                 return;
141             }
142
143             Port gwPort = potentialGwPort.get();
144             List<FixedIps> fixedIpsFromGwPort = gwPort.getFixedIps();
145             if (fixedIpsFromGwPort == null || fixedIpsFromGwPort.isEmpty()) {
146                 LOG.warn("Illegal state - router gateway port {} does not contain fixed IPs {}",
147                         gatewayPortId.getValue(), gwPort);
148                 rwTx.cancel();
149                 return;
150             }
151
152             // router can have only one external network
153             FixedIps ipWithSubnetFromGwPort = fixedIpsFromGwPort.get(0);
154             Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet> potentialSubnet = SubnetUtils.findSubnet(ipWithSubnetFromGwPort.getSubnetId(), newNeutron.getSubnets());
155             if (!potentialSubnet.isPresent()) {
156                 LOG.warn("Illegal state - Subnet {} does not exist for router {}.",
157                         ipWithSubnetFromGwPort.getSubnetId(), newRouter);
158                 rwTx.cancel();
159                 return;
160             }
161             IpPrefix gatewayIp =  MappingUtils.ipAddressToIpPrefix(potentialSubnet.get().getGatewayIp());
162             boolean registeredExternalGateway = registerExternalGateway(tenantId, gatewayIp,
163                     routerL3CtxId, new NetworkDomainId(ipWithSubnetFromGwPort.getSubnetId().getValue()));
164             if (!registeredExternalGateway) {
165                 LOG.warn("Could not add L3Prefix as gateway of default route. Gateway port {}", gwPort);
166                 rwTx.cancel();
167                 return;
168             }
169             addNeutronExtGwGbpMapping(routerL3CtxId, gatewayIp, rwTx);
170             NetworkDomain subnetDomain = createSubnetWithVirtualRouterIp(gatewayIp, new NetworkDomainId(ipWithSubnetFromGwPort.getSubnetId()
171                 .getValue()));
172             rwTx.merge(LogicalDatastoreType.CONFIGURATION, L2L3IidFactory.subnetIid(tenantId, subnetDomain.getNetworkDomainId()),
173                     subnetDomain);
174             ContextId l2BdId = new ContextId(potentialSubnet.get().getNetworkId().getValue());
175             Optional<ForwardingContext> optBd = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,
176                     L2L3IidFactory.l2BridgeDomainIid(tenantId, l2BdId), rwTx);
177             if (!optBd.isPresent()) {
178                 LOG.warn(
179                         "Could not read L2-Bridge-Domain {} Modifiaction of it's parent to L3-Context of router {} aborted.",
180                         l2BdId, newRouter.getUuid());
181                 rwTx.cancel();
182                 return;
183             }
184             ForwardingContext l2BdWithGw = new ForwardingContextBuilder(optBd.get())
185             .setParent(MappingUtils.createParent(routerL3CtxId, MappingUtils.L3_CONTEXT))
186             .build();
187             rwTx.put(LogicalDatastoreType.CONFIGURATION, L2L3IidFactory.l2BridgeDomainIid(tenantId, l2BdId),
188                     l2BdWithGw);
189         }
190         updateTenantForwarding(newNeutron, oldRouter, newRouter, new L3ContextId(routerL3CtxId), tenantId, rwTx);
191         DataStoreHelper.submitToDs(rwTx);
192     }
193
194     private boolean registerExternalGateway(TenantId tenantId, IpPrefix ipPrefix, ContextId routerl3ContextId,
195             NetworkDomainId networkDomainId) {
196         AddressEndpointRegBuilder addrEpBuilder = new AddressEndpointRegBuilder();
197         addrEpBuilder.setAddressType(IpPrefixType.class);
198         addrEpBuilder.setAddress(MappingUtils.ipPrefixToStringIpAddress(ipPrefix));
199         addrEpBuilder.setContextId(routerl3ContextId);
200         addrEpBuilder.setContextType(MappingUtils.L3_CONTEXT);
201         addrEpBuilder.setTenant(tenantId);
202         addrEpBuilder.setNetworkContainment(new NetworkContainmentBuilder().setContainment(
203                 new NetworkDomainContainmentBuilder().setNetworkDomainId(networkDomainId).build()).build());
204         addrEpBuilder.setEndpointGroup(ImmutableList.of(MappingUtils.EPG_EXTERNAL_ID));
205         addrEpBuilder.setTimestamp(System.currentTimeMillis());
206         return epRegistrator.registerEndpoint(addrEpBuilder.build());
207     }
208
209     private NetworkDomain createSubnetWithVirtualRouterIp(IpPrefix gatewayIp, NetworkDomainId subnetId) {
210         Subnet subnet = new SubnetBuilder().setVirtualRouterIp(MappingUtils.ipPrefixToIpAddress(gatewayIp.getValue())).build();
211         return new NetworkDomainBuilder().setKey(new NetworkDomainKey(subnetId, MappingUtils.SUBNET))
212             .addAugmentation(SubnetAugmentForwarding.class,
213                     new SubnetAugmentForwardingBuilder().setSubnet(subnet).build())
214             .build();
215     }
216
217     @Deprecated
218     private void updateTenantForwarding(Neutron newNeutron, Router oldRouter, Router newRouter, L3ContextId l3ContextId, TenantId tenantId, ReadWriteTransaction rwTx) {
219         InstanceIdentifier<L3Context> l3ContextIid =
220                 IidFactory.l3ContextIid(tenantId, l3ContextId);
221          Optional<L3Context> optL3Context = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, l3ContextIid, rwTx);
222          L3Context l3Context = null;
223          if (optL3Context.isPresent()) {
224              l3Context = optL3Context.get();
225          } else { // add L3 context if missing
226              l3Context = createL3CtxFromRouter(newRouter);
227              rwTx.put(LogicalDatastoreType.CONFIGURATION, l3ContextIid, l3Context);
228          }
229
230          if (newRouter.getGatewayPortId() != null && oldRouter.getGatewayPortId() == null) {
231              // external network is attached to router
232              Uuid gatewayPortId = newRouter.getGatewayPortId();
233              Optional<Port> potentialGwPort = PortUtils.findPort(gatewayPortId, newNeutron.getPorts());
234              if (!potentialGwPort.isPresent()) {
235                  LOG.warn("Illegal state - router gateway port {} does not exist for router {}.",
236                          gatewayPortId.getValue(), newRouter);
237                  rwTx.cancel();
238                  return;
239              }
240
241              Port gwPort = potentialGwPort.get();
242              List<FixedIps> fixedIpsFromGwPort = gwPort.getFixedIps();
243              if (fixedIpsFromGwPort == null || fixedIpsFromGwPort.isEmpty()) {
244                  LOG.warn("Illegal state - router gateway port {} does not contain fixed IPs {}",
245                          gatewayPortId.getValue(), gwPort);
246                  rwTx.cancel();
247                  return;
248              }
249
250              // router can have only one external network
251              FixedIps ipWithSubnetFromGwPort = fixedIpsFromGwPort.get(0);
252              Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet> potentialSubnet = SubnetUtils.findSubnet(ipWithSubnetFromGwPort.getSubnetId(), newNeutron.getSubnets());
253              if (!potentialSubnet.isPresent()) {
254                  LOG.warn("Illegal state - Subnet {} does not exist for router {}.",
255                          ipWithSubnetFromGwPort.getSubnetId(), newRouter);
256                  rwTx.cancel();
257                  return;
258              }
259              IpAddress gatewayIp =  potentialSubnet.get().getGatewayIp();
260              boolean registeredExternalGateway = epRegistrator.registerL3EpAsExternalGateway(tenantId, gatewayIp,
261                      l3ContextId, new NetworkDomainId(ipWithSubnetFromGwPort.getSubnetId().getValue()));
262              if (!registeredExternalGateway) {
263                  LOG.warn("Could not add L3Prefix as gateway of default route. Gateway port {}", gwPort);
264                  rwTx.cancel();
265                  return;
266              }
267              EndpointL3Key epL3Key = new EndpointL3Key(gatewayIp, l3ContextId);
268              addNeutronExtGwMapping(epL3Key, rwTx);
269
270              boolean registeredDefaultRoute = epRegistrator.registerExternalL3PrefixEndpoint(MappingUtils.DEFAULT_ROUTE,
271                      l3ContextId, gatewayIp, tenantId);
272              if (!registeredDefaultRoute) {
273                  LOG.warn("Could not add EndpointL3Prefix as default route. Gateway port {}", gwPort);
274                  rwTx.cancel();
275                  return;
276              }
277              org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.Subnet subnetWithGw =
278                      new org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.SubnetBuilder().setId(new SubnetId(ipWithSubnetFromGwPort.getSubnetId().getValue()))
279                          .setVirtualRouterIp(gatewayIp)
280                  .build();
281              rwTx.merge(LogicalDatastoreType.CONFIGURATION, IidFactory.subnetIid(tenantId, subnetWithGw.getId()),
282                      subnetWithGw);
283              L2BridgeDomainId l2BdId = new L2BridgeDomainId(potentialSubnet.get().getNetworkId().getValue());
284              Optional<L2BridgeDomain> optBd = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,
285                      IidFactory.l2BridgeDomainIid(tenantId, l2BdId), rwTx);
286              if (!optBd.isPresent()) {
287                  LOG.warn(
288                          "Could not read L2-Bridge-Domain {} Modifiaction of it's parent to L3-Context of router {} aborted.",
289                          l2BdId, newRouter.getUuid());
290                  rwTx.cancel();
291                  return;
292              }
293              L2BridgeDomain l2BdWithGw = new L2BridgeDomainBuilder(optBd.get())
294                  .setParent(new L3ContextId(l3ContextId.getValue()))
295                  .build();
296              rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.l2BridgeDomainIid(tenantId, l2BdId),
297                      l2BdWithGw);
298          } 
299     }
300
301     private static @Nonnull ForwardingContext createL3ContextFromRouter(
302             Router router) {
303         Name l3ContextName = null;
304         if (!Strings.isNullOrEmpty(router.getName())) {
305             l3ContextName = new Name(router.getName());
306         }
307         return new ForwardingContextBuilder().setContextId(new ContextId(router.getUuid().getValue()))
308             .setContextType(MappingUtils.L3_CONTEXT)
309             .setName(new Name(l3ContextName.getValue()))
310             .build();
311     }
312
313     @Deprecated
314     private static @Nonnull L3Context createL3CtxFromRouter(Router router) {
315         Name l3ContextName = null;
316         if (!Strings.isNullOrEmpty(router.getName())) {
317             l3ContextName = new Name(router.getName());
318         }
319         return new L3ContextBuilder().setId(new L3ContextId(router.getUuid().getValue()))
320             .setName(l3ContextName)
321             .setDescription(new Description(MappingUtils.NEUTRON_ROUTER + router.getUuid().getValue()))
322             .build();
323     }
324
325     private static void addNeutronExtGwGbpMapping(ContextId contextId, IpPrefix ipPrefix, ReadWriteTransaction rwTx) {
326         ExternalGatewayAsEndpoint externalGatewayL3Endpoint = MappingFactory.createEaxternalGatewayAsEndpoint(
327                 contextId, ipPrefix);
328         rwTx.put(LogicalDatastoreType.OPERATIONAL,
329                 NeutronGbpIidFactory.externalGatewayAsEndpoint(contextId, ipPrefix, MappingUtils.L3_CONTEXT), externalGatewayL3Endpoint, true);
330     }
331
332     @Deprecated
333     private static void addNeutronExtGwMapping(EndpointL3Key epL3Key, ReadWriteTransaction rwTx) {
334         ExternalGatewayAsL3Endpoint externalGatewayL3Endpoint =
335                 MappingFactory.createExternalGatewayByL3Endpoint(epL3Key);
336         rwTx.put(LogicalDatastoreType.OPERATIONAL,
337                 NeutronGbpIidFactory.externalGatewayAsL3Endpoint(epL3Key.getL3Context(), epL3Key.getIpAddress()),
338                 externalGatewayL3Endpoint, true);
339     }
340
341     @Override
342     public void onDeleted(Router router, Neutron oldNeutron, Neutron newNeutron) {
343         LOG.trace("deleted router - {}", router);
344     }
345
346 }