1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.bremersee.dccon.repository.cli;
18
19 import java.io.BufferedReader;
20 import java.io.IOException;
21 import java.io.StringReader;
22 import java.time.LocalDateTime;
23 import java.time.OffsetDateTime;
24 import java.time.ZoneOffset;
25 import java.time.format.DateTimeFormatter;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.List;
29 import java.util.function.BiFunction;
30 import lombok.extern.slf4j.Slf4j;
31 import org.bremersee.dccon.model.DhcpLease;
32 import org.springframework.util.StringUtils;
33
34
35
36
37
38
39
40
41
42
43
44
45
46 public interface DhcpLeaseParser extends CommandExecutorResponseParser<List<DhcpLease>> {
47
48
49
50
51 String MAC = "MAC ";
52
53
54
55
56 String IP = " IP ";
57
58
59
60
61 String HOSTNAME = " HOSTNAME ";
62
63
64
65
66 String HOSTNAME_UNKNOWN = "-NA-";
67
68
69
70
71 String BEGIN = " BEGIN ";
72
73
74
75
76 String END = " END ";
77
78
79
80
81 String MANUFACTURER = " MANUFACTURER ";
82
83
84
85
86
87
88 static DhcpLeaseParser defaultParser() {
89 return new Default();
90 }
91
92
93
94
95
96
97
98 @SuppressWarnings("unused")
99 static DhcpLeaseParser defaultParser(BiFunction<String, String, String> unknownHostConverter) {
100 return new Default(unknownHostConverter);
101 }
102
103
104
105
106 @Slf4j
107 class Default implements DhcpLeaseParser {
108
109 private BiFunction<String, String, String> unknownHostConverter;
110
111
112
113
114 Default() {
115 this(null);
116 }
117
118
119
120
121
122
123
124
125
126
127 Default(
128 BiFunction<String, String, String> unknownHostConverter) {
129 if (unknownHostConverter == null) {
130 this.unknownHostConverter = (mac, ip) -> "dhcp-" + ip.replace(".", "-");
131 } else {
132 this.unknownHostConverter = unknownHostConverter;
133 }
134 }
135
136 @Override
137 public List<DhcpLease> parse(final CommandExecutorResponse response) {
138 if (!response.stdoutHasText()) {
139 log.warn("Dhcp lease list command did not produce output. Error is [{}].",
140 response.getStderr());
141 return Collections.emptyList();
142 }
143 final String output = response.getStdout();
144 try (final BufferedReader reader = new BufferedReader(new StringReader(output))) {
145 return parseDhcpLeaseList(reader);
146
147 } catch (IOException e) {
148 log.error("Parsing dhcp lease list failed:\n" + output + "\n", e);
149 return Collections.emptyList();
150 }
151 }
152
153 private List<DhcpLease> parseDhcpLeaseList(final BufferedReader reader) throws IOException {
154 final List<DhcpLease> leases = new ArrayList<>();
155 String line;
156 while ((line = reader.readLine()) != null) {
157 line = line.trim();
158 final String mac = findDhcpLeasePart(line, MAC, IP);
159 final String ip = findDhcpLeasePart(line, IP, HOSTNAME);
160 String hostname = findDhcpLeasePart(line, HOSTNAME, BEGIN);
161 if (HOSTNAME_UNKNOWN.equalsIgnoreCase(hostname) && ip != null) {
162 hostname = unknownHostConverter.apply(mac, ip);
163 }
164 final String begin = findDhcpLeasePart(line, BEGIN, END);
165 final String end = findDhcpLeasePart(line, END, MANUFACTURER);
166 final String manufacturer = findDhcpLeasePart(line, MANUFACTURER, null);
167 if (mac != null && ip != null && hostname != null && begin != null && end != null) {
168 final DhcpLease lease = new DhcpLease(
169 mac.replace("-", ":").trim().toLowerCase(),
170 ip,
171 hostname,
172 parseDhcpLeaseTime(begin),
173 parseDhcpLeaseTime(end),
174 manufacturer);
175 leases.add(lease);
176 }
177 }
178 return leases;
179 }
180
181 private String findDhcpLeasePart(String line, String field, String nextField) {
182 if (!StringUtils.hasText(line) || !StringUtils.hasText(field)) {
183 return null;
184 }
185 int start = line.indexOf(field);
186 if (start < 0) {
187 return null;
188 }
189 start = start + field.length();
190 int end = StringUtils.hasText(nextField)
191 ? line.indexOf(nextField, start)
192 : line.length();
193 if (end < 0 || end <= start) {
194 return null;
195 }
196 return line.substring(start, end).trim();
197 }
198
199 private OffsetDateTime parseDhcpLeaseTime(String time) {
200 if (!StringUtils.hasText(time)) {
201 return null;
202 }
203 final LocalDateTime localDateTime = LocalDateTime.parse(
204 time,
205 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
206
207 return OffsetDateTime.of(localDateTime, ZoneOffset.UTC);
208 }
209
210 }
211
212 }