diff --git a/docs/changelog/96183.yaml b/docs/changelog/96183.yaml new file mode 100644 index 0000000000000..a0f44cf458148 --- /dev/null +++ b/docs/changelog/96183.yaml @@ -0,0 +1,5 @@ +pr: 96183 +summary: Add MAC type in the convert processor. +area: Ingest +type: enhancement +issues: [] diff --git a/docs/reference/ingest/processors/convert.asciidoc b/docs/reference/ingest/processors/convert.asciidoc index 0c915a75038f8..2b24702412597 100644 --- a/docs/reference/ingest/processors/convert.asciidoc +++ b/docs/reference/ingest/processors/convert.asciidoc @@ -7,7 +7,7 @@ Converts a field in the currently ingested document to a different type, such as converting a string to an integer. If the field value is an array, all members will be converted. -The supported types include: `integer`, `long`, `float`, `double`, `string`, `boolean`, `ip`, and `auto`. +The supported types include: `integer`, `long`, `float`, `double`, `string`, `boolean`, `ip`, `mac`, and `auto`. Specifying `boolean` will set the field to true if its string value is equal to `true` (ignore case), to false if its string value is equal to `false` (ignore case), or it will throw an exception otherwise. @@ -15,6 +15,8 @@ false if its string value is equal to `false` (ignore case), or it will throw an Specifying `ip` will set the target field to the value of `field` if it contains a valid IPv4 or IPv6 address that can be indexed into an <>. +Specifying `mac` will set the target field to the value of `field` if t contains a valid 48-bit/64-bit MAC Address. Moreover, it would convert the MAC Address in RFC 7042 format. + Specifying `auto` will attempt to convert the string-valued `field` into the closest non-string, non-IP type. For example, a field whose value is `"true"` will be converted to its respective boolean type: `true`. Do note that float takes precedence of double in `auto`. A value of `"242.15"` will "automatically" be converted to diff --git a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/ConvertProcessor.java b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/ConvertProcessor.java index 3ba21de567a0c..0536d2006b287 100644 --- a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/ConvertProcessor.java +++ b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/ConvertProcessor.java @@ -97,6 +97,27 @@ public Object convert(Object value) { return value; } }, + MAC { + @Override + public Object convert(Object value) { + StringBuilder stringBuilder = new StringBuilder(); + String stringValue = value.toString().toUpperCase().replaceAll("[^a-fA-F0-9]", ""); + + // throw error if invalid 48-bit/64-bit MAC Address. + if ((stringValue.length() == 12 || stringValue.length() == 16) == false) throw new IllegalArgumentException( + "[" + value + "] is not a valid MAC Address" + ); + + for (int i = 0; i < stringValue.length(); i++) { + if (i != 0 && i % 2 == 0) { + stringBuilder.append('-'); + } + stringBuilder.append(stringValue.charAt(i)); + } + + return stringBuilder.toString(); + } + }, STRING { @Override public Object convert(Object value) { diff --git a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/ConvertProcessorTests.java b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/ConvertProcessorTests.java index f1b7433e10a0f..af2790359ce0a 100644 --- a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/ConvertProcessorTests.java +++ b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/ConvertProcessorTests.java @@ -379,6 +379,48 @@ public void testConvertIpList() throws Exception { assertThat(ingestDocument.getFieldValue(fieldName, List.class), equalTo(expectedList)); } + public void testValidMACConvert() throws Exception { + List fieldValue = new ArrayList<>(); + List expectedList = new ArrayList<>(); + Map testValueMap = new HashMap<>(); + testValueMap.put("10:AC:AF:BC:16:22", "10-AC-AF-BC-16-22"); + testValueMap.put("10-AC-AF-BC-16-22", "10-AC-AF-BC-16-22"); + testValueMap.put("10A.CAF.BC1.622", "10-AC-AF-BC-16-22"); + testValueMap.put("10:ac:af:bc:16:22", "10-AC-AF-BC-16-22"); + testValueMap.put("00:01:10:ac:af:bc:16:22", "00-01-10-AC-AF-BC-16-22"); + testValueMap.put("00-01-10-AC-AF-BC-16-22", "00-01-10-AC-AF-BC-16-22"); + + testValueMap.forEach((key, value) -> { + fieldValue.add(key); + expectedList.add(value); + }); + + IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random()); + String fieldName = RandomDocumentPicks.addRandomField(random(), ingestDocument, fieldValue); + Processor processor = new ConvertProcessor(randomAlphaOfLength(10), null, fieldName, fieldName, Type.MAC, false); + processor.execute(ingestDocument); + assertThat(ingestDocument.getFieldValue(fieldName, List.class), equalTo(expectedList)); + } + + public void testInvalidMACConvert() throws Exception { + List fieldValue = new ArrayList<>(); + fieldValue.add("XX:xx:AF:BC:16:22"); + fieldValue.add("XX:01:10:ac:af:bc:16:22"); + fieldValue.add("::01:10:ac:af:bc:16:22"); + fieldValue.add("00:00:01:10:ac:af:bc:16:22"); + fieldValue.add("01-10-ac-af-bc-16-22"); + fieldValue.add("10:AC:AF:BC:16:2"); + + String value = fieldValue.get(randomIntBetween(0, fieldValue.size() - 1)); + + IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random()); + String fieldName = RandomDocumentPicks.addRandomField(random(), ingestDocument, value); + Processor processor = new ConvertProcessor(randomAlphaOfLength(10), null, fieldName, fieldName, Type.MAC, false); + + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> processor.execute(ingestDocument)); + assertThat(e.getMessage(), containsString("[" + value + "] is not a valid MAC Address")); + } + public void testConvertString() throws Exception { IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random()); Object fieldValue;