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