Skip to content

Commit 992e4cd

Browse files
authored
Added support for tags as fields (#55)
* Added support for tags * Fixed rollup bug * Minor changes to data models * Updated README * Fixed latest sample mode * Bumped version number
1 parent 258eef8 commit 992e4cd

File tree

10 files changed

+142
-51
lines changed

10 files changed

+142
-51
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,9 @@ fields: # A list of fields
229229
# Host CPU type
230230
- alias: hostCPUType
231231
prop: $parent:HostSystem.cpu|cpuModel # Reference to a metric in a parent
232+
# A vCenter tag
233+
- alias: costcenter
234+
tag: costcenter
232235
```
233236

234237
### Global directives
@@ -247,6 +250,12 @@ fields: # A list of fields
247250
* allMetrics: Exports all metrics for every resource. This option is intended mainly for the JSON output format and will
248251
most likely not work for table-oriented outputs, such as CSV and SQL. If specified, the ```fields``` attribute is
249252
ignored.
253+
254+
### Field properties
255+
* alias: The name of the field as it will appear in the output
256+
* prop: Name of a property on the resource from which the value is fetched.
257+
* metric: Name of a metric on the resource from which the value is fetched.
258+
* tag: Name of a vCenter tag category on the resource from which the value is fetched.
250259

251260
### Special properties in the definition file
252261

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
</snapshots>
1818
</repository>
1919
</repositories>
20-
<version>3.2.0</version>
20+
<version>3.3.0</version>
2121
<properties>
2222
<maven.compiler.source>1.8</maven.compiler.source>
2323
<maven.compiler.target>1.8</maven.compiler.target>

samples/tags.yaml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Copyright 2017-2021 VMware, Inc. All Rights Reserved.
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
resourceType: VMWARE:VirtualMachine
18+
rollupType: AVG
19+
rollupMinutes: 1440 # 24 hours
20+
dateFormat: "yyyy-MM-dd HH:mm:ss"
21+
fields:
22+
# CPU fields
23+
- alias: cpuDemand
24+
metric: cpu|demandPct
25+
# Memory fields
26+
- alias: memDemand
27+
metric: mem|guest_demand
28+
# Diskspace fields
29+
- alias: vmUsed
30+
metric: diskspace|used
31+
# Daily price
32+
- alias: dailyPrice
33+
metric: summary|metering|value
34+
# Guest OS
35+
- alias: guestOS
36+
prop: config|guestFullName
37+
# Host name
38+
- alias: hostName
39+
prop: summary|parentHost
40+
# Cost center tag
41+
- alias: costcenter
42+
tag: costcenter
43+
# Cost center tag
44+
- alias: owner
45+
tag: owner
46+
47+

src/main/java/com/vmware/vropsexport/Config.java

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.vmware.vropsexport.json.JsonConfig;
2323
import com.vmware.vropsexport.sql.SQLConfig;
2424
import com.vmware.vropsexport.wavefront.WavefrontConfig;
25+
2526
import java.text.DateFormat;
2627
import java.text.SimpleDateFormat;
2728
import java.util.regex.Matcher;
@@ -51,21 +52,38 @@ public void setReplacement(final char replacement) {
5152
}
5253
}
5354

54-
@SuppressWarnings("unused")
5555
public static class Field {
56+
enum Kind {
57+
METRIC,
58+
PROPERTY,
59+
TAG
60+
}
61+
62+
protected static final String TAG_PROP_PREFIX = "summary|tagJson#";
63+
5664
private String alias;
5765
private String metric;
5866
private String prop;
5967

68+
public void setTag(final String tag) {
69+
this.prop = TAG_PROP_PREFIX + tag;
70+
}
71+
6072
public Field() {}
6173

62-
public Field(final String alias, final String name, final boolean isMetric) {
74+
public Field(final String alias, final String name, final Kind kind) {
6375
super();
6476
this.alias = alias;
65-
if (isMetric) {
66-
metric = name;
67-
} else {
68-
prop = name;
77+
switch (kind) {
78+
case METRIC:
79+
setMetric(name);
80+
break;
81+
case PROPERTY:
82+
setProp(name);
83+
break;
84+
case TAG:
85+
setTag(name);
86+
break;
6987
}
7088
}
7189

src/main/java/com/vmware/vropsexport/Exporter.java

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -167,15 +167,15 @@ conf, getStatKeysForResourceKind(conf.getAdapterKind(), conf.getResourceKind()))
167167
+ ". should be on the form ResourceKind:resourceName");
168168
}
169169
// TODO: No way of specifying adapter type here. Should there be?
170-
final NamedResource[] pResources =
170+
final List<NamedResource> pResources =
171171
fetchResources(m.group(1), null, m.group(2), 0).getResourceList();
172-
if (pResources.length == 0) {
172+
if (pResources.size() == 0) {
173173
throw new ExporterException("Parent not found");
174174
}
175-
if (pResources.length > 1) {
175+
if (pResources.size() > 1) {
176176
throw new ExporterException("Parent spec is not unique");
177177
}
178-
parentId = pResources[0].getIdentifier();
178+
parentId = pResources.get(0).getIdentifier();
179179
}
180180

181181
int page = 0;
@@ -192,9 +192,9 @@ conf, getStatKeysForResourceKind(conf.getAdapterKind(), conf.getResourceKind()))
192192
fetchResources(conf.getResourceKind(), conf.getAdapterKind(), namePattern, page++);
193193
}
194194

195-
final NamedResource[] resources = resPage.getResourceList();
195+
final List<NamedResource> resources = resPage.getResourceList();
196196
// If we got an empty set back, we ran out of pages.
197-
if (resources.length == 0) {
197+
if (resources.size() == 0) {
198198
break;
199199
}
200200

@@ -210,15 +210,15 @@ conf, getStatKeysForResourceKind(conf.getAdapterKind(), conf.getResourceKind()))
210210

211211
// We don't want to make the chunks so big that not all threads will have work to do.
212212
// Make sure that doesn't happen.
213-
chunkSize = Math.min(chunkSize, 1 + (resources.length / executor.getMaximumPoolSize()));
213+
chunkSize = Math.min(chunkSize, 1 + (resources.size() / executor.getMaximumPoolSize()));
214214
if (verbose) {
215215
log.debug("Adjusted chunk size is " + chunkSize + " resources");
216216
}
217217
int i = 0;
218218
ArrayList<NamedResource> chunk = new ArrayList<>(chunkSize);
219219
for (final NamedResource res : resources) {
220220
chunk.add(res);
221-
if (chunk.size() >= chunkSize || i == resources.length - 1) {
221+
if (chunk.size() >= chunkSize || i == resources.size() - 1) {
222222

223223
// Child relationships may return objects of the wrong type, so we have
224224
// to check the type here.
@@ -337,8 +337,9 @@ private InputStream fetchLatestMetrics(final NamedResource[] resList, final RowM
337337
Arrays.stream(resList).map(r -> r.getIdentifier()).collect(Collectors.toList()),
338338
true,
339339
"LATEST",
340+
"MINUTES",
341+
1,
340342
1,
341-
null,
342343
null,
343344
null,
344345
stats);
@@ -354,10 +355,11 @@ private InputStream queryMetrics(
354355
Arrays.stream(resList).map(r -> r.getIdentifier()).collect(Collectors.toList()),
355356
false,
356357
conf.getRollupType(),
358+
"MINUTES",
359+
(int) conf.getRollupMinutes(),
357360
null,
358361
begin,
359362
end,
360-
"MINUTES",
361363
stats);
362364
// log.debug("Metric query: " + new ObjectMapper().writeValueAsString(q));
363365
return client.postJsonReturnStream("/suite-api/api/resources/stats/query", q);
@@ -406,7 +408,7 @@ public NamedResource getParentOf(final String id, final String parentType)
406408
PageOfResources.class,
407409
"relationshipType=PARENT");
408410
final NamedResource res =
409-
Arrays.stream(page.getResourceList())
411+
page.getResourceList().stream()
410412
.filter(r -> r.getResourceKey().get("resourceKindKey").equals(parentType))
411413
.findFirst()
412414
.orElse(null);
@@ -477,7 +479,9 @@ public void generateExportDefinition(final String adapterAndResourceKind, final
477479

478480
final List<String> metrics = getStatKeysForResourceKind(adapterKind, resourceKind);
479481
final List<Config.Field> fields =
480-
metrics.stream().map(s -> new Config.Field(s, s, true)).collect(Collectors.toList());
482+
metrics.stream()
483+
.map(s -> new Config.Field(s, s, Config.Field.Kind.METRIC))
484+
.collect(Collectors.toList());
481485
final Config config = new Config();
482486
final Config.Field[] fieldArr = new Config.Field[fields.size()];
483487
fields.toArray(fieldArr);

src/main/java/com/vmware/vropsexport/RowMetadata.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ public int getPropertyIndex(final String property) {
151151
return propMap.getOrDefault(property, -1);
152152
}
153153

154+
public int getTagIndex(final String tag) {
155+
return getPropertyIndex(Config.Field.TAG_PROP_PREFIX + tag);
156+
}
157+
154158
public int getMetricIndexByAlias(final String metric) {
155159
return metricAliasMap.getOrDefault(metric, -1);
156160
}

src/main/java/com/vmware/vropsexport/StatsProcessor.java

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.fasterxml.jackson.core.JsonFactory;
2121
import com.fasterxml.jackson.core.JsonParser;
2222
import com.fasterxml.jackson.core.JsonToken;
23+
import com.fasterxml.jackson.databind.ObjectMapper;
2324
import com.vmware.vropsexport.exceptions.ExporterException;
2425
import com.vmware.vropsexport.models.NamedResource;
2526
import com.vmware.vropsexport.processors.ParentSplicer;
@@ -93,7 +94,6 @@ public int process(
9394
}
9495

9596
// Process stat-list { stat [ ...
96-
//
9797
expect(p, "stat-list");
9898
expect(p, JsonToken.START_OBJECT);
9999
expect(p, "stat");
@@ -102,7 +102,6 @@ public int process(
102102
while (p.nextToken() != JsonToken.END_ARRAY) {
103103

104104
// Process timestamps[ ...
105-
//
106105
expectCurrent(p, JsonToken.START_OBJECT);
107106
expect(p, "timestamps");
108107
expect(p, JsonToken.START_ARRAY);
@@ -111,7 +110,6 @@ public int process(
111110
long ts = p.getLongValue();
112111

113112
// Align timestamp if needed
114-
//
115113
final int align = conf.getAlign() * 1000;
116114
if (align != 0) {
117115
ts = ((ts + align / 2) / align) * align;
@@ -125,13 +123,11 @@ public int process(
125123
expect(p, JsonToken.END_OBJECT);
126124

127125
// Keep skipping members until we've found the data node
128-
//
129126
while (!expectMaybe(p, "data")) {
130127
skipMember(p, null);
131128
}
132129

133130
// Process data[ ...
134-
//
135131
expect(p, JsonToken.START_ARRAY);
136132
final int metricIdx = meta.getMetricIndex(statKey);
137133
int i = 0;
@@ -160,18 +156,15 @@ public int process(
160156
}
161157

162158
// End of stat-list and values object
163-
//
164159
expect(p, JsonToken.END_OBJECT);
165160
expect(p, JsonToken.END_OBJECT);
166161
Rowset rs = new Rowset(resourceId, rows);
167162
rows = null; // Make the GC release this a bit earlier
168163

169164
// Splice in properties
170-
//
171165
if (dataProvider != null) {
172166
if (meta.hasProperties()) {
173167
// Put in resource id if requested.
174-
//
175168
final int idIdx = meta.getPropertyIndex("$resId");
176169
if (idIdx != -1) {
177170
for (final Row row : rs.getRows().values()) {
@@ -180,7 +173,6 @@ public int process(
180173
}
181174

182175
// Put in name if requested
183-
//
184176
final int nameIdx = meta.getPropertyIndex("$resName");
185177
if (nameIdx != -1) {
186178
final String name = dataProvider.getResourceName(resourceId);
@@ -190,7 +182,6 @@ public int process(
190182
}
191183

192184
// Splice in properties
193-
//
194185
if (meta.needsPropertyLoad()) {
195186
final Map<String, String> props = dataProvider.fetchProps(resourceId);
196187
for (final Map.Entry<String, String> e : props.entrySet()) {
@@ -201,11 +192,25 @@ public int process(
201192
}
202193
}
203194
}
195+
// Splice in tags
196+
String tags = props.get("summary|tagJson");
197+
if (tags != null && !"none".equals(tags)) {
198+
ObjectMapper om = new ObjectMapper();
199+
List<Map<String, String>> parsed = om.readValue(tags, List.class);
200+
for (Map<String, String> tag : parsed) {
201+
int idx = meta.getTagIndex(tag.get("category"));
202+
if (idx != -1) {
203+
String tagValue = tag.get("name");
204+
for (final Row row : rs.getRows().values()) {
205+
row.setProp(idx, tagValue);
206+
}
207+
}
208+
}
209+
}
204210
}
205211
}
206212

207213
// Splice in data from parent
208-
//
209214
final RowMetadata pMeta = meta.forParent();
210215
if (pMeta.isValid()) {
211216
final long now = System.currentTimeMillis();
@@ -218,7 +223,6 @@ public int process(
218223
cached = rowsetCache.get(cacheKey);
219224
}
220225
// Try cache first! Chances are we've seen this parent many times.
221-
//
222226
if (cached != null) {
223227
if (verbose) {
224228
log.debug(
@@ -227,7 +231,6 @@ public int process(
227231
ParentSplicer.spliceRows(rs, cached);
228232
} else {
229233
// Not in cache. Fetch it the hard (and slow) way!
230-
//
231234
if (verbose) {
232235
log.debug(
233236
"Cache miss for parent "
@@ -261,7 +264,6 @@ public int process(
261264
}
262265

263266
// Compactify if needed
264-
//
265267
if (conf.isCompact()) {
266268
rs = compactify(rs, meta);
267269
}
@@ -277,13 +279,11 @@ public int process(
277279

278280
private Rowset compactify(final Rowset rs, final RowMetadata meta) throws ExporterException {
279281
// No need to process empty rowsets
280-
//
281282
if (rs.getRows().size() == 0) {
282283
return rs;
283284
}
284285

285286
// Calculate range according to compactification algorithm.
286-
//
287287
final long startTime = System.currentTimeMillis();
288288
final TreeMap<Long, Row> rows = rs.getRows();
289289
final long start;
@@ -316,7 +316,6 @@ private Rowset compactify(final Rowset rs, final RowMetadata meta) throws Export
316316
}
317317

318318
// Compactify everything that fits within the timerange into a single row
319-
//
320319
final Row target = meta.newRow(ts);
321320
for (final Row r : rows.values()) {
322321
if (r.getTimestamp() <= end && r.getTimestamp() >= start) {

0 commit comments

Comments
 (0)