Convert DataChangeListeners to DataTreeChangeListeners
[groupbasedpolicy.git] / renderers / faas / src / main / java / org / opendaylight / groupbasedpolicy / renderer / faas / FaasEndpointManagerListener.java
1 /*
2  * Copyright (c) 2015 Huawei Technologies and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.groupbasedpolicy.renderer.faas;
10
11 import static com.google.common.base.Preconditions.checkNotNull;
12
13 import com.google.common.annotations.VisibleForTesting;
14 import com.google.common.base.Optional;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.List;
18 import java.util.UUID;
19 import java.util.concurrent.Executor;
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
22 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
23 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
24 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
25 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
26 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
27 import org.opendaylight.faas.uln.datastore.api.UlnDatastoreApi;
28 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
29 import org.opendaylight.groupbasedpolicy.util.IetfModelCodec;
30 import org.opendaylight.groupbasedpolicy.util.IidFactory;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.faas.faas.endpoint.rev151009.FaasEndpointContext;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.faas.logical.faas.common.rev151013.Text;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.faas.logical.faas.common.rev151013.Uuid;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.faas.logical.faas.endpoints.locations.rev151013.endpoints.locations.container.endpoints.locations.EndpointLocationBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.faas.logical.faas.ports.rev151013.ports.container.ports.port.PrivateIps;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.faas.logical.faas.ports.rev151013.ports.container.ports.port.PrivateIpsBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L2BridgeDomainId;
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.endpoint.fields.L3Address;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.faas.rev151009.mapped.tenants.entities.mapped.entity.MappedEndpoint;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.faas.rev151009.mapped.tenants.entities.mapped.entity.MappedEndpointBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.faas.rev151009.mapped.tenants.entities.mapped.entity.MappedEndpointKey;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.faas.rev151009.mapped.tenants.entities.mapped.entity.MappedSubnet;
48 import org.opendaylight.yangtools.concepts.ListenerRegistration;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 public class FaasEndpointManagerListener implements AutoCloseable {
53
54     private static final Logger LOG = LoggerFactory.getLogger(FaasEndpointManagerListener.class);
55     private final List<ListenerRegistration<?>> listenerRegistrations = new ArrayList<>();
56     private final FaasPolicyManager policyManager;
57     private final DataBroker dataProvider;
58
59     public FaasEndpointManagerListener(FaasPolicyManager policyManager, DataBroker dataProvider,
60             Executor executor) {
61         this.policyManager = policyManager;
62         this.dataProvider = dataProvider;
63
64         checkNotNull(dataProvider);
65         listenerRegistrations.add(dataProvider.registerDataTreeChangeListener(new DataTreeIdentifier<>(
66             LogicalDatastoreType.OPERATIONAL, IidFactory.endpointsIidWildcard().child(Endpoint.class)),
67             changes -> executor.execute(() -> onEndpointChanged(changes))));
68
69         listenerRegistrations.add(dataProvider.registerDataTreeChangeListener(new DataTreeIdentifier<>(
70             LogicalDatastoreType.OPERATIONAL, IidFactory.endpointsIidWildcard().child(EndpointL3.class)),
71             changes -> executor.execute(() -> onEndpointL3Changed(changes))));
72     }
73
74     @Override
75     public void close() throws Exception {
76         for (ListenerRegistration<?> reg: listenerRegistrations) {
77             reg.close();
78         }
79     }
80
81     private void onEndpointChanged(Collection<DataTreeModification<Endpoint>> changes) {
82         for (DataTreeModification<Endpoint> change: changes) {
83             DataObjectModification<Endpoint> rootNode = change.getRootNode();
84             switch (rootNode.getModificationType()) {
85                 case SUBTREE_MODIFIED:
86                 case WRITE:
87                     Endpoint updatedEndpoint = rootNode.getDataAfter();
88                     LOG.debug("Updated Endpoint {}", updatedEndpoint);
89                     if (validate(updatedEndpoint)) {
90                         policyManager.registerTenant(updatedEndpoint.getTenant(), updatedEndpoint.getEndpointGroup());
91                         processEndpoint(updatedEndpoint);
92                     }
93                     break;
94                 case DELETE:
95                     Endpoint deletedEndpoint = rootNode.getDataBefore();
96                     LOG.debug("Removed Endpoint {}", deletedEndpoint);
97                     removeFaasEndpointLocationIfExist(deletedEndpoint.getTenant(), deletedEndpoint.getL2Context(),
98                             deletedEndpoint.getMacAddress());
99                     break;
100                 default:
101                     break;
102             }
103         }
104     }
105
106     private void onEndpointL3Changed(Collection<DataTreeModification<EndpointL3>> changes) {
107         for (DataTreeModification<EndpointL3> change: changes) {
108             DataObjectModification<EndpointL3> rootNode = change.getRootNode();
109             switch (rootNode.getModificationType()) {
110                 case SUBTREE_MODIFIED:
111                 case WRITE:
112                     LOG.debug("Updated EndpointL3 {}", rootNode.getDataAfter());
113                     break;
114                 case DELETE:
115                     EndpointL3 endpoint = rootNode.getDataBefore();
116                     LOG.debug("Removed EndpointL3 {}", endpoint);
117                     removeFaasEndpointLocationIfExist(endpoint.getTenant(), endpoint.getL2Context(),
118                             endpoint.getMacAddress());
119                     break;
120                 default:
121                     break;
122             }
123         }
124     }
125
126     protected void processEndpoint(Endpoint endpoint) {
127         Uuid tenantId = policyManager.getFaasTenantId(endpoint.getTenant());
128         if (tenantId == null) {
129             LOG.error("Failed Endpoint Registration. Couldn't find faas tenant Id. Endpoint {}", endpoint);
130             return;
131         }
132         EndpointLocationBuilder epLocBuilder = new EndpointLocationBuilder();
133         epLocBuilder.setDescription(new Text("gbp-endpoint"));
134         epLocBuilder.setName(new Text(endpoint.getL2Context().getValue()));
135         epLocBuilder.setTenantId(tenantId);
136         epLocBuilder.setFaasPortRefId(endpoint.getAugmentation(FaasEndpointContext.class).getFaasPortRefId());
137         Uuid epId = getFaasEndpointId(endpoint);
138         if (epId == null) {
139             LOG.error("Failed Endpoint registration. Couldn't Create Faas Endpoint Id");
140             return;
141         }
142         epLocBuilder.setUuid(epId);
143         Uuid faasSubnetId = getFaasSubnetId(endpoint);
144         List<PrivateIps> privateIpAddresses = new ArrayList<>();
145         for (L3Address ip : endpoint.getL3Address()) {
146             PrivateIpsBuilder ipBuilder = new PrivateIpsBuilder();
147             ipBuilder.setIpAddress(IetfModelCodec.ipAddress2013(ip.getIpAddress()));
148             ipBuilder.setSubnetId(faasSubnetId);
149             privateIpAddresses.add(ipBuilder.build());
150         }
151         if (!UlnDatastoreApi.attachEndpointToSubnet(epLocBuilder, faasSubnetId, IetfModelCodec.macAddress2013(endpoint.getMacAddress()),
152                 privateIpAddresses, null)) {
153             LOG.error("Failed Endpoint Registration. Failed to Attach Endpoint to Faas Logical Network. Endpoint {}",
154                     endpoint);
155         }
156     }
157
158     private Uuid getFaasEndpointId(Endpoint endpoint) {
159         MappedEndpoint mEndpoint1 = getMappedEndpoint(endpoint);
160         if (mEndpoint1 != null) {
161             return mEndpoint1.getEndpointLocation();
162         }
163         synchronized (this) {// must be atomic
164             MappedEndpoint mEndpoint2 = getMappedEndpoint(endpoint);
165             if (mEndpoint2 != null) {
166                 return mEndpoint2.getEndpointLocation();
167             }
168             MappedEndpointBuilder mBuilder = new MappedEndpointBuilder();
169             mBuilder.setL2Context(endpoint.getL2Context());
170             mBuilder.setMacAddress(endpoint.getMacAddress());
171             mBuilder.setEndpointLocation(new Uuid(UUID.randomUUID().toString()));
172             MappedEndpoint mEndpoint = mBuilder.build();
173             WriteTransaction wTx = dataProvider.newWriteOnlyTransaction();
174             wTx.put(LogicalDatastoreType.OPERATIONAL, FaasIidFactory.mappedEndpointIid(
175                     endpoint.getTenant(), new MappedEndpointKey(endpoint.getL2Context(), endpoint.getMacAddress())),
176                     mEndpoint);
177             if (DataStoreHelper.submitToDs(wTx)) {
178                 LOG.debug("Cached in Datastore Mapped Endpoint {}", mEndpoint);
179                 return mEndpoint.getEndpointLocation();
180             } else {
181                 LOG.error("Couldn't Cache in Datastore Mapped Endpoint {}", mEndpoint);
182                 return null;
183             }
184         }
185     }
186
187     @VisibleForTesting
188     Uuid getFaasSubnetId(Endpoint endpoint) {
189         if (endpoint.getEndpointGroup() == null) {
190             LOG.error("Failed Endpoint registration -- No Endpoint-Group Id in endpoint {}", endpoint);
191             return null;
192         }
193         SubnetId subnetId = null;
194         if (endpoint.getNetworkContainment() != null) {
195             LOG.trace("Subnet is defined based on endpoint containment value {}", endpoint.getNetworkContainment()
196                 .getValue());
197             subnetId = new SubnetId(endpoint.getNetworkContainment());
198         }
199         if (subnetId == null) {
200             LOG.error("Failed Endpoint registration -- Couldn't find a subnet for endpoint {}", endpoint.getKey());
201             return null;
202         }
203         LOG.debug("Using subnetId {} for endpoint {}", subnetId, endpoint.getKey());
204         policyManager.registerSubnetWithEpg(endpoint.getEndpointGroup(), endpoint.getTenant(), subnetId);
205
206         Optional<MappedSubnet> subnetOp = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL,
207                 FaasIidFactory.mappedSubnetIid(endpoint.getTenant(), subnetId),
208                 dataProvider.newReadWriteTransaction());
209         if (subnetOp.isPresent()) {
210             return subnetOp.get().getFaasSubnetId();
211         }
212         LOG.error("Failed Endpoint registration -- Couldn't find Mapped Subnet Id based on GBP Subnet Id {}", subnetId);
213         return null;
214     }
215
216     @VisibleForTesting
217     boolean validate(Endpoint endpoint) {
218         if (endpoint.getL2Context() == null) {
219             LOG.error("Endpoint Failed Validation -- Missing L2 Context. Endpoint {}", endpoint);
220             return false;
221         }
222         if (endpoint.getL3Address() == null) {
223             LOG.error("Endpoint Failed Validation -- Missing L3 Address. Endpoint {}", endpoint);
224             return false;
225         }
226         if (endpoint.getMacAddress() == null) {
227             LOG.error("Endpoint Failed Validation -- Missing Mac Address. Endpoint {}", endpoint);
228             return false;
229         }
230         if (endpoint.getTenant() == null) {
231             LOG.error("Endpoint Failed Validation -- Missing Tenant Id. Endpoint {}", endpoint);
232             return false;
233         }
234         if (endpoint.getEndpointGroup() == null) {
235             LOG.error("Endpoint Failed Validation -- Missing Endpoint-Group. Endpoint {}", endpoint);
236             return false;
237         }
238         FaasEndpointContext faasEpAug = endpoint.getAugmentation(FaasEndpointContext.class);
239         if (faasEpAug == null || faasEpAug.getFaasPortRefId() == null) {
240             LOG.error("Endpoint Failed Validation -- Missing Required Faas Info. Endpoint {}", endpoint);
241             return false;
242         }
243         return true;
244     }
245
246     private void removeFaasEndpointLocationIfExist(TenantId tenantId, L2BridgeDomainId l2BridgeDomainId,
247             MacAddress macAddress) {
248         synchronized (this) {
249             MappedEndpointKey mappedEndpointKey = new MappedEndpointKey(l2BridgeDomainId, macAddress);
250             ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
251             Optional<MappedEndpoint> endpointOp = DataStoreHelper.removeIfExists(LogicalDatastoreType.OPERATIONAL,
252                     FaasIidFactory.mappedEndpointIid(tenantId, mappedEndpointKey), rwTx);
253             DataStoreHelper.submitToDs(rwTx);
254             if (endpointOp.isPresent()) {
255                 UlnDatastoreApi.removeEndpointLocationFromDsIfExists(policyManager.getFaasTenantId(tenantId),
256                         endpointOp.get().getEndpointLocation());
257             }
258         }
259     }
260
261     private MappedEndpoint getMappedEndpoint(Endpoint endpoint) {
262         MappedEndpointKey mappedEndpointKey = new MappedEndpointKey(endpoint.getL2Context(), endpoint.getMacAddress());
263         Optional<MappedEndpoint> endpointOp = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL,
264                 FaasIidFactory.mappedEndpointIid(endpoint.getTenant(), mappedEndpointKey),
265                 dataProvider.newReadWriteTransaction());
266         if (endpointOp.isPresent()) {
267             return endpointOp.get();
268         }
269         return null;
270     }
271 }