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