2 * Copyright (c) 2016 Hewlett Packard Enterprise, Co. and others. All rights reserved.
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
9 package org.opendaylight.unimgr.mef.netvirt;
11 import java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.List;
14 import java.util.stream.Collectors;
16 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
17 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
18 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
19 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
20 import org.opendaylight.unimgr.api.UnimgrDataTreeChangeListener;
21 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.MefService;
22 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.mef.service.choice.evc.choice.Evc;
23 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.mef.service.choice.evc.choice.evc.unis.Uni;
24 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.mef.service.choice.evc.choice.evc.unis.uni.EvcUniCeVlans;
25 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.mef.service.choice.evc.choice.evc.unis.uni.evc.uni.ce.vlans.EvcUniCeVlan;
26 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.EvcElan;
27 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.EvcElanBuilder;
28 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.mef.service.choice.evc.choice.evc.ElanPorts;
29 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.mef.service.mef.service.choice.evc.choice.evc.ElanPortsBuilder;
31 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.types.rev150526.EvcType;
32 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.types.rev150526.EvcUniRoleType;
33 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.types.rev150526.RetailSvcIdType;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeInterface.EtreeInterfaceType;
35 import org.opendaylight.yangtools.concepts.ListenerRegistration;
36 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
40 import com.google.common.base.Optional;
42 import jline.internal.Log;
44 public class EvcListener extends UnimgrDataTreeChangeListener<Evc> implements IUniAwareService {
46 private static final Logger log = LoggerFactory.getLogger(EvcListener.class);
47 private ListenerRegistration<EvcListener> evcListenerRegistration;
48 private final IUniPortManager uniPortManager;
49 private final UniQosManager uniQosManager;
50 @SuppressWarnings("unused")
51 private final UniAwareListener uniAwareListener;
53 public EvcListener(final DataBroker dataBroker, final UniPortManager uniPortManager,
54 final UniQosManager uniQosManager) {
56 this.uniPortManager = uniPortManager;
57 this.uniQosManager = uniQosManager;
58 this.uniAwareListener = new UniAwareListener(dataBroker, this);
62 public void registerListener() {
64 final DataTreeIdentifier<Evc> dataTreeIid = new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
65 MefServicesUtils.getEvcsInstanceIdentifier());
66 evcListenerRegistration = dataBroker.registerDataTreeChangeListener(dataTreeIid, this);
67 log.info("EvcDataTreeChangeListener created and registered");
68 } catch (final Exception e) {
69 log.error("Evc DataChange listener registration failed !", e);
70 throw new IllegalStateException("Evc registration Listener failed.", e);
75 public void close() throws Exception {
76 evcListenerRegistration.close();
80 public void add(DataTreeModification<Evc> newDataObject) {
81 if (newDataObject.getRootPath() != null && newDataObject.getRootNode() != null) {
82 log.info("evc {} created", newDataObject.getRootNode().getIdentifier());
83 addEvc(newDataObject);
88 public void remove(DataTreeModification<Evc> removedDataObject) {
89 if (removedDataObject.getRootPath() != null && removedDataObject.getRootNode() != null) {
90 log.info("evc {} deleted", removedDataObject.getRootNode().getIdentifier());
91 removeEvc(removedDataObject);
96 public void update(DataTreeModification<Evc> modifiedDataObject) {
97 if (modifiedDataObject.getRootPath() != null && modifiedDataObject.getRootNode() != null) {
98 log.info("evc {} updated", modifiedDataObject.getRootNode().getIdentifier());
99 updateEvc(modifiedDataObject);
104 public void connectUni(String uniId) {
105 List<RetailSvcIdType> allEvcs = MefServicesUtils.getAllEvcsServiceIds(dataBroker);
106 allEvcs = (allEvcs != null) ? allEvcs : Collections.emptyList();
108 for (RetailSvcIdType evcSerId : allEvcs) {
109 InstanceIdentifier<Evc> evcId = MefServicesUtils.getEvcInstanceIdentifier(evcSerId);
110 Evc evc = MefServicesUtils.getEvc(dataBroker, evcId);
112 Log.error("Inconsistent data for svcId {}", evcSerId);
116 String instanceName = evc.getEvcId().getValue();
117 boolean isEtree = evc.getEvcType() == EvcType.RootedMultipoint;
119 List<Uni> toConnect = new ArrayList<>();
120 List<Uni> unis = (evc.getUnis() != null) ? evc.getUnis().getUni() : null;
121 unis = (unis != null) ? unis : Collections.emptyList();
122 for (Uni uni : unis) {
123 if (uni.getUniId().getValue().equals(uniId)) {
124 Log.info("Connecting Uni {} to svc id {}", uniId, evcSerId);
130 EvcElan evcElan = getOperEvcElan(evcId);
131 if (evcElan == null) {
132 NetvirtUtils.createElanInstance(dataBroker, instanceName, isEtree, evc.getSegmentationId());
133 evcElan = getOperEvcElan(evcId);
134 if (evcElan == null) {
135 log.error("Evc {} has not been created as required. Nothing to reconnect", evcId);
140 for (Uni uni : toConnect) {
141 createUniElanInterfaces(evcId, instanceName, uni, isEtree);
143 updateQos(toConnect);
148 public void disconnectUni(String uniId) {
149 List<RetailSvcIdType> allEvcs = MefServicesUtils.getAllEvcsServiceIds(dataBroker);
150 allEvcs = (allEvcs != null) ? allEvcs : Collections.emptyList();
152 for (RetailSvcIdType evcSerId : allEvcs) {
153 InstanceIdentifier<Evc> evcId = MefServicesUtils.getEvcInstanceIdentifier(evcSerId);
154 Evc evc = MefServicesUtils.getEvc(dataBroker, evcId);
156 Log.error("Inconsistent data for svcId {}", evcSerId);
160 String instanceName = evc.getEvcId().getValue();
161 List<Uni> toDisconnect = new ArrayList<>();
162 List<Uni> unis = (evc.getUnis() != null) ? evc.getUnis().getUni() : null;
163 unis = (unis != null) ? unis : Collections.emptyList();
164 for (Uni uni : unis) {
165 if (uni.getUniId().getValue().equals(uniId)) {
166 Log.info("Disconnecting Uni {} from svc id {}", uniId, evcSerId);
167 toDisconnect.add(uni);
172 EvcElan evcElan = getOperEvcElan(evcId);
173 if (evcElan == null) {
174 log.error("Evc {} has not been created as required. Nothing to disconnect", evcId);
178 updateQos(toDisconnect);
179 for (Uni uni : toDisconnect) {
180 removeUniElanInterfaces(evcId, instanceName, uni);
186 private void addEvc(DataTreeModification<Evc> newDataObject) {
188 Evc data = newDataObject.getRootNode().getDataAfter();
189 String instanceName = data.getEvcId().getValue();
190 boolean isEtree = data.getEvcType() == EvcType.RootedMultipoint;
191 InstanceIdentifier<Evc> evcId = newDataObject.getRootPath().getRootIdentifier();
193 synchronized (instanceName.intern()) {
194 NetvirtUtils.createElanInstance(dataBroker, instanceName, isEtree, data.getSegmentationId(), data.getMacTimeout());
197 if (data.getUnis() == null) {
198 log.info("No UNI's in service {}, exiting", instanceName);
201 for (Uni uni : data.getUnis().getUni()) {
202 createUniElanInterfaces(evcId, instanceName, uni, isEtree);
204 updateQos(data.getUnis().getUni());
206 } catch (final Exception e) {
207 log.error("Add evc failed !", e);
211 private void removeEvc(DataTreeModification<Evc> removedDataObject) {
213 Evc data = removedDataObject.getRootNode().getDataBefore();
214 InstanceIdentifier<Evc> evcId = removedDataObject.getRootPath().getRootIdentifier();
215 List<Uni> uniToRemove = data.getUnis() != null && data.getUnis().getUni() != null ? data.getUnis().getUni()
216 : Collections.emptyList();
218 synchronized (data.getEvcId().getValue().intern()) {
219 updateQos(uniToRemove);
220 EvcElan evcElan = getOperEvcElan(evcId);
221 if (evcElan == null) {
222 log.error("Evc {} has not been created as required. Nothing to remove", data.getEvcId().getValue());
226 String instanceName = evcElan.getElanId();
228 for (Uni uni : uniToRemove) {
229 removeUniElanInterfaces(evcId, instanceName, uni);
232 log.info("Removing elan instance: " + instanceName);
233 NetvirtUtils.deleteElanInstance(dataBroker, instanceName);
234 removeOperEvcElan(evcId);
236 } catch (final Exception e) {
237 log.error("Remove evc failed !", e);
241 private void updateEvc(DataTreeModification<Evc> modifiedDataObject) {
242 InstanceIdentifier<Evc> evcId = modifiedDataObject.getRootPath().getRootIdentifier();
245 Evc original = modifiedDataObject.getRootNode().getDataBefore();
246 Evc update = modifiedDataObject.getRootNode().getDataAfter();
248 List<Uni> originalUni = original.getUnis() != null && original.getUnis().getUni() != null
249 ? original.getUnis().getUni() : Collections.emptyList();
250 List<Uni> updateUni = update.getUnis() != null && update.getUnis().getUni() != null
251 ? update.getUnis().getUni() : Collections.emptyList();
253 synchronized (original.getEvcId().getValue().intern()) {
255 String instanceName = original.getEvcId().getValue();
256 boolean isEtree = update.getEvcType() == EvcType.RootedMultipoint;
257 log.info("Updating {} instance: {}", isEtree ? "etree" : "elan", instanceName);
259 // Changed Uni will be deleted / recreated
260 List<Uni> uniToRemove = new ArrayList<>(originalUni);
261 uniToRemove.removeAll(updateUni);
262 for (Uni uni : uniToRemove) {
263 removeUniElanInterfaces(evcId, instanceName, uni);
265 updateQos(uniToRemove);
267 List<Uni> uniToCreate = new ArrayList<>(updateUni);
268 uniToCreate.removeAll(originalUni);
269 for (Uni uni : uniToCreate) {
270 createUniElanInterfaces(evcId, instanceName, uni, isEtree);
272 updateQos(uniToCreate);
274 } catch (final Exception e) {
275 log.error("Update evc failed !", e);
279 private void createUniElanInterfaces(InstanceIdentifier<Evc> evcId, String instanceName, Uni uni, boolean isEtree) {
280 EvcUniRoleType role = uni.getRole();
281 EvcUniCeVlans evcUniCeVlans = uni.getEvcUniCeVlans();
283 List<EvcUniCeVlan> evcUniCeVlan = evcUniCeVlans != null && evcUniCeVlans.getEvcUniCeVlan() != null
284 && !evcUniCeVlans.getEvcUniCeVlan().isEmpty() ? evcUniCeVlans.getEvcUniCeVlan()
285 : Collections.emptyList();
287 for (EvcUniCeVlan ceVlan : evcUniCeVlan) {
288 Long vlan = safeCastVlan(ceVlan.getVid());
289 uniPortManager.addCeVlan(uni.getUniId().getValue(), vlan);
292 if (evcUniCeVlan.isEmpty()) {
293 String interfaceName = uniPortManager.getUniVlanInterface(uni.getUniId().getValue(), Long.valueOf(0));
294 if (interfaceName == null) {
295 String errorMessage = String.format("Uni %s Interface for vlan %d is not operational ", uni.getUniId(),
297 Log.error(errorMessage);
298 throw new UnsupportedOperationException(errorMessage);
300 if (isOperEvcElanPort(evcId, interfaceName)) {
301 log.info("elan interface for elan {} vlan {} interface {} exists already", instanceName, 0,
305 log.info("Creting elan interface for elan {} vlan {} interface {}", instanceName, 0, interfaceName);
306 NetvirtUtils.createElanInterface(dataBroker, instanceName, interfaceName, roleToInterfaceType(role),
308 uniQosManager.mapUniPortBandwidthLimits(uni.getUniId().getValue(), interfaceName,
309 uni.getIngressBwProfile());
310 setOperEvcElanPort(evcId, instanceName, interfaceName);
312 for (EvcUniCeVlan ceVlan : evcUniCeVlan) {
313 Long vlan = safeCastVlan(ceVlan.getVid());
314 String interfaceName = uniPortManager.getUniVlanInterface(uni.getUniId().getValue(), vlan);
315 if (interfaceName == null) {
316 String errorMessage = String.format("Uni %s Interface for vlan %d is not operational ",
318 Log.error(errorMessage);
319 throw new UnsupportedOperationException(errorMessage);
321 if (isOperEvcElanPort(evcId, interfaceName)) {
322 log.info("elan interface for elan {} vlan {} interface {} exists already", instanceName, 0,
326 log.info("Creting elan interface for elan {} vlan {} interface {}", instanceName, 0, interfaceName);
327 NetvirtUtils.createElanInterface(dataBroker, instanceName, interfaceName, roleToInterfaceType(role),
329 uniQosManager.mapUniPortBandwidthLimits(uni.getUniId().getValue(), interfaceName,
330 uni.getIngressBwProfile());
331 setOperEvcElanPort(evcId, instanceName, interfaceName);
336 private void removeUniElanInterfaces(InstanceIdentifier<Evc> evcId, String instanceName, Uni uni) {
337 EvcUniCeVlans evcUniCeVlans = uni.getEvcUniCeVlans();
339 List<EvcUniCeVlan> evcUniCeVlan = evcUniCeVlans != null && evcUniCeVlans.getEvcUniCeVlan() != null
340 && !evcUniCeVlans.getEvcUniCeVlan().isEmpty() ? evcUniCeVlans.getEvcUniCeVlan()
341 : Collections.emptyList();
343 if (evcUniCeVlan.isEmpty()) {
344 String interfaceName = uniPortManager.getUniVlanInterface(uni.getUniId().getValue(), Long.valueOf(0));
345 if (interfaceName == null || !isOperEvcElanPort(evcId, interfaceName)) {
346 log.info("elan interface for elan {} vlan {} is not operational, nothing to remove", instanceName, 0,
348 interfaceName = uniPortManager.getUniVlanInterfaceName(uni.getUniId().getValue(), null);
350 removeElanInterface(evcId, uni.getUniId().getValue(), interfaceName);
352 for (EvcUniCeVlan ceVlan : evcUniCeVlan) {
353 Long vlan = safeCastVlan(ceVlan.getVid());
354 String interfaceName = uniPortManager.getUniVlanInterface(uni.getUniId().getValue(), vlan);
355 if (interfaceName == null || !isOperEvcElanPort(evcId, interfaceName)) {
356 log.info("elan interface for elan {} vlan {} is not operational, nothing to remove", instanceName,
357 vlan, interfaceName);
358 interfaceName = uniPortManager.getUniVlanInterfaceName(uni.getUniId().getValue(), vlan);
360 removeElanInterface(evcId, uni.getUniId().getValue(), interfaceName);
364 for (EvcUniCeVlan ceVlan : evcUniCeVlan) {
365 Long vlan = safeCastVlan(ceVlan.getVid());
366 uniPortManager.removeCeVlan(uni.getUniId().getValue(), vlan);
370 private void removeElanInterface(InstanceIdentifier<Evc> identifier, String uniId, String interfaceName) {
371 log.info("Removing elan interface: " + interfaceName);
372 uniQosManager.unMapUniPortBandwidthLimits(uniId, interfaceName);
373 NetvirtUtils.deleteElanInterface(dataBroker, interfaceName);
375 EvcElan evcElan = getOperEvcElan(identifier);
376 if (evcElan == null) {
377 log.error("Removing non-operational Elan interface {}", interfaceName);
380 deleteOperEvcElanPort(identifier, interfaceName);
383 // Expected from API is Long
384 private Long safeCastVlan(Object vid) {
385 if (!(vid instanceof Long)) {
386 String errorMessage = String.format("vlan id %s cannot be cast to Long", vid);
387 log.error(errorMessage);
388 throw new UnsupportedOperationException(errorMessage);
393 private static EtreeInterfaceType roleToInterfaceType(EvcUniRoleType role) {
394 if (role == EvcUniRoleType.Root) {
395 return EtreeInterfaceType.Root;
397 return EtreeInterfaceType.Leaf;
401 private EvcElan getOperEvcElan(InstanceIdentifier<Evc> identifier) {
402 InstanceIdentifier<EvcElan> path = identifier.augmentation(EvcElan.class);
403 Optional<EvcElan> evcElan = MdsalUtils.read(dataBroker, LogicalDatastoreType.OPERATIONAL, path);
404 if (evcElan.isPresent()) {
405 return evcElan.get();
411 private void removeOperEvcElan(InstanceIdentifier<Evc> identifier) {
412 final InstanceIdentifier<MefService> serviceId = identifier.firstIdentifierOf(MefService.class);
413 MdsalUtils.delete(dataBroker, LogicalDatastoreType.OPERATIONAL, serviceId);
416 private boolean isOperEvcElanPort(InstanceIdentifier<Evc> identifier, String elanPort) {
417 EvcElan evcElan = getOperEvcElan(identifier);
418 if (evcElan == null || evcElan.getElanPorts() == null) {
421 List<ElanPorts> exPorts = evcElan.getElanPorts();
422 return exPorts.stream().anyMatch(p -> p.getPortId().equals(elanPort));
425 private void setOperEvcElanPort(InstanceIdentifier<Evc> identifier, String elanName, String elanPort) {
426 InstanceIdentifier<EvcElan> path = identifier.augmentation(EvcElan.class);
427 EvcElan evcElan = getOperEvcElan(identifier);
428 EvcElanBuilder evcElanBuilder = evcElan != null ? new EvcElanBuilder(evcElan) : new EvcElanBuilder();
429 List<ElanPorts> exPorts = evcElan != null && evcElan.getElanPorts() != null ? evcElan.getElanPorts()
432 ElanPortsBuilder portB = new ElanPortsBuilder();
433 portB.setPortId(elanPort);
434 exPorts.add(portB.build());
435 evcElanBuilder.setElanId(elanName);
436 evcElanBuilder.setElanPorts(exPorts);
437 MdsalUtils.write(dataBroker, LogicalDatastoreType.OPERATIONAL, path, evcElanBuilder.build());
440 private void deleteOperEvcElanPort(InstanceIdentifier<Evc> identifier, String elanPort) {
441 InstanceIdentifier<EvcElan> path = identifier.augmentation(EvcElan.class);
442 EvcElan evcElan = getOperEvcElan(identifier);
443 EvcElanBuilder evcElanBuilder = null;
444 List<ElanPorts> exPorts = Collections.emptyList();
445 if (evcElan != null) {
446 evcElanBuilder = new EvcElanBuilder(evcElan);
447 exPorts = evcElan.getElanPorts() != null ? evcElan.getElanPorts() : Collections.emptyList();
449 Log.error("Deleting non-operational Elan port {}", elanPort);
452 List<ElanPorts> newList = exPorts.stream().filter(p -> !p.getPortId().equals(elanPort))
453 .collect(Collectors.toList());
454 evcElanBuilder.setElanPorts(newList);
455 MdsalUtils.write(dataBroker, LogicalDatastoreType.OPERATIONAL, path, evcElanBuilder.build());
458 private void updateQos(List<Uni> uniToUpdate) {
459 uniToUpdate.forEach(u -> uniQosManager.setUniBandwidthLimits(u.getUniId()));