BUG-624 make netconf tcp address optional in config.ini with default value set to...
[controller.git] / opendaylight / netconf / netconf-ssh / src / main / java / org / opendaylight / controller / netconf / ssh / osgi / NetconfSSHActivator.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  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 package org.opendaylight.controller.netconf.ssh.osgi;
9
10 import static com.google.common.base.Preconditions.checkNotNull;
11
12 import java.io.File;
13 import java.io.FileInputStream;
14 import java.io.IOException;
15 import java.net.InetSocketAddress;
16 import org.apache.commons.io.FilenameUtils;
17 import org.apache.commons.io.IOUtils;
18 import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
19 import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
20 import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator;
21 import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
22 import org.opendaylight.controller.usermanager.IUserManager;
23 import org.osgi.framework.BundleActivator;
24 import org.osgi.framework.BundleContext;
25 import org.osgi.framework.ServiceReference;
26 import org.osgi.util.tracker.ServiceTracker;
27 import org.osgi.util.tracker.ServiceTrackerCustomizer;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 /**
32  * Activator for netconf SSH bundle which creates SSH bridge between netconf client and netconf server. Activator
33  * starts SSH Server in its own thread. This thread is closed when activator calls stop() method. Server opens socket
34  * and listens for client connections. Each client connection creation is handled in separate
35  * {@link org.opendaylight.controller.netconf.ssh.threads.SocketThread} thread.
36  * This thread creates two additional threads {@link org.opendaylight.controller.netconf.ssh.threads.IOThread}
37  * forwarding data from/to client.IOThread closes servers session and server connection when it gets -1 on input stream.
38  * {@link org.opendaylight.controller.netconf.ssh.threads.IOThread}'s run method waits for -1 on input stream to finish.
39  * All threads are daemons.
40  **/
41 public class NetconfSSHActivator implements BundleActivator{
42
43     private NetconfSSHServer server;
44     private static final Logger logger =  LoggerFactory.getLogger(NetconfSSHActivator.class);
45     private IUserManager iUserManager;
46     private BundleContext context = null;
47
48     private ServiceTrackerCustomizer<IUserManager, IUserManager> customizer = new ServiceTrackerCustomizer<IUserManager, IUserManager>(){
49         @Override
50         public IUserManager addingService(final ServiceReference<IUserManager> reference) {
51             logger.trace("Service {} added, let there be SSH bridge.", reference);
52             iUserManager =  context.getService(reference);
53             try {
54                 onUserManagerFound(iUserManager);
55             } catch (final Exception e) {
56                 logger.trace("Can't start SSH server due to {}",e);
57             }
58             return iUserManager;
59         }
60         @Override
61         public void modifiedService(final ServiceReference<IUserManager> reference, final IUserManager service) {
62             logger.trace("Replacing modified service {} in netconf SSH.", reference);
63             server.addUserManagerService(service);
64         }
65         @Override
66         public void removedService(final ServiceReference<IUserManager> reference, final IUserManager service) {
67             logger.trace("Removing service {} from netconf SSH. " +
68                     "SSH won't authenticate users until IUserManager service will be started.", reference);
69             removeUserManagerService();
70         }
71     };
72
73
74     @Override
75     public void start(final BundleContext context) {
76         this.context = context;
77         listenForManagerService();
78     }
79
80     @Override
81     public void stop(BundleContext context) throws IOException {
82         if (server != null){
83             server.stop();
84             logger.trace("Netconf SSH bridge is down ...");
85         }
86     }
87     private void startSSHServer() throws IOException {
88         checkNotNull(this.iUserManager, "No user manager service available.");
89         logger.trace("Starting netconf SSH  bridge.");
90         final InetSocketAddress sshSocketAddress = NetconfConfigUtil.extractSSHNetconfAddress(context,
91                 NetconfConfigUtil.DEFAULT_NETCONF_SSH_ADDRESS);
92         final InetSocketAddress tcpSocketAddress = NetconfConfigUtil.extractTCPNetconfClientAddress(context,
93                NetconfConfigUtil.DEFAULT_NETCONF_TCP_ADDRESS);
94
95         String path =  FilenameUtils.separatorsToSystem(NetconfConfigUtil.getPrivateKeyPath(context));
96
97         if (path.isEmpty()) {
98             throw new IllegalStateException("Missing netconf.ssh.pk.path key in configuration file.");
99         }
100
101         final File privateKeyFile = new File(path);
102         final String privateKeyPEMString;
103         if (privateKeyFile.exists() == false) {
104             // generate & save to file
105             try {
106                 privateKeyPEMString = PEMGenerator.generateTo(privateKeyFile);
107             } catch (Exception e) {
108                 logger.error("Exception occurred while generating PEM string {}", e);
109                 throw new IllegalStateException("Error generating RSA key from file " + path);
110             }
111         } else {
112             // read from file
113             try (FileInputStream fis = new FileInputStream(path)) {
114                 privateKeyPEMString = IOUtils.toString(fis);
115             } catch (final IOException e) {
116                 logger.error("Error reading RSA key from file '{}'", path);
117                 throw new IOException("Error reading RSA key from file " + path, e);
118             }
119         }
120         final AuthProvider authProvider = new AuthProvider(iUserManager, privateKeyPEMString);
121         this.server = NetconfSSHServer.start(sshSocketAddress.getPort(), tcpSocketAddress, authProvider);
122
123         final Thread serverThread = new Thread(server, "netconf SSH server thread");
124         serverThread.setDaemon(true);
125         serverThread.start();
126         logger.trace("Netconf SSH  bridge up and running.");
127     }
128
129     private void onUserManagerFound(final IUserManager userManager) throws Exception{
130         if (server!=null && server.isUp()){
131            server.addUserManagerService(userManager);
132         } else {
133            startSSHServer();
134         }
135     }
136     private void removeUserManagerService(){
137         this.server.removeUserManagerService();
138     }
139     private void listenForManagerService(){
140         final ServiceTracker<IUserManager, IUserManager> listenerTracker = new ServiceTracker<>(context, IUserManager.class,customizer);
141         listenerTracker.open();
142     }
143 }