2 * Copyright (c) 2016 Brocade Communication Systems 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.netconf.callhome.mount;
11 import static com.google.common.base.Preconditions.checkArgument;
13 import com.google.common.annotations.VisibleForTesting;
14 import com.google.common.base.Optional;
15 import com.google.common.util.concurrent.CheckedFuture;
17 import java.io.IOException;
18 import java.net.InetSocketAddress;
19 import java.util.ArrayList;
20 import java.util.Iterator;
21 import java.util.List;
23 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
24 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
25 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
26 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
27 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
28 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
29 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
30 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
31 import org.opendaylight.netconf.callhome.protocol.CallHomeAuthorizationProvider;
32 import org.opendaylight.netconf.callhome.protocol.NetconfCallHomeServer;
33 import org.opendaylight.netconf.callhome.protocol.NetconfCallHomeServerBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.callhome.device.status.rev170112.Device1;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.callhome.device.status.rev170112.Device1Builder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.NetconfCallhomeServer;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.netconf.callhome.server.AllowedDevices;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.netconf.callhome.server.allowed.devices.Device;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.netconf.callhome.server.allowed.devices.DeviceBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.netconf.callhome.server.allowed.devices.DeviceKey;
41 import org.opendaylight.yangtools.concepts.ListenerRegistration;
42 import org.opendaylight.yangtools.yang.binding.DataObject;
43 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
48 public class IetfZeroTouchCallHomeServerProvider implements AutoCloseable, DataChangeListener {
49 private static final String APPNAME = "CallHomeServer";
50 static final InstanceIdentifier<AllowedDevices> ALL_DEVICES = InstanceIdentifier.create(NetconfCallhomeServer.class).child(AllowedDevices.class);
52 private static final Logger LOG = LoggerFactory.getLogger(IetfZeroTouchCallHomeServerProvider.class);
54 private final DataBroker dataBroker;
55 private final CallHomeMountDispatcher mountDispacher;
56 private CallHomeAuthProviderImpl authProvider;
58 protected NetconfCallHomeServer server;
60 private ListenerRegistration<IetfZeroTouchCallHomeServerProvider> listenerReg = null;
62 private static final String CALL_HOME_PORT_KEY = "DefaultCallHomePort";
63 private static String configurationPath = "etc" + File.pathSeparator + "ztp-callhome-config.cfg";
64 private int port = 0; // 0 = use default in NetconfCallHomeBuilder
65 private CallhomeStatusReporter statusReporter;
67 public IetfZeroTouchCallHomeServerProvider(DataBroker dataBroker, CallHomeMountDispatcher mountDispacher) {
68 this.dataBroker = dataBroker;
69 this.mountDispacher = mountDispacher;
70 this.authProvider = new CallHomeAuthProviderImpl(dataBroker);
71 this.statusReporter = new CallhomeStatusReporter(dataBroker);
75 // Register itself as a listener to changes in Devices subtree
77 LOG.info("Initializing provider for {}", APPNAME);
78 loadConfigurableValues(configurationPath);
80 dataBroker.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, ALL_DEVICES, this, AsyncDataBroker.DataChangeScope.SUBTREE);
81 LOG.info("Initialization complete for {}", APPNAME);
82 } catch (IOException | Configuration.ConfigurationException e) {
83 LOG.error("Unable to successfully initialize", e);
87 private void loadConfigurableValues(String configurationPath)
88 throws Configuration.ConfigurationException {
90 Configuration configuration = new Configuration(configurationPath);
91 port = configuration.getAsPort(CALL_HOME_PORT_KEY);
92 } catch (Configuration.ConfigurationException e) {
93 LOG.error("Problem trying to load configuration values from {}", configurationPath, e);
97 private CallHomeAuthorizationProvider getCallHomeAuthorization() {
98 return new CallHomeAuthProviderImpl(dataBroker);
101 private void initializeServer() throws IOException {
102 LOG.info("Initializing Call Home server instance");
103 CallHomeAuthorizationProvider provider = this.getCallHomeAuthorization();
104 NetconfCallHomeServerBuilder builder = new NetconfCallHomeServerBuilder(
105 provider, mountDispacher, statusReporter);
107 builder.setBindAddress(new InetSocketAddress(port));
108 server = builder.build();
110 mountDispacher.createTopology();
111 LOG.info("Initialization complete for Call Home server instance");
115 void assertValid(Object obj, String description) {
117 throw new RuntimeException(String.format("Failed to find %s in IetfZeroTouchCallHomeProvider.initialize()", description));
121 public void close() throws Exception {
122 authProvider.close();
123 statusReporter.close();
125 // FIXME unbind the server
126 if (this.listenerReg != null) {
129 if (server != null) {
133 LOG.info("Successfully closed provider for {}", APPNAME);
137 public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
139 // In case of any changes to the devices datatree, register the changed values with callhome server
140 // As of now, no way to add a new callhome client key to the CallHomeAuthorization instance since
141 // its created under CallHomeAuthorizationProvider.
142 // Will have to redesign a bit here.
143 // CallHomeAuthorization.
144 ReadOnlyTransaction roConfigTx = dataBroker.newReadOnlyTransaction();
145 CheckedFuture<Optional<AllowedDevices>, ReadFailedException> devicesFuture =
146 roConfigTx.read(LogicalDatastoreType.CONFIGURATION, IetfZeroTouchCallHomeServerProvider.ALL_DEVICES);
148 if (hasDeletedDevices(change))
149 handleDeletedDevices(change);
152 for (Device confDevice : getReadDevices(devicesFuture)) {
153 readAndUpdateStatus(confDevice);
155 } catch (ReadFailedException e) {
156 LOG.error("Error trying to read the whitelist devices: {}", e);
160 private boolean hasDeletedDevices(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
161 return change.getRemovedPaths() != null;
164 private void handleDeletedDevices(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
165 checkArgument(change.getRemovedPaths() != null);
167 ReadWriteTransaction opTx = dataBroker.newReadWriteTransaction();
169 Set<InstanceIdentifier<?>> removedDevices = change.getRemovedPaths();
170 int numRemoved = removedDevices.size();
172 Iterator<InstanceIdentifier<?>> iterator = removedDevices.iterator();
173 while (iterator.hasNext()) {
174 InstanceIdentifier<?> removedIID = iterator.next();
175 LOG.info("Deleting the entry for callhome device {}", removedIID);
176 opTx.delete(LogicalDatastoreType.OPERATIONAL, removedIID);
183 private List<Device> getReadDevices(CheckedFuture<Optional<AllowedDevices>, ReadFailedException> devicesFuture)
184 throws ReadFailedException {
185 Optional<AllowedDevices> opt = devicesFuture.checkedGet();
186 if (opt.isPresent()) {
187 AllowedDevices confDevices = opt.get();
188 if (confDevices != null) {
189 LOG.debug("Read {} devices", confDevices.getDevice().size());
190 return confDevices.getDevice();
194 LOG.debug("Failed to read devices");
195 return new ArrayList<>();
198 private void readAndUpdateStatus(Device cfgDevice) throws ReadFailedException {
199 InstanceIdentifier<Device> deviceIID = InstanceIdentifier.create(NetconfCallhomeServer.class)
200 .child(AllowedDevices.class)
201 .child(Device.class, new DeviceKey(cfgDevice.getUniqueId()));
203 ReadWriteTransaction tx = dataBroker.newReadWriteTransaction();
204 CheckedFuture<Optional<Device>, ReadFailedException> deviceFuture = tx.read(LogicalDatastoreType.OPERATIONAL, deviceIID);
206 Optional<Device> opDevGet = deviceFuture.checkedGet();
207 Device1 devStatus = new Device1Builder().setDeviceStatus(Device1.DeviceStatus.DISCONNECTED).build();
208 if (opDevGet.isPresent()) {
209 Device opDevice = opDevGet.get();
210 devStatus = opDevice.getAugmentation(Device1.class);
213 Device newOpDevice = new DeviceBuilder()
214 .addAugmentation(Device1.class, devStatus)
215 .setSshHostKey(cfgDevice.getSshHostKey())
216 .setUniqueId(cfgDevice.getUniqueId()).build();
218 cfgDevice = newOpDevice;
220 tx.merge(LogicalDatastoreType.OPERATIONAL, deviceIID, cfgDevice);