Added support of multiple routers from neutron
[groupbasedpolicy.git] / neutron-mapper / src / main / java / org / opendaylight / groupbasedpolicy / neutron / mapper / mapping / NeutronRouterAware.java
1 package org.opendaylight.groupbasedpolicy.neutron.mapper.mapping;
2
3 import static com.google.common.base.Preconditions.checkNotNull;
4
5 import java.util.ArrayList;
6 import java.util.List;
7
8 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
9 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
10 import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
11 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
12 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
13 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.DataStoreHelper;
14 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.IidFactory;
15 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.MappingUtils;
16 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.MappingUtils.ForwardingCtx;
17 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.NeutronUtils;
18 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.Utils;
19 import org.opendaylight.neutron.spi.INeutronPortCRUD;
20 import org.opendaylight.neutron.spi.INeutronRouterAware;
21 import org.opendaylight.neutron.spi.INeutronSubnetCRUD;
22 import org.opendaylight.neutron.spi.NeutronCRUDInterfaces;
23 import org.opendaylight.neutron.spi.NeutronPort;
24 import org.opendaylight.neutron.spi.NeutronRouter;
25 import org.opendaylight.neutron.spi.NeutronRouter_Interface;
26 import org.opendaylight.neutron.spi.NeutronSecurityRule;
27 import org.opendaylight.neutron.spi.NeutronSubnet;
28 import org.opendaylight.neutron.spi.Neutron_IPs;
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.opendaylight.groupbasedpolicy.common.rev140421.Description;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L2FloodDomainId;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L3ContextId;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Name;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubnetId;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.EndpointService;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.UnregisterEndpointInputBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.unregister.endpoint.input.L3;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.unregister.endpoint.input.L3Builder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.mapper.rev150223.mappings.network.mappings.NetworkMapping;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L2BridgeDomain;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L2BridgeDomainBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L3Context;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L3ContextBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Subnet;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.SubnetBuilder;
49 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52
53 import com.google.common.base.Optional;
54 import com.google.common.base.Strings;
55 import com.google.common.collect.ImmutableList;
56
57 public class NeutronRouterAware implements INeutronRouterAware {
58
59     private static final Logger LOG = LoggerFactory.getLogger(NeutronRouterAware.class);
60     private static final NeutronRouterAware INSTANCE = new NeutronRouterAware();
61     private static DataBroker dataProvider;
62     private static EndpointService epService;
63
64     private NeutronRouterAware() {
65         if (NeutronRouterAware.INSTANCE != null) {
66             throw new IllegalStateException("Already instantiated");
67         }
68     }
69
70     public static NeutronRouterAware getInstance() {
71         return NeutronRouterAware.INSTANCE;
72     }
73
74     public static void init(DataBroker dataProvider, EndpointService epService) {
75         NeutronRouterAware.dataProvider = checkNotNull(dataProvider);
76         NeutronRouterAware.epService = checkNotNull(epService);
77     }
78
79     @Override
80     public int canCreateRouter(NeutronRouter router) {
81         LOG.trace("canCreateRouter - {}", router);
82         // nothing to consider
83         return StatusCode.OK;
84     }
85
86     @Override
87     public void neutronRouterCreated(NeutronRouter router) {
88         LOG.trace("neutronRouterCreated - {}", router);
89         // TODO Li msunal external gateway
90     }
91
92     @Override
93     public int canUpdateRouter(NeutronRouter delta, NeutronRouter original) {
94         LOG.trace("canUpdateRouter - delta: {} original: {}", delta, original);
95         // TODO Li msunal external gateway
96         return StatusCode.OK;
97     }
98
99     @Override
100     public void neutronRouterUpdated(NeutronRouter router) {
101         LOG.trace("neutronRouterUpdated - {}", router);
102         // TODO Li msunal external gateway
103     }
104
105     @Override
106     public int canDeleteRouter(NeutronRouter router) {
107         LOG.trace("canDeleteRouter - {}", router);
108         // nothing to consider
109         return StatusCode.OK;
110     }
111
112     @Override
113     public void neutronRouterDeleted(NeutronRouter router) {
114         LOG.trace("neutronRouterDeleted - {}", router);
115         ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
116         TenantId tenantId = new TenantId(Utils.normalizeUuid(router.getTenantID()));
117         Optional<EndpointGroup> potentialEpg = DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION,
118                 IidFactory.endpointGroupIid(tenantId, MappingUtils.EPG_ROUTER_ID), rwTx);
119         if (!potentialEpg.isPresent()) {
120             LOG.warn("Illegal state - Endpoint group {} does not exist.", MappingUtils.EPG_ROUTER_ID.getValue());
121             rwTx.cancel();
122             return;
123         }
124         DataStoreHelper.submitToDs(rwTx);
125     }
126
127     @Override
128     public int canAttachInterface(NeutronRouter router, NeutronRouter_Interface routerInterface) {
129         LOG.trace("canAttachInterface - router: {} interface: {}", router, routerInterface);
130         try (ReadOnlyTransaction rTx = dataProvider.newReadOnlyTransaction()) {
131             L3ContextId l3ContextIdFromRouterId = new L3ContextId(router.getID());
132             TenantId tenantId = new TenantId(Utils.normalizeUuid(router.getTenantID()));
133             SubnetId subnetId = new SubnetId(routerInterface.getSubnetUUID());
134             Optional<Subnet> potentialSubnet = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,
135                     IidFactory.subnetIid(tenantId, subnetId), rTx);
136             if (!potentialSubnet.isPresent()) {
137                 LOG.warn("Illegal state - subnet {} does not exist.", subnetId.getValue());
138                 return StatusCode.NOT_FOUND;
139             }
140             Subnet subnet = potentialSubnet.get();
141             L2FloodDomainId l2FdId = new L2FloodDomainId(subnet.getParent().getValue());
142             ForwardingCtx fwCtx = MappingUtils.createForwardingContext(tenantId, l2FdId, rTx);
143             if (fwCtx.getL3Context() != null && fwCtx.getL3Context().equals(l3ContextIdFromRouterId)) {
144                 // TODO Be msunal
145                 LOG.warn("Illegal state - Neutron mapper does not support multiple router interfaces in the same subnet yet.");
146                 return StatusCode.FORBIDDEN;
147             }
148             return StatusCode.OK;
149         }
150     }
151
152     @Override
153     public void neutronRouterInterfaceAttached(NeutronRouter router, NeutronRouter_Interface routerInterface) {
154         LOG.trace("neutronRouterInterfaceAttached - router: {} interface: {}", router, routerInterface);
155         INeutronPortCRUD portInterface = NeutronCRUDInterfaces.getINeutronPortCRUD(this);
156         if (portInterface == null) {
157             LOG.warn("Illegal state - No provider for {}", INeutronPortCRUD.class.getName());
158             return;
159         }
160
161         INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD(this);
162         if (subnetInterface == null) {
163             LOG.warn("Illegal state - No provider for {}", INeutronSubnetCRUD.class.getName());
164             return;
165         }
166
167         ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
168         TenantId tenantId = new TenantId(Utils.normalizeUuid(router.getTenantID()));
169         L3ContextId l3ContextIdFromRouterId = new L3ContextId(router.getID());
170         InstanceIdentifier<L3Context> l3ContextIidForRouterId = IidFactory.l3ContextIid(tenantId,
171                 l3ContextIdFromRouterId);
172         Optional<L3Context> potentialL3ContextForRouter = DataStoreHelper.readFromDs(
173                 LogicalDatastoreType.CONFIGURATION, l3ContextIidForRouterId, rwTx);
174         // add L3 context if missing
175         if (!potentialL3ContextForRouter.isPresent()) {
176             Name l3ContextName = null;
177             if (!Strings.isNullOrEmpty(router.getName())) {
178                 l3ContextName = new Name(router.getName());
179             }
180             L3Context l3Context = new L3ContextBuilder().setId(l3ContextIdFromRouterId)
181                 .setName(l3ContextName)
182                 .setDescription(new Description(MappingUtils.NEUTRON_ROUTER__ + router.getID()))
183                 .build();
184             rwTx.put(LogicalDatastoreType.CONFIGURATION, l3ContextIidForRouterId, l3Context);
185         }
186
187         SubnetId subnetId = new SubnetId(routerInterface.getSubnetUUID());
188         Optional<Subnet> potentialSubnet = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,
189                 IidFactory.subnetIid(tenantId, subnetId), rwTx);
190         if (!potentialSubnet.isPresent()) {
191             LOG.warn("Illegal state - subnet {} does not exist.", subnetId.getValue());
192             rwTx.cancel();
193             return;
194         }
195
196         // Based on Neutron Northbound - Port representing router interface contains exactly on
197         // fixed IP
198         NeutronPort routerPort = portInterface.getPort(routerInterface.getPortUUID());
199         Subnet subnet = new SubnetBuilder(potentialSubnet.get()).setVirtualRouterIp(
200                 Utils.createIpAddress(routerPort.getFixedIPs().get(0).getIpAddress())).build();
201         rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.subnetIid(tenantId, subnetId), subnet);
202         if (subnet.getParent() == null) {
203             LOG.warn("Illegal state - subnet {} does not have a parent.", subnetId.getValue());
204             rwTx.cancel();
205             return;
206         }
207
208         L2FloodDomainId l2FdId = new L2FloodDomainId(subnet.getParent().getValue());
209         ForwardingCtx fwCtx = MappingUtils.createForwardingContext(tenantId, l2FdId, rwTx);
210         if (fwCtx.getL2BridgeDomain() == null) {
211             LOG.warn("Illegal state - l2-flood-domain {} does not have a parent.", l2FdId.getValue());
212             rwTx.cancel();
213             return;
214         }
215
216         L2BridgeDomain l2BridgeDomain = new L2BridgeDomainBuilder(fwCtx.getL2BridgeDomain()).setParent(
217                 l3ContextIdFromRouterId).build();
218         rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.l2BridgeDomainIid(tenantId, l2BridgeDomain.getId()),
219                 l2BridgeDomain);
220
221         // create security rules for router
222         List<NeutronSecurityRule> routerSecRules = createRouterSecRules(routerPort, null, rwTx);
223         if (routerSecRules == null) {
224             rwTx.cancel();
225             return;
226         }
227         for (NeutronSecurityRule routerSecRule : routerSecRules) {
228             boolean isRouterSecRuleAdded = NeutronSecurityRuleAware.addNeutronSecurityRule(routerSecRule, rwTx);
229             if (!isRouterSecRuleAdded) {
230                 rwTx.cancel();
231                 return;
232             }
233         }
234
235         List<L3> l3Eps = new ArrayList<>();
236         L3ContextId oldL3ContextId = fwCtx.getL3Context().getId();
237         NeutronSubnet neutronSubnet = subnetInterface.getSubnet(subnetId.getValue());
238         List<NeutronPort> portsInNeutronSubnet = neutronSubnet.getPortsInSubnet();
239         for (NeutronPort port : portsInNeutronSubnet) {
240             boolean isPortAdded = NeutronPortAware.addNeutronPort(port, rwTx, epService);
241             if (!isPortAdded) {
242                 rwTx.cancel();
243                 return;
244             }
245             // TODO Li msunal this has to be rewrite when OFOverlay renderer will support l3-endpoints.
246             Neutron_IPs firstIp = MappingUtils.getFirstIp(port.getFixedIPs());
247             if (firstIp != null) {
248                 l3Eps.add(new L3Builder().setL3Context(oldL3ContextId)
249                     .setIpAddress(Utils.createIpAddress(firstIp.getIpAddress()))
250                     .build());
251             }
252         }
253
254         if (!l3Eps.isEmpty()) {
255             epService.unregisterEndpoint(new UnregisterEndpointInputBuilder().setL3(l3Eps).build());
256         }
257
258         DataStoreHelper.submitToDs(rwTx);
259     }
260
261     public static List<NeutronSecurityRule> createRouterSecRules(NeutronPort port, EndpointGroupId consumerEpgId,
262             ReadTransaction rTx) {
263         TenantId tenantId = new TenantId(Utils.normalizeUuid(port.getTenantID()));
264         Neutron_IPs firstIp = MappingUtils.getFirstIp(port.getFixedIPs());
265         if (firstIp == null) {
266             LOG.warn("Illegal state - Router port does not have an IP address.");
267             return null;
268         }
269         SubnetId routerSubnetId = new SubnetId(firstIp.getSubnetUUID());
270         Optional<Subnet> potentialSubnet = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,
271                 IidFactory.subnetIid(tenantId, routerSubnetId), rTx);
272         if (!potentialSubnet.isPresent()) {
273             LOG.warn("Illegal state - Subnet {} where is router port does not exist.", routerSubnetId.getValue());
274             return null;
275         }
276         IpPrefix ipSubnet = potentialSubnet.get().getIpPrefix();
277         NeutronSecurityRule routerRuleEgress = createRouterSecRule(port.getID(), tenantId, ipSubnet, consumerEpgId,
278                 true);
279         NeutronSecurityRule routerRuleIngress = createRouterSecRule(port.getID(), tenantId, ipSubnet, consumerEpgId,
280                 false);
281         return ImmutableList.of(routerRuleEgress, routerRuleIngress);
282     }
283
284     private static NeutronSecurityRule createRouterSecRule(String ruleUuid, TenantId tenantId, IpPrefix ipSubnet,
285             EndpointGroupId consumerEpgId, boolean isEgress) {
286         NeutronSecurityRule dhcpSecRule = new NeutronSecurityRule();
287         dhcpSecRule.setSecurityRuleGroupID(MappingUtils.EPG_ROUTER_ID.getValue());
288         dhcpSecRule.setSecurityRuleTenantID(tenantId.getValue());
289         dhcpSecRule.setSecurityRuleRemoteIpPrefix(Utils.getStringIpPrefix(ipSubnet));
290         if (isEgress) {
291             dhcpSecRule.setSecurityRuleUUID(NeutronUtils.EGRESS + "__" + ruleUuid);
292             dhcpSecRule.setSecurityRuleDirection(NeutronUtils.EGRESS);
293         } else {
294             dhcpSecRule.setSecurityRuleUUID(NeutronUtils.INGRESS + "__" + ruleUuid);
295             dhcpSecRule.setSecurityRuleDirection(NeutronUtils.INGRESS);
296         }
297         if (ipSubnet.getIpv4Prefix() != null) {
298             dhcpSecRule.setSecurityRuleEthertype(NeutronUtils.IPv4);
299         } else {
300             dhcpSecRule.setSecurityRuleEthertype(NeutronUtils.IPv6);
301         }
302         return dhcpSecRule;
303     }
304
305     @Override
306     public int canDetachInterface(NeutronRouter router, NeutronRouter_Interface routerInterface) {
307         LOG.trace("canDetachInterface - router: {} interface: {}", router, routerInterface);
308         // nothing to consider
309         return StatusCode.OK;
310     }
311
312     @Override
313     public void neutronRouterInterfaceDetached(NeutronRouter router, NeutronRouter_Interface routerInterface) {
314         LOG.trace("neutronRouterInterfaceDetached - router: {} interface: {}", router, routerInterface);
315         INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD(this);
316         if (subnetInterface == null) {
317             LOG.warn("Illegal state - No provider for {}", INeutronSubnetCRUD.class.getName());
318             return;
319         }
320
321         ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
322         TenantId tenantId = new TenantId(Utils.normalizeUuid(router.getTenantID()));
323         L3ContextId l3ContextId = new L3ContextId(router.getID());
324         SubnetId subnetId = new SubnetId(routerInterface.getSubnetUUID());
325         InstanceIdentifier<L3Context> l3ContextIid = IidFactory.l3ContextIid(tenantId, l3ContextId);
326         DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION,
327                 IidFactory.l3ContextIid(tenantId, l3ContextId), rwTx);
328
329         Optional<Subnet> potentialSubnet = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,
330                 IidFactory.subnetIid(tenantId, subnetId), rwTx);
331         if (!potentialSubnet.isPresent()) {
332             LOG.warn("Illegal state - subnet {} does not exist.", subnetId.getValue());
333             rwTx.cancel();
334             return;
335         }
336
337         Subnet subnet = new SubnetBuilder(potentialSubnet.get()).setVirtualRouterIp(null).build();
338         rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.subnetIid(tenantId, subnetId), subnet);
339
340         L2FloodDomainId l2FdId = new L2FloodDomainId(subnet.getParent().getValue());
341         ForwardingCtx fwCtx = MappingUtils.createForwardingContext(tenantId, l2FdId, rwTx);
342         if (fwCtx.getL2BridgeDomain() == null) {
343             LOG.warn("Illegal state - l2-flood-domain {} does not have a parent.", l2FdId.getValue());
344             rwTx.cancel();
345             return;
346         }
347
348         Optional<NetworkMapping> potentialNetworkMapping = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL,
349                 IidFactory.networkMappingIid(l2FdId), rwTx);
350         if (!potentialNetworkMapping.isPresent()) {
351             LOG.warn("Illegal state - network-mapping {} does not exist.", l2FdId.getValue());
352             rwTx.cancel();
353             return;
354         }
355
356         L2BridgeDomain l2BridgeDomain = new L2BridgeDomainBuilder(fwCtx.getL2BridgeDomain()).setParent(
357                 potentialNetworkMapping.get().getL3ContextId()).build();
358         rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.l2BridgeDomainIid(tenantId, l2BridgeDomain.getId()),
359                 l2BridgeDomain);
360
361         NeutronSubnet neutronSubnet = subnetInterface.getSubnet(subnetId.getValue());
362         List<NeutronPort> portsInNeutronSubnet = neutronSubnet.getPortsInSubnet();
363         for (NeutronPort port : portsInNeutronSubnet) {
364             boolean isPortAdded = NeutronPortAware.addNeutronPort(port, rwTx, epService);
365             if (!isPortAdded) {
366                 rwTx.cancel();
367                 return;
368             }
369         }
370     }
371
372 }