refactoring to support delete\update of servics.
[unimgr.git] / netvirt / src / main / java / org / opendaylight / unimgr / mef / netvirt / EvcListener.java
1 /*
2  * Copyright (c) 2016 Hewlett Packard Enterprise, Co. 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.unimgr.mef.netvirt;
10
11 import java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.List;
14 import java.util.stream.Collectors;
15
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
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;
30
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.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeInterface.EtreeInterfaceType;
34 import org.opendaylight.yangtools.concepts.ListenerRegistration;
35 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 import com.google.common.base.Optional;
40
41 import jline.internal.Log;
42
43 public class EvcListener extends UnimgrDataTreeChangeListener<Evc> {
44
45     private static final Logger log = LoggerFactory.getLogger(EvcListener.class);
46     private ListenerRegistration<EvcListener> evcListenerRegistration;
47     private final IUniPortManager uniPortManager;
48
49     public EvcListener(final DataBroker dataBroker, final UniPortManager uniPortManager) {
50         super(dataBroker);
51         this.uniPortManager = uniPortManager;
52         registerListener();
53     }
54
55     public void registerListener() {
56         try {
57             final DataTreeIdentifier<Evc> dataTreeIid = new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
58                     MefServicesUtils.getEvcsInstanceIdentifier());
59             evcListenerRegistration = dataBroker.registerDataTreeChangeListener(dataTreeIid, this);
60             log.info("EvcDataTreeChangeListener created and registered");
61         } catch (final Exception e) {
62             log.error("Evc DataChange listener registration failed !", e);
63             throw new IllegalStateException("Evc registration Listener failed.", e);
64         }
65     }
66
67     @Override
68     public void close() throws Exception {
69         evcListenerRegistration.close();
70     }
71
72     @Override
73     public void add(DataTreeModification<Evc> newDataObject) {
74         if (newDataObject.getRootPath() != null && newDataObject.getRootNode() != null) {
75             log.info("evc {} created", newDataObject.getRootNode().getIdentifier());
76             addEvc(newDataObject);
77         }
78     }
79
80     @Override
81     public void remove(DataTreeModification<Evc> removedDataObject) {
82         if (removedDataObject.getRootPath() != null && removedDataObject.getRootNode() != null) {
83             log.info("evc {} deleted", removedDataObject.getRootNode().getIdentifier());
84             removeEvc(removedDataObject);
85         }
86     }
87
88     @Override
89     public void update(DataTreeModification<Evc> modifiedDataObject) {
90         if (modifiedDataObject.getRootPath() != null && modifiedDataObject.getRootNode() != null) {
91             log.info("evc {} updated", modifiedDataObject.getRootNode().getIdentifier());
92             updateEvc(modifiedDataObject);
93         }
94     }
95
96     private void addEvc(DataTreeModification<Evc> newDataObject) {
97         try {
98             Evc data = newDataObject.getRootNode().getDataAfter();
99             String instanceName = data.getEvcId().getValue();
100             boolean isEtree = data.getEvcType() == EvcType.RootedMultipoint;
101             InstanceIdentifier<Evc> evcId = newDataObject.getRootPath().getRootIdentifier();
102
103             synchronized (instanceName.intern()) {
104                 NetvirtUtils.createElanInstance(dataBroker, instanceName, isEtree);
105
106                 // Create interfaces
107                 if (data.getUnis() == null) {
108                     log.info("No UNI's in service {}, exiting", instanceName);
109                     return;
110                 }
111                 for (Uni uni : data.getUnis().getUni()) {
112                     createUniElanInterfaces(evcId, instanceName, uni, isEtree);
113                 }
114             }
115         } catch (final Exception e) {
116             log.error("Add evc failed !", e);
117         }
118     }
119
120     private void removeEvc(DataTreeModification<Evc> removedDataObject) {
121         try {
122             Evc data = removedDataObject.getRootNode().getDataBefore();
123             InstanceIdentifier<Evc> evcId = removedDataObject.getRootPath().getRootIdentifier();
124             List<Uni> uniToRemove = data.getUnis() != null && data.getUnis().getUni() != null
125                     ? data.getUnis().getUni() : Collections.emptyList();
126
127
128             synchronized (data.getEvcId().getValue().intern()) {
129                 EvcElan evcElan = getOperEvcElan(evcId);
130                 if (evcElan == null) {
131                     log.error("Evc {} has not been created as required. Nothing to remove", data.getEvcId().getValue());
132                 }
133
134                 String instanceName = evcElan.getElanId();
135
136                 for (Uni uni : uniToRemove) {
137                     removeUniElanInterfaces(evcId, instanceName, uni);
138                 }
139
140                 log.info("Removing elan instance: " + instanceName);
141                 NetvirtUtils.deleteElanInstance(dataBroker, instanceName);
142                 removeOperEvcElan(evcId);
143             }
144         } catch (final Exception e) {
145             log.error("Remove evc failed !", e);
146         }
147     }
148
149     private void updateEvc(DataTreeModification<Evc> modifiedDataObject) {
150         InstanceIdentifier<Evc> evcId = modifiedDataObject.getRootPath().getRootIdentifier();
151
152         try {
153             Evc original = modifiedDataObject.getRootNode().getDataBefore();
154             Evc update = modifiedDataObject.getRootNode().getDataAfter();
155
156             List<Uni> originalUni = original.getUnis() != null && original.getUnis().getUni() != null
157                     ? original.getUnis().getUni() : Collections.emptyList();
158             List<Uni> updateUni = update.getUnis() != null && update.getUnis().getUni() != null
159                     ? update.getUnis().getUni() : Collections.emptyList();
160
161             synchronized (original.getEvcId().getValue().intern()) {
162
163                 String instanceName = original.getEvcId().getValue();
164                 boolean isEtree = update.getEvcType() == EvcType.RootedMultipoint;
165                 log.info("Updating {} instance: {}", isEtree ? "etree" : "elan", instanceName);
166
167                 // Changed Uni will be deleted / recreated
168                 List<Uni> uniToRemove = new ArrayList<>(originalUni);
169                 uniToRemove.removeAll(updateUni);
170                 for (Uni uni : uniToRemove) {
171                     removeUniElanInterfaces(evcId, instanceName, uni);
172                 }
173
174                 List<Uni> uniToCreate = new ArrayList<>(updateUni);
175                 uniToCreate.removeAll(originalUni);
176                 for (Uni uni : uniToCreate) {
177                     createUniElanInterfaces(evcId, instanceName, uni, isEtree);
178                 }
179             }
180         } catch (final Exception e) {
181             log.error("Update evc failed !", e);
182         }
183     }
184
185     private void createUniElanInterfaces(InstanceIdentifier<Evc> evcId, String instanceName, Uni uni, boolean isEtree) {
186         EvcUniRoleType role = uni.getRole();
187         EvcUniCeVlans evcUniCeVlans = uni.getEvcUniCeVlans();
188
189         List<EvcUniCeVlan> evcUniCeVlan = evcUniCeVlans != null && evcUniCeVlans.getEvcUniCeVlan() != null
190                 && !evcUniCeVlans.getEvcUniCeVlan().isEmpty() ? evcUniCeVlans.getEvcUniCeVlan()
191                         : Collections.emptyList();
192
193         for (EvcUniCeVlan ceVlan : evcUniCeVlan) {
194             Long vlan = safeCastVlan(ceVlan.getVid());
195             uniPortManager.addCeVlan(uni.getUniId().getValue(), vlan);
196         }
197
198         if (evcUniCeVlan.isEmpty()) {
199             String interfaceName = uniPortManager.getUniVlanInterface(uni.getUniId().getValue(), Long.valueOf(0));
200             if (isOperEvcElanPort(evcId, interfaceName)) {
201                 log.info("elan interface for elan {} vlan {} interface {} exists already", instanceName, 0,
202                         interfaceName);
203                 return;
204             }
205             log.info("Creting elan interface for elan {} vlan {} interface {}", instanceName, 0, interfaceName);
206             NetvirtUtils.createElanInterface(dataBroker, instanceName, interfaceName, roleToInterfaceType(role),
207                     isEtree);
208             setOperEvcElanPort(evcId, instanceName, interfaceName);
209         } else {
210             for (EvcUniCeVlan ceVlan : evcUniCeVlan) {
211                 Long vlan = safeCastVlan(ceVlan.getVid());
212                 String interfaceName = uniPortManager.getUniVlanInterface(uni.getUniId().getValue(), vlan);
213                 if (isOperEvcElanPort(evcId, interfaceName)) {
214                     log.info("elan interface for elan {} vlan {} interface {} exists already", instanceName, 0,
215                             interfaceName);
216                     return;
217                 }
218                 log.info("Creting elan interface for elan {} vlan {} interface {}", instanceName, 0, interfaceName);
219                 NetvirtUtils.createElanInterface(dataBroker, instanceName, interfaceName, roleToInterfaceType(role),
220                         isEtree);
221                 setOperEvcElanPort(evcId, instanceName, interfaceName);
222             }
223         }
224     }
225
226     private void removeUniElanInterfaces(InstanceIdentifier<Evc> evcId, String instanceName, Uni uni) {
227         EvcUniCeVlans evcUniCeVlans = uni.getEvcUniCeVlans();
228
229         List<EvcUniCeVlan> evcUniCeVlan = evcUniCeVlans != null && evcUniCeVlans.getEvcUniCeVlan() != null
230                 && !evcUniCeVlans.getEvcUniCeVlan().isEmpty() ? evcUniCeVlans.getEvcUniCeVlan()
231                         : Collections.emptyList();
232
233         for (EvcUniCeVlan ceVlan : evcUniCeVlan) {
234             Long vlan = safeCastVlan(ceVlan.getVid());
235             uniPortManager.removeCeVlan(uni.getUniId().getValue(), vlan);
236         }
237
238         if (evcUniCeVlan.isEmpty()) {
239             String interfaceName = uniPortManager.getUniVlanInterface(uni.getUniId().getValue(), Long.valueOf(0));
240             if (!isOperEvcElanPort(evcId, interfaceName)) {
241                 log.info("elan interface for elan {} vlan {} is not operational, nothing to remove", instanceName, 0,
242                         interfaceName);
243                 return;
244             }
245             removeElanInterface(evcId, interfaceName);
246         } else {
247             for (EvcUniCeVlan ceVlan : evcUniCeVlan) {
248                 Long vlan = safeCastVlan(ceVlan.getVid());
249                 String interfaceName = uniPortManager.getUniVlanInterface(uni.getUniId().getValue(), vlan);
250                 if (!isOperEvcElanPort(evcId, interfaceName)) {
251                     log.info("elan interface for elan {} vlan {} is not operational, nothing to remove", instanceName,
252                             vlan, interfaceName);
253                     return;
254                 }
255                 removeElanInterface(evcId, interfaceName);
256             }
257         }
258     }
259
260     private void removeElanInterface(InstanceIdentifier<Evc> identifier, String interfaceName) {
261         log.info("Removing elan interface: " + interfaceName);
262         NetvirtUtils.deleteElanInterface(dataBroker, interfaceName);
263
264         EvcElan evcElan = getOperEvcElan(identifier);
265         if (evcElan == null) {
266             log.error("Removing non-operational Elan interface {}", interfaceName);
267         }
268         deleteOperEvcElanPort(identifier, interfaceName);
269     }
270
271     // Expected from API is Long
272     private Long safeCastVlan(Object vid) {
273         if (!(vid instanceof Long)) {
274             String errorMessage = String.format("vlan id %s cannot be cast to Long", vid);
275             log.error(errorMessage);
276             throw new UnsupportedOperationException(errorMessage);
277         }
278         return (Long) vid;
279     }
280
281     private static EtreeInterfaceType roleToInterfaceType(EvcUniRoleType role) {
282         if (role == EvcUniRoleType.Root) {
283             return EtreeInterfaceType.Root;
284         } else {
285             return EtreeInterfaceType.Leaf;
286         }
287     }
288
289     private EvcElan getOperEvcElan(InstanceIdentifier<Evc> identifier) {
290         InstanceIdentifier<EvcElan> path = identifier.augmentation(EvcElan.class);
291         Optional<EvcElan> evcElan = MdsalUtils.read(dataBroker, LogicalDatastoreType.OPERATIONAL, path);
292         if (evcElan.isPresent()) {
293             return evcElan.get();
294         } else {
295             return null;
296         }
297     }
298
299     private void removeOperEvcElan(InstanceIdentifier<Evc> identifier) {
300         InstanceIdentifier<EvcElan> path = identifier.augmentation(EvcElan.class);
301         MdsalUtils.delete(dataBroker, LogicalDatastoreType.OPERATIONAL, path);
302     }
303
304     private boolean isOperEvcElanPort(InstanceIdentifier<Evc> identifier, String elanPort) {
305         EvcElan evcElan = getOperEvcElan(identifier);
306         if (evcElan == null || evcElan.getElanPorts() == null) {
307             return false;
308         }
309         List<ElanPorts> exPorts = evcElan.getElanPorts();
310         return exPorts.stream().anyMatch(p -> p.getPortId().equals(elanPort));
311     }
312
313     private void setOperEvcElanPort(InstanceIdentifier<Evc> identifier, String elanName, String elanPort) {
314         InstanceIdentifier<EvcElan> path = identifier.augmentation(EvcElan.class);
315         EvcElan evcElan = getOperEvcElan(identifier);
316         EvcElanBuilder evcElanBuilder = evcElan != null ? new EvcElanBuilder(evcElan) : new EvcElanBuilder();
317         List<ElanPorts> exPorts =  evcElan != null && evcElan.getElanPorts() != null ? evcElan.getElanPorts() : new ArrayList<>();
318
319         ElanPortsBuilder portB = new ElanPortsBuilder();
320         portB.setPortId(elanPort);
321         exPorts.add(portB.build());
322         evcElanBuilder.setElanId(elanName);
323         evcElanBuilder.setElanPorts(exPorts);
324         MdsalUtils.write(dataBroker, LogicalDatastoreType.OPERATIONAL, path, evcElanBuilder.build());
325     }
326
327     private void deleteOperEvcElanPort(InstanceIdentifier<Evc> identifier, String elanPort) {
328         InstanceIdentifier<EvcElan> path = identifier.augmentation(EvcElan.class);
329         EvcElan evcElan = getOperEvcElan(identifier);
330         EvcElanBuilder evcElanBuilder = null;
331         List<ElanPorts> exPorts = Collections.emptyList();
332         if (evcElan != null) {
333             evcElanBuilder = new EvcElanBuilder(evcElan);
334             exPorts = evcElan.getElanPorts() != null ? evcElan.getElanPorts() : Collections.emptyList();
335         } else {
336             Log.error("Deleting non-operational Elan port {}", elanPort);
337             return;
338         }
339         List<ElanPorts> newList = exPorts.stream().filter(p -> !p.getPortId().equals(elanPort))
340                 .collect(Collectors.toList());
341         evcElanBuilder.setElanPorts(newList);
342         MdsalUtils.write(dataBroker, LogicalDatastoreType.OPERATIONAL, path, evcElanBuilder.build());
343     }
344
345 }