propagate datastore exceptions all the way to northbound
[neutron.git] / neutron-hostconfig / ovs / src / main / java / org / opendaylight / neutron / hostconfig / ovs / NeutronHostconfigOvsListener.java
1 /*
2  * Copyright (c) 2017 Intel Corporation.  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.neutron.hostconfig.ovs;
10
11 import com.google.common.base.Preconditions;
12 import com.google.common.collect.Maps;
13
14 import java.util.Collection;
15 import java.util.Locale;
16 import java.util.Map;
17 import javax.annotation.Nonnull;
18 import javax.annotation.PostConstruct;
19 import javax.annotation.PreDestroy;
20 import javax.inject.Inject;
21 import javax.inject.Singleton;
22 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
23 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
24 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
25 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
26 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
27 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
28 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
29 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
30 import org.opendaylight.neutron.hostconfig.utils.NeutronHostconfigUtils;
31 import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils;
32 import org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.OpenvswitchExternalIds;
35 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
36 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
37 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
38 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
39 import org.opendaylight.yangtools.concepts.ListenerRegistration;
40 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
41
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 @Singleton
46 public class NeutronHostconfigOvsListener implements ClusteredDataTreeChangeListener<Node> {
47     private static final Logger LOG = LoggerFactory.getLogger(NeutronHostconfigOvsListener.class);
48     private final DataBroker dataBroker;
49     private final SouthboundUtils southboundUtils;
50     private final NeutronHostconfigUtils neutronHostconfig;
51     private ListenerRegistration<DataTreeChangeListener<Node>> listenerRegistration;
52     private static final String OS_HOST_CONFIG_HOST_ID_KEY = "odl_os_hostconfig_hostid";
53     private static final String OS_HOST_CONFIG_CONFIG_KEY_PREFIX = "odl_os_hostconfig_config_odl_";
54     private static int HOST_TYPE_STR_LEN = 8;
55
56     @Inject
57     public NeutronHostconfigOvsListener(final DataBroker dataBroker) {
58         this.dataBroker = dataBroker;
59         MdsalUtils mdsalUtils = new MdsalUtils(dataBroker);
60         this.southboundUtils = new SouthboundUtils(mdsalUtils);
61         this.neutronHostconfig = new NeutronHostconfigUtils(dataBroker);
62     }
63
64     private void processChanges(Collection<DataTreeModification<Node>> changes)
65             throws TransactionCommitFailedException {
66         LOG.info("onDataTreeChanged: Received Data Tree Changed ...", changes);
67         for (DataTreeModification<Node> change : changes) {
68             final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
69             final DataObjectModification<Node> mod = change.getRootNode();
70             LOG.info("onDataTreeChanged: Received Data Tree Changed Update of Type={} for Key={}",
71                     mod.getModificationType(), key);
72             switch (mod.getModificationType()) {
73                 case DELETE:
74                     updateHostConfig(mod.getDataAfter(), NeutronHostconfigUtils.Action.DELETE);
75                     break;
76                 case SUBTREE_MODIFIED:
77                     updateHostConfig(mod.getDataAfter(), NeutronHostconfigUtils.Action.UPDATE);
78                     break;
79                 case WRITE:
80                     updateHostConfig(mod.getDataAfter(), NeutronHostconfigUtils.Action.ADD);
81                     break;
82                 default:
83                     LOG.error("onDataTreeChanged: Invalid modification type={}",
84                             mod.getModificationType());
85                     break;
86             }
87         }
88     }
89
90     @Override
91     public void onDataTreeChanged(@Nonnull Collection<DataTreeModification<Node>> changes) {
92         Preconditions.checkNotNull(changes, "Changes may not be null!");
93         try {
94             processChanges(changes);
95         } catch (TransactionCommitFailedException e) {
96             LOG.error("Transaction commit failed; ignorining changes: ", changes, e);
97         }
98     }
99
100     private InstanceIdentifier<Node> createNodeIdentifier() {
101         return InstanceIdentifier
102                 .create(NetworkTopology.class)
103                 .child(Topology.class, new TopologyKey(SouthboundUtils.OVSDB_TOPOLOGY_ID))
104                 .child(Node.class);
105     }
106
107     @PostConstruct
108     public void init() {
109         LOG.info("{} start", getClass().getSimpleName());
110         DataTreeIdentifier<Node> dataTreeIdentifier =
111                 new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL, createNodeIdentifier());
112         LOG.info("Neutron Hostconfig DataChange listener registration {}", dataTreeIdentifier);
113         listenerRegistration = dataBroker.registerDataTreeChangeListener(dataTreeIdentifier, this);
114     }
115
116     @PreDestroy
117     public void close() throws Exception {
118         if (listenerRegistration != null) {
119             listenerRegistration.close();
120             LOG.trace("HostConfig listener Closed");
121         }
122     }
123
124     private void updateHostConfig(Node node, NeutronHostconfigUtils.Action action)
125             throws TransactionCommitFailedException {
126         String hostId = getExternalId(node, OS_HOST_CONFIG_HOST_ID_KEY);
127         if (hostId == null) {
128             return;
129         }
130         for (Map.Entry<String, String> entry : extractHostConfig(node).entrySet()) {
131             neutronHostconfig.updateMdsal(neutronHostconfig.buildHostConfigInfo(hostId, entry.getKey(),
132                     entry.getValue()), action);
133         }
134     }
135
136     private Map<String, String> extractHostConfig(Node node) {
137         Map<String, String> config = Maps.newHashMap();
138         OvsdbNodeAugmentation ovsdbNode = getOvsdbNodeAugmentation(node);
139         if (ovsdbNode != null && ovsdbNode.getOpenvswitchExternalIds() != null) {
140             for (OpenvswitchExternalIds openvswitchExternalIds : ovsdbNode.getOpenvswitchExternalIds()) {
141                 if (openvswitchExternalIds.getExternalIdKey().startsWith(OS_HOST_CONFIG_CONFIG_KEY_PREFIX)) {
142                     // Extract the host type. Max 8 characters after
143                     // suffix OS_HOST_CONFIG_CONFIG_KEY_PREFIX.length()
144                     String hostType = openvswitchExternalIds.getExternalIdKey().substring(
145                             OS_HOST_CONFIG_CONFIG_KEY_PREFIX.length());
146                     if (hostType.length() > 0) {
147                         if (hostType.length() > HOST_TYPE_STR_LEN) {
148                             hostType = hostType.substring(0, HOST_TYPE_STR_LEN);
149                         }
150                         hostType = "ODL " + hostType.toUpperCase(Locale.ROOT);
151                         if (null != openvswitchExternalIds.getExternalIdValue()) {
152                             config.put(hostType, openvswitchExternalIds.getExternalIdValue());
153                         }
154                     }
155                 }
156             }
157         }
158         return config;
159     }
160
161     private String getExternalId(Node node, String key) {
162         OvsdbNodeAugmentation ovsdbNode = getOvsdbNodeAugmentation(node);
163         if (ovsdbNode != null && ovsdbNode.getOpenvswitchExternalIds() != null) {
164             for (OpenvswitchExternalIds openvswitchExternalIds : ovsdbNode.getOpenvswitchExternalIds()) {
165                 if (openvswitchExternalIds.getExternalIdKey().equals(key)) {
166                     return openvswitchExternalIds.getExternalIdValue();
167                 }
168             }
169         }
170         return null;
171     }
172
173     private OvsdbNodeAugmentation getOvsdbNodeAugmentation(Node node) {
174         OvsdbNodeAugmentation ovsdbNode = southboundUtils.extractOvsdbNode(node);
175         if (ovsdbNode == null) {
176             Node nodeFromReadOvsdbNode = southboundUtils.readOvsdbNode(node);
177             if (nodeFromReadOvsdbNode != null) {
178                 ovsdbNode = southboundUtils.extractOvsdbNode(nodeFromReadOvsdbNode);
179             }
180         }
181         return ovsdbNode;
182     }
183 }