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 com.google.common.annotations.VisibleForTesting;
12 import com.google.common.base.Optional;
13 import com.google.common.util.concurrent.ListenableFuture;
14 import java.io.IOException;
15 import java.net.InetSocketAddress;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.HashSet;
19 import java.util.List;
21 import java.util.concurrent.ExecutionException;
22 import javax.annotation.Nonnull;
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.binding.api.ReadOnlyTransaction;
29 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
30 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
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.InstanceIdentifier;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
46 public class IetfZeroTouchCallHomeServerProvider implements AutoCloseable, DataTreeChangeListener<AllowedDevices> {
47 private static final String APPNAME = "CallHomeServer";
48 static final InstanceIdentifier<AllowedDevices> ALL_DEVICES = InstanceIdentifier.create(NetconfCallhomeServer.class)
49 .child(AllowedDevices.class);
51 private static final Logger LOG = LoggerFactory.getLogger(IetfZeroTouchCallHomeServerProvider.class);
53 private final DataBroker dataBroker;
54 private final CallHomeMountDispatcher mountDispacher;
55 private final CallHomeAuthProviderImpl authProvider;
57 protected NetconfCallHomeServer server;
59 private ListenerRegistration<IetfZeroTouchCallHomeServerProvider> listenerReg = null;
61 private static final String CALL_HOME_PORT_KEY = "DefaultCallHomePort";
62 private int port = 0; // 0 = use default in NetconfCallHomeBuilder
63 private final CallhomeStatusReporter statusReporter;
65 public IetfZeroTouchCallHomeServerProvider(final DataBroker dataBroker,
66 final CallHomeMountDispatcher mountDispacher) {
67 this.dataBroker = dataBroker;
68 this.mountDispacher = mountDispacher;
69 this.authProvider = new CallHomeAuthProviderImpl(dataBroker);
70 this.statusReporter = new CallhomeStatusReporter(dataBroker);
74 // Register itself as a listener to changes in Devices subtree
76 LOG.info("Initializing provider for {}", APPNAME);
78 listenerReg = dataBroker.registerDataTreeChangeListener(
79 new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, ALL_DEVICES), this);
80 LOG.info("Initialization complete for {}", APPNAME);
81 } catch (IOException | Configuration.ConfigurationException e) {
82 LOG.error("Unable to successfully initialize", e);
86 public void setPort(final String portStr) {
88 Configuration configuration = new Configuration();
89 configuration.set(CALL_HOME_PORT_KEY, portStr);
90 port = configuration.getAsPort(CALL_HOME_PORT_KEY);
91 LOG.info("Setting port for call home server to {}", portStr);
92 } catch (Configuration.ConfigurationException e) {
93 LOG.error("Problem trying to set port for call home server {}", portStr, 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(provider, mountDispacher,
107 builder.setBindAddress(new InetSocketAddress(port));
109 server = builder.build();
111 mountDispacher.createTopology();
112 LOG.info("Initialization complete for Call Home server instance");
116 void assertValid(final Object obj, final String description) {
118 throw new RuntimeException(
119 String.format("Failed to find %s in IetfZeroTouchCallHomeProvider.initialize()", description));
124 public void close() throws Exception {
125 authProvider.close();
126 statusReporter.close();
128 // FIXME unbind the server
129 if (this.listenerReg != null) {
132 if (server != null) {
136 LOG.info("Successfully closed provider for {}", APPNAME);
140 public void onDataTreeChanged(@Nonnull final Collection<DataTreeModification<AllowedDevices>> changes) {
141 // In case of any changes to the devices datatree, register the changed values with callhome server
142 // As of now, no way to add a new callhome client key to the CallHomeAuthorization instance since
143 // its created under CallHomeAuthorizationProvider.
144 // Will have to redesign a bit here.
145 // CallHomeAuthorization.
146 ReadOnlyTransaction roConfigTx = dataBroker.newReadOnlyTransaction();
147 ListenableFuture<Optional<AllowedDevices>> devicesFuture = roConfigTx
148 .read(LogicalDatastoreType.CONFIGURATION, IetfZeroTouchCallHomeServerProvider.ALL_DEVICES);
150 Set<InstanceIdentifier<?>> deletedDevices = new HashSet<>();
151 for (DataTreeModification<AllowedDevices> change : changes) {
152 DataObjectModification<AllowedDevices> rootNode = change.getRootNode();
153 switch (rootNode.getModificationType()) {
155 deletedDevices.add(change.getRootPath().getRootIdentifier());
162 handleDeletedDevices(deletedDevices);
165 for (Device confDevice : getReadDevices(devicesFuture)) {
166 readAndUpdateStatus(confDevice);
168 } catch (ExecutionException | InterruptedException e) {
169 LOG.error("Error trying to read the whitelist devices: {}", e);
173 private void handleDeletedDevices(final Set<InstanceIdentifier<?>> deletedDevices) {
174 if (deletedDevices.isEmpty()) {
178 ReadWriteTransaction opTx = dataBroker.newReadWriteTransaction();
180 for (InstanceIdentifier<?> removedIID : deletedDevices) {
181 LOG.info("Deleting the entry for callhome device {}", removedIID);
182 opTx.delete(LogicalDatastoreType.OPERATIONAL, removedIID);
188 private static List<Device> getReadDevices(final ListenableFuture<Optional<AllowedDevices>> devicesFuture)
189 throws InterruptedException, ExecutionException {
190 Optional<AllowedDevices> opt = devicesFuture.get();
191 return opt.isPresent() ? opt.get().getDevice() : Collections.emptyList();
194 private void readAndUpdateStatus(Device cfgDevice) throws InterruptedException, ExecutionException {
195 InstanceIdentifier<Device> deviceIID = InstanceIdentifier.create(NetconfCallhomeServer.class)
196 .child(AllowedDevices.class).child(Device.class, new DeviceKey(cfgDevice.getUniqueId()));
198 ReadWriteTransaction tx = dataBroker.newReadWriteTransaction();
199 ListenableFuture<Optional<Device>> deviceFuture = tx.read(LogicalDatastoreType.OPERATIONAL, deviceIID);
201 Optional<Device> opDevGet = deviceFuture.get();
202 Device1 devStatus = new Device1Builder().setDeviceStatus(Device1.DeviceStatus.DISCONNECTED).build();
203 if (opDevGet.isPresent()) {
204 Device opDevice = opDevGet.get();
205 devStatus = opDevice.augmentation(Device1.class);
208 cfgDevice = new DeviceBuilder().addAugmentation(Device1.class, devStatus)
209 .setSshHostKey(cfgDevice.getSshHostKey()).setUniqueId(cfgDevice.getUniqueId()).build();
211 tx.merge(LogicalDatastoreType.OPERATIONAL, deviceIID, cfgDevice);