From b9da6d41eb9959c59073fb93055cd1a6b1458ece Mon Sep 17 00:00:00 2001 From: Ian H Pittwood Date: Tue, 2 Jul 2019 14:39:32 -0500 Subject: [PATCH] Revise formatting of excel specs The previous excel spec seemed to lack a consistent structure. This change redesigns the spec to more simply and generically find where data is located in the spreadsheet. Change-Id: I98c1553531897e8c623caa8a1b9334ea915a3f49 --- .../examples/SiteDesignSpec_v0.1.xlsx | Bin 17291 -> 10674 bytes spyglass_plugin_xls/examples/excel_spec.yaml | 126 ++++--- spyglass_plugin_xls/excel_parser.py | 339 ++++++++---------- spyglass_plugin_xls/exceptions.py | 16 + tests/conftest.py | 157 +++++++- tests/shared/SiteDesignSpec_v0.1.xlsx | Bin 17291 -> 10674 bytes tests/shared/excel_spec.yaml | 128 ++++--- tests/shared/invalid_excel_spec.yaml | 126 ++++--- tests/unit/test_excel.py | 42 +-- tests/unit/test_excel_parser.py | 92 +++-- 10 files changed, 660 insertions(+), 366 deletions(-) diff --git a/spyglass_plugin_xls/examples/SiteDesignSpec_v0.1.xlsx b/spyglass_plugin_xls/examples/SiteDesignSpec_v0.1.xlsx index cdf827808bc2f887f635c8985579449e8234fb03..cccc87ef750824d40bd6bea6a9474027ea0128aa 100644 GIT binary patch literal 10674 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 literal 17291 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 1: - network_data[network]['is_common'] = False - else: - network_data[network]['is_common'] = True - LOG.debug( - "private network data extracted from\ - excel:\n%s", pprint.pformat(network_data)) - """ return network_data def get_public_network_data(self): """Read public network data from public ip data""" - network_data = {} - ws = self._get_workbook() - oam_row = self.excel_specs["specs"][self.spec]["oam_ip_row"] - oam_col = self.excel_specs["specs"][self.spec]["oam_ip_col"] - oam_vlan_col = self.excel_specs["specs"][self.spec]["oam_vlan_col"] - ingress_row = self.excel_specs["specs"][self.spec]["ingress_ip_row"] - oob_row = self.excel_specs["specs"][self.spec]["oob_net_row"] - col = self.excel_specs["specs"][self.spec]["oob_net_start_col"] - end_col = self.excel_specs["specs"][self.spec]["oob_net_end_col"] + oam_net = self.loaded_data['public']['oam']['ip'] + if type(oam_net) is str: + oam_net = [oam_net] + network_data = { "oam": { - "subnet": [ws.cell(row=oam_row, column=oam_col).value], - "vlan": ws.cell(row=oam_row, column=oam_vlan_col).value, + 'subnet': oam_net, + 'vlan': re.sub( + r'\W+', '', self.loaded_data['public']['oam']['vlan']) }, - "ingress": ws.cell(row=ingress_row, column=oam_col).value, + "ingress": self.loaded_data['public']['ingress']['ip'], "oob": { "subnet": [] } } - while col <= end_col: - cell_value = ws.cell(row=oob_row, column=col).value - if cell_value: - network_data["oob"]["subnet"].append(self.sanitize(cell_value)) - col += 1 + + for entry in self.loaded_data['public']['oob']: + oob_net = entry['ip'] + if oob_net: + network_data["oob"]["subnet"].append(self.sanitize(oob_net)) LOG.debug( "public network data extracted from\ excel:\n%s", @@ -240,47 +248,22 @@ class ExcelParser(object): """Read location, dns, ntp and ldap data""" site_info = {} - provided_sheetname = self.excel_specs["specs"][ - self.spec]["ipmi_sheet_name"] - ws = self._get_workbook() - dns_row = self.excel_specs["specs"][self.spec]["dns_row"] - dns_col = self.excel_specs["specs"][self.spec]["dns_col"] - ntp_row = self.excel_specs["specs"][self.spec]["ntp_row"] - ntp_col = self.excel_specs["specs"][self.spec]["ntp_col"] - domain_row = self.excel_specs["specs"][self.spec]["domain_row"] - domain_col = self.excel_specs["specs"][self.spec]["domain_col"] - login_domain_row = self.excel_specs["specs"][ - self.spec]["login_domain_row"] - ldap_col = self.excel_specs["specs"][self.spec]["ldap_col"] - global_group = self.excel_specs["specs"][self.spec]["global_group"] - ldap_search_url_row = self.excel_specs["specs"][ - self.spec]["ldap_search_url_row"] - dns_servers = ws.cell(row=dns_row, column=dns_col).value - ntp_servers = ws.cell(row=ntp_row, column=ntp_col).value - try: - if dns_servers is None: - raise RuntimeError( - ( - "No value for dns_server from:{} Sheet:'{}' ", - "Row:{} Col:{}", - ).format( - self.file_name, provided_sheetname, dns_row, dns_col)) - except RuntimeError as rerror: - LOG.critical(rerror) - sys.exit("Tugboat exited!!") + dns_servers = self.loaded_data['site_info']['dns'] + ntp_servers = self.loaded_data['site_info']['ntp'] + if dns_servers is None: + raise exceptions.MissingData( + missing_data='dns servers', section='site_info') dns_servers = list(filter(None, re.split(" |,|\n", dns_servers))) ntp_servers = list(filter(None, re.split(" |,|\n", ntp_servers))) site_info = { "location": self.get_location_data(), "dns": dns_servers, "ntp": ntp_servers, - "domain": ws.cell(row=domain_row, column=domain_col).value, + "domain": self.loaded_data['site_info']['domain'], "ldap": { - "subdomain": ws.cell(row=login_domain_row, - column=ldap_col).value, - "common_name": ws.cell(row=global_group, - column=ldap_col).value, - "url": ws.cell(row=ldap_search_url_row, column=ldap_col).value, + "subdomain": self.loaded_data['site_info']['subdomain'], + "common_name": self.loaded_data['site_info']['global_group'], + "url": self.loaded_data['site_info']['ldap'], }, } LOG.debug( @@ -292,59 +275,37 @@ class ExcelParser(object): def get_location_data(self): """Read location data from the site and zone sheet""" - - ws = self._get_workbook() - corridor_row = self.excel_specs["specs"][self.spec]["corridor_row"] - column = self.excel_specs["specs"][self.spec]["column"] - site_name_row = self.excel_specs["specs"][self.spec]["site_name_row"] - state_name_row = self.excel_specs["specs"][self.spec]["state_name_row"] - country_name_row = self.excel_specs["specs"][ - self.spec]["country_name_row"] - clli_name_row = self.excel_specs["specs"][self.spec]["clli_name_row"] return { - "corridor": ws.cell(row=corridor_row, column=column).value, - "name": ws.cell(row=site_name_row, column=column).value, - "state": ws.cell(row=state_name_row, column=column).value, - "country": ws.cell(row=country_name_row, column=column).value, - "physical_location": ws.cell(row=clli_name_row, - column=column).value, + "corridor": self.loaded_data['location']['corridor'], + "name": self.loaded_data['location']['sitename'], + "state": self.loaded_data['location']['state'], + "country": self.loaded_data['location']['country'], + "physical_location": self.loaded_data['location']['clli'], } def validate_sheet_names_with_spec(self): """Checks is sheet name in spec file matches with excel file""" - spec = list(self.excel_specs["specs"].keys())[0] - spec_item = self.excel_specs["specs"][spec] sheet_name_list = [] - ipmi_header_sheet_name = spec_item["ipmi_sheet_name"] - sheet_name_list.append(ipmi_header_sheet_name) - private_ip_sheet_name = spec_item["private_ip_sheet"] - sheet_name_list.append(private_ip_sheet_name) - public_ip_sheet_name = spec_item["public_ip_sheet"] - sheet_name_list.append(public_ip_sheet_name) - dns_ntp_ldap_sheet_name = spec_item["dns_ntp_ldap_sheet"] - sheet_name_list.append(dns_ntp_ldap_sheet_name) - location_sheet_name = spec_item["location_sheet"] - sheet_name_list.append(location_sheet_name) - for sheetname in sheet_name_list: + for key, data in self.loaded_spec.items(): + sheet_name_list.append(data['sheet_name']) + for sheet_name in sheet_name_list: workbook_object, extracted_sheetname = ( - self.get_xl_obj_and_sheetname(sheetname)) + self.get_xl_obj_and_sheetname(sheet_name)) if workbook_object is not None: wb = workbook_object - sheetname = extracted_sheetname + sheet_name = extracted_sheetname else: wb = self.wb_combined - if sheetname not in wb.sheetnames: - raise RuntimeError( - "SheetName '{}' not found ".format(sheetname)) + if sheet_name not in wb.sheetnames: + raise exceptions.ExcelSheetNotFound(sheet_name=sheet_name) LOG.info("Sheet names in excel spec validated") def get_data(self): """Create a dict with combined data""" - self.validate_sheet_names_with_spec() ipmi_data = self.get_ipmi_data() network_data = self.get_private_network_data() public_network_data = self.get_public_network_data() diff --git a/spyglass_plugin_xls/exceptions.py b/spyglass_plugin_xls/exceptions.py index a610009..2262967 100644 --- a/spyglass_plugin_xls/exceptions.py +++ b/spyglass_plugin_xls/exceptions.py @@ -25,3 +25,19 @@ class ExcelFileNotSpecified(SpyglassBaseException): class ExcelSpecNotSpecified(SpyglassBaseException): message = 'Engineering excel spec not specified' + + +class InvalidSpec(SpyglassBaseException): + message = ( + 'Series type dataset is missing iter index type. ' + 'Possible index types are "row" or "col".') + + +class MissingData(SpyglassBaseException): + message = 'No %(missing_data) specified for %(section)' + + +class ExcelSheetNotFound(SpyglassBaseException): + message = ( + 'Sheet name %(sheet_name) could not be resolved in the given ' + 'Excel files.') diff --git a/tests/conftest.py b/tests/conftest.py index 167aa21..7fbef09 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -15,6 +15,153 @@ import pytest +@pytest.fixture(scope='class') +def raw_excel_data(request): + request.cls.raw_excel_data = { + 'ipmi': [ + { + 'hostname': 'cab2r72c12', + 'ipmi_address': '10.0.220.138', + 'ipmi_gateway': '10.0.220.129', + 'host_profile': 'dp-r720' + }, { + 'hostname': 'cab2r72c13', + 'ipmi_address': '10.0.220.139', + 'ipmi_gateway': '10.0.220.129', + 'host_profile': 'dp-r720' + }, { + 'hostname': 'cab2r72c14', + 'ipmi_address': '10.0.220.140', + 'ipmi_gateway': '10.0.220.129', + 'host_profile': 'dp-r720' + }, { + 'hostname': 'cab2r72c15', + 'ipmi_address': '10.0.220.141', + 'ipmi_gateway': '10.0.220.129', + 'host_profile': 'dp-r720' + }, { + 'hostname': 'cab2r72c16', + 'ipmi_address': '10.0.220.142', + 'ipmi_gateway': '10.0.220.129', + 'host_profile': 'cp-r720' + }, { + 'hostname': 'cab2r72c17', + 'ipmi_address': '10.0.220.143', + 'ipmi_gateway': '10.0.220.129', + 'host_profile': 'cp-r720' + }, { + 'hostname': 'cab2r73c12', + 'ipmi_address': '10.0.220.170', + 'ipmi_gateway': '10.0.220.161', + 'host_profile': 'dp-r720' + }, { + 'hostname': 'cab2r73c13', + 'ipmi_address': '10.0.220.171', + 'ipmi_gateway': '10.0.220.161', + 'host_profile': 'dp-r720' + }, { + 'hostname': 'cab2r73c14', + 'ipmi_address': '10.0.220.172', + 'ipmi_gateway': '10.0.220.161', + 'host_profile': 'dp-r720' + }, { + 'hostname': 'cab2r73c15', + 'ipmi_address': '10.0.220.173', + 'ipmi_gateway': '10.0.220.161', + 'host_profile': 'dp-r720' + }, { + 'hostname': 'cab2r73c16', + 'ipmi_address': '10.0.220.174', + 'ipmi_gateway': '10.0.220.161', + 'host_profile': 'cp-r720' + }, { + 'hostname': 'cab2r73c17', + 'ipmi_address': '10.0.220.175', + 'ipmi_gateway': '10.0.220.161', + 'host_profile': 'cp-r720' + } + ], + 'private_vlan': [ + { + 'net_type': 'iSCSI/Storage', + 'vlan': 'vlan23' + }, { + 'net_type': 'PXE', + 'vlan': 'vlan21' + }, { + 'net_type': 'Calico BGP peering addresses', + 'vlan': 'vlan22' + }, { + 'net_type': 'Overlay', + 'vlan': 'vlan24' + }, { + 'net_type': 'CNI Pod addresses', + 'vlan': 'n/a' + } + ], + 'private_net': [ + { + 'vlan': 'vlan23', + 'ip': '30.31.1.0/25' + }, { + 'vlan': 'vlan21', + 'ip': '30.30.4.0/25' + }, { + 'vlan': 'vlan21', + 'ip': '30.30.4.128/25' + }, { + 'vlan': 'vlan21', + 'ip': '30.30.5.0/25' + }, { + 'vlan': 'vlan21', + 'ip': '30.30.5.128/25' + }, { + 'vlan': 'vlan22', + 'ip': '30.29.1.0/25' + }, { + 'vlan': 'vlan24', + 'ip': '30.19.0.0/25' + } + ], + 'public': { + 'oam': { + 'vlan': 'vlan-21', + 'ip': '10.0.220.0/26' + }, + 'ingress': { + 'ip': '10.0.220.72/29' + }, + 'oob': [ + { + 'ip': '10.0.220.128/27' + }, { + 'ip': '10.0.220.160/27' + }, { + 'ip': '10.0.220.192/27' + }, { + 'ip': '10.0.220.224/27' + } + ] + }, + 'site_info': { + 'domain': 'dmy00.example.com', + 'subdomain': 'testitservices', + 'global_group': 'AA-AAA-dmy00', + 'ldap': 'url: ldap://ldap.example.com', + 'ntp': '150.234.210.5 (ns1.example.com)', + 'dns': '40.40.40.40 (ntp1.example.com),' + '\n41.41.41.41 (ntp2.example.com)' + }, + 'location': { + 'sitename': 'SampleSiteName', + 'corridor': 'Corridor 1', + 'state': 'New Jersey', + 'country': 'SampleCountry', + 'clli': 'XXXXXX21' + } + } + + @pytest.fixture(scope='class') def site_data(request): request.cls.site_data = { @@ -99,12 +246,12 @@ def site_data(request): 'network_data': { 'private': { 'iSCSI/Storage': { - 'vlan': 'vlan 23', + 'vlan': 'vlan23', 'subnet': ['30.31.1.0/25'], 'is_common': True }, 'PXE': { - 'vlan': 'vlan 21', + 'vlan': 'vlan21', 'subnet': [ '30.30.4.0/25', '30.30.4.128/25', '30.30.5.0/25', '30.30.5.128/25' @@ -112,12 +259,12 @@ def site_data(request): 'is_common': True }, 'Calico BGP peering addresses': { - 'vlan': 'vlan 22', + 'vlan': 'vlan22', 'subnet': ['30.29.1.0/25'], 'is_common': True }, 'Overlay': { - 'vlan': 'vlan 24', + 'vlan': 'vlan24', 'subnet': ['30.19.0.0/25'], 'is_common': True } @@ -125,7 +272,7 @@ def site_data(request): 'public': { 'oam': { 'subnet': ['10.0.220.0/26'], - 'vlan': 'VLAN-21' + 'vlan': 'vlan21' }, 'ingress': '10.0.220.72/29', 'oob': { diff --git a/tests/shared/SiteDesignSpec_v0.1.xlsx b/tests/shared/SiteDesignSpec_v0.1.xlsx index cdf827808bc2f887f635c8985579449e8234fb03..cccc87ef750824d40bd6bea6a9474027ea0128aa 100644 GIT binary patch literal 10674 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 literal 17291 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