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