f3ce425a3632ea5871346b90c6a656ed8967092e
[vtn.git] /
1 /*
2  * Copyright (c) 2015 NEC 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.vtn.manager.internal.routing;
10
11 import java.util.ArrayList;
12 import java.util.HashSet;
13 import java.util.List;
14 import java.util.Set;
15 import java.util.concurrent.ConcurrentSkipListSet;
16
17 import org.slf4j.Logger;
18 import org.slf4j.LoggerFactory;
19
20 import org.opendaylight.vtn.manager.PathPolicy;
21 import org.opendaylight.vtn.manager.VTNException;
22
23 import org.opendaylight.vtn.manager.internal.TxContext;
24 import org.opendaylight.vtn.manager.internal.TxTask;
25 import org.opendaylight.vtn.manager.internal.VTNManagerProvider;
26 import org.opendaylight.vtn.manager.internal.util.ChangedData;
27 import org.opendaylight.vtn.manager.internal.util.DataStoreListener;
28 import org.opendaylight.vtn.manager.internal.util.DataStoreUtils;
29 import org.opendaylight.vtn.manager.internal.util.IdentifiedData;
30 import org.opendaylight.vtn.manager.internal.util.MiscUtils;
31 import org.opendaylight.vtn.manager.internal.util.XmlConfigFile;
32 import org.opendaylight.vtn.manager.internal.util.concurrent.VTNFuture;
33 import org.opendaylight.vtn.manager.internal.util.pathpolicy.PathPolicyConfigBuilder;
34 import org.opendaylight.vtn.manager.internal.util.pathpolicy.PathPolicyUtils;
35 import org.opendaylight.vtn.manager.internal.util.rpc.RpcException;
36 import org.opendaylight.vtn.manager.internal.util.tx.AbstractTxTask;
37
38 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
39 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
40 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
41 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
42
43 import org.opendaylight.yangtools.yang.binding.DataObject;
44 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
45
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.pathpolicy.rev150209.VtnPathPolicies;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.pathpolicy.rev150209.VtnPathPoliciesBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.pathpolicy.rev150209.vtn.path.policies.VtnPathPolicy;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.pathpolicy.rev150209.vtn.path.policies.VtnPathPolicyKey;
50
51 /**
52  * A data change listener that listens change of path policy configuration.
53  */
54 final class PathPolicyListener
55     extends DataStoreListener<VtnPathPolicy, PathPolicyChange> {
56     /**
57      * Logger instance.
58      */
59     private static final Logger  LOG =
60         LoggerFactory.getLogger(PathPolicyListener.class);
61
62     /**
63      * A graph that keeps network topology.
64      */
65     private final TopologyGraph  topology;
66
67     /**
68      * A set of path policy IDs laoded by {@link PathPolicyLoadTask}.
69      */
70     private Set<Integer>  loadedPolicies;
71
72     /**
73      * MD-SAL transaction task to load path policy configuration.
74      *
75      * <p>
76      *   This task returns current {@link VtnPathPolicies} instance.
77      * </p>
78      */
79     private class PathPolicyLoadTask extends AbstractTxTask<VtnPathPolicies> {
80         /**
81          * Resume the configuration for the given path policy.
82          *
83          * @param vlist   A list of {@link VtnPathPolicy} instance to store
84          *                resumed configuration.
85          * @param loaded  A set of loaded path policy IDs.
86          * @param key     A string representation of the policy ID.
87          * @param pp      A {@link PathPolicy} instance.
88          */
89         private void resume(List<VtnPathPolicy> vlist, Set<Integer> loaded,
90                             String key, PathPolicy pp) {
91             Integer pid = pp.getPolicyId();
92             try {
93                 if (!key.equals(String.valueOf(pid))) {
94                     String msg = new StringBuilder("Unexpected ID: ").
95                         append(pid).append(": expected=").append(key).
96                         toString();
97                     throw new IllegalArgumentException(msg);
98                 }
99                 VtnPathPolicy vpp = new PathPolicyConfigBuilder.Data().
100                     set(pp).getBuilder().build();
101                 vlist.add(vpp);
102                 loaded.add(pid);
103             } catch (RpcException | RuntimeException e) {
104                 String msg = MiscUtils.joinColon(
105                     "Ignore invalid path policy configuration",
106                     pp, e.getMessage());
107                 LOG.warn(msg, e);
108             }
109         }
110
111         /**
112          * {@inheritDoc}
113          */
114         @Override
115         public VtnPathPolicies execute(TxContext ctx) throws VTNException {
116             loadedPolicies = null;
117             Set<Integer> loaded = new ConcurrentSkipListSet<>();
118
119             // Load configuration from file.
120             XmlConfigFile.Type ftype = XmlConfigFile.Type.PATHPOLICY;
121             List<VtnPathPolicy> vlist = new ArrayList<>();
122             for (String key: XmlConfigFile.getKeys(ftype)) {
123                 PathPolicy pp = XmlConfigFile.load(
124                     ftype, key, PathPolicy.class);
125                 if (pp != null) {
126                     resume(vlist, loaded, key, pp);
127                 }
128             }
129
130             VtnPathPoliciesBuilder builder = new VtnPathPoliciesBuilder();
131             if (!vlist.isEmpty()) {
132                 builder.setVtnPathPolicy(vlist);
133             }
134             InstanceIdentifier<VtnPathPolicies> path =
135                 InstanceIdentifier.create(VtnPathPolicies.class);
136
137             // Remove old configuration, and install loaded configuration.
138             LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
139             ReadWriteTransaction tx = ctx.getReadWriteTransaction();
140             DataStoreUtils.delete(tx, oper, path);
141
142             VtnPathPolicies policies = builder.build();
143             tx.put(oper, path, policies, true);
144             if (!loaded.isEmpty()) {
145                 loadedPolicies = loaded;
146             }
147
148             return policies;
149         }
150
151         /**
152          * {@inheritDoc}
153          */
154         @Override
155         public void onSuccess(VTNManagerProvider provider,
156                               VtnPathPolicies result) {
157             List<VtnPathPolicy> vlist = result.getVtnPathPolicy();
158             if (vlist != null) {
159                 // Create route resolvers for path policies.
160                 for (VtnPathPolicy vpp: vlist) {
161                     Integer id = vpp.getId();
162                     LOG.info("{}: Path policy has been loaded.", id);
163                     topology.updateResolver(id);
164                 }
165             }
166         }
167     }
168
169     /**
170      * MD-SAL transaction task to save current path policy configuration.
171      *
172      * <p>
173      *   This task returns current {@link VtnPathPolicies} instance.
174      * </p>
175      */
176     private static class PathPolicySaveTask
177         extends AbstractTxTask<VtnPathPolicies> {
178         /**
179          * Set {@code true} if the root container has been created.
180          */
181         private boolean  created;
182
183         /**
184          * {@inheritDoc}
185          */
186         @Override
187         public VtnPathPolicies execute(TxContext ctx) throws VTNException {
188             created = false;
189
190             // Load current configuration.
191             InstanceIdentifier<VtnPathPolicies> path =
192                 InstanceIdentifier.create(VtnPathPolicies.class);
193             LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
194             ReadWriteTransaction tx = ctx.getReadWriteTransaction();
195             VtnPathPolicies policies =
196                 DataStoreUtils.read(tx, oper, path).orNull();
197             if (policies == null) {
198                 // Initialize the path policy container.
199                 VtnPathPoliciesBuilder builder = new VtnPathPoliciesBuilder();
200                 policies = builder.build();
201                 tx.put(oper, path, builder.build(), true);
202                 created = true;
203             }
204
205             return policies;
206         }
207
208         /**
209          * {@inheritDoc}
210          */
211         @Override
212         public void onSuccess(VTNManagerProvider provider,
213                               VtnPathPolicies result) {
214             if (created) {
215                 LOG.info(
216                     "An empty path policy container has been created.");
217             }
218
219             XmlConfigFile.Type ftype = XmlConfigFile.Type.PATHPOLICY;
220             Set<String> names = new HashSet<>();
221             List<VtnPathPolicy> vlist = result.getVtnPathPolicy();
222             if (vlist != null) {
223                 for (VtnPathPolicy vpp: vlist) {
224                     // Save configuration into a file.
225                     PathPolicy pp = PathPolicyUtils.toPathPolicy(vpp);
226                     String key = pp.getPolicyId().toString();
227                     XmlConfigFile.save(ftype, key, pp);
228                     names.add(key);
229                 }
230             }
231
232             // Remove obsolete configuration files.
233             XmlConfigFile.deleteAll(ftype, names);
234         }
235     }
236
237     /**
238      * Construct a new instance.
239      *
240      * @param provider  VTN Manager provider service.
241      * @param topo      A {@link TopologyGraph} instance.
242      */
243     PathPolicyListener(VTNManagerProvider provider, TopologyGraph topo) {
244         super(VtnPathPolicy.class);
245         topology = topo;
246         registerListener(provider.getDataBroker(),
247                          LogicalDatastoreType.OPERATIONAL,
248                          DataChangeScope.SUBTREE);
249     }
250
251     /**
252      * Post a MD-SAL transaction task to initialize configuration.
253      *
254      * @param provider  VTN Manager provider service.
255      * @param master    {@code true} if the local node is the configuration
256      *                  provider.
257      * @return  A {@link VTNFuture} instance.
258      */
259     VTNFuture<?> initConfig(VTNManagerProvider provider, boolean master) {
260         TxTask<?> task = (master)
261             ? new PathPolicyLoadTask() : new PathPolicySaveTask();
262         return provider.post(task);
263     }
264
265     /**
266      * Return the path policy identifier in the given instance identifier.
267      *
268      * @param path  Path to the path policy configuration.
269      * @return  An {@link Integer} instance which represents the path policy
270      *          identifier. {@code null} on failure.
271      */
272     private Integer getIdentifier(InstanceIdentifier<VtnPathPolicy> path) {
273         VtnPathPolicyKey key =
274             path.firstKeyOf(VtnPathPolicy.class, VtnPathPolicyKey.class);
275         if (key == null) {
276             return null;
277         }
278
279         return key.getId();
280     }
281
282     // DataStoreListener
283
284     /**
285      * {@inheritDoc}
286      */
287     @Override
288     protected PathPolicyChange enterEvent(
289         AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> ev) {
290         return new PathPolicyChange(topology);
291     }
292
293     /**
294      * {@inheritDoc}
295      */
296     @Override
297     protected void exitEvent(PathPolicyChange ectx) {
298         ectx.apply(LOG);
299     }
300
301     /**
302      * {@inheritDoc}
303      */
304     @Override
305     protected void onCreated(PathPolicyChange ectx,
306                              IdentifiedData<VtnPathPolicy> data) {
307         InstanceIdentifier<VtnPathPolicy> path = data.getIdentifier();
308         Integer id = getIdentifier(path);
309         if (id == null) {
310             LOG.warn("Ignore broken creation event: path={}, value={}",
311                      path, data.getValue());
312         } else {
313             // Do nothing if the specified event was caused by the initial
314             // setup.
315             Set<Integer> loaded = loadedPolicies;
316             if (loaded == null || !loaded.remove(id)) {
317                 VtnPathPolicy vpp = data.getValue();
318                 ectx.addUpdated(id, PathPolicyUtils.toPathPolicy(vpp));
319             } else if (loaded.isEmpty()) {
320                 LOG.debug("All loaded path policies have been notified.");
321                 loadedPolicies = null;
322             }
323         }
324     }
325
326     /**
327      * {@inheritDoc}
328      */
329     @Override
330     protected void onUpdated(PathPolicyChange ectx,
331                              ChangedData<VtnPathPolicy> data) {
332         InstanceIdentifier<VtnPathPolicy> path = data.getIdentifier();
333         VtnPathPolicy vpp = data.getValue();
334         Integer id = getIdentifier(path);
335         if (id == null) {
336             LOG.warn("Ignore broken update event: path={}, old={}, new={}",
337                      path, vpp, data.getOldValue());
338         } else {
339             ectx.addUpdated(id, PathPolicyUtils.toPathPolicy(vpp));
340         }
341     }
342
343     /**
344      * {@inheritDoc}
345      */
346     @Override
347     protected void onRemoved(PathPolicyChange ectx,
348                              IdentifiedData<VtnPathPolicy> data) {
349         InstanceIdentifier<VtnPathPolicy> path = data.getIdentifier();
350         Integer id = getIdentifier(path);
351         if (id == null) {
352             LOG.warn("Ignore broken removal event: path={}, value={}",
353                      path, data.getValue());
354         } else {
355             ectx.addRemoved(id);
356         }
357     }
358
359     /**
360      * {@inheritDoc}
361      */
362     @Override
363     protected InstanceIdentifier<VtnPathPolicy> getWildcardPath() {
364         return InstanceIdentifier.builder(VtnPathPolicies.class).
365             child(VtnPathPolicy.class).build();
366     }
367
368     // CloseableContainer
369
370     /**
371      * {@inheritDoc}
372      */
373     @Override
374     protected Logger getLogger() {
375         return LOG;
376     }
377 }