]> git.basschouten.com Git - openhab-addons.git/blob
473613281671f8b9320191eb3917ba6c5cf5b477
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.mikrotik.internal.model;
14
15 import java.util.ArrayList;
16 import java.util.HashMap;
17 import java.util.HashSet;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Optional;
21 import java.util.Set;
22
23 import javax.net.SocketFactory;
24
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 import me.legrange.mikrotik.ApiConnection;
31 import me.legrange.mikrotik.ApiConnectionException;
32 import me.legrange.mikrotik.MikrotikApiException;
33
34 /**
35  * The {@link RouterosDevice} class is wrapped inside a bridge thing and responsible for communication with
36  * Mikrotik device, data fetching, caching and aggregation.
37  *
38  * @author Oleg Vivtash - Initial contribution
39  */
40 @NonNullByDefault
41 public class RouterosDevice {
42     private final Logger logger = LoggerFactory.getLogger(RouterosDevice.class);
43
44     private final String host;
45     private final int port;
46     private final int connectionTimeout;
47     private final String login;
48     private final String password;
49     private @Nullable ApiConnection connection;
50
51     public static final String PROP_ID_KEY = ".id";
52     public static final String PROP_TYPE_KEY = "type";
53     public static final String PROP_NAME_KEY = "name";
54     public static final String PROP_SSID_KEY = "ssid";
55
56     private static final String CMD_PRINT_IFACES = "/interface/print";
57     private static final String CMD_PRINT_IFACE_TYPE_TPL = "/interface/%s/print";
58     private static final String CMD_MONTOR_IFACE_MONITOR_TPL = "/interface/%s/monitor numbers=%s once";
59     private static final String CMD_PRINT_CAPS_IFACES = "/caps-man/interface/print";
60     private static final String CMD_PRINT_CAPSMAN_REGS = "/caps-man/registration-table/print";
61     private static final String CMD_PRINT_WIRELESS_REGS = "/interface/wireless/registration-table/print";
62     private static final String CMD_PRINT_RESOURCE = "/system/resource/print";
63     private static final String CMD_PRINT_RB_INFO = "/system/routerboard/print";
64
65     private final List<RouterosInterfaceBase> interfaceCache = new ArrayList<>();
66     private final List<RouterosCapsmanRegistration> capsmanRegistrationCache = new ArrayList<>();
67     private final List<RouterosWirelessRegistration> wirelessRegistrationCache = new ArrayList<>();
68     private final Set<String> monitoredInterfaces = new HashSet<>();
69     private final Map<String, String> wlanSsid = new HashMap<>();
70
71     private @Nullable RouterosSystemResources resourcesCache;
72     private @Nullable RouterosRouterboardInfo rbInfo;
73
74     private static Optional<RouterosInterfaceBase> createTypedInterface(Map<String, String> interfaceProps) {
75         RouterosInterfaceType ifaceType = RouterosInterfaceType.resolve(interfaceProps.get(PROP_TYPE_KEY));
76         if (ifaceType == null) {
77             return Optional.empty();
78         }
79         switch (ifaceType) {
80             case ETHERNET:
81                 return Optional.of(new RouterosEthernetInterface(interfaceProps));
82             case BRIDGE:
83                 return Optional.of(new RouterosBridgeInterface(interfaceProps));
84             case CAP:
85                 return Optional.of(new RouterosCapInterface(interfaceProps));
86             case WLAN:
87                 return Optional.of(new RouterosWlanInterface(interfaceProps));
88             case PPPOE_CLIENT:
89                 return Optional.of(new RouterosPPPoECliInterface(interfaceProps));
90             case L2TP_SERVER:
91                 return Optional.of(new RouterosL2TPSrvInterface(interfaceProps));
92             case L2TP_CLIENT:
93                 return Optional.of(new RouterosL2TPCliInterface(interfaceProps));
94             default:
95                 return Optional.empty();
96         }
97     }
98
99     public RouterosDevice(String host, int port, String login, String password) {
100         this.host = host;
101         this.port = port;
102         this.login = login;
103         this.password = password;
104         this.connectionTimeout = ApiConnection.DEFAULT_CONNECTION_TIMEOUT;
105     }
106
107     public boolean isConnected() {
108         ApiConnection conn = this.connection;
109         return conn != null && conn.isConnected();
110     }
111
112     public void start() throws MikrotikApiException {
113         login();
114         updateRouterboardInfo();
115     }
116
117     public void stop() {
118         ApiConnection conn = this.connection;
119         if (conn != null && conn.isConnected()) {
120             logout();
121         }
122     }
123
124     public void login() throws MikrotikApiException {
125         logger.debug("Attempting login to {} ...", host);
126         ApiConnection conn = ApiConnection.connect(SocketFactory.getDefault(), host, port, connectionTimeout);
127         conn.login(login, password);
128         logger.debug("Logged in to RouterOS at {} !", host);
129         this.connection = conn;
130     }
131
132     public void logout() {
133         ApiConnection conn = this.connection;
134         logger.debug("Logging out of {}", host);
135         if (conn != null) {
136             logger.debug("Closing connection to {}", host);
137             try {
138                 conn.close();
139             } catch (ApiConnectionException e) {
140                 logger.debug("Logout error", e);
141             } finally {
142                 this.connection = null;
143             }
144         }
145     }
146
147     public boolean registerForMonitoring(String interfaceName) {
148         return monitoredInterfaces.add(interfaceName);
149     }
150
151     public boolean unregisterForMonitoring(String interfaceName) {
152         return monitoredInterfaces.remove(interfaceName);
153     }
154
155     public void refresh() throws MikrotikApiException {
156         synchronized (this) {
157             updateResources();
158             updateInterfaceData();
159             updateCapsmanRegistrations();
160             updateWirelessRegistrations();
161         }
162     }
163
164     public @Nullable RouterosRouterboardInfo getRouterboardInfo() {
165         return rbInfo;
166     }
167
168     public @Nullable RouterosSystemResources getSysResources() {
169         return resourcesCache;
170     }
171
172     public @Nullable RouterosCapsmanRegistration findCapsmanRegistration(String macAddress) {
173         Optional<RouterosCapsmanRegistration> searchResult = capsmanRegistrationCache.stream()
174                 .filter(registration -> macAddress.equalsIgnoreCase(registration.getMacAddress())).findFirst();
175         return searchResult.orElse(null);
176     }
177
178     public @Nullable RouterosWirelessRegistration findWirelessRegistration(String macAddress) {
179         Optional<RouterosWirelessRegistration> searchResult = wirelessRegistrationCache.stream()
180                 .filter(registration -> macAddress.equalsIgnoreCase(registration.getMacAddress())).findFirst();
181         return searchResult.orElse(null);
182     }
183
184     @SuppressWarnings("null")
185     public @Nullable RouterosInterfaceBase findInterface(String name) {
186         Optional<RouterosInterfaceBase> searchResult = interfaceCache.stream()
187                 .filter(iface -> iface.getName() != null && iface.getName().equalsIgnoreCase(name)).findFirst();
188         return searchResult.orElse(null);
189     }
190
191     @SuppressWarnings("null")
192     private void updateInterfaceData() throws MikrotikApiException {
193         ApiConnection conn = this.connection;
194         if (conn == null) {
195             return;
196         }
197
198         List<Map<String, String>> ifaceResponse = conn.execute(CMD_PRINT_IFACES);
199
200         Set<String> interfaceTypesToPoll = new HashSet<>();
201         this.wlanSsid.clear();
202         this.interfaceCache.clear();
203         ifaceResponse.forEach(props -> {
204             Optional<RouterosInterfaceBase> ifaceOpt = createTypedInterface(props);
205             if (ifaceOpt.isPresent()) {
206                 RouterosInterfaceBase iface = ifaceOpt.get();
207                 if (iface.hasDetailedReport()) {
208                     interfaceTypesToPoll.add(iface.getApiType());
209                 }
210                 this.interfaceCache.add(iface);
211             }
212         });
213
214         Map<String, Map<String, String>> typedIfaceResponse = new HashMap<>();
215         for (String ifaceApiType : interfaceTypesToPoll) {
216             String cmd = String.format(CMD_PRINT_IFACE_TYPE_TPL, ifaceApiType);
217             if (ifaceApiType.compareTo("cap") == 0) {
218                 cmd = CMD_PRINT_CAPS_IFACES;
219             }
220             connection.execute(cmd).forEach(propMap -> {
221                 String ifaceName = propMap.get(PROP_NAME_KEY);
222                 if (ifaceName != null) {
223                     if (typedIfaceResponse.containsKey(ifaceName)) {
224                         typedIfaceResponse.get(ifaceName).putAll(propMap);
225                     } else {
226                         typedIfaceResponse.put(ifaceName, propMap);
227                     }
228                 }
229             });
230         }
231
232         for (RouterosInterfaceBase ifaceModel : interfaceCache) {
233             // Enrich with detailed data
234
235             Map<String, String> additionalIfaceProps = typedIfaceResponse.get(ifaceModel.getName());
236             if (additionalIfaceProps != null) {
237                 ifaceModel.mergeProps(additionalIfaceProps);
238             }
239             // Get monitor data
240             if (ifaceModel.hasMonitor() && monitoredInterfaces.contains(ifaceModel.getName())) {
241                 String cmd = String.format(CMD_MONTOR_IFACE_MONITOR_TPL, ifaceModel.getApiType(), ifaceModel.getName());
242                 List<Map<String, String>> monitorProps = connection.execute(cmd);
243                 ifaceModel.mergeProps(monitorProps.get(0));
244             }
245             // Note SSIDs for non-CAPsMAN wireless clients
246             String ifaceName = ifaceModel.getName();
247             String ifaceSsid = ifaceModel.getProperty(PROP_SSID_KEY);
248             if (ifaceName != null && ifaceSsid != null && !ifaceName.isBlank() && !ifaceSsid.isBlank()) {
249                 this.wlanSsid.put(ifaceName, ifaceSsid);
250             }
251         }
252     }
253
254     private void updateCapsmanRegistrations() throws MikrotikApiException {
255         ApiConnection conn = this.connection;
256         if (conn == null) {
257             return;
258         }
259         List<Map<String, String>> response = conn.execute(CMD_PRINT_CAPSMAN_REGS);
260         if (response != null) {
261             capsmanRegistrationCache.clear();
262             response.forEach(reg -> capsmanRegistrationCache.add(new RouterosCapsmanRegistration(reg)));
263         }
264     }
265
266     private void updateWirelessRegistrations() throws MikrotikApiException {
267         ApiConnection conn = this.connection;
268         if (conn == null) {
269             return;
270         }
271         List<Map<String, String>> response = conn.execute(CMD_PRINT_WIRELESS_REGS);
272         wirelessRegistrationCache.clear();
273         response.forEach(props -> {
274             String wlanIfaceName = props.get("interface");
275             String wlanSsidName = wlanSsid.get(wlanIfaceName);
276
277             if (wlanSsidName != null && wlanIfaceName != null && !wlanIfaceName.isBlank() && !wlanSsidName.isBlank()) {
278                 props.put(PROP_SSID_KEY, wlanSsidName);
279             }
280             wirelessRegistrationCache.add(new RouterosWirelessRegistration(props));
281         });
282     }
283
284     private void updateResources() throws MikrotikApiException {
285         ApiConnection conn = this.connection;
286         if (conn == null) {
287             return;
288         }
289         List<Map<String, String>> response = conn.execute(CMD_PRINT_RESOURCE);
290         this.resourcesCache = new RouterosSystemResources(response.get(0));
291     }
292
293     private void updateRouterboardInfo() throws MikrotikApiException {
294         ApiConnection conn = this.connection;
295         if (conn == null) {
296             return;
297         }
298         List<Map<String, String>> response = conn.execute(CMD_PRINT_RB_INFO);
299         this.rbInfo = new RouterosRouterboardInfo(response.get(0));
300     }
301 }