diff --git a/README.md b/README.md index 0f5c248a..e9cb7249 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ By default, there is a `GenericProvider` that supports a `SimpleProcessor` using - Equinix - EXA (formerly GTT) - HGC +- Google - Lumen - Megaport - Momentum diff --git a/circuit_maintenance_parser/__init__.py b/circuit_maintenance_parser/__init__.py index f22c54d2..f9940ef4 100644 --- a/circuit_maintenance_parser/__init__.py +++ b/circuit_maintenance_parser/__init__.py @@ -16,6 +16,7 @@ Equinix, EUNetworks, GTT, + Google, HGC, Lumen, Megaport, @@ -43,6 +44,7 @@ CrownCastle, Equinix, EUNetworks, + Google, GTT, HGC, Lumen, diff --git a/circuit_maintenance_parser/parsers/google.py b/circuit_maintenance_parser/parsers/google.py new file mode 100644 index 00000000..ef7ef2f6 --- /dev/null +++ b/circuit_maintenance_parser/parsers/google.py @@ -0,0 +1,44 @@ +"""Google parser.""" +import logging +import re +from datetime import datetime + +from circuit_maintenance_parser.parser import Html, Impact, CircuitImpact, Status + +# pylint: disable=too-many-nested-blocks, too-many-branches + +logger = logging.getLogger(__name__) + + +class HtmlParserGoogle1(Html): + """Notifications Parser for Google notifications.""" + + def parse_html(self, soup): + """Execute parsing.""" + data = {} + data["circuits"] = [] + data["status"] = Status.CONFIRMED + + for span in soup.find_all("span"): + if span.string is None: + continue + if span.string.strip() == "Start Time:": + dt_str = span.next_sibling.string.strip() + data["start"] = self.dt2ts(datetime.strptime(dt_str, "%Y-%m-%d %H:%M:%S %z UTC")) + elif span.string.strip() == "End Time:": + dt_str = span.next_sibling.string.strip() + data["end"] = self.dt2ts(datetime.strptime(dt_str, "%Y-%m-%d %H:%M:%S %z UTC")) + elif span.string.strip() == "Peer ASN:": + data["account"] = span.parent.next_sibling.string.strip() + elif span.string.strip() == "Google Neighbor Address(es):": + googleaddr = span.parent.next_sibling.string.strip() + elif span.string.strip() == "Peer Neighbor Address(es):": + cid = googleaddr + "-" + span.parent.next_sibling.string.strip() + data["circuits"].append(CircuitImpact(circuit_id=cid, impact=Impact.OUTAGE)) + + summary = list(soup.find("div").find("div").strings)[-1].strip() + match = re.search(r" - Reference (.*)$", summary) + data["summary"] = summary + data["maintenance_id"] = match[1] + + return [data] diff --git a/circuit_maintenance_parser/provider.py b/circuit_maintenance_parser/provider.py index cf40789a..aded2224 100644 --- a/circuit_maintenance_parser/provider.py +++ b/circuit_maintenance_parser/provider.py @@ -26,6 +26,7 @@ from circuit_maintenance_parser.parsers.crowncastle import HtmlParserCrownCastle1 from circuit_maintenance_parser.parsers.equinix import HtmlParserEquinix, SubjectParserEquinix from circuit_maintenance_parser.parsers.gtt import HtmlParserGTT1 +from circuit_maintenance_parser.parsers.google import HtmlParserGoogle1 from circuit_maintenance_parser.parsers.hgc import HtmlParserHGC1, HtmlParserHGC2, SubjectParserHGC1 from circuit_maintenance_parser.parsers.lumen import HtmlParserLumen1 from circuit_maintenance_parser.parsers.megaport import HtmlParserMegaport1 @@ -252,6 +253,15 @@ class EUNetworks(GenericProvider): _default_organizer = "noc@eunetworks.com" +class Google(GenericProvider): + """Google provider custom class.""" + + _processors: List[GenericProcessor] = [ + CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserGoogle1]), + ] + _default_organizer = "noc-noreply@google.com" + + class GTT(GenericProvider): """EXA (formerly GTT) provider custom class.""" diff --git a/tests/unit/data/google/google1.html b/tests/unit/data/google/google1.html new file mode 100644 index 00000000..9990a28e --- /dev/null +++ b/tests/unit/data/google/google1.html @@ -0,0 +1,64 @@ +
+
Google Network Maintenance Notification - Reference PCR/123456
+
+ +
+ This message was sent to: [sample@example.net]
+ This address was selected from the Google ISP Portal. You can update your preferences by navigating to "Configuration > Contacts" and adjusting Email Subscriptions for each user. You can find additional details about Contacts here. +
+ +
+
+ + Details:
+ Start Time: 2023-12-05 20:55:00 +0000 UTC
+ End Time: 2023-12-06 05:05:00 +0000 UTC
+ Duration: 8h10m0s
+ + + + + + =20 + + + + + =20 + + + + + =20 +
Google ASN:15169Peer ASN:65001
Google Neighbor Address(es):192.0.2.1Peer Neighbor Address(es):192.0.2.0
Google Neighbor Address(es):2001:db8::1Peer Neighbor Address(es):2001:db8::
+ +

+ Google systems will automatically re-route the traffic away from the peering link(s) under maintenance. +

+

+ Depending on the nature of the work, your BGP session with Google may stay up through the maintenance window. Traffic may shift to alternate sessions with Google and/or your upstream provider(s). +

+

+ Questions about this event can be sent to noc@google.com. +

+

+ Thank you for peering with Google!
+

+
+
+
+ PRIVILEGE AND CONFIDENTIALITY NOTICE: The information in this + e-mail communication and any attached documents may be privileged, + confidential or otherwise protected from disclosure and is intended only + for the use of the designated recipient(s). If the reader is neither the + intended recipient nor an employee or agent thereof who is responsible + for delivering it to the intended recipient, you are hereby notified + that any review, dissemination, distribution, copying or other use of + this communication is strictly prohibited. If you have received this + communication in error, please immediately notify us by return e-mail + and promptly delete the original electronic e-mail communication and + any attached documentation. Receipt by anyone other than the intended + recipient is not a waiver of any attorney-client or work-product + privilege. +
+
diff --git a/tests/unit/data/google/google1_parser_result.json b/tests/unit/data/google/google1_parser_result.json new file mode 100644 index 00000000..a7d84b51 --- /dev/null +++ b/tests/unit/data/google/google1_parser_result.json @@ -0,0 +1,20 @@ +[ + { + "account": "65001", + "circuits": [ + { + "circuit_id": "192.0.2.1-192.0.2.0", + "impact": "OUTAGE" + }, + { + "circuit_id": "2001:db8::1-2001:db8::", + "impact": "OUTAGE" + } + ], + "end": 1701839100, + "maintenance_id": "PCR/123456", + "start": 1701809700, + "status": "CONFIRMED", + "summary": "Network Maintenance Notification - Reference PCR/123456" + } +] diff --git a/tests/unit/data/google/google2.eml b/tests/unit/data/google/google2.eml new file mode 100644 index 00000000..5e8c58cb --- /dev/null +++ b/tests/unit/data/google/google2.eml @@ -0,0 +1,157 @@ +Date: Wed, 06 Dec 2023 00:40:05 +0000 +Subject: [Scheduled] Google Planned Network Maintenance Notification - Reference PCR/123456 +From: Google NOC +Content-Type: multipart/alternative; boundary="000000000000a556e4060bffffff" + +--000000000000a556e4060bffffff +Content-Type: text/plain; charset="UTF-8"; format=flowed; delsp=yes + +Google Network Maintenance Notification - Reference PCR/123456 + + +This message was sent to: [sample@example.net] +This address was selected from the Google ISP Portal. You can update your +preferences by navigating to "Configuration > Contacts" and adjusting Email +Subscriptions for each user. You can find additional details about Contacts +here. + + +Details: +Start Time: 2023-12-05 20:55:00 +0000 UTC +End Time: 2023-12-06 05:05:00 +0000 UTC +Duration: 8h10m0s + + +Google ASN: 15169 Peer ASN: 65001 + +Google Neighbor Address(es): 192.0.2.1 Peer Neighbor Address(es): +192.0.2.0 + +Google Neighbor Address(es): 2001:db8::1 Peer Neighbor +Address(es): 2001:db8:: + + +Google systems will automatically re-route the traffic away from the +peering link(s) under maintenance. + +Depending on the nature of the work, your BGP session with Google may stay +up through the maintenance window. Traffic may shift to alternate sessions +with Google and/or your upstream provider(s). + +Questions about this event can be sent to noc@google.com. + +Thank you for peering with Google! + + + +PRIVILEGE AND CONFIDENTIALITY NOTICE: The information in this e-mail +communication and any attached documents may be privileged, confidential or +otherwise protected from disclosure and is intended only for the use of the +designated recipient(s). If the reader is neither the intended recipient +nor an employee or agent thereof who is responsible for delivering it to +the intended recipient, you are hereby notified that any review, +dissemination, distribution, copying or other use of this communication is +strictly prohibited. If you have received this communication in error, +please immediately notify us by return e-mail and promptly delete the +original electronic e-mail communication and any attached documentation. +Receipt by anyone other than the intended recipient is not a waiver of any +attorney-client or work-product privilege. + + +--000000000000a556e4060bffffff +Content-Type: text/html; charset="UTF-8" +Content-Transfer-Encoding: quoted-printable + + +
+
Google Network Maintenance No= +tification - Reference PCR/123456
+
+ =20 +
+ This message was sent to: [sample@example.net]
+ This address was selected from the Google ISP Portal. You can update your preferences by navigating to "C= +onfiguration > Contacts" and adjusting Email Subscriptions for each user. Y= +ou can find additional details about Contacts here. +
+ +
+
+ =20 + Details:
+ Start Time: 2023-12-05 20:55:00= + +0000 UTC
+ End Time: 2023-12-06 05:05:00 &= +#43;0000 UTC
+ Duration: 8h10m0s
+ + + + + =20 + + + + + =20 + + + + + =20 +
Google ASN:151= +69Peer ASN:65001<= +/td> +
Google Neighbor Address(es):192.0.2.1Peer Neighbor Address(es):192.0.2.0
Google Neighbor Address(es):2001:db8::1Peer Neighbor Address(es):2001:db8::
+ +

+ Google systems will automatically re-route the traffic away from the peer= +ing link(s) under maintenance. +

+

+ Depending on the nature of the work, your BGP session with Google may sta= +y up through the maintenance window. Traffic may shift to alternate session= +s with Google and/or your upstream provider(s). +

+

+ Questions about this event can be sent to noc@google.com. +

+

+ Thank you for peering with Google!
+

+
+
+
+ PRIVILEGE AND CONFIDENTIALITY NOTICE: The information in this + e-mail communication and any attached documents may be privileged, + confidential or otherwise protected from disclosure and is intended onl= +y + for the use of the designated recipient(s). If the reader is neither th= +e + intended recipient nor an employee or agent thereof who is responsible + for delivering it to the intended recipient, you are hereby notified + that any review, dissemination, distribution, copying or other use of + this communication is strictly prohibited. If you have received this + communication in error, please immediately notify us by return e-mail + and promptly delete the original electronic e-mail communication and + any attached documentation. Receipt by anyone other than the intended + recipient is not a waiver of any attorney-client or work-product + privilege. +
+
+ +--000000000000a556e4060bffffff-- diff --git a/tests/unit/data/google/google2_result.json b/tests/unit/data/google/google2_result.json new file mode 100644 index 00000000..ce194ea5 --- /dev/null +++ b/tests/unit/data/google/google2_result.json @@ -0,0 +1,25 @@ +[ + { + "account": "65001", + "circuits": [ + { + "circuit_id": "192.0.2.1-192.0.2.0", + "impact": "OUTAGE" + }, + { + "circuit_id": "2001:db8::1-2001:db8::", + "impact": "OUTAGE" + } + ], + "end": 1701839100, + "maintenance_id": "PCR/123456", + "organizer": "noc-noreply@google.com", + "provider": "google", + "sequence": 1, + "stamp": 1701823205, + "start": 1701809700, + "status": "CONFIRMED", + "summary": "Network Maintenance Notification - Reference PCR/123456", + "uid": "0" + } +] diff --git a/tests/unit/test_e2e.py b/tests/unit/test_e2e.py index 489ad9a0..a1152ef7 100644 --- a/tests/unit/test_e2e.py +++ b/tests/unit/test_e2e.py @@ -21,6 +21,7 @@ Colt, CrownCastle, EUNetworks, + Google, GTT, HGC, Lumen, @@ -420,6 +421,16 @@ Path(dir_path, "data", "hgc", "hgc2_result.json"), ], ), + # Google + ( + Google, + [ + ("email", Path(dir_path, "data", "google", "google2.eml")), + ], + [ + Path(dir_path, "data", "google", "google2_result.json"), + ], + ), # Lumen ( Lumen, diff --git a/tests/unit/test_parsers.py b/tests/unit/test_parsers.py index c2baa672..644cecea 100644 --- a/tests/unit/test_parsers.py +++ b/tests/unit/test_parsers.py @@ -14,6 +14,7 @@ from circuit_maintenance_parser.parsers.colt import CsvParserColt1, SubjectParserColt1, SubjectParserColt2 from circuit_maintenance_parser.parsers.crowncastle import HtmlParserCrownCastle1 from circuit_maintenance_parser.parsers.equinix import HtmlParserEquinix, SubjectParserEquinix +from circuit_maintenance_parser.parsers.google import HtmlParserGoogle1 from circuit_maintenance_parser.parsers.gtt import HtmlParserGTT1 from circuit_maintenance_parser.parsers.hgc import HtmlParserHGC1, HtmlParserHGC2 from circuit_maintenance_parser.parsers.lumen import HtmlParserLumen1 @@ -297,6 +298,12 @@ Path(dir_path, "data", "equinix", "equinix9.eml"), Path(dir_path, "data", "equinix", "equinix9_result.json"), ), + # Google + ( + HtmlParserGoogle1, + Path(dir_path, "data", "google", "google1.html"), + Path(dir_path, "data", "google", "google1_parser_result.json"), + ), # GTT ( HtmlParserGTT1,