From 6174c80f5d4434b89dbd396b142e7f4aefaa1a2d Mon Sep 17 00:00:00 2001 From: "Ian H. Pittwood" Date: Fri, 26 Apr 2019 16:01:24 -0500 Subject: [PATCH] Initial commit Adds code for tugboat plugin from Airship Spyglass. --- .gitignore | 116 +++++++ .zuul.yaml | 18 + LICENSE | 201 +++++++++++ README.rst | 10 + doc/requirements.txt | 3 + doc/source/conf.py | 130 ++++++++ doc/source/index.rst | 96 ++++++ examples/SiteDesignSpec_v0.1.xlsx | Bin 0 -> 17291 bytes examples/excel_spec.yaml | 63 ++++ examples/site_config.yaml | 33 ++ requirements.txt | 4 + setup.py | 34 ++ spyglass-plugin-xls/__init__.py | 1 + spyglass-plugin-xls/check_exceptions.py | 35 ++ spyglass-plugin-xls/cli.py | 151 +++++++++ spyglass-plugin-xls/excel.py | 358 ++++++++++++++++++++ spyglass-plugin-xls/excel_parser.py | 423 ++++++++++++++++++++++++ test-requirements.txt | 8 + tools/gate/whitespace-linter.sh | 9 + tox.ini | 57 ++++ 20 files changed, 1750 insertions(+) create mode 100644 .gitignore create mode 100644 .zuul.yaml create mode 100644 LICENSE create mode 100644 README.rst create mode 100644 doc/requirements.txt create mode 100644 doc/source/conf.py create mode 100644 doc/source/index.rst create mode 100644 examples/SiteDesignSpec_v0.1.xlsx create mode 100644 examples/excel_spec.yaml create mode 100644 examples/site_config.yaml create mode 100644 requirements.txt create mode 100644 setup.py create mode 100644 spyglass-plugin-xls/__init__.py create mode 100644 spyglass-plugin-xls/check_exceptions.py create mode 100644 spyglass-plugin-xls/cli.py create mode 100644 spyglass-plugin-xls/excel.py create mode 100644 spyglass-plugin-xls/excel_parser.py create mode 100644 test-requirements.txt create mode 100755 tools/gate/whitespace-linter.sh create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a741440 --- /dev/null +++ b/.gitignore @@ -0,0 +1,116 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +*.tgz + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.testrepository/* +cover/* +results/* +.stestr/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# dotenv +.env + +# virtualenv +.venv +venv/ +ENV/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +# pycharm-ide +.idea/ + +# osx +.DS_Store + +# git +Changelog +AUTHORS + +# Ansible +*.retry diff --git a/.zuul.yaml b/.zuul.yaml new file mode 100644 index 0000000..922d40b --- /dev/null +++ b/.zuul.yaml @@ -0,0 +1,18 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +- project: + check: + jobs: + - openstack-tox-pep8 + gate: + jobs: + - openstack-tox-pep8 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..8acd160 --- /dev/null +++ b/README.rst @@ -0,0 +1,10 @@ +Spyglass Excel Plugin +--------------------- + +Tugboat is a Spyglass plugin to generate airship-seaworthy site manifest files +from an excel based engineering spec. The plugin is configured with an Excel +sheet and its corresponding excel specification as inputs. Spyglass uses this +plugin to construct an intermediary yaml which is processed further using J2 +templates to generate site manifests. + +Spyglass: https://opendev.org/airship/spyglass diff --git a/doc/requirements.txt b/doc/requirements.txt new file mode 100644 index 0000000..b13935d --- /dev/null +++ b/doc/requirements.txt @@ -0,0 +1,3 @@ +# Documentation +sphinx>=2.0.1 +sphinx_rtd_theme==0.4.3 diff --git a/doc/source/conf.py b/doc/source/conf.py new file mode 100644 index 0000000..fff90ae --- /dev/null +++ b/doc/source/conf.py @@ -0,0 +1,130 @@ +# -*- coding: utf-8 -*- +# +# shipyard documentation build configuration file, created by +# sphinx-quickstart on Sat Sep 16 03:40:50 2017. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys +import sphinx_rtd_theme + +sys.path.insert(0, os.path.abspath('../../')) + + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.todo', + 'sphinx.ext.viewcode', +] + +# Add any paths that contain templates here, relative to this directory. +# templates_path = [] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'spyglass-plugin-xls' +copyright = u'2019 AT&T Intellectual Property.' +author = u'The Airship Authors' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = u'0.0.1' +# The full version, including alpha/beta/rc tags. +release = u'0.0.1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = [] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "sphinx_rtd_theme" +html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = [] + + +# -- Options for HTMLHelp output ------------------------------------------ + +# Output file base name for HTML help builder. +htmlhelp_basename = 'ucpintdoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} diff --git a/doc/source/index.rst b/doc/source/index.rst new file mode 100644 index 0000000..feb54b6 --- /dev/null +++ b/doc/source/index.rst @@ -0,0 +1,96 @@ +.. + Copyright 2018 AT&T Intellectual Property. + All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); you may + not use this file except in compliance with the License. You may obtain + a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. + +============== +Tugboat Plugin +============== + +What is Tugboat Plugin? +----------------------- + +Tugboat is a Spyglass plugin to generate airship-seaworthy site manifest files +from an excel based engineering spec. The plugin is configured with an Excel +sheet and its corresponding excel specification as inputs. Spyglass uses this +plugin to construct an intermediary yaml which is processed further using J2 +templates to generate site manifests. + +Excel specification +------------------- +Excel Spec is like an index to the Excel sheet to look for the data to be +collected by the tool. Excel Spec Sample specifies all the details that +need to be filled by the Deployment Engineer. + +Below is the definition for each key in the Excel spec + +:: + + + ipmi_sheet_name - name of the sheet from where IPMI and host profile information is to be read + start_row - row number from where the IPMI and host profile information starts + end_row - row number from where the IPMI and host profile information ends + hostname_col - column number where the hostnames are to be read from + ipmi_address_col - column number from where the ipmi addresses are to be read + host_profile_col - column number from where the host profiles are to be read + ipmi_gateway_col - column number from where the ipmi gateways are to be read + private_ip_sheet - name of the sheet which has the private IP information + net_type_col - column number from where the network type is to be read + vlan_col - column number from where the network vlan is to be read + vlan_start_row - row number from where the vlan information starts + vlan_end_row - row number from where the vlan information ends + net_start_row - row number from where the network information starts + net_end_row - row number from where the network information ends + net_col - column number where the IP ranges for network is to be read + net_vlan_col - column number where the vlan information is present in the pod wise network section + public_ip_sheet - name of the sheet which has the public IP information + oam_vlan_col - column number from where the OAM vlan information is to be read from + oam_ip_row - row number from where the OAM network information is to be read from + oam_ip_col - column number from where the OAM network information is to be read from + oob_net_row - row number which has the OOB network subnet ranges + oob_net_start_col - column number from where the OOB network ranges start + oob_net_end_col - column number from where the OOB network ranges end + ingress_ip_row - row number from where the Ingress network information is to be read from + dns_ntp_ldap_sheet - name of the sheet which has the DNS, NTP and LDAP information + login_domain_row - row number which has the ldap login domain + ldap_col - column number which has the all ldap related information + global_group - row number which has the ldap group information + ldap_search_url_row - row number which has the ldap url + ntp_row - row number which has the ntp information + ntp_col - column number which has the ntp information + dns_row - row number which has the dns information + dns_col - column number which has the dns information + domain_row - row number which has the domain information + domain_col - column number which has the domain information + location_sheet - name of the sheet which has the location information + column - column number which has all the information + corridor_row - row number which has the corridor information + site_name_row - row number which has the site name + state_name_row - row number which has the state name + country_name_row - row number which has the country name + clli_name_row - row number which has CLLI information + +Example: Tugboat Plugin Usage +----------------------------- + +1. Required Input(Refer to 'spyglass/examples' folder to get these inputs) + + a) Excel File: SiteDesignSpec_v0.1.xlsx + b) Excel Spec: excel_spec_upstream.yaml + c) Site Config: site_config.yaml + +2. Spyglass CLI Command: +:: + + spyglass -mg -t tugboat -x SiteDesignSpec_v0.1.xlsx -e excel_spec_upstream.yaml -d site_config.yaml -s airship-seaworthy --template_dir=NRrp>wmbCM*E`K$!>h;l2HTum8m}FrYkQ+sA;|sdmgK+@%`sFQlRZ3a=AG zi+l?r{fP)Y+{PUo+IJ&dLs{jcAR!=uvhKjg!WlF-q} z_|Qm$4ku)G2tpZ+2E^*TA80Vh08Y#nB8a~WE5Bn>q@{+XCTgIL6^mf~3Mtg?usYv- zo)}Al2{$4R_9PGInOdRg)6S5~#Xcz;sEsWXMQN2($Gt}Juw$BZHQ!kmx1a~wB`x8)B9CzjJjf%7%^|{Cy&h2C;Ht!&f28};9yxrWz5xBcf?r3uXAM7B$RLpNwX{pIl zE87D+fFJyB+~aWO0?YkY8;`rOK_-y5hj!;9-pl(k`}q?DNdDhswMm769SG|H|n9V!`~EN3Tqfk?UuG4Y?412_3$lUyDN&lyVgiZzodr@sn6Z zXo@Z*#aZj7z(rKX4g?YN?euy1zOwcy`goY=Zkwet5*d}7q}jD9B=zaDGXyocLyG9< z%B=w;mwCYaUAma0JEco!EOkXoS-#ZB28rnGm2e&6B%K;I6iP8}2s%%?zviHf#=7A{ zHSoNk(pgnV?N|1^-uVHH z7qPpZv31XlaAr5ks~4?o*03Tm3-SZgq}UKy9>7<#k>zSM+kJo?s=s3RawH&GDEgmVN5Z(} z00V5$MQ~T(bm!-UH~H^eo>ak|%_|TLb;zMI)<)%g?#~%X=WMk_sZ)idEH{i}rq7(W z+ju0->r|BWxS(x6GAEj#b?@cW?g_)1nA`5*(W1m_tLW(2CYP*a<&4|;iOV?@_0#(U zla#e^Sxk8Iq*4cm1RSxAXacJd+C((=AG4#nS@SY1BC-__UH7pEd|*c&?QAw!bfROs zd~l|&uwX{QVG{FVXzK$kUUbnlK7hIBSxm=T_%@B6D2_uUjicwZ9l zE9YO=Or`Ru?Fs{82gV^E;-}k^LnKaF?NNU{5jaqrIIa;9j0{bUAQ45f@+E;O-wVkI z&96s9BA!Tk+o$JaABR#QG!h^fRLzs8E=lu1Q5VzYSAy27`P>eY)Q6M@C94SYWA%^r zcF!`ebTijPPw)hQOi(mA)S|sMu0N1AP_%srrf6kL;MC5jvZDd{*Z}62SWE!cn@*oD z-zbEczNmlr2?G~BV-Y#&gda9(%;2dRG5Kto)E6$x*i=mof3aRLESfUd(^`Nk-mCEk zwarq=Ks~}%p4i|Kj5$@ywCce*X#NY`2nHa;pCCkMB=IwyWXp*;MdcS>#${nDDynqn zQE850WUhhY^?rJsl4{c?$}1M!kmkfiNormGPs)B)8M1)q1qx1&Ftc_~YX~b>52Th0_ReifwT0(F8 zBJ-rt+ZP5wh_8X&TQwgcZFc1J8Z&bI%GEiOm8@o+$Q7reskV@xZtOt{?OFbyH{9pe z9~%qmEaG&tF_IC5Gr9q_w36G%F6GdIK|T8%WC+58KYo99*Ey^mKSdcvf7!9}3~8vv zf$DA_;TgqU=`+qosg^mSkk@R&pwEpwuerC(O*8L|`GerO(V|La2Uc=Sd~=|(9{ucL zALBOV{=jp3y0dJ&03mwUu~Ch=5je!aZ=FP&Mi(cLEn`PQ=~DtEgsX`Tcfk^IzMKKB zbZD>0DcI<&KSr*e@VdJfw)>465-mjko*^fW*K_x4a1IVYPC3!7H%bd<(Dk8qji1*D z{kh5r`$nYcx$^aGVVC=TCI07Ij%JSSUK|1l=z$ps2<{(i)zQh_+QjkK+igxuD>jn@ z)kn|bC&+g87V>b9b%L!~$+YTm47IcBkI*heLaYiu68j%T^KbV&&8-|zpb0>;F{m>3 zUXK&64@=VP)`jv<*rZf_SIP@^EE>>;2+x$dGJC!`2NASVRG*DD;6CF@@9D&Sw=?bj z$tE#E2g}b<``Qeb|awVwYTx&FWkK^bay~e?egvYdVdHj`sTsOp0@2&@!zxCmxH6SQHXH ze^xwNWFq=rD%}K}H05ujWY+;kl`+R<)oK%=G}j_q;4IG8)@nUOl{MA6~pw3l1B$pM77bDjLEw#+free*P%U4&IRj=c;G;LYp(Y z@j%?TVx8cV+SNwXc<6+T5Xun`8HTW+Pg{or)-e=_Qy>yx{Zo zGM8wZ2DW`7pa3h3ybV2IQdD~uKPQ&*F3<+ZvTwnbg0V@Ff*U-7;@zwJ*ANaQAth7ef48iyxKE%!KFpXF;CLf&286B_kNyHuQIyCu}P7J#Yf`_Rw z+mx`plGsavk^tFTX*3sehd|}}4?ttNP6Vn65~?pwDGobLFygbGSZ5o7!^v9vqYyN+ zxGSFd{oIskkih&1r7jboN`K4OYh@{vhB*9UhCP04Qa zau2_RcHPkSQkh1`1WuY|M*LdUjN4SX`;RRtlXgOl5ruwaJ50IzMgbC=NUDm)0@`TH zlfgvax0nRPS4;|p2@}iX#o4eQhAlu_l`tl9K{F1~AuaW?gpa@|4!NC*MVOC|OfWtPq6kZh8bp$saP-hH#4 z@((YWx0XK~1LH|3p3(JRewHp~ol>0AZgSk$85om39f~0ImRbck)|!k?M_m`G&LQ#B zv`9}*PDd<~Ny(=#D8EV@jvSwTLR?*NixaGKV8t8LB;u&o9F%bW5~x$AbWt07R)F4; z$58e8f<{^_MGA>6vkC~~?l}2sFKis-(3ShLD#??BmJ_zX#;J(cfLbwtxzA1P?HK@f zj;wlTV{78#)NC;wMI}{2L`F&C!B;YmSrJ;B*>5c|hV401g97zqzU?!|JwD7yR7>$m zxg4}e37}a#GJ6||Xi>m6-7YKB1LV6>$u}1touZ-SM>|#wfeSjnIor}YvGZx!!uG~T zSO@#H?=A;utMWI2jiDL}6 z;GFNaAEY)Tw6=fBAb+`$f^t?AGsfp- zUp_?QFpNQwt?(4Yh&Vb6S809mlz`$Am?WhBSir0R)r(Q2S`EsUj&W7-{n9&yF_Wp0 zYxtJ#v&;Eq1wXi=kLobq(A&)@uZ$(3a5P;);yExhE+_`$ux|BUx|*{&_n8IVmmN0T zfx3P%4qH@s|N9uf$Oehs^e(Ed)OY2lbZ11`U5%ihsrV=gxO8qm)W6!e?~nH&J_Ues!_i{` z8(sw#w?mCMUhOE;b^jc<{RF&PxhQsvhQjvko#30d_~n+7C&$mR27zidAmHZ=xq#B+ zQ2Vpi!PVNTy!F|->X?E)RgCk`GWGrb*PWf!Y1p$ZzAc$(n)O+IyW}3EO562fqK@%+ z;{nr6zGNFE4Y$q94LHp3Vlt-2u+sdTNjD-O1c4Gl-B9$TFU z)3B0bk2lj{)vxD}5g(=ov{hS!22>G|Kjjb#j+5o5)SQmJy(`xLRJqKvr=5UzRU7qg zB9i=N9y*$vm^eB9xySgU!t{T47-I>&vLHl=As6IbAnEP3S|~=+%esvc2)d2DKz*ll zRIs3B?cSc8=8ZDZ7zp?j%w9LI#wiuc+;<^VEBwfs>p(&?z3SMc9oCC#Uwa%BjFVu3 zkm1t|=5=&G=1!F>66{S6j!_5K2h}CHQ3x-PcqF@ri8sxL?y8iVU<)gIl!Bv1mb4-r zaf7sxkgs4uSr}FO+aT20zGdS*ty^{=cwEGPPe&srmjat*PEiEek5IMfA@;wSUAI`f zR+A!}kfM82bg2BC*wFYq^E}KegEFM?co=uG`R$;`hyf6YzmFZA^ej>TYL~Em7}DnY zw}wi1*N^LycUNNy1rQMCf6Urn^OdD(W4FQq`{vX03emrjI$$S(!-99r)epDix7EDj z?Sj|Dcr47TW%0w;cP`Q>zFEyvFGJLQEVO_!A;JCT3LwuxU-`w+XSHSBdW-8c?MlbT z>qpDamxtS}@6ML-_^uYcvDQ0Xbw6I+o(~Av&2)JdJqLq8kYfOYYcLAc2RB?R4MLie zZX+?5qa-!(<$g8uvmJCaDd%%Sp@!%5=%bHB?c zK(8m>x(m^bLTsX|L{&L*d!bPTl89Gv7_Dwff;tCrReXGS?#aBU3>Rwj(hZ``Pculq z!wnIOFA8J)tJ*X_vTpz{9`Fgab}pP_{26QIOFM&|H;XI97ZY}NN&Y_jr`lRSxrx0W zCZJQGngg93@Onq-PZd@h3j9Jldz^P8KH0y0BzT3*1B;tGMv_P{CH6JI?g9Yo{w#Id z`^nGMUHjuFJGPf%#Y2^Y%PAud+P0m~TjZXcC-4_xeKd^IcB~DMP^+; z$cGbLha+A6`CAq5oSW<=J8fT9m{)V>J>YeB{icT`sr2f^Y|CDRpO#5x#uijv@gH(U zB{#>b3n;a9s|KZjVJLO_B2noxsyeB1*c4PMSR~dsPU%>n+S;ZWuomPYqWfDP# znWUvDO(X4ON7&4yhM927U(IN|0&<0pF}9im#1Nh92igzXgN>miQl)J&;TZ_rpvOSN z1r{C7KfbUlJfUhN7*>K2^)>8?0@!$mf~eOC8D9fDP#}>|RF(ytoXEBOwx|jptCWeO z*DaYW?aL`jGTfl*W5g26G^Umb50mWPJnAnxE@Cnqc)7GH@tJy}b>-vqJPrqGA%jlB zNQf&I24Vwep4IIZ$4)UOTyP{^mPiv1B*;u;m4q4B31n(sw64UYdz$WKI&?(_xCIZo zwe?ngR5j!mw@Z{01GCj|lBxYVVkK^Od@f1a#4gbg4ouY9bQyn_lf>4U)6q&=(qYFx zyw36&8*!$cT>%~xL&H+G)>ZC{_DqCGc+p)+>x^IAdgro(xE=R>&-F>YJNb!p-DJZ- z+DXRO(kS@6g4y64GbV^Hx8T&!lM}dUwcX0yacSyy^o3%Eij_q|;}&WGy6p>P{5PjT zKp7oEb_G0MjV35i)pf|NpFuiAv{1M2&oWp+>^G zmBy_Qx9l*WCO6=#E^bZ^Lq!r1(MCUDUxRPiv7_&Q7217%;feq=ee+mxcZEhCKg@&q0sNf#@w;v;pW(ZIk95bmyQLPBpfMK+Y#j~z8l=h zlGCQ6*EP)iC>L1%CdDDo_TA8@qk+;XG#8y7Jowd&&^^SZ($C_R%ZQp~0Qy<85{2f8 zpSQE?7;M@qLKiXptQmu4vq$j?YJu&5)?V`@AU4HPl01ppE6(a^wLlB|fG77h;bsPkC+ zDg%0Sp`E$WMwkk9!BbH^xs=qxu3Zx_LdC*HSm|n}m6kF7kNbc_9xB1mRLC{17CEI{ zV1v%1dH!F>!TDztcZkjRNmf(sQ>bAsn0FQ_Z^7EJ-<)UU&6n`*R0R18%|YrZT5%~E3MBwl zf*_qbZQv{>?1bE55JI%vaLa?J6_5*tXct4+eUez55p_+{$~{h;PW;4&X2S;PZ3XSy(ODKiVo zDGu+P(BqA+xHB^4Ls?48MZ!tJJMcNOg%b^91yL^GEJ7FvZ)aF>?Q{2v+PQ*DGDBx@ z5Pc4~5eZTgiBkoYJTlB+A*b~avKI30jr!JA%q_hXj|RYl%;Y*%*?kVdgFq^Blq1E@ zj9*A`IIB0`9+|%2sBGIetqUqbd_8+X?x8=HUVJ1A(wkd@dL)BdynL&sfzgevLTPck zFt`eIhJ9{}<@=DbZq`TkQm@SJdPbTL0r5oVQz8PNTkku(yw_xX|EMa!7Z(SIITcm_ zw>1)j7vwTiuzAoT9y%XTu4I`tKw0})PS*JbI; zA*r^j;88A{ZNalOu6eFQ#BF{`fb2JTP2)RzI&p*LDj5gG!`v6W?RSSZ?J!PoY!s@e{mnWP_A@pSRAlZyS4lRK^3R~x=5J+&YP@{i?42f^)G3D^zAdean?<2`YAHu~8Tu(!% zDORk)BuRMC4KS4V^kyO=IfYA>^z|(7klSL67lezV+)(>cN0wi=)Bo|_2&_IiNvl^%Usm0WlaTtmnH>O@tk(mpz0O^71u znssT4345F@?H&_gt}c?AVc3VJ}i1_)SN61eju-E?pGS zjX8+{xW8D^LjFsiF97P6-3F;KIG(7=4H%`4t?0qca9~8b`GqELo}G|BhsX2n^5`bC zo5%O|@UdU7zO{*3$`*o5N$Gq(R`2KQE5^EB_uJ+175mW~#Z})x_sipGYWL48E|7meB=yq=7&D+k;=OtRW_mp+p{}6Swlc zi7{{uEu!-l*Am;74ZOx6SjVSstIun(58B8>V`_y{Z51)ZIAbO3p_f zR|nRF4~JP@G00K^pKq{=)V39uqiaQOz(vA^m9q4Y$~TsX24m*ehz9CU@UcFVFA00N zhAj#VSR@c})_$PE_(8<@V6sIFS>0^i;JR|caIEQ!K(H*ngdWBlz=WO?r>vWxV|)7*JZ8##F;MDA;0L?WkU}bz2cY6bXg7)${yJ< z@G_gMANB|aB4;TV!44S$+PfuAq9HZRQa#Ym-E{0neeD{C^!3(x&bmOcyezTAPwi|n zi3%1#-Fsk>P%*2WV2D$m9g&=?E$dp9Yz^&xtis7-Y5-)V6WG!DD8A=hZcuryf{kIJ zezN^`p>%95NySY8oh8%qQ(t3N=w=r@B+;11EL)BBKqdNTca0_6+1`!jen$#tjfu9M z9zjvc$S6jc^)+4W`QreN7B7n6QJ1!S2aVIBSP#y)8&F^yUa@CF1^VX4fEta!kvR;c@h_$v0me~6xjx(o}(JvM08S;eb zv8jFY98TrN5!LfI=WCLk=b^IBsm>DoBRL^KjOj83$Ak)c@BtXQVuCfN(&{XDAF(B0 zXH=xAX6eIzqSq%NorvdT-@u@WiD&2=CpQQ(0HyEKaLS;)Z@6`4b}bi?OE+xAali%)hF&&F5>q&U z$c#w~pj@=#>7F>%iGJnUI1XD^rt!hlG2+32-*;r}fAzQOECHxo+!!pQ(07 zD>kCU6LUh(KNV(7bcbG)a1s&YtFqSrWMTy#Se<#P{{?>Op4WN9OPOXAOM;n?xvH{2 zR>qa-)jqVNmzqzQR@LHo;bM+>(s|sZI*{Kv2icCpd1ITOp=>WvV-9r(z*6bp!e*(& z-5=0XD{K?ErfL>6i+tg69k@DDrUEfgUp?nvQ!_TMW#eLxksyX}j&Pt1eI|K$<^U?9 z=K+#l0=GnbZ>&nv$8I+vG1{#My&~`x*#)Lpbcr5xK2O;^=*%)7FJ#_;!4T+V%AQE0s{KR`#(A;{!qeMS~`w}Ey&*8 zD))RXFT8Ah4}r>ARoN0{*`ZoZP0oBdSc1rlOM{DXBV*kgs#0m_ibKa810y5(-a+sX z0{v%pdX=d&Rc<#K&OzXjgvC%bS<&;Ccmr@KwpgWPWjwuj4vY$JzGBetD)qYb(-mENAY4 zW0{ldb4wu7;TkXJqTI^S#c_#z1;EM2HPZ92yZxz(tXu9D{^tVA27s@N*R~XD&iuq% z=O|xJ^5H?jxBUM3>{ftQf95UL1aKkm;8QN)dvSunA>o@t5erayY`b`~Tz@NboutR( zFoX(RFzR-Han}2acIxzStkE{7Qelcfwt28yJA3Y*t@<`F`E||czTh>fcoZNC?5)_@ ztmF8Q`x?5oYMu52ZmVHKetdy?1monMJ?Qf`d!LzpfAe#3DIJ*3g_pajRZnSEJbA+& zmqy-k7r3}*DR`MM-lc_=b$$Bxgk*d-?eJt;;N0c`F1)+mcGi7UeEW!t(dv$N9(Fqv zjx7Dnv+_9zGY=4FfLF=m$M)0NQBpaIwbAOQx7KN9{H#l`mZ}+6zLSC#3-DH;UGz-? z*O9Xqh=(urN%ScRJESMk=RCW;LePO9QEq9XHkn|T?$;U6e*{D^dlq|e<4%f{VMDP- zIZasreKNLhL@>%@;NK6Q3tctWqbHQ^ACj$+w`tk)6K=N=={Is{~lSU5Tq74`GnVUzf+utj~v z;5$Ji3>$%pa=Q$8aj;_ASnXFm5|I^OQGP(75FyTubJzuNi-@3N-AmD>4b;p$Ro_Efj+-0AkuBXpGaAo zETef$0iz(}V|tGR&$MOP8%3XI5oS}2;iLAld%)&4deagdaV08&mpApL=R`ma+ECk;s|5q2P=&9|@$sEZ!B;lFPcbh^GH=<0IDahgEkLk(K>^)YzGB?`OY&gz zyd}|>`M?OUVC|~Hj-Pp|p7?p1N9DbLHCCPGG%=W@da$v@n|^XKn8fqvDq!6U7~hs| zgcoKruCAd#salJ}t0vL3GqLV6E?`Bj@AnY4DvIKv1FYG5Otd>lCGwauO=CkXZ+#Ab z$*S{NU_S)gF?`K}^O3dFSFJfESeUfe^KIdCZi255d^Y2J;1oot?{^008=5nC?I^mp zt&WY%S;)@ z48Mr<=>DymQSBG0Qf&7C?d0GO`pGZ)lRMcp^RLnRgoJc;T_JJM*9iNFy0pX$PtvgE zF8< zcNqCe(Oom+(37y24uczal0SP|e;+T>pD=byUh83ha!v^B@jYVHcZso>L&@1>=?=pl_3@#D*4(6!3vt;7Q3^9 z79eRIK;uOzC?9^$!Fx$qHc-;iH-rkB>=v5{flh2YM){m1uomg=*>o`E|Id+ zTu4w!I{Mk1lxguff(1}`hjQ`Rt4zB#*DbcR9#_nRBezzAe(Jq%t)pLk)=ksC4=5#y;wbW8<@O(H^EsxL$=m4x^8g*P=pvvr!WH`p%Lhs`)mLtY)mj`=cf9 z8!3~$)8tX3V(vLC_(d(YdFZSJsEa};73;~1w%g+*7eZK^L=&y6>USNr7b29!%~(dN zIqO_UloNCu4et}mNwq5Uf4&#(`vdUgN)j&P;XKkM38xu}7gE$3Ml)0W8MVupxnN#) ziX$0`Ars~aRYQ+&MG35waM{zOHyj&<%Jt;M??0R|KEf^4I8Fb)_l%IY&tPigio|@! zv6>&+Cs9vv<(s`W}l3YI64H5(N znjINLtzx{*^F^~cwH&i>;@oPNB2+;TW=Ol)0f2Ytu|w95M>`RB^4*F}pbGoxE5I_+ zbT96#ORvEFW*5O3DqV`h1&T!NP=V#bVnXyB&#?kycQxsg|2Ka)2Eb+KUX$V;t+DtZ z=yb&{HVSG4Ig2r++-B;y^%<)au5yK~l8T)M`*5O9ehUVj|?x0WT^Y^~<9Zs>rjbBtm844w$9XKP=-aU}g)->D2^; z+OX*Z<-luA5;=9V8pYlFIer%%BBoT0(!a{(>mk>A%atWq?+P#bd2NBZM7W_V6g}C7 z%Gvw$EL~W;LDzS3$)t&$5Acb`AUf_eZ18vc^ONruc1qDo*p?e*n`fq!WVVV6#i&hQ zZKf1?w~9aBUhYn)13lV+%Qq9`E&qgYyiha21aBp{55twJNJ)Hc~lI`$gXEB5(42uD}oR$_IVjr4jgLM{Sf zGdF0@_itsi8hqwR`aR^<=KT~M={-Ex*v?4a!Oq^1-pJ0u^b!<1bD)1f4T7L>E zsa87qy{r;UEnRdbE=&3=g-U4zKDC}WJPAVNGy(@0bgL37-* zut2?xEQA7_krKj}v5Wrkca#1C>6|8K@IPF+2D2N$2)>PUegznxw$UBW5QR+2T|U1D zFO^|0eAz&QP~Hzvvh{0SNSfp>a&xHJ+QVl=6!y7n*aR) zD6A*6&?q_aI%S=&GPO+FFd_KLMm)heYdUG__P7w?(y9xATX)zf>StI`(8zd(F$+=B z0P{AK{QkpfVA96lF2R9i1tXaEkOX}qAfWfCgkN#K_V4<^$;3g)#L4NGqWHbeNM~qa zd#dJXk2a6R2OPn|1(FPu5^hrq28)(}=;%)cvO2Ubw!ePr;7Z>ssKe$tDbu{%Zq|U1 zFjZcFC?rOVcO^g~>CZbb0&AJgP*r3B6I~@=mWF;5X2J!M5W-J|3Aa$Pu`FYVFWu-| z@_Mk&y?pbe`~2Ls;p(th2q=SepJHl+9Q53h8X{IcjQVE(od~D=ay&5IAGL@G#`|R? zgXQz}in&!zx9<(D1|`fZZKkwD9|u}hc3d!dk0Wtq0c2nbn4D4Cxi9xPwtUeJ=tDaS z`RgT?4Lu^2)M~J*$wk-M_HA`6IRVzBp5cH2ddB%u$pS@>>r5I28bOrN!)ZeYK7j3s z8PI?^cK9X+>6JApr>d14jYxnZS-#7noQ4PPvYQWDDr+p)@HQSoN{>3=u>9cGZw1Z13yaya)zOP;zXae-*7l&ZWv*2 zEu*0u9X^tq)?&E9R#7`vk~B?>jIwh62_(626K$~hu#$S(9NGX@glSbXnM&QU5O|&Q zK$VN=XGUmDKtRGEA~L;3GZCjp@NlW5gR^kOzB7oFMA87&h$?N^pq#SdrNu~X++wph zHtH_YN&!jl@mw4g(}?6LMTQ9?S~8L^7iWyMx}p;L^yN4xn6_EC$x<_{1RR!P=aJ~J zrRA%5WXvXoW<;o$Y#O^G*gk+-gpDt`ooMSy8y+h9AffQwbmJJF`rdhJM5ve5jLCHkd>hBiyK+^cv_Kw z2=Ghw2a2Pm&14x&5i9}yZCO>8Sy`igyG(h?7(cCQ#w=yheBGhpOhk3ibopLR!%_b7 zj|SU*36fxF{f!(@UrHk=nM4IY^-fXhhn6SSY3T}xe49fW`bbBO<%m7~(YlCKe!oKe zOnjWNPeF0iypc=nd+Ul-E#p3D-+EYdn5kj^7A&EK7F~WQ3Eu5fI*^ub2!~c;1!@(t zB)nwSS5h53&EVEvR+tVp{{_0TC~1}ia*0p1CEw0i@oeEw-BjGf;HDxCVN4AqRn;)8 z6;ndapv_s9kRQeQwNI(gy%|za*JwhrouRg01PaJQ1@nW@AiX292*5fOas-6v_vP`g zwis~=b?H9!70vc>1RgdrFNZ{dB#`A(>r^We*+d$bf9Q!mhK^PF$gR$WnCu`OHeCcQ z&UnT`*e`C{DW#pZ6HFHIkkRi0 zR&lSrf0@!6!-p^hgf;4DrVMG^EEGX|D(mC~=$elxT8`E|836ZJ9|5O9CIQ%Q%b;re z(bM(!NJ2)eJ<%EWb-p{y}UsDid=PgKsR@f7o zS5S}j0UyGlJBb5@S1&1l#~KY|y#q>s4#rjtw*YY8U-gjkdX2id*vI{Bl6&nBw@eRdP!4Y>pDqi2XL~R zuzH=e9;F(#ZNC@+z_9JfU3D>l41KD@$dpn&u(tbD{I=|R1R!9a>ufd2e5kb_gnJ-~II zW~br7RXJV9^Bab|5qFbio+o|nj;iB1%bo=_52+9o#c%E)l%mXxuotwyR|4s(MHFA~44Xv>KNgW|Z}Nl$hpPr45G8Obu-i8?S{ zzaC#q?XDMcU-(+(J9=gX@8nuUKL0q0K2THXSrGbsIVBe}=!*B_s_EJGlW+5`{t9P1 zZO3+?Dv8>o=Zd>S@#fLc2KU=*^89_)&Di?NU6+H)_TGW+aqja(@$H`S&f{^v-Po6O zpC{NOYve2L<%90qvx)YNNs3gLx4l(}#96U1lndL3)vB3vlW~~RVEpyPnfbc=>x_f@ zB?QtGc0KkO+tPd?W-WP)!?O9L2TuP_L#-L}yCm@rN|d9NvmM4&8Rw`fE@D&{2TZLu-NWwSr@ zm5Q-e^+M@-0(+YZfMMo1uq1!AW!_7}X-8eM;_v-EMz&4aA?HgrD?XcbCC&~~k{4AO zH$(qS^PtJxmr`eh6hpeH*UhFX;j1-5SdQdF85eRyBe;4-&LLhRlULe(F>7GRWaD1- zPoa)ypIqz!#~(L06*(}Y2v=K#SGlkK^fIePZt1s~`-i!=KSdGTP(>Z=HBD$x9A}fL zzlb>b-{FjryNm>(Fw0GwVc=i!p6yf-xvl>+Q8?2~%->Ixxle~aj#0~f)vvdZ;oyI* zd~9I!8m$2a%meDZl&(p}EG4@>b-%rzcKsu~8TbS3yQAdq{|exrzuG^`zxiVTd8vN~ z`1kzee=`0mE8j)qU$T{dXZ-h^&3|Q_2LHo8{~t0qf5-VfgYPdSnD=M?EuZgq=HFAQ z{$l?6o-X*#{8xh2@2tOvhX2J%{T|ZuXTSfiK=I!Je~+5}3sC>vvGwnOf5lM$j`DkG z(O)S3cz>Y$9%%GC!0%_He*plK{p~RRI4S)d<#)H-UnpaAf1vz_lkRtfe|H-E#Sa8z z&jbYYA1;L7*?-^g{RMEt^2Y%Ff3| 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 = {} + provided_sheetname = self.excel_specs["specs"][ + self.spec]["public_ip_sheet"] + workbook_object, extracted_sheetname = self.get_xl_obj_and_sheetname( + provided_sheetname) + if workbook_object is not None: + ws = workbook_object[extracted_sheetname] + else: + ws = self.wb_combined[provided_sheetname] + 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"] + network_data = { + "oam": { + "subnet": [ws.cell(row=oam_row, column=oam_col).value], + "vlan": ws.cell(row=oam_row, column=oam_vlan_col).value, + }, + "ingress": ws.cell(row=ingress_row, column=oam_col).value, + } + network_data["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 + LOG.debug( + "public network data extracted from\ + excel:\n%s", + pprint.pformat(network_data), + ) + return network_data + + def get_site_info(self): + """Read location, dns, ntp and ldap data""" + + site_info = {} + provided_sheetname = self.excel_specs["specs"][ + self.spec]["dns_ntp_ldap_sheet"] + workbook_object, extracted_sheetname = self.get_xl_obj_and_sheetname( + provided_sheetname) + if workbook_object is not None: + ws = workbook_object[extracted_sheetname] + else: + ws = self.wb_combined[provided_sheetname] + 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 = dns_servers.replace("\n", " ") + ntp_servers = ntp_servers.replace("\n", " ") + if "," in dns_servers: + dns_servers = dns_servers.split(",") + else: + dns_servers = dns_servers.split() + if "," in ntp_servers: + ntp_servers = ntp_servers.split(",") + else: + ntp_servers = ntp_servers.split() + site_info = { + "location": self.get_location_data(), + "dns": dns_servers, + "ntp": ntp_servers, + "domain": ws.cell(row=domain_row, column=domain_col).value, + "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, + }, + } + LOG.debug( + "Site Info extracted from\ + excel:\n%s", + pprint.pformat(site_info), + ) + return site_info + + def get_location_data(self): + """Read location data from the site and zone sheet""" + + provided_sheetname = self.excel_specs["specs"][ + self.spec]["location_sheet"] + workbook_object, extracted_sheetname = self.get_xl_obj_and_sheetname( + provided_sheetname) + if workbook_object is not None: + ws = workbook_object[extracted_sheetname] + else: + ws = self.wb_combined[provided_sheetname] + 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, + } + + 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) + try: + for sheetname in sheet_name_list: + workbook_object, extracted_sheetname = ( + self.get_xl_obj_and_sheetname(sheetname)) + if workbook_object is not None: + wb = workbook_object + sheetname = extracted_sheetname + else: + wb = self.wb_combined + + if sheetname not in wb.sheetnames: + raise RuntimeError( + "SheetName '{}' not found ".format(sheetname)) + except RuntimeError as rerror: + LOG.critical(rerror) + sys.exit("Tugboat exited!!") + + 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() + site_info_data = self.get_site_info() + data = { + "ipmi_data": ipmi_data, + "network_data": { + "private": network_data, + "public": public_network_data, + }, + "site_info": site_info_data, + } + LOG.debug( + "Location data extracted from\ + excel:\n%s", + pprint.pformat(data), + ) + return data + + def combine_excel_design_specs(self, filenames): + """Combines multiple excel file to a single design spec""" + + design_spec = Workbook() + for exel_file in filenames: + loaded_workbook = load_workbook(exel_file, data_only=True) + for names in loaded_workbook.sheetnames: + design_spec_worksheet = design_spec.create_sheet(names) + loaded_workbook_ws = loaded_workbook[names] + for row in loaded_workbook_ws: + for cell in row: + design_spec_worksheet[ + cell.coordinate].value = cell.value + return design_spec + + def get_xl_obj_and_sheetname(self, sheetname): + """The logic confirms if the sheetname is specified for example as: + + 'MTN57a_AEC_Network_Design_v1.6.xlsx:Public IPs' + """ + + if re.search(".xlsx", sheetname) or re.search(".xls", sheetname): + """ Extract file name """ + source_xl_file = sheetname.split(":")[0] + wb = load_workbook(source_xl_file, data_only=True) + return [wb, sheetname.split(":")[1]] + else: + return [None, sheetname] diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 0000000..b4a7d5e --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1,8 @@ +# Formatting +yapf==0.27.0 + +# Linting +hacking>=1.1.0,<1.2.0 # Apache-2.0 + +# Security +bandit>=1.5.0 diff --git a/tools/gate/whitespace-linter.sh b/tools/gate/whitespace-linter.sh new file mode 100755 index 0000000..55f062d --- /dev/null +++ b/tools/gate/whitespace-linter.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -x + +RES=$(git grep -E -l " +$") + +if [[ -n $RES ]]; then + exit 1 +fi diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..822df86 --- /dev/null +++ b/tox.ini @@ -0,0 +1,57 @@ +[tox] +envlist = pep8, bandit, docs +minversion = 2.3.1 +skipsdist = True + +[testenv] +usedevelop = True +setenv = + VIRTUAL_ENV={envdir} + LANGUAGE=en_US + LC_ALL=en_US.utf-8 +deps = + -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt +passenv = http_proxy https_proxy HTTP_PROXY HTTPS_PROXY no_proxy NO_PROXY PBR_VERSION +whitelist_externals = + find +commands = + find . -type f -name "*.pyc" -delete + {toxinidir}/tools/gate/run-unit-tests.sh '{posargs}' + +[testenv:fmt] +basepython = python3 +deps = + -r{toxinidir}/test-requirements.txt +commands = + yapf -ir {toxinidir}/spyglass-plugin-xls + +[testenv:pep8] +basepython = python3 +deps = + -r{toxinidir}/test-requirements.txt +commands = + bash -c "{toxinidir}/tools/gate/whitespace-linter.sh" + yapf -dr {toxinidir}/spyglass-plugin-xls {toxinidir}/setup.py + flake8 {toxinidir}/spyglass-plugin-xls + bandit -r spyglass-plugin-xls -n 5 +whitelist_externals = + bash + +[testenv:bandit] +deps = + bandit +commands = bandit -r spyglass-plugin-xls -n 5 + +[flake8] +ignore = E125,E251,W503 + +[testenv:docs] +basepython = python3 +deps = + -r{toxinidir}/requirements.txt + -r{toxinidir}/doc/requirements.txt +commands = + rm -rf doc/build + sphinx-build -b html doc/source doc/build -n -W -v +whitelist_externals = rm