Merge "Bug 3269 Implementation of floating ip in neutron-mapper"
[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 javax.annotation.Nonnull;
9 import javax.annotation.Nullable;
10
11 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
12 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
13 import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
14 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
15 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
16 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.MappingUtils;
17 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.MappingUtils.ForwardingCtx;
18 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.NeutronMapperIidFactory;
19 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.NeutronUtils;
20 import org.opendaylight.groupbasedpolicy.neutron.mapper.util.Utils;
21 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
22 import org.opendaylight.groupbasedpolicy.util.IidFactory;
23 import org.opendaylight.neutron.spi.INeutronPortCRUD;
24 import org.opendaylight.neutron.spi.INeutronRouterAware;
25 import org.opendaylight.neutron.spi.INeutronSubnetCRUD;
26 import org.opendaylight.neutron.spi.NeutronCRUDInterfaces;
27 import org.opendaylight.neutron.spi.NeutronPort;
28 import org.opendaylight.neutron.spi.NeutronRouter;
29 import org.opendaylight.neutron.spi.NeutronRouter_Interface;
30 import org.opendaylight.neutron.spi.NeutronSecurityRule;
31 import org.opendaylight.neutron.spi.NeutronSubnet;
32 import org.opendaylight.neutron.spi.Neutron_IPs;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpPrefix;
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.EndpointGroupId;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L2FloodDomainId;
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.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.EndpointService;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.UnregisterEndpointInputBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.unregister.endpoint.input.L3;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.unregister.endpoint.input.L3Builder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.mapper.rev150223.mappings.network.mappings.NetworkMapping;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L2BridgeDomain;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L2BridgeDomainBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L3Context;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L3ContextBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Subnet;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.SubnetBuilder;
54 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57
58 import com.google.common.base.Optional;
59 import com.google.common.base.Strings;
60 import com.google.common.collect.ImmutableList;
61
62 public class NeutronRouterAware implements INeutronRouterAware {
63
64     private static final Logger LOG = LoggerFactory.getLogger(NeutronRouterAware.class);
65     private static final NeutronRouterAware INSTANCE = new NeutronRouterAware();
66     private static DataBroker dataProvider;
67     private static EndpointService epService;
68
69     private NeutronRouterAware() {
70         if (NeutronRouterAware.INSTANCE != null) {
71             throw new IllegalStateException("Already instantiated");
72         }
73     }
74
75     public static NeutronRouterAware getInstance() {
76         return NeutronRouterAware.INSTANCE;
77     }
78
79     public static void init(DataBroker dataProvider, EndpointService epService) {
80         NeutronRouterAware.dataProvider = checkNotNull(dataProvider);
81         NeutronRouterAware.epService = checkNotNull(epService);
82     }
83
84     @Override
85     public int canCreateRouter(NeutronRouter router) {
86         LOG.trace("canCreateRouter - {}", router);
87         // nothing to consider
88         return StatusCode.OK;
89     }
90
91     @Override
92     public void neutronRouterCreated(NeutronRouter router) {
93         LOG.trace("neutronRouterCreated - {}", router);
94         // TODO Li msunal external gateway
95     }
96
97     @Override
98     public int canUpdateRouter(NeutronRouter delta, NeutronRouter original) {
99         LOG.trace("canUpdateRouter - delta: {} original: {}", delta, original);
100         // TODO Li msunal external gateway
101         return StatusCode.OK;
102     }
103
104     @Override
105     public void neutronRouterUpdated(NeutronRouter router) {
106         LOG.trace("neutronRouterUpdated - {}", router);
107         if (router.getExternalGatewayInfo() == null || router.getExternalGatewayInfo().getExternalFixedIPs() == null) {
108             LOG.trace("neutronRouterUpdated - not an external Gateway");
109             return;
110         }
111
112         INeutronPortCRUD portInterface = NeutronCRUDInterfaces.getINeutronPortCRUD(this);
113         if (portInterface == null) {
114             LOG.warn("Illegal state - No provider for {}", INeutronPortCRUD.class.getName());
115             return;
116         }
117
118         ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
119         TenantId tenantId = new TenantId(Utils.normalizeUuid(router.getTenantID()));
120         L3ContextId l3ContextIdFromRouterId = new L3ContextId(router.getID());
121         InstanceIdentifier<L3Context> l3ContextIidForRouterId = IidFactory.l3ContextIid(tenantId,
122                 l3ContextIdFromRouterId);
123         Optional<L3Context> potentialL3ContextForRouter = DataStoreHelper.readFromDs(
124                 LogicalDatastoreType.CONFIGURATION, l3ContextIidForRouterId, rwTx);
125         L3Context l3Context = null;
126         if (potentialL3ContextForRouter.isPresent()) {
127             l3Context = potentialL3ContextForRouter.get();
128         } else { // add L3 context if missing
129             l3Context = createL3ContextFromRouter(router);
130             rwTx.put(LogicalDatastoreType.CONFIGURATION, l3ContextIidForRouterId, l3Context);
131         }
132
133         INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD(this);
134         if (subnetInterface == null) {
135             LOG.warn("Illegal state - No provider for {}", INeutronSubnetCRUD.class.getName());
136             return;
137         }
138         NeutronSubnet defaultSubnet = subnetInterface.getSubnet(router.getExternalGatewayInfo()
139             .getExternalFixedIPs()
140             .get(0)
141             .getSubnetUUID());;
142         IpAddress defaultGateway = null;
143         if (defaultSubnet != null) {
144             defaultGateway = Utils.createIpAddress(defaultSubnet.getGatewayIP());
145         }
146         // Create L3Prefix Endpoints for all routes
147         if (router.getRoutes().isEmpty()) {
148             List<String> defaultRoute = ImmutableList.of("0.0.0.0/0");
149             router.setRoutes(defaultRoute);
150
151         }
152         if (l3ContextIdFromRouterId != null) {
153             for (String route : router.getRoutes()) {
154                 IpPrefix ipPrefix = Utils.createIpPrefix(route);
155                 boolean addedL3Prefix = NeutronPortAware.addL3PrefixEndpoint(l3ContextIdFromRouterId, ipPrefix,
156                         defaultGateway, tenantId, rwTx, epService);
157                 if (!addedL3Prefix) {
158                     LOG.warn("Could not add EndpointL3Prefix for Neutron route {} for router {}", route, router.getID());
159                     rwTx.cancel();
160                     return;
161                 }
162             }
163         }
164         for (Neutron_IPs externalFixedIp : router.getExternalGatewayInfo().getExternalFixedIPs()) {
165             NeutronPort routerPort = portInterface.getPort(router.getGatewayPortId());
166             IpAddress ipAddress = Utils.createIpAddress(routerPort.getFixedIPs().get(0).getIpAddress());
167             // External subnet associated with gateway port should use the gateway IP not router IP.
168             NeutronSubnet neutronSubnet = subnetInterface.getSubnet(externalFixedIp.getSubnetUUID());
169             ipAddress = Utils.createIpAddress(neutronSubnet.getGatewayIP());
170             SubnetId subnetId = new SubnetId(externalFixedIp.getSubnetUUID());
171             Subnet subnet = resolveSubnetWithVirtualRouterIp(tenantId, subnetId, ipAddress, rwTx);
172             if (subnet == null) {
173                 rwTx.cancel();
174                 return;
175             }
176             rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.subnetIid(tenantId, subnet.getId()), subnet);
177
178             if (Strings.isNullOrEmpty(routerPort.getTenantID())) {
179                 routerPort.setTenantID(router.getTenantID());
180             }
181             // create security rules for router
182             List<NeutronSecurityRule> routerSecRules = createRouterSecRules(routerPort, null, rwTx);
183             if (routerSecRules == null) {
184                 rwTx.cancel();
185                 return;
186             }
187             for (NeutronSecurityRule routerSecRule : routerSecRules) {
188                 boolean isRouterSecRuleAdded = NeutronSecurityRuleAware.addNeutronSecurityRule(routerSecRule, rwTx);
189                 if (!isRouterSecRuleAdded) {
190                     rwTx.cancel();
191                     return;
192                 }
193             }
194
195             boolean isSuccessful = setNewL3ContextToEpsFromSubnet(tenantId, l3Context, subnet, rwTx);
196             if (!isSuccessful) {
197                 rwTx.cancel();
198                 return;
199             }
200         }
201
202         DataStoreHelper.submitToDs(rwTx);
203     }
204
205     @Override
206     public int canDeleteRouter(NeutronRouter router) {
207         LOG.trace("canDeleteRouter - {}", router);
208         // nothing to consider
209         return StatusCode.OK;
210     }
211
212     @Override
213     public void neutronRouterDeleted(NeutronRouter router) {
214         LOG.trace("neutronRouterDeleted - {}", router);
215         ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
216         TenantId tenantId = new TenantId(Utils.normalizeUuid(router.getTenantID()));
217         Optional<EndpointGroup> potentialEpg = DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION,
218                 IidFactory.endpointGroupIid(tenantId, MappingUtils.EPG_ROUTER_ID), rwTx);
219         if (!potentialEpg.isPresent()) {
220             LOG.warn("Illegal state - Endpoint group {} does not exist.", MappingUtils.EPG_ROUTER_ID.getValue());
221             rwTx.cancel();
222             return;
223         }
224         DataStoreHelper.submitToDs(rwTx);
225     }
226
227     @Override
228     public int canAttachInterface(NeutronRouter router, NeutronRouter_Interface routerInterface) {
229         LOG.trace("canAttachInterface - router: {} interface: {}", router, routerInterface);
230         try (ReadOnlyTransaction rTx = dataProvider.newReadOnlyTransaction()) {
231             L3ContextId l3ContextIdFromRouterId = new L3ContextId(router.getID());
232             TenantId tenantId = new TenantId(Utils.normalizeUuid(router.getTenantID()));
233             SubnetId subnetId = new SubnetId(routerInterface.getSubnetUUID());
234             Optional<Subnet> potentialSubnet = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,
235                     IidFactory.subnetIid(tenantId, subnetId), rTx);
236             if (!potentialSubnet.isPresent()) {
237                 LOG.warn("Illegal state - subnet {} does not exist.", subnetId.getValue());
238                 return StatusCode.NOT_FOUND;
239             }
240             Subnet subnet = potentialSubnet.get();
241             L2FloodDomainId l2FdId = new L2FloodDomainId(subnet.getParent().getValue());
242             ForwardingCtx fwCtx = MappingUtils.createForwardingContext(tenantId, l2FdId, rTx);
243             if (fwCtx.getL3Context() != null && fwCtx.getL3Context().getId().equals(l3ContextIdFromRouterId)) {
244                 // TODO Be msunal
245                 LOG.warn("Illegal state - Neutron mapper does not support multiple router interfaces in the same subnet yet.");
246                 return StatusCode.FORBIDDEN;
247             }
248             return StatusCode.OK;
249         }
250     }
251
252     @Override
253     public void neutronRouterInterfaceAttached(NeutronRouter router, NeutronRouter_Interface routerInterface) {
254         LOG.trace("neutronRouterInterfaceAttached - router: {} interface: {}", router, routerInterface);
255         INeutronPortCRUD portInterface = NeutronCRUDInterfaces.getINeutronPortCRUD(this);
256         if (portInterface == null) {
257             LOG.warn("Illegal state - No provider for {}", INeutronPortCRUD.class.getName());
258             return;
259         }
260
261         ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
262         TenantId tenantId = new TenantId(Utils.normalizeUuid(router.getTenantID()));
263         L3ContextId l3ContextIdFromRouterId = new L3ContextId(router.getID());
264         InstanceIdentifier<L3Context> l3ContextIidForRouterId = IidFactory.l3ContextIid(tenantId,
265                 l3ContextIdFromRouterId);
266         Optional<L3Context> potentialL3ContextForRouter = DataStoreHelper.readFromDs(
267                 LogicalDatastoreType.CONFIGURATION, l3ContextIidForRouterId, rwTx);
268         L3Context l3Context = null;
269         if (potentialL3ContextForRouter.isPresent()) {
270             l3Context = potentialL3ContextForRouter.get();
271         } else { // add L3 context if missing
272             l3Context = createL3ContextFromRouter(router);
273             rwTx.put(LogicalDatastoreType.CONFIGURATION, l3ContextIidForRouterId, l3Context);
274         }
275
276         // Based on Neutron Northbound - Port representing router interface
277         // contains exactly on fixed IP
278         NeutronPort routerPort = portInterface.getPort(routerInterface.getPortUUID());
279         SubnetId subnetId = new SubnetId(routerInterface.getSubnetUUID());
280         IpAddress ipAddress = Utils.createIpAddress(routerPort.getFixedIPs().get(0).getIpAddress());
281         Subnet subnet = resolveSubnetWithVirtualRouterIp(tenantId, subnetId, ipAddress, rwTx);
282         if (subnet == null) {
283             rwTx.cancel();
284             return;
285         }
286         rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.subnetIid(tenantId, subnet.getId()), subnet);
287
288         // create security rules for router
289         List<NeutronSecurityRule> routerSecRules = createRouterSecRules(routerPort, null, rwTx);
290         if (routerSecRules == null) {
291             rwTx.cancel();
292             return;
293         }
294         for (NeutronSecurityRule routerSecRule : routerSecRules) {
295             boolean isRouterSecRuleAdded = NeutronSecurityRuleAware.addNeutronSecurityRule(routerSecRule, rwTx);
296             if (!isRouterSecRuleAdded) {
297                 rwTx.cancel();
298                 return;
299             }
300         }
301
302         boolean isSuccessful = setNewL3ContextToEpsFromSubnet(tenantId, l3Context, subnet, rwTx);
303         if (!isSuccessful) {
304             rwTx.cancel();
305             return;
306         }
307
308         DataStoreHelper.submitToDs(rwTx);
309     }
310
311     private static @Nonnull L3Context createL3ContextFromRouter(NeutronRouter router) {
312         Name l3ContextName = null;
313         if (!Strings.isNullOrEmpty(router.getName())) {
314             l3ContextName = new Name(router.getName());
315         }
316         return new L3ContextBuilder().setId(new L3ContextId(router.getID()))
317             .setName(l3ContextName)
318             .setDescription(new Description(MappingUtils.NEUTRON_ROUTER__ + router.getID()))
319             .build();
320     }
321
322     private @Nullable Subnet resolveSubnetWithVirtualRouterIp(TenantId tenantId, SubnetId subnetId,
323             IpAddress ipAddress, ReadTransaction rTx) {
324         Optional<Subnet> potentialSubnet = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,
325                 IidFactory.subnetIid(tenantId, subnetId), rTx);
326         if (!potentialSubnet.isPresent()) {
327             LOG.warn("Illegal state - subnet {} does not exist.", subnetId.getValue());
328             return null;
329         }
330
331         // TODO: Li alagalah: Add gateways and prefixes instead of
332         // VirtualRouterID
333         return new SubnetBuilder(potentialSubnet.get()).setVirtualRouterIp(ipAddress).build();
334     }
335
336     /**
337      * @return {@code false} if illegal state occurred; {@code true} otherwise
338      */
339     public boolean setNewL3ContextToEpsFromSubnet(TenantId tenantId, L3Context l3Context, Subnet subnet,
340             ReadWriteTransaction rwTx) {
341         if (subnet.getParent() == null) {
342             LOG.warn("Illegal state - subnet {} does not have a parent.", subnet.getId().getValue());
343             return false;
344         }
345
346         L2FloodDomainId l2FdId = new L2FloodDomainId(subnet.getParent().getValue());
347         ForwardingCtx fwCtx = MappingUtils.createForwardingContext(tenantId, l2FdId, rwTx);
348         if (fwCtx.getL2BridgeDomain() == null) {
349             LOG.warn("Illegal state - l2-flood-domain {} does not have a parent.", l2FdId.getValue());
350             return false;
351         }
352
353         L2BridgeDomain l2BridgeDomain = new L2BridgeDomainBuilder(fwCtx.getL2BridgeDomain()).setParent(
354                 l3Context.getId()).build();
355         rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.l2BridgeDomainIid(tenantId, l2BridgeDomain.getId()),
356                 l2BridgeDomain);
357
358         INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD(this);
359         if (subnetInterface == null) {
360             LOG.warn("Illegal state - No provider for {}", INeutronSubnetCRUD.class.getName());
361             return false;
362         }
363
364         List<L3> l3Eps = new ArrayList<>();
365         L3ContextId oldL3ContextId = fwCtx.getL3Context().getId();
366         NeutronSubnet neutronSubnet = subnetInterface.getSubnet(subnet.getId().getValue());
367         List<NeutronPort> portsInNeutronSubnet = neutronSubnet.getPortsInSubnet();
368         for (NeutronPort port : portsInNeutronSubnet) {
369             boolean isPortAdded = NeutronPortAware.addNeutronPort(port, rwTx, epService);
370             if (!isPortAdded) {
371                 return false;
372             }
373             // TODO Li msunal this has to be rewrite when OFOverlay renderer
374             // will support l3-endpoints.
375             Neutron_IPs firstIp = MappingUtils.getFirstIp(port.getFixedIPs());
376             if (firstIp != null) {
377                 l3Eps.add(new L3Builder().setL3Context(oldL3ContextId)
378                     .setIpAddress(Utils.createIpAddress(firstIp.getIpAddress()))
379                     .build());
380             }
381         }
382
383         if (!l3Eps.isEmpty()) {
384             epService.unregisterEndpoint(new UnregisterEndpointInputBuilder().setL3(l3Eps).build());
385         }
386         return true;
387     }
388
389     public static List<NeutronSecurityRule> createRouterSecRules(NeutronPort port, EndpointGroupId consumerEpgId,
390             ReadTransaction rTx) {
391         TenantId tenantId = new TenantId(Utils.normalizeUuid(port.getTenantID()));
392         Neutron_IPs firstIp = MappingUtils.getFirstIp(port.getFixedIPs());
393         if (firstIp == null) {
394             LOG.warn("Illegal state - Router port does not have an IP address.");
395             return null;
396         }
397         SubnetId routerSubnetId = new SubnetId(firstIp.getSubnetUUID());
398         Optional<Subnet> potentialSubnet = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,
399                 IidFactory.subnetIid(tenantId, routerSubnetId), rTx);
400         if (!potentialSubnet.isPresent()) {
401             LOG.warn("Illegal state - Subnet {} where is router port does not exist.", routerSubnetId.getValue());
402             return null;
403         }
404         IpPrefix ipSubnet = potentialSubnet.get().getIpPrefix();
405         NeutronSecurityRule routerRuleEgress = createRouterSecRule(port.getID(), tenantId, ipSubnet, consumerEpgId,
406                 true);
407         NeutronSecurityRule routerRuleIngress = createRouterSecRule(port.getID(), tenantId, ipSubnet, consumerEpgId,
408                 false);
409         return ImmutableList.of(routerRuleEgress, routerRuleIngress);
410     }
411
412     private static NeutronSecurityRule createRouterSecRule(String ruleUuid, TenantId tenantId, IpPrefix ipSubnet,
413             EndpointGroupId consumerEpgId, boolean isEgress) {
414         NeutronSecurityRule dhcpSecRule = new NeutronSecurityRule();
415         dhcpSecRule.setSecurityRuleGroupID(MappingUtils.EPG_ROUTER_ID.getValue());
416         dhcpSecRule.setSecurityRuleTenantID(tenantId.getValue());
417         dhcpSecRule.setSecurityRuleRemoteIpPrefix(Utils.getStringIpPrefix(ipSubnet));
418         if (isEgress) {
419             dhcpSecRule.setSecurityRuleUUID(NeutronUtils.EGRESS + "__" + ruleUuid);
420             dhcpSecRule.setSecurityRuleDirection(NeutronUtils.EGRESS);
421         } else {
422             dhcpSecRule.setSecurityRuleUUID(NeutronUtils.INGRESS + "__" + ruleUuid);
423             dhcpSecRule.setSecurityRuleDirection(NeutronUtils.INGRESS);
424         }
425         if (ipSubnet.getIpv4Prefix() != null) {
426             dhcpSecRule.setSecurityRuleEthertype(NeutronUtils.IPv4);
427         } else {
428             dhcpSecRule.setSecurityRuleEthertype(NeutronUtils.IPv6);
429         }
430         return dhcpSecRule;
431     }
432
433     @Override
434     public int canDetachInterface(NeutronRouter router, NeutronRouter_Interface routerInterface) {
435         LOG.trace("canDetachInterface - router: {} interface: {}", router, routerInterface);
436         // nothing to consider
437         return StatusCode.OK;
438     }
439
440     @Override
441     public void neutronRouterInterfaceDetached(NeutronRouter router, NeutronRouter_Interface routerInterface) {
442         LOG.trace("neutronRouterInterfaceDetached - router: {} interface: {}", router, routerInterface);
443         INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD(this);
444         if (subnetInterface == null) {
445             LOG.warn("Illegal state - No provider for {}", INeutronSubnetCRUD.class.getName());
446             return;
447         }
448
449         ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
450         TenantId tenantId = new TenantId(Utils.normalizeUuid(router.getTenantID()));
451         L3ContextId l3ContextId = new L3ContextId(router.getID());
452         SubnetId subnetId = new SubnetId(routerInterface.getSubnetUUID());
453         InstanceIdentifier<L3Context> l3ContextIid = IidFactory.l3ContextIid(tenantId, l3ContextId);
454         DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION,
455                 IidFactory.l3ContextIid(tenantId, l3ContextId), rwTx);
456
457         Optional<Subnet> potentialSubnet = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION,
458                 IidFactory.subnetIid(tenantId, subnetId), rwTx);
459         if (!potentialSubnet.isPresent()) {
460             LOG.warn("Illegal state - subnet {} does not exist.", subnetId.getValue());
461             rwTx.cancel();
462             return;
463         }
464
465         Subnet subnet = new SubnetBuilder(potentialSubnet.get()).setVirtualRouterIp(null).build();
466         rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.subnetIid(tenantId, subnetId), subnet);
467
468         L2FloodDomainId l2FdId = new L2FloodDomainId(subnet.getParent().getValue());
469         ForwardingCtx fwCtx = MappingUtils.createForwardingContext(tenantId, l2FdId, rwTx);
470         if (fwCtx.getL2BridgeDomain() == null) {
471             LOG.warn("Illegal state - l2-flood-domain {} does not have a parent.", l2FdId.getValue());
472             rwTx.cancel();
473             return;
474         }
475
476         Optional<NetworkMapping> potentialNetworkMapping = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL,
477                 NeutronMapperIidFactory.networkMappingIid(l2FdId), rwTx);
478         if (!potentialNetworkMapping.isPresent()) {
479             LOG.warn("Illegal state - network-mapping {} does not exist.", l2FdId.getValue());
480             rwTx.cancel();
481             return;
482         }
483
484         L2BridgeDomain l2BridgeDomain = new L2BridgeDomainBuilder(fwCtx.getL2BridgeDomain()).setParent(
485                 potentialNetworkMapping.get().getL3ContextId()).build();
486         rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.l2BridgeDomainIid(tenantId, l2BridgeDomain.getId()),
487                 l2BridgeDomain);
488
489         NeutronSubnet neutronSubnet = subnetInterface.getSubnet(subnetId.getValue());
490         List<NeutronPort> portsInNeutronSubnet = neutronSubnet.getPortsInSubnet();
491         for (NeutronPort port : portsInNeutronSubnet) {
492             boolean isPortAdded = NeutronPortAware.addNeutronPort(port, rwTx, epService);
493             if (!isPortAdded) {
494                 rwTx.cancel();
495                 return;
496             }
497         }
498     }
499
500 }