0af47195b7600e038d1a058ba59dba1561ac6d48
[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.config;
10
11 import java.net.NetworkInterface;
12 import java.util.Enumeration;
13 import java.util.Map;
14 import java.util.TreeMap;
15 import java.util.concurrent.TimeoutException;
16 import java.util.concurrent.atomic.AtomicReference;
17
18 import org.slf4j.Logger;
19 import org.slf4j.LoggerFactory;
20
21 import org.opendaylight.vtn.manager.VTNException;
22 import org.opendaylight.vtn.manager.util.EtherAddress;
23
24 import org.opendaylight.vtn.manager.internal.TxContext;
25 import org.opendaylight.vtn.manager.internal.VTNConfig;
26 import org.opendaylight.vtn.manager.internal.VTNManagerProvider;
27 import org.opendaylight.vtn.manager.internal.util.DataStoreUtils;
28 import org.opendaylight.vtn.manager.internal.util.XmlConfigFile;
29 import org.opendaylight.vtn.manager.internal.util.concurrent.VTNFuture;
30 import org.opendaylight.vtn.manager.internal.util.tx.AbstractTxTask;
31 import org.opendaylight.vtn.manager.internal.util.tx.TxQueueImpl;
32
33 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
34 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
35 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
36
37 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
38
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.config.rev150209.VtnConfig;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.config.rev150209.VtnConfigBuilder;
41
42 /**
43  * VTN global configuration manager.
44  */
45 public final class VTNConfigManager implements AutoCloseable, VTNConfig {
46     /**
47      * Logger instance.
48      */
49     private static final Logger  LOG =
50         LoggerFactory.getLogger(VTNConfigManager.class);
51
52     /**
53      * Instance identifier of the global configuration.
54      */
55     static final InstanceIdentifier<VtnConfig> CONFIG_IDENT =
56         InstanceIdentifier.create(VtnConfig.class);
57
58     /**
59      * The key associated with the global configuration file.
60      */
61     static final String  KEY_VTN_CONFIG = "vtn-config";
62
63     /**
64      * The current configuration.
65      */
66     private final AtomicReference<VTNConfigImpl>  current;
67
68     /**
69      * A MD-SAL datastore transaction queue for the global configuration.
70      */
71     private final AtomicReference<TxQueueImpl>  configQueue =
72         new AtomicReference<TxQueueImpl>();
73
74     /**
75      * Listener of VTN configuration for configuration view.
76      */
77     private final AtomicReference<ConfigListener>  confListener =
78         new AtomicReference<ConfigListener>();
79
80     /**
81      * Listener of VTN configuration for operational view.
82      */
83     private final AtomicReference<OperationalListener>  operListener =
84         new AtomicReference<OperationalListener>();
85
86     /**
87      * Set true if the local node is configuration provider.
88      */
89     private boolean  configProvider;
90
91     /**
92      * MD-SAL transaction task for initialization of VTN configuration data.
93      *
94      * <p>
95      *   This task returns the current value of "init-state".
96      * </p>
97      */
98     private static final class ConfigInitTask extends AbstractTxTask<Boolean> {
99         /**
100          * MAC address of the local node.
101          */
102         private final EtherAddress  macAddress;
103
104         /**
105          * A {@link VTNConfigImpl} instance to be saved into file.
106          */
107         private VTNConfigImpl  saveConfig;
108
109         /**
110          * Construct a new instance.
111          *
112          * @param mac  MAC address of the local node.
113          */
114         private ConfigInitTask(EtherAddress mac) {
115             macAddress = mac;
116         }
117
118         /**
119          * {@inheritDoc}
120          */
121         @Override
122         public Boolean execute(TxContext ctx) throws VTNException {
123             saveConfig = null;
124
125             // Read current VTN configuration in configuration view.
126             LogicalDatastoreType cstore = LogicalDatastoreType.CONFIGURATION;
127             ReadWriteTransaction tx = ctx.getReadWriteTransaction();
128             VtnConfig vcfg =
129                 DataStoreUtils.read(tx, cstore, CONFIG_IDENT).orNull();
130             if (vcfg == null) {
131                 // Try to load configuration from file.
132                 VTNConfigImpl vconf = XmlConfigFile.load(
133                     XmlConfigFile.Type.CONFIG, KEY_VTN_CONFIG,
134                     VTNConfigImpl.class);
135                 if (vconf != null) {
136                     vcfg = vconf.getJaxbValue().build();
137                     tx.put(cstore, CONFIG_IDENT, vcfg, true);
138                 }
139             } else {
140                 // Save current configuration into file.
141                 saveConfig = new VTNConfigImpl(vcfg);
142             }
143
144             // Read current VTN configuration in operational view.
145             Boolean ret;
146             LogicalDatastoreType ostore = LogicalDatastoreType.OPERATIONAL;
147             VtnConfig voper = DataStoreUtils.read(tx, ostore, CONFIG_IDENT).
148                 orNull();
149             if (voper == null) {
150                 // Initialize operational view.
151                 VtnConfigBuilder builder =
152                     VTNConfigImpl.builder(vcfg, macAddress);
153
154                 // Set false to init-state to notify that the configuration
155                 // is initializing.
156                 builder.setInitState(Boolean.FALSE);
157                 tx.put(ostore, CONFIG_IDENT, builder.build(), true);
158                 ret = null;
159             } else {
160                 ret = voper.isInitState();
161             }
162
163             return ret;
164         }
165
166         /**
167          * {@inheritDoc}
168          */
169         @Override
170         public void onSuccess(VTNManagerProvider provider, Boolean result) {
171             if (saveConfig != null) {
172                 XmlConfigFile.save(XmlConfigFile.Type.CONFIG, KEY_VTN_CONFIG,
173                                    saveConfig);
174             }
175         }
176     }
177
178     /**
179      * MD-SAL transaction task to change "init-state" to true.
180      */
181     private static class ConfigInitDoneTask extends AbstractTxTask<Void> {
182         /**
183          * {@inheritDoc}
184          */
185         @Override
186         public Void execute(TxContext ctx) throws VTNException {
187             ReadWriteTransaction tx = ctx.getReadWriteTransaction();
188             LogicalDatastoreType ostore = LogicalDatastoreType.OPERATIONAL;
189             VtnConfigBuilder builder = new VtnConfigBuilder();
190             builder.setInitState(Boolean.TRUE);
191             tx.merge(ostore, CONFIG_IDENT, builder.build(), true);
192             return null;
193         }
194
195         /**
196          * {@inheritDoc}
197          */
198         @Override
199         public void onSuccess(VTNManagerProvider provider, Void result) {
200             LOG.info("VTN configuration has been initialized.");
201         }
202     }
203
204     /**
205      * Determine the MAC address of the local controller.
206      *
207      * @return  An {@link EtherAddress} which represents the MAC address of
208      *          the controller. {@code null} on failure.
209      */
210     static EtherAddress getLocalMacAddress() {
211         Enumeration<NetworkInterface> nifs = null;
212         try {
213             nifs = NetworkInterface.getNetworkInterfaces();
214         } catch (Exception e) {
215             LOG.warn("Failed to get network interfaces.", e);
216             return null;
217         }
218
219         EtherAddress ea = getMacAddress(nifs);
220         if (ea == null) {
221             LOG.warn("No network interface was found.");
222         }
223
224         return ea;
225     }
226
227     /**
228      * Get a MAC address configured in the given interface.
229      *
230      * @param nifs  An enumeration of  {@link NetworkInterface} instances.
231      * @return  An {@link EtherAddress} which represents the MAC address of
232      *          the controller. {@code null} on failure.
233      */
234     private static EtherAddress getMacAddress(
235         Enumeration<NetworkInterface> nifs) {
236         if (nifs == null) {
237             return null;
238         }
239
240         // Sort interfaces by index.
241         // This code expects that lower index is assigned to physical network
242         // interface.
243         Map<Integer, NetworkInterface> niMap = new TreeMap<>();
244         while (nifs.hasMoreElements()) {
245             NetworkInterface nif = nifs.nextElement();
246             niMap.put(nif.getIndex(), nif);
247         }
248
249         NetworkInterface altIf = null;
250         EtherAddress altAddr = null;
251         for (NetworkInterface nif: niMap.values()) {
252             try {
253                 if (nif.isLoopback() || nif.isVirtual() ||
254                     nif.isPointToPoint()) {
255                     continue;
256                 }
257
258                 byte[] mac = nif.getHardwareAddress();
259                 if (mac == null) {
260                     continue;
261                 }
262
263                 EtherAddress ea = new EtherAddress(mac);
264                 if (nif.isUp()) {
265                     LOG.debug("Use HW address of {} as local address: {}",
266                               nif.getName(), ea.getText());
267                     return ea;
268                 }
269
270                 if (altIf == null) {
271                     altIf = nif;
272                     altAddr = ea;
273                 }
274             } catch (Exception e) {
275                 LOG.debug("Ignore network interface: " + nif.getName(), e);
276             }
277         }
278
279         if (altAddr != null) {
280             LOG.debug("Use inactive HW address of {} as local address: ",
281                       altIf.getName(), altAddr.getText());
282             return altAddr;
283         }
284
285         return null;
286     }
287
288     /**
289      * Construct a new instance.
290      *
291      * @param provider  A VTN Manager provider service.
292      */
293     public VTNConfigManager(VTNManagerProvider provider) {
294         XmlConfigFile.init();
295
296         // Determine MAC address of the local node.
297         EtherAddress ea = getLocalMacAddress();
298         VTNConfigImpl vconf = new VTNConfigImpl(ea);
299         if (ea == null) {
300             ea = vconf.getControllerMacAddress();
301             LOG.warn("Use custom MAC address as local address: {}",
302                      ea.getText());
303         } else {
304             LOG.info("Local MAC address: {}", ea.getText());
305         }
306         current = new AtomicReference<VTNConfigImpl>(vconf);
307
308         try {
309             initialize(provider, ea);
310         } catch (RuntimeException e) {
311             LOG.error("Failed to initialize VTN config manager.", e);
312             close();
313             throw e;
314         }
315     }
316
317     /**
318      * Complete the initialization of global configuration.
319      */
320     public void initDone() {
321         if (configProvider) {
322             TxQueueImpl cfq = configQueue.get();
323             if (cfq != null) {
324                 // Change init-state to true.
325                 ConfigInitDoneTask task = new ConfigInitDoneTask();
326                 cfq.post(task);
327             }
328         }
329     }
330
331     /**
332      * Determine whether the local node is the configuration provider or not.
333      *
334      * @return  {@code true} if the local node is the configuration provider.
335      *          Otherwise {@code false}.
336      */
337     public boolean isConfigProvider() {
338         return configProvider;
339     }
340
341     // AutoCloseable
342
343     /**
344      * Close the configuration service.
345      */
346     @Override
347     public void close() {
348         ConfigListener cfg = confListener.getAndSet(null);
349         if (cfg != null) {
350             cfg.close();
351         }
352
353         OperationalListener op = operListener.getAndSet(null);
354         if (op != null) {
355             op.close();
356         }
357
358         TxQueueImpl cfq = configQueue.getAndSet(null);
359         if (cfq != null) {
360             cfq.close();
361         }
362     }
363
364     /**
365      * Initialize the VTN configuration.
366      *
367      * @param provider  A VTN Manager provider service.
368      * @param mac       MAC address of the local node.
369      */
370     private void initialize(VTNManagerProvider provider, EtherAddress mac) {
371         DataBroker broker = provider.getDataBroker();
372         TxQueueImpl cfq = new TxQueueImpl("VTN Config", provider);
373         configQueue.set(cfq);
374         confListener.set(new ConfigListener(cfq, broker, mac));
375         OperationalListener opl = new OperationalListener(broker, current);
376         operListener.set(opl);
377
378         // Post initialization task.
379         ConfigInitTask task = new ConfigInitTask(mac);
380         VTNFuture<Boolean> f = cfq.postFirst(task);
381
382         // Start transaction queue processing.
383         cfq.start();
384
385         // Wait for the initialization task to complete.
386         Boolean initState;
387         try {
388             initState = f.checkedGet();
389         } catch (Exception e) {
390             String msg = "Failed to initialize global configuration.";
391             LOG.error(msg, e);
392             throw new IllegalStateException(msg, e);
393         }
394
395         if (initState == null) {
396             LOG.debug("The local node is configuration provider.");
397             configProvider = true;
398             return;
399         }
400
401         // We need to wait for another controller in the cluster to complete
402         // initialization.
403         long millis = current.get().getInitTimeout();
404         try {
405             opl.awaitConfig(millis);
406         } catch (InterruptedException e) {
407             String msg = "Initialization thread was interrupted.";
408             LOG.error(msg, e);
409             throw new IllegalStateException(msg, e);
410         } catch (TimeoutException e) {
411             LOG.warn("Initialization did not complete within {} milliseconds.",
412                      millis);
413             configProvider = true;
414         }
415     }
416
417     // VTNConfig
418
419     /**
420      * {@inheritDoc}
421      */
422     @Override
423     public int getTopologyWait() {
424         return current.get().getTopologyWait();
425     }
426
427     /**
428      * {@inheritDoc}
429      */
430     @Override
431     public int getL2FlowPriority() {
432         return current.get().getL2FlowPriority();
433     }
434
435     /**
436      * {@inheritDoc}
437      */
438     @Override
439     public int getFlowModTimeout() {
440         return current.get().getFlowModTimeout();
441     }
442
443     /**
444      * {@inheritDoc}
445      */
446     @Override
447     public int getBulkFlowModTimeout() {
448         return current.get().getBulkFlowModTimeout();
449     }
450
451     /**
452      * {@inheritDoc}
453      */
454     @Override
455     public int getInitTimeout() {
456         return current.get().getInitTimeout();
457     }
458
459     /**
460      * {@inheritDoc}
461      */
462     @Override
463     public int getMaxRedirections() {
464         return current.get().getMaxRedirections();
465     }
466
467     /**
468      * {@inheritDoc}
469      */
470     @Override
471     public EtherAddress getControllerMacAddress() {
472         return current.get().getControllerMacAddress();
473     }
474 }