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.binding.api.WriteTransaction;
20 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
21 import org.opendaylight.unimgr.api.UnimgrDataTreeChangeListener;
22 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.mef.services.MefService;
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;
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;
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.UniKey;
26 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;
27 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;
28 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.EvcElan;
29 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.services.rev150526.EvcElanBuilder;
30 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;
31 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;
33 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.types.rev150526.EvcType;
34 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.types.rev150526.EvcUniRoleType;
35 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.types.rev150526.Identifier45;
36 import org.opendaylight.yang.gen.v1.http.metroethernetforum.org.ns.yang.mef.types.rev150526.RetailSvcIdType;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeInterface.EtreeInterfaceType;
38 import org.opendaylight.yangtools.concepts.ListenerRegistration;
39 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
43 import com.google.common.base.Optional;
45 import jline.internal.Log;
47 public class EvcListener extends UnimgrDataTreeChangeListener<Evc> implements IUniAwareService {
49 private static final Logger log = LoggerFactory.getLogger(EvcListener.class);
50 private ListenerRegistration<EvcListener> evcListenerRegistration;
51 private final IUniPortManager uniPortManager;
52 private final UniQosManager uniQosManager;
53 @SuppressWarnings("unused")
54 private final UniAwareListener uniAwareListener;
56 public EvcListener(final DataBroker dataBroker, final UniPortManager uniPortManager,
57 final UniQosManager uniQosManager) {
59 this.uniPortManager = uniPortManager;
60 this.uniQosManager = uniQosManager;
61 this.uniAwareListener = new UniAwareListener(dataBroker, this);
65 public void registerListener() {
67 final DataTreeIdentifier<Evc> dataTreeIid = new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
68 MefServicesUtils.getEvcsInstanceIdentifier());
69 evcListenerRegistration = dataBroker.registerDataTreeChangeListener(dataTreeIid, this);
70 log.info("EvcDataTreeChangeListener created and registered");
71 } catch (final Exception e) {
72 log.error("Evc DataChange listener registration failed !", e);
73 throw new IllegalStateException("Evc registration Listener failed.", e);
78 public void close() throws Exception {
79 evcListenerRegistration.close();
83 public void add(DataTreeModification<Evc> newDataObject) {
84 if (newDataObject.getRootPath() != null && newDataObject.getRootNode() != null) {
85 log.info("evc {} created", newDataObject.getRootNode().getIdentifier());
86 addEvc(newDataObject);
91 public void remove(DataTreeModification<Evc> removedDataObject) {
92 if (removedDataObject.getRootPath() != null && removedDataObject.getRootNode() != null) {
93 log.info("evc {} deleted", removedDataObject.getRootNode().getIdentifier());
94 removeEvc(removedDataObject);
99 public void update(DataTreeModification<Evc> modifiedDataObject) {
100 if (modifiedDataObject.getRootPath() != null && modifiedDataObject.getRootNode() != null) {
101 log.info("evc {} updated", modifiedDataObject.getRootNode().getIdentifier());
102 updateEvc(modifiedDataObject);
107 public void connectUni(String uniId) {
108 List<RetailSvcIdType> allEvcs = MefServicesUtils.getAllEvcsServiceIds(dataBroker);
109 allEvcs = allEvcs != null ? allEvcs : Collections.emptyList();
111 for (RetailSvcIdType evcSerId : allEvcs) {
112 InstanceIdentifier<Evc> evcId = MefServicesUtils.getEvcInstanceIdentifier(evcSerId);
113 Evc evc = MefServicesUtils.getEvc(dataBroker, evcId);
115 Log.error("Inconsistent data for svcId {}", evcSerId);
119 String instanceName = evc.getEvcId().getValue();
120 boolean isEtree = evc.getEvcType() == EvcType.RootedMultipoint;
122 List<Uni> toConnect = new ArrayList<>();
123 List<Uni> unis = evc.getUnis() != null ? evc.getUnis().getUni() : null;
124 unis = unis != null ? unis : Collections.emptyList();
125 for (Uni uni : unis) {
126 if (uni.getUniId().getValue().equals(uniId)) {
127 Log.info("Connecting Uni {} to svc id {}", uniId, evcSerId);
133 EvcElan evcElan = getOperEvcElan(evcId);
134 if (evcElan == null) {
135 NetvirtUtils.createElanInstance(dataBroker, instanceName, isEtree, evc.getSegmentationId());
136 evcElan = getOperEvcElan(evcId);
137 if (evcElan == null) {
138 log.error("Evc {} has not been created as required. Nothing to reconnect", evcId);
143 for (Uni uni : toConnect) {
144 createUniElanInterfaces(evcId, instanceName, uni, isEtree);
146 updateQos(toConnect);
151 public void disconnectUni(String uniId) {
152 List<RetailSvcIdType> allEvcs = MefServicesUtils.getAllEvcsServiceIds(dataBroker);
153 allEvcs = allEvcs != null ? allEvcs : Collections.emptyList();
155 for (RetailSvcIdType evcSerId : allEvcs) {
156 InstanceIdentifier<Evc> evcId = MefServicesUtils.getEvcInstanceIdentifier(evcSerId);
157 Evc evc = MefServicesUtils.getEvc(dataBroker, evcId);
159 Log.error("Inconsistent data for svcId {}", evcSerId);
163 String instanceName = evc.getEvcId().getValue();
164 List<Uni> toDisconnect = new ArrayList<>();
165 List<Uni> unis = evc.getUnis() != null ? evc.getUnis().getUni() : null;
166 unis = unis != null ? unis : Collections.emptyList();
167 for (Uni uni : unis) {
168 if (uni.getUniId().getValue().equals(uniId)) {
169 Log.info("Disconnecting Uni {} from svc id {}", uniId, evcSerId);
170 toDisconnect.add(uni);
175 EvcElan evcElan = getOperEvcElan(evcId);
176 if (evcElan == null) {
177 log.error("Evc {} has not been created as required. Nothing to disconnect", evcId);
181 updateQos(toDisconnect);
182 for (Uni uni : toDisconnect) {
183 removeUniElanInterfaces(evcId, instanceName, uni);
189 private void addEvc(DataTreeModification<Evc> newDataObject) {
191 Evc data = newDataObject.getRootNode().getDataAfter();
192 String instanceName = data.getEvcId().getValue();
193 boolean isEtree = data.getEvcType() == EvcType.RootedMultipoint;
194 InstanceIdentifier<Evc> evcId = newDataObject.getRootPath().getRootIdentifier();
196 synchronized (instanceName.intern()) {
197 NetvirtUtils.createElanInstance(dataBroker, instanceName, isEtree, data.getSegmentationId(),
198 data.getMacTimeout());
201 if (data.getUnis() == null) {
202 log.info("No UNI's in service {}, exiting", instanceName);
205 for (Uni uni : data.getUnis().getUni()) {
206 createUniElanInterfaces(evcId, instanceName, uni, isEtree);
208 updateQos(data.getUnis().getUni());
210 } catch (final Exception e) {
211 log.error("Add evc failed !", e);
215 private void removeEvc(DataTreeModification<Evc> removedDataObject) {
217 Evc data = removedDataObject.getRootNode().getDataBefore();
218 InstanceIdentifier<Evc> evcId = removedDataObject.getRootPath().getRootIdentifier();
219 List<Uni> uniToRemove = data.getUnis() != null && data.getUnis().getUni() != null ? data.getUnis().getUni()
220 : Collections.emptyList();
222 synchronized (data.getEvcId().getValue().intern()) {
223 updateQos(uniToRemove);
224 EvcElan evcElan = getOperEvcElan(evcId);
225 if (evcElan == null) {
226 log.error("Evc {} has not been created as required. Nothing to remove", data.getEvcId().getValue());
230 String instanceName = evcElan.getElanId();
232 for (Uni uni : uniToRemove) {
233 removeUniElanInterfaces(evcId, instanceName, uni);
236 log.info("Removing elan instance: " + instanceName);
237 NetvirtUtils.deleteElanInstance(dataBroker, instanceName);
238 removeOperEvcElan(evcId);
240 } catch (final Exception e) {
241 log.error("Remove evc failed !", e);
245 private void updateEvc(DataTreeModification<Evc> modifiedDataObject) {
246 InstanceIdentifier<Evc> evcId = modifiedDataObject.getRootPath().getRootIdentifier();
249 Evc original = modifiedDataObject.getRootNode().getDataBefore();
250 Evc update = modifiedDataObject.getRootNode().getDataAfter();
252 List<Uni> originalUni = original.getUnis() != null && original.getUnis().getUni() != null
253 ? original.getUnis().getUni() : Collections.emptyList();
254 List<UniKey> originalUniIds = originalUni.stream().map(u -> u.getKey())
255 .collect(Collectors.toList());
256 List<Uni> updateUni = update.getUnis() != null && update.getUnis().getUni() != null
257 ? update.getUnis().getUni() : Collections.emptyList();
258 List<UniKey> updateUniIds = updateUni.stream().map(u -> u.getKey()).collect(Collectors.toList());
260 synchronized (original.getEvcId().getValue().intern()) {
262 String instanceName = original.getEvcId().getValue();
263 boolean isEtree = update.getEvcType() == EvcType.RootedMultipoint;
264 log.info("Updating {} instance: {}", isEtree ? "etree" : "elan", instanceName);
266 // Changed Uni will be deleted / recreated
267 List<Uni> uniToRemove = new ArrayList<>(originalUni);
268 uniToRemove.removeIf(u -> updateUniIds.contains(u.getKey()));
269 for (Uni uni : uniToRemove) {
270 removeUniElanInterfaces(evcId, instanceName, uni);
272 updateQos(uniToRemove);
274 List<Uni> uniToCreate = new ArrayList<>(updateUni);
275 uniToCreate.removeIf(u -> originalUniIds.contains(u.getKey()));
276 uniToCreate.removeAll(originalUni);
277 for (Uni uni : uniToCreate) {
278 createUniElanInterfaces(evcId, instanceName, uni, isEtree);
280 updateQos(uniToCreate);
282 List<Uni> uniToUpdate = new ArrayList<>(updateUni);
283 uniToUpdate.removeIf(u -> !originalUniIds.contains(u.getKey()));
284 updateUnis(uniToUpdate);
286 } catch (final Exception e) {
287 log.error("Update evc failed !", e);
291 private void createUniElanInterfaces(InstanceIdentifier<Evc> evcId, String instanceName, Uni uni, boolean isEtree) {
292 EvcUniRoleType role = uni.getRole();
293 EvcUniCeVlans evcUniCeVlans = uni.getEvcUniCeVlans();
295 List<EvcUniCeVlan> evcUniCeVlan = evcUniCeVlans != null && evcUniCeVlans.getEvcUniCeVlan() != null
296 && !evcUniCeVlans.getEvcUniCeVlan().isEmpty() ? evcUniCeVlans.getEvcUniCeVlan()
297 : Collections.emptyList();
299 for (EvcUniCeVlan ceVlan : evcUniCeVlan) {
300 Long vlan = safeCastVlan(ceVlan.getVid());
301 uniPortManager.addCeVlan(uni.getUniId().getValue(), vlan);
304 if (evcUniCeVlan.isEmpty()) {
305 String interfaceName = uniPortManager.getUniVlanInterface(uni.getUniId().getValue(), Long.valueOf(0));
306 if (interfaceName == null) {
307 String errorMessage = String.format("Uni %s Interface for vlan %d is not operational ", uni.getUniId(),
309 Log.error(errorMessage);
310 throw new UnsupportedOperationException(errorMessage);
312 if (isOperEvcElanPort(evcId, interfaceName)) {
313 log.info("elan interface for elan {} vlan {} interface {} exists already", instanceName, 0,
317 log.info("Creting elan interface for elan {} vlan {} interface {}", instanceName, 0, interfaceName);
318 NetvirtUtils.createElanInterface(dataBroker, instanceName, interfaceName, roleToInterfaceType(role),
320 if (uni.isPortSecurityEnabled() && uni.getSecurityGroups() != null && !uni.getSecurityGroups().isEmpty()) {
321 WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
322 NetvirtUtils.addAclToInterface(interfaceName, uni.getSecurityGroups(), tx);
323 MdsalUtils.commitTransaction(tx);
325 uniQosManager.mapUniPortBandwidthLimits(uni.getUniId().getValue(), interfaceName,
326 uni.getIngressBwProfile());
327 setOperEvcElanPort(evcId, instanceName, interfaceName);
329 for (EvcUniCeVlan ceVlan : evcUniCeVlan) {
330 Long vlan = safeCastVlan(ceVlan.getVid());
331 String interfaceName = uniPortManager.getUniVlanInterface(uni.getUniId().getValue(), vlan);
332 if (interfaceName == null) {
333 String errorMessage = String.format("Uni %s Interface for vlan %d is not operational ",
335 Log.error(errorMessage);
336 throw new UnsupportedOperationException(errorMessage);
338 if (isOperEvcElanPort(evcId, interfaceName)) {
339 log.info("elan interface for elan {} vlan {} interface {} exists already", instanceName, 0,
343 log.info("Creting elan interface for elan {} vlan {} interface {}", instanceName, 0, interfaceName);
344 NetvirtUtils.createElanInterface(dataBroker, instanceName, interfaceName, roleToInterfaceType(role),
346 if (uni.isPortSecurityEnabled() && uni.getSecurityGroups() != null && !uni.getSecurityGroups().isEmpty()) {
347 WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
348 NetvirtUtils.addAclToInterface(interfaceName, uni.getSecurityGroups(), tx);
349 MdsalUtils.commitTransaction(tx); }
350 uniQosManager.mapUniPortBandwidthLimits(uni.getUniId().getValue(), interfaceName,
351 uni.getIngressBwProfile());
352 setOperEvcElanPort(evcId, instanceName, interfaceName);
357 private void removeUniElanInterfaces(InstanceIdentifier<Evc> evcId, String instanceName, Uni uni) {
358 EvcUniCeVlans evcUniCeVlans = uni.getEvcUniCeVlans();
360 List<EvcUniCeVlan> evcUniCeVlan = evcUniCeVlans != null && evcUniCeVlans.getEvcUniCeVlan() != null
361 && !evcUniCeVlans.getEvcUniCeVlan().isEmpty() ? evcUniCeVlans.getEvcUniCeVlan()
362 : Collections.emptyList();
364 if (evcUniCeVlan.isEmpty()) {
365 String interfaceName = uniPortManager.getUniVlanInterface(uni.getUniId().getValue(), Long.valueOf(0));
366 if (interfaceName == null || !isOperEvcElanPort(evcId, interfaceName)) {
367 log.info("elan interface for elan {} vlan {} is not operational, nothing to remove", instanceName, 0,
369 interfaceName = uniPortManager.getUniVlanInterfaceName(uni.getUniId().getValue(), null);
371 removeElanInterface(evcId, uni.getUniId().getValue(), interfaceName);
373 for (EvcUniCeVlan ceVlan : evcUniCeVlan) {
374 Long vlan = safeCastVlan(ceVlan.getVid());
375 String interfaceName = uniPortManager.getUniVlanInterface(uni.getUniId().getValue(), vlan);
376 if (interfaceName == null || !isOperEvcElanPort(evcId, interfaceName)) {
377 log.info("elan interface for elan {} vlan {} is not operational, nothing to remove", instanceName,
378 vlan, interfaceName);
379 interfaceName = uniPortManager.getUniVlanInterfaceName(uni.getUniId().getValue(), vlan);
381 removeElanInterface(evcId, uni.getUniId().getValue(), interfaceName);
385 for (EvcUniCeVlan ceVlan : evcUniCeVlan) {
386 Long vlan = safeCastVlan(ceVlan.getVid());
387 uniPortManager.removeCeVlan(uni.getUniId().getValue(), vlan);
391 private void removeElanInterface(InstanceIdentifier<Evc> identifier, String uniId, String interfaceName) {
392 log.info("Removing elan interface: " + interfaceName);
393 uniQosManager.unMapUniPortBandwidthLimits(uniId, interfaceName);
394 NetvirtUtils.deleteElanInterface(dataBroker, interfaceName);
396 EvcElan evcElan = getOperEvcElan(identifier);
397 if (evcElan == null) {
398 log.error("Removing non-operational Elan interface {}", interfaceName);
401 deleteOperEvcElanPort(identifier, interfaceName);
404 // Expected from API is Long
405 private Long safeCastVlan(Object vid) {
406 if (!(vid instanceof Long)) {
407 String errorMessage = String.format("vlan id %s cannot be cast to Long", vid);
408 log.error(errorMessage);
409 throw new UnsupportedOperationException(errorMessage);
414 private static EtreeInterfaceType roleToInterfaceType(EvcUniRoleType role) {
415 if (role == EvcUniRoleType.Root) {
416 return EtreeInterfaceType.Root;
418 return EtreeInterfaceType.Leaf;
422 private EvcElan getOperEvcElan(InstanceIdentifier<Evc> identifier) {
423 InstanceIdentifier<EvcElan> path = identifier.augmentation(EvcElan.class);
424 Optional<EvcElan> evcElan = MdsalUtils.read(dataBroker, LogicalDatastoreType.OPERATIONAL, path);
425 if (evcElan.isPresent()) {
426 return evcElan.get();
432 private void removeOperEvcElan(InstanceIdentifier<Evc> identifier) {
433 final InstanceIdentifier<MefService> serviceId = identifier.firstIdentifierOf(MefService.class);
434 MdsalUtils.delete(dataBroker, LogicalDatastoreType.OPERATIONAL, serviceId);
437 private boolean isOperEvcElanPort(InstanceIdentifier<Evc> identifier, String elanPort) {
438 EvcElan evcElan = getOperEvcElan(identifier);
439 if (evcElan == null || evcElan.getElanPorts() == null) {
442 List<ElanPorts> exPorts = evcElan.getElanPorts();
443 return exPorts.stream().anyMatch(p -> p.getPortId().equals(elanPort));
446 private void setOperEvcElanPort(InstanceIdentifier<Evc> identifier, String elanName, String elanPort) {
447 InstanceIdentifier<EvcElan> path = identifier.augmentation(EvcElan.class);
448 EvcElan evcElan = getOperEvcElan(identifier);
449 EvcElanBuilder evcElanBuilder = evcElan != null ? new EvcElanBuilder(evcElan) : new EvcElanBuilder();
450 List<ElanPorts> exPorts = evcElan != null && evcElan.getElanPorts() != null ? evcElan.getElanPorts()
453 ElanPortsBuilder portB = new ElanPortsBuilder();
454 portB.setPortId(elanPort);
455 exPorts.add(portB.build());
456 evcElanBuilder.setElanId(elanName);
457 evcElanBuilder.setElanPorts(exPorts);
458 MdsalUtils.write(dataBroker, LogicalDatastoreType.OPERATIONAL, path, evcElanBuilder.build());
461 private void deleteOperEvcElanPort(InstanceIdentifier<Evc> identifier, String elanPort) {
462 InstanceIdentifier<EvcElan> path = identifier.augmentation(EvcElan.class);
463 EvcElan evcElan = getOperEvcElan(identifier);
464 EvcElanBuilder evcElanBuilder = null;
465 List<ElanPorts> exPorts = Collections.emptyList();
466 if (evcElan != null) {
467 evcElanBuilder = new EvcElanBuilder(evcElan);
468 exPorts = evcElan.getElanPorts() != null ? evcElan.getElanPorts() : Collections.emptyList();
470 Log.error("Deleting non-operational Elan port {}", elanPort);
473 List<ElanPorts> newList = exPorts.stream().filter(p -> !p.getPortId().equals(elanPort))
474 .collect(Collectors.toList());
475 evcElanBuilder.setElanPorts(newList);
476 MdsalUtils.write(dataBroker, LogicalDatastoreType.OPERATIONAL, path, evcElanBuilder.build());
479 private void updateQos(List<Uni> uniToUpdate) {
480 uniToUpdate.forEach(u -> uniQosManager.setUniBandwidthLimits(u.getUniId()));
483 private void updateUnis(List<Uni> uniToUpdate) {
484 uniToUpdate.forEach(u -> uniQosManager.updateUni(u.getUniId(), u.getIngressBwProfile()));
485 updateQos(uniToUpdate);