forked from JulieChv/Analyse_Reseaux
ajout doc + correction
This commit is contained in:
@@ -6,6 +6,7 @@ Cet outil permet de **parser les configurations de différents types de firewall
|
||||
|
||||
- Il fournit également la possibilité de générer une **matrice de flux au format Excel** pour visualiser les communications et règles de trafic dans l’infrastructure.
|
||||
- Il founit également la possibilité de générer une **matrice de routage au format Excel** pour visualiser les routes statiques dans l’infrastructure.
|
||||
- Il founit également la possibilité de générer le **détail des interfaces au format Excel** pour visualiser la configuration des interfaces.
|
||||
|
||||
## Fonctionnalités principales
|
||||
|
||||
@@ -46,6 +47,10 @@ Cet outil permet de **parser les configurations de différents types de firewall
|
||||
- Script Python qui utilise le JSON normalisé pour générer automatiquement une matrice Excel détaillant :
|
||||
- les routes statiques
|
||||
|
||||
7. **Génération des interfaces**
|
||||
- Script Python qui utilise le JSON normalisé pour générer automatiquement une matrice Excel détaillant :
|
||||
- la configuration des interfaces
|
||||
|
||||
## Utilisation
|
||||
|
||||
### Pré-requis
|
||||
@@ -59,9 +64,9 @@ pip install -r .\src\requirements.txt
|
||||
|
||||
#### Commandes principales
|
||||
```bash
|
||||
python3 .\src\main.py stormshield .\src\input\backup\ -f -r
|
||||
python3 .\src\main.py paloalto .\src\input\nomfichier -f -r
|
||||
python3 .\src\main.py forcepoint .\src\input\nomfichier -f -r
|
||||
python3 .\src\main.py stormshield .\src\input\backup\ -f -r -i
|
||||
python3 .\src\main.py paloalto .\src\input\nomfichier -f -r -i
|
||||
python3 .\src\main.py forcepoint .\src\input\nomfichier -f -r -i
|
||||
```
|
||||
#### Options
|
||||
|
||||
@@ -70,6 +75,8 @@ python3 .\src\main.py forcepoint .\src\input\nomfichier -f -r
|
||||
| -o [nom_fichier] | Spécifie le nom du fichier JSON de sortie (optionnel)
|
||||
| -f | Génère un rapport Excel de type matrice de flux (optionnel)
|
||||
| -r | Génère un rapport Excel de type matrice de routage (optionnel)
|
||||
| -i | Generate interface report (optional)
|
||||
|
||||
---
|
||||
|
||||
## Arborescence du projet
|
||||
|
||||
@@ -6,6 +6,7 @@ from scripts.json_Stormshield import generate_json_stormshield
|
||||
from scripts.json_Forcepoint import generate_json_forcepoint
|
||||
from scripts.export_matrice_flux import export_to_excel as export_flux_to_excel
|
||||
from scripts.export_matrice_routage import export_to_excel as export_routing_to_excel
|
||||
from scripts.export_interfaces import export_to_excel as export_interfaces_to_excel
|
||||
|
||||
def verify_if_file_exists(name):
|
||||
base, ext = os.path.splitext(name)
|
||||
@@ -24,6 +25,7 @@ def main():
|
||||
print(" -o <output_file> Specify output JSON file name (optional)")
|
||||
print(" -f Generate matrix flux report (optional)")
|
||||
print(" -r Generate routing matrix report (optional)")
|
||||
print(" -i Generate interface report (optional)")
|
||||
sys.exit(1)
|
||||
|
||||
firewall_type = sys.argv[1].lower()
|
||||
@@ -85,5 +87,21 @@ def main():
|
||||
excel_file = export_routing_to_excel(output_file_json, output_file_excel)
|
||||
print(f"✓ Processus terminé. Fichiers générés:\n - JSON: {output_file_json}\n - Excel: {excel_file}")
|
||||
|
||||
if "-i" in sys.argv:
|
||||
print(f"\nGénération de l'export les interfaces...")
|
||||
if "-o" in sys.argv:
|
||||
o_index = sys.argv.index("-o")
|
||||
if o_index + 1 < len(sys.argv):
|
||||
output_file_excel = os.path.join(f"{output_path}interfaces_{firewall_type}_{sys.argv[o_index + 1]}.xlsx")
|
||||
else:
|
||||
print("Erreur: nom de fichier de sortie manquant après '-o'.")
|
||||
sys.exit(1)
|
||||
else:
|
||||
timestamp = time.strftime("%Y%m%d")
|
||||
output_file_excel = f"{output_path}interfaces_{firewall_type}_{timestamp}.xlsx"
|
||||
output_file_excel = verify_if_file_exists(output_file_excel)
|
||||
excel_file = export_interfaces_to_excel(output_file_json, output_file_excel)
|
||||
print(f"✓ Processus terminé. Fichiers générés:\n - JSON: {output_file_json}\n - Excel: {excel_file}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
201
Parseurs_config_Firewall/src/scripts/export_interfaces.py
Normal file
201
Parseurs_config_Firewall/src/scripts/export_interfaces.py
Normal file
@@ -0,0 +1,201 @@
|
||||
import json
|
||||
import pandas as pd
|
||||
from openpyxl import load_workbook
|
||||
from openpyxl.styles import PatternFill, Border, Side, Alignment, Font
|
||||
from openpyxl.utils import get_column_letter
|
||||
|
||||
|
||||
center = Alignment(horizontal="center", vertical="center", wrap_text=True)
|
||||
left = Alignment(horizontal="left", vertical="center", wrap_text=True)
|
||||
|
||||
header_fill = PatternFill(start_color="2F5496", fill_type="solid")
|
||||
iface_fill = PatternFill(start_color="BDD7EE", fill_type="solid")
|
||||
sub_fill = PatternFill(start_color="DDEBF7", fill_type="solid")
|
||||
disabled_fill= PatternFill(start_color="F2F2F2", fill_type="solid")
|
||||
|
||||
thick_border = Border(*(Side(style="thick"),)*4)
|
||||
thin_border = Border(*(Side(style="thin"),)*4)
|
||||
medium_side = Side(style="medium")
|
||||
|
||||
|
||||
def _thin(top=False, bottom=False, left_s=False, right_s=False):
|
||||
return Border(
|
||||
top=Side(style="thin") if top else Side(style=None),
|
||||
bottom=Side(style="thin") if bottom else Side(style=None),
|
||||
left=Side(style="thin") if left_s else Side(style=None),
|
||||
right=Side(style="thin") if right_s else Side(style=None),
|
||||
)
|
||||
|
||||
|
||||
def _build_vrf_map(data):
|
||||
"""Retourne {interface_name: vrf_name} depuis network-instances."""
|
||||
vrf_map = {}
|
||||
for ni in data.get("openconfig-network-instance:network-instances", {}).get("network-instance", []):
|
||||
vrf_name = ni.get("name", "")
|
||||
for iface in ni.get("interfaces", {}).get("interface", []):
|
||||
iface_id = iface.get("id", "")
|
||||
if iface_id:
|
||||
vrf_map[iface_id] = vrf_name
|
||||
return vrf_map
|
||||
|
||||
|
||||
def _parse_interfaces(data):
|
||||
vrf_map = _build_vrf_map(data)
|
||||
rows = []
|
||||
|
||||
for iface in data.get("openconfig-interfaces:interfaces", {}).get("interface", []):
|
||||
cfg = iface.get("config", {})
|
||||
iface_name = cfg.get("name", iface.get("name", ""))
|
||||
iface_type = cfg.get("type", "")
|
||||
iface_desc = cfg.get("description", "")
|
||||
enabled = cfg.get("enabled", True)
|
||||
|
||||
subinterfaces = iface.get("subinterfaces", {}).get("subinterface", [])
|
||||
|
||||
if not subinterfaces:
|
||||
rows.append({
|
||||
"Interface": iface_name,
|
||||
"Type": iface_type,
|
||||
"Description": iface_desc,
|
||||
"Enabled": "Oui" if enabled else "Non",
|
||||
"VLAN": "",
|
||||
"IP": "",
|
||||
"Préfixe": "",
|
||||
"VRF": vrf_map.get(iface_name, ""),
|
||||
"_is_sub": False,
|
||||
})
|
||||
else:
|
||||
for sub in subinterfaces:
|
||||
sub_cfg = sub.get("config", {})
|
||||
vlan = sub.get("index", sub_cfg.get("index", ""))
|
||||
sub_name = f"{iface_name}.{vlan}" if vlan != 0 else iface_name
|
||||
sub_enabled = sub_cfg.get("enabled", enabled)
|
||||
|
||||
addresses = (
|
||||
sub.get("oc-ip:ipv4", {})
|
||||
.get("oc-ip:addresses", {})
|
||||
.get("oc-ip:address", [])
|
||||
)
|
||||
|
||||
if not addresses:
|
||||
rows.append({
|
||||
"Interface": iface_name,
|
||||
"Type": iface_type,
|
||||
"Description": iface_desc,
|
||||
"Enabled": "Oui" if sub_enabled else "Non",
|
||||
"VLAN": "" if vlan == 0 else vlan,
|
||||
"IP": "",
|
||||
"Préfixe": "",
|
||||
"VRF": vrf_map.get(sub_name, vrf_map.get(iface_name, "")),
|
||||
"_is_sub": vlan != 0,
|
||||
})
|
||||
else:
|
||||
for addr in addresses:
|
||||
addr_cfg = addr.get("oc-ip:config", {})
|
||||
rows.append({
|
||||
"Interface": iface_name,
|
||||
"Type": iface_type,
|
||||
"Description": iface_desc,
|
||||
"Enabled": "Oui" if sub_enabled else "Non",
|
||||
"VLAN": "" if vlan == 0 else vlan,
|
||||
"IP": addr_cfg.get("oc-ip:ip", ""),
|
||||
"Préfixe": addr_cfg.get("oc-ip:prefix-length", ""),
|
||||
"VRF": vrf_map.get(sub_name, vrf_map.get(iface_name, "")),
|
||||
"_is_sub": vlan != 0,
|
||||
})
|
||||
|
||||
return rows
|
||||
|
||||
|
||||
|
||||
def _style_interfaces(ws):
|
||||
COLS = {
|
||||
"Interface": 1,
|
||||
"Type": 2,
|
||||
"Description": 3,
|
||||
"Enabled": 4,
|
||||
"VLAN": 5,
|
||||
"IP": 6,
|
||||
"Préfixe": 7,
|
||||
"VRF": 8,
|
||||
}
|
||||
N = len(COLS)
|
||||
|
||||
for col in range(1, N + 1):
|
||||
cell = ws.cell(row=1, column=col)
|
||||
cell.fill = header_fill
|
||||
cell.font = Font(bold=True, color="FFFFFF", size=10)
|
||||
cell.alignment = center
|
||||
cell.border = thin_border
|
||||
|
||||
ws.freeze_panes = "A2"
|
||||
|
||||
max_width = {c: len(str(ws.cell(row=1, column=c).value or "")) for c in range(1, N + 1)}
|
||||
|
||||
prev_iface = None
|
||||
group_start = 2
|
||||
|
||||
for row in range(2, ws.max_row + 1):
|
||||
iface_val = ws.cell(row=row, column=COLS["Interface"]).value
|
||||
enabled = ws.cell(row=row, column=COLS["Enabled"]).value
|
||||
is_last = (row == ws.max_row)
|
||||
next_iface = ws.cell(row=row + 1, column=COLS["Interface"]).value if not is_last else None
|
||||
|
||||
if enabled == "Non":
|
||||
row_fill = disabled_fill
|
||||
elif ws.cell(row=row, column=COLS["VLAN"]).value:
|
||||
row_fill = sub_fill
|
||||
else:
|
||||
row_fill = iface_fill
|
||||
|
||||
for col in range(1, N + 1):
|
||||
cell = ws.cell(row=row, column=col)
|
||||
if col != COLS["Interface"]:
|
||||
cell.fill = row_fill
|
||||
cell.alignment = left if col not in (COLS["Enabled"], COLS["VLAN"], COLS["Préfixe"]) else center
|
||||
cell.border = thin_border
|
||||
|
||||
val = cell.value
|
||||
max_width[col] = max(max_width[col], len(str(val or "")))
|
||||
|
||||
if iface_val != next_iface or is_last:
|
||||
if row > group_start:
|
||||
ws.merge_cells(
|
||||
start_row=group_start, start_column=COLS["Interface"],
|
||||
end_row=row, end_column=COLS["Interface"]
|
||||
)
|
||||
for r in range(group_start, row + 1):
|
||||
c = ws.cell(r, COLS["Interface"])
|
||||
c.fill = iface_fill
|
||||
c.font = Font(bold=True, size=10)
|
||||
c.alignment = center
|
||||
c.border = thick_border
|
||||
group_start = row + 1
|
||||
|
||||
ws.row_dimensions[row].height = 18
|
||||
|
||||
for col, width in max_width.items():
|
||||
ws.column_dimensions[get_column_letter(col)].width = min(width + 4, 45)
|
||||
|
||||
|
||||
def export_to_excel(json_file_path: str, output_file_excel: str) -> str:
|
||||
with open(json_file_path, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
|
||||
rows = _parse_interfaces(data)
|
||||
|
||||
export_rows = [{k: v for k, v in r.items() if k != "_is_sub"} for r in rows]
|
||||
|
||||
df = pd.DataFrame(export_rows, columns=[
|
||||
"Interface", "Type", "Description", "Enabled", "VLAN", "IP", "Préfixe", "VRF"
|
||||
])
|
||||
|
||||
with pd.ExcelWriter(output_file_excel, engine="openpyxl") as writer:
|
||||
df.to_excel(writer, sheet_name="Interfaces", index=False)
|
||||
|
||||
wb = load_workbook(output_file_excel)
|
||||
_style_interfaces(wb["Interfaces"])
|
||||
wb.save(output_file_excel)
|
||||
|
||||
print(f"✓ Export interfaces OK : {output_file_excel}")
|
||||
return output_file_excel
|
||||
@@ -8,6 +8,8 @@ from openpyxl import load_workbook
|
||||
from scripts.style_excel.style_address_groups import style_address_groups
|
||||
from scripts.style_excel.style_service_groups import style_service_groups
|
||||
from scripts.style_excel.style_matrice_flux import style_matrice_flux
|
||||
from scripts.style_excel.style_addresses import style_addresses
|
||||
from scripts.style_excel.style_services import style_services
|
||||
|
||||
def export_to_excel(json_file_path, output_file_excel):
|
||||
"""
|
||||
@@ -192,20 +194,46 @@ def export_to_excel(json_file_path, output_file_excel):
|
||||
for m in cfg.get("members", [])
|
||||
])
|
||||
|
||||
df_addresses = pd.DataFrame([
|
||||
{
|
||||
"Name": name,
|
||||
"IP / Masque": cfg.get("ip_netmask", "") if "/" in cfg.get("ip_netmask", "")
|
||||
else (cfg.get("ip_netmask", "") + "/32" if cfg.get("ip_netmask") else ""),
|
||||
"Description": cfg.get("description", ""),
|
||||
"Tags": cfg.get("misc", ""),
|
||||
}
|
||||
for name, cfg in sorted(addresses.items())
|
||||
])
|
||||
|
||||
df_services = pd.DataFrame([
|
||||
{
|
||||
"Name": name,
|
||||
"Protocol": cfg.get("protocol", ""),
|
||||
"Port": cfg.get("port", ""),
|
||||
"Description": cfg.get("description", ""),
|
||||
}
|
||||
for name, cfg in sorted(services.items())
|
||||
])
|
||||
|
||||
# === Export Excel ===
|
||||
firewall_type = data.get("firewall-device", {}).get("type", "firewall").replace(" ","_").lower()
|
||||
date = time.strftime("%Y%m%d")
|
||||
|
||||
with pd.ExcelWriter(output_file_excel, engine="openpyxl") as writer:
|
||||
df_addr.to_excel(writer,sheet_name="Address-Groups",index=False)
|
||||
df_srv.to_excel(writer,sheet_name="Service-Groups",index=False)
|
||||
df.to_excel(writer,sheet_name="Matrice Flux",index=False,startrow=1)
|
||||
df_addresses.to_excel(writer, sheet_name="Addresses", index=False)
|
||||
df_addr.to_excel(writer, sheet_name="Address-Groups", index=False)
|
||||
df_services.to_excel(writer, sheet_name="Services", index=False)
|
||||
df_srv.to_excel(writer, sheet_name="Service-Groups", index=False)
|
||||
df.to_excel(writer, sheet_name="Matrice Flux", index=False, startrow=1)
|
||||
|
||||
|
||||
wb = load_workbook(output_file_excel)
|
||||
if "Sheet1" in wb.sheetnames:
|
||||
del wb["Sheet1"]
|
||||
|
||||
style_addresses(wb["Addresses"])
|
||||
style_address_groups(wb["Address-Groups"], address_groups)
|
||||
style_services(wb["Services"])
|
||||
style_service_groups(wb["Service-Groups"], service_groups)
|
||||
style_matrice_flux(wb["Matrice Flux"])
|
||||
|
||||
|
||||
@@ -38,37 +38,46 @@ class ParserMixin:
|
||||
"name": interface.name,
|
||||
"config": {
|
||||
"name": interface.name,
|
||||
"type": "iana-if-type:ethernetCsmacd" if interface.interface_type == "ethernet" else "iana-if-type:ieee8023adLag",
|
||||
"type": interface.interface_type if interface.interface_type else None,
|
||||
"enabled": interface.enabled,
|
||||
"description": interface.comment or ""
|
||||
}
|
||||
}
|
||||
|
||||
if interface.ip:
|
||||
intf_config["subinterfaces"] = {
|
||||
"subinterface": [
|
||||
{
|
||||
subinterfaces = [
|
||||
{
|
||||
"index": interface.misc or 0,
|
||||
"config": {
|
||||
"index": interface.misc or 0,
|
||||
"config": {
|
||||
"index": interface.misc or 0,
|
||||
"enabled": interface.enabled
|
||||
},
|
||||
"oc-ip:ipv4": {
|
||||
"oc-ip:addresses": {
|
||||
"oc-ip:address": [
|
||||
{
|
||||
"enabled": interface.enabled
|
||||
},
|
||||
"oc-ip:ipv4": {
|
||||
"oc-ip:addresses": {
|
||||
"oc-ip:address": [
|
||||
{
|
||||
"oc-ip:ip": interface.ip,
|
||||
"oc-ip:config": {
|
||||
"oc-ip:ip": interface.ip,
|
||||
"oc-ip:config": {
|
||||
"oc-ip:ip": interface.ip,
|
||||
"oc-ip:prefix-length": interface.netmask if interface.netmask else 24
|
||||
}
|
||||
"oc-ip:prefix-length": interface.netmask if interface.netmask else 24
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
if interface.interface_type == "vlan":
|
||||
subinterfaces.append({
|
||||
"index": interface.vlan or None,
|
||||
"config": {
|
||||
"index": interface.vlan or None,
|
||||
}
|
||||
})
|
||||
|
||||
intf_config["subinterfaces"] = {"subinterface": subinterfaces}
|
||||
|
||||
|
||||
openconfig["openconfig-interfaces:interfaces"]["interface"].append(intf_config)
|
||||
|
||||
|
||||
@@ -46,13 +46,17 @@ class StormshieldParser(ParserMixin):
|
||||
current_section = line.strip("[]")
|
||||
continue
|
||||
|
||||
inline_comment = ""
|
||||
if "#" in line:
|
||||
inline_comment = line.split("#", 1)[1].strip()
|
||||
|
||||
if current_section in ("Host", "Network", "Fqdn"):
|
||||
match = re.match(r"([^=]+)=([^,#]+)", line)
|
||||
if match:
|
||||
name = match.group(1).strip()
|
||||
ip = match.group(2).strip()
|
||||
self.config["address_objects"].append(
|
||||
AddressObject(name=name, ip_netmask=ip)
|
||||
AddressObject(name=name, ip_netmask=ip, description=inline_comment)
|
||||
)
|
||||
|
||||
elif current_section == "Service":
|
||||
@@ -62,7 +66,7 @@ class StormshieldParser(ParserMixin):
|
||||
port = match.group(2)
|
||||
proto = match.group(3).lower()
|
||||
self.config["service_objects"].append(
|
||||
ServiceObject(name=name, protocol=proto, port=port)
|
||||
ServiceObject(name=name, protocol=proto, port=port, description=inline_comment)
|
||||
)
|
||||
|
||||
def _parse_address_groups(self):
|
||||
@@ -156,18 +160,7 @@ class StormshieldParser(ParserMixin):
|
||||
|
||||
def _add_interface(self, section_name, data):
|
||||
"""Crée un objet Interface à partir d’une section complète"""
|
||||
if section_name.lower().startswith("vlan"):
|
||||
iface_type = "vlan"
|
||||
elif section_name.lower().startswith("bridge"):
|
||||
iface_type = "bridge"
|
||||
elif section_name.lower().startswith("wifi"):
|
||||
iface_type = "wifi"
|
||||
elif section_name.lower().startswith("loopback"):
|
||||
iface_type = "loopback"
|
||||
elif section_name.lower().startswith("agg"):
|
||||
iface_type = "aggregate"
|
||||
else:
|
||||
iface_type = "ethernet"
|
||||
iface_type = section_name.rstrip("0123456789")
|
||||
|
||||
enabled = data.get("State", "0") == "1"
|
||||
name = data.get("Name", section_name)
|
||||
@@ -187,6 +180,7 @@ class StormshieldParser(ParserMixin):
|
||||
comment=comment,
|
||||
interface_type=iface_type,
|
||||
enabled=enabled,
|
||||
vlan=data.get("Tag") if data.get("Tag") and iface_type == "vlan" else None
|
||||
)
|
||||
|
||||
self.config["interfaces"].append(interface)
|
||||
|
||||
@@ -61,6 +61,7 @@ class Interface:
|
||||
comment: str = None
|
||||
interface_type: str = None
|
||||
enabled: bool = True
|
||||
vlan: str = None
|
||||
|
||||
@dataclass
|
||||
class SecurityRule:
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
from openpyxl.styles import PatternFill, Border, Side, Alignment, Font
|
||||
from openpyxl.utils import get_column_letter
|
||||
|
||||
center = Alignment(horizontal="center", vertical="center")
|
||||
left = Alignment(horizontal="left", vertical="center")
|
||||
|
||||
red_fill = PatternFill(start_color="FF9999", fill_type="solid")
|
||||
yellow_fill = PatternFill(start_color="FFFF99", fill_type="solid")
|
||||
|
||||
thick_border = Border(*(Side(style="thick"),)*4)
|
||||
thin_border = Border(*(Side(style="thin"),)*4)
|
||||
|
||||
def style_addresses(ws):
|
||||
|
||||
col_name, col_ip, col_desc, col_tags = 1, 2, 3, 4
|
||||
|
||||
max_width = {1: 0, 2: 0, 3: 0, 4: 0}
|
||||
|
||||
for row in range(2, ws.max_row + 1):
|
||||
name = ws.cell(row=row, column=col_name).value
|
||||
ip = ws.cell(row=row, column=col_ip).value
|
||||
desc = ws.cell(row=row, column=col_desc).value
|
||||
tags = ws.cell(row=row, column=col_tags).value
|
||||
|
||||
ws.cell(row=row, column=col_name).fill = red_fill
|
||||
ws.cell(row=row, column=col_name).font = Font(bold=True)
|
||||
ws.cell(row=row, column=col_name).alignment = left
|
||||
ws.cell(row=row, column=col_name).border = thin_border
|
||||
|
||||
for col in (col_ip, col_desc, col_tags):
|
||||
ws.cell(row=row, column=col).alignment = left
|
||||
ws.cell(row=row, column=col).border = thick_border
|
||||
ws.cell(row=row, column=col).fill = yellow_fill
|
||||
|
||||
max_width[1] = max(max_width[1], len(str(name or "")))
|
||||
max_width[2] = max(max_width[2], len(str(ip or "")))
|
||||
max_width[3] = max(max_width[3], len(str(desc or "")))
|
||||
max_width[4] = max(max_width[4], len(str(tags or "")))
|
||||
|
||||
for col in max_width:
|
||||
ws.column_dimensions[get_column_letter(col)].width = max_width[col] + 2
|
||||
|
||||
for row in range(2, ws.max_row + 1):
|
||||
ws.row_dimensions[row].height = 18
|
||||
@@ -0,0 +1,51 @@
|
||||
from openpyxl.styles import PatternFill, Border, Side, Alignment, Font
|
||||
from openpyxl.utils import get_column_letter
|
||||
|
||||
center = Alignment(horizontal="center", vertical="center")
|
||||
left = Alignment(horizontal="left", vertical="center")
|
||||
|
||||
red_fill = PatternFill(start_color="FF9999", fill_type="solid")
|
||||
yellow_fill = PatternFill(start_color="FFFF99", fill_type="solid")
|
||||
|
||||
thick_border = Border(*(Side(style="thick"),)*4)
|
||||
thin_border = Border(*(Side(style="thin"),)*4)
|
||||
|
||||
def style_services(ws):
|
||||
|
||||
col_name, col_proto, col_port, col_desc = 1, 2, 3, 4
|
||||
|
||||
max_width = {1: 0, 2: 0, 3: 0, 4: 0}
|
||||
|
||||
for row in range(2, ws.max_row + 1):
|
||||
name = ws.cell(row=row, column=col_name).value
|
||||
proto = ws.cell(row=row, column=col_proto).value
|
||||
port = ws.cell(row=row, column=col_port).value
|
||||
desc = ws.cell(row=row, column=col_desc).value
|
||||
|
||||
ws.cell(row=row, column=col_name).fill = red_fill
|
||||
ws.cell(row=row, column=col_name).font = Font(bold=True)
|
||||
ws.cell(row=row, column=col_name).alignment = left
|
||||
ws.cell(row=row, column=col_name).border = thin_border
|
||||
|
||||
ws.cell(row=row, column=col_proto).alignment = center
|
||||
ws.cell(row=row, column=col_proto).border = thick_border
|
||||
ws.cell(row=row, column=col_proto).fill = yellow_fill
|
||||
|
||||
ws.cell(row=row, column=col_port).alignment = center
|
||||
ws.cell(row=row, column=col_port).border = thick_border
|
||||
ws.cell(row=row, column=col_port).fill = yellow_fill
|
||||
|
||||
ws.cell(row=row, column=col_desc).alignment = left
|
||||
ws.cell(row=row, column=col_desc).border = thick_border
|
||||
ws.cell(row=row, column=col_desc).fill = yellow_fill
|
||||
|
||||
max_width[1] = max(max_width[1], len(str(name or "")))
|
||||
max_width[2] = max(max_width[2], len(str(proto or "")))
|
||||
max_width[3] = max(max_width[3], len(str(port or "")))
|
||||
max_width[4] = max(max_width[4], len(str(desc or "")))
|
||||
|
||||
for col in max_width:
|
||||
ws.column_dimensions[get_column_letter(col)].width = max_width[col] + 2
|
||||
|
||||
for row in range(2, ws.max_row + 1):
|
||||
ws.row_dimensions[row].height = 18
|
||||
Reference in New Issue
Block a user