Here's an example that shows you how to export and process asset inventory data. We'll visualize security patch data that we pre-process in Python in a web page using Javascript.
Exporting the patch data you want to visualize
In the device inventory, select a device for which you want to visualize asset data. This is easiest done by using the filter fields below the column headers. If you know the device ID or the device name, input it in the respective field. Or narrow the result list down by restricting the list to desktops, in a specific location, from a specific vendor etc.
Then, click "Export to JSON" to export the configuration data.
Pre-processing the asset data
In theory, exported data could be processed directly in Javascript, but stripping the data down to what we need is easier done in an intermediate step for which we use Python.
From all the software installed on a device, we pick only the security patches, by filtering for the software category "Patch" and the software type "Security". From these entries, the only item that we store is the patch date (installation date of the patch). This data is finally stored in a list with one dictionary entry per year, identifying the number of installed security patches and the number of patch sessions (different patch days). This list is then saved to disk.
import json
with open('C:\\Users\\USERNAME\\Downloads\\OT-BASE Devices.json') as d:
pid = json.load(d)
software = pid["devices"][0]["software"]
patchDates = []
result = []
for s in software:
if s['category'] == "Patch" and s['type'] == "Security" and s['installDate'] != "":
patchDates.append(s['installDate'])
startYear = int(patchDates[-1][0:4])
endYear = int(patchDates[0][0:4])
for y in range(startYear, endYear + 1):
pd = [s for s in patchDates if s.startswith(str(y))]
y_list = []
for p in pd:
if p not in y_list:
y_list.append(p)
result.append({"Year":y, "nPatches": len(pd), "patchDays": len(y_list)})
with open('C:\\Users\\USERNAME\\Documents\\OT-BASE\\Python\\patch numbers.json', 'w') as outfile:
json.dump(result, outfile)
Visualizing the data using the D3 graphics library
The resulting JSON file can be visualized easily using the D3 graphics library. We use it to display a bar chart, with one bar for every year. The height of the bar indicates the number of patches that have been installed in this year. We also display the number of patch sessions as a numerical value on top of each bar.
Here's the Javascript code:
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<style>
.bar {
fill: #3e5874;
}
.axis {
font: 20px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>
// set the dimensions of the canvas
var margin = {top: 120, right: 20, bottom: 140, left: 120},
width = 1200 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
// set the ranges
var x = d3.scale.ordinal().rangeRoundBands([0, width], .05);
var y = d3.scale.linear().range([height, 0]);
// define the axis
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10);
// add the SVG element
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
// load JSON data
d3.json("patch numbers.json", function(error, data) {
data.forEach(function(d) {
d.Year = d.Year;
d.nPatches = d.nPatches;
});
// scale the range of the data
x.domain(data.map(function(d) { return d.Year; }));
y.domain([0, d3.max(data, function(d) { return d.nPatches; })]);
// title
svg.append("text")
.attr("x", (width / 2))
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.style("font-size", "28px")
.style("font-family", "sans-serif")
.style("font-weight", "bold")
.text("Security Patch Consistency");
// x axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)" );
// y axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -70)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Number of patches installed");
// bar chart
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.Year); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.nPatches); })
.attr("height", function(d) { return height - y(d.nPatches); });
// number of patch days
svg.selectAll(".text")
.data(data)
.enter()
.append("text")
.attr("class","label")
.attr("x", function(d) { return x(d.Year) + x.rangeBand() / 2; })
.attr("y", function(d) { return y(d.nPatches) - 20; })
.attr("dy", ".75em")
.style("text-anchor", "middle")
.style("font-size", "22px")
.style("font-family", "sans-serif")
.text(function(d) { return d.patchDays + "X"; });
});
</script>
</body>
To run this code, store it on your development web server and access the respective page with your browser. The output looks something like this:
The script could easily be enhanced for allowing the user to select specific devices, include other metics such as average number of patches for a sample, median number of patch sessions etc.
Comments
0 comments
Please sign in to leave a comment.