Skip to content

Commit

Permalink
ability to save from interactive
Browse files Browse the repository at this point in the history
  • Loading branch information
FloSch62 committed Apr 18, 2024
1 parent 0ce6dd1 commit d6bd854
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 3 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ pip install -r requirements.txt

This section provides a brief overview on how to use the `drawio2clab` and `clab2drawio` tools. For detailed instructions, including command-line options and examples, please refer to the dedicated usage sections in their respective documentation files.

Detailed Usages: [drawio2clab.md](docs/drawio2clab.md#usage) and [clab2drawio.md](docs/drawio2clab.md#usage)
Detailed Usages: [drawio2clab.md](docs/drawio2clab.md#usage) and [clab2drawio.md](docs/clab2drawio.md#usage)

## drawio2clab

Expand Down
31 changes: 29 additions & 2 deletions clab2drawio.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from lib.Link import Link
from lib.Node import Node
from lib.Grafana import GrafanaDashboard
from lib.Yaml_processor import YAMLProcessor
from collections import defaultdict
from prompt_toolkit.shortcuts import checkboxlist_dialog, yes_no_dialog
import yaml, argparse, os, re, random
Expand Down Expand Up @@ -646,7 +647,7 @@ def load_styles_from_config(config_path):

return styles

def interactive_mode(nodes, icon_to_group_mapping):
def interactive_mode(nodes, icon_to_group_mapping, containerlab_data, output_file, processor):
# Initialize previous summary with existing node labels
previous_summary = {"Levels": {}, "Icons": {}}
for node_name, node in nodes.items():
Expand Down Expand Up @@ -690,6 +691,13 @@ def interactive_mode(nodes, icon_to_group_mapping):
summary["Levels"].setdefault(level, []).append(node_name)
tmp_nodes.remove(node_name)

# Check if 'labels' section exists, create it if necessary
if "labels" not in containerlab_data["topology"]["nodes"][node_name]:
containerlab_data["topology"]["nodes"][node_name]["labels"] = {}

# Update containerlab_data with graph-level
containerlab_data["topology"]["nodes"][node_name]["labels"]["graph-level"] = level

tmp_nodes = list(nodes.keys())
icons = list(icon_to_group_mapping.keys())

Expand Down Expand Up @@ -718,6 +726,13 @@ def interactive_mode(nodes, icon_to_group_mapping):
summary["Icons"].setdefault(icon, []).append(node_name)
tmp_nodes.remove(node_name)

# Check if 'labels' section exists, create it if necessary
if "labels" not in containerlab_data["topology"]["nodes"][node_name]:
containerlab_data["topology"]["nodes"][node_name]["labels"] = {}

# Update containerlab_data with graph-icon
containerlab_data["topology"]["nodes"][node_name]["labels"]["graph-icon"] = icon

# Generate summary tree for user confirmation
summary_tree = ""
for key, info in summary.items():
Expand All @@ -739,6 +754,17 @@ def interactive_mode(nodes, icon_to_group_mapping):
elif result:
break # Exit the loop if user confirms the summary

# Prompt user if they want to update the ContainerLab file
update_file = yes_no_dialog(title='Update ContainerLab File', text="Do you want to update the ContainerLab file with the new configuration?").run()

if update_file:
# Save the updated containerlab_data to the output file using processor.save_yaml
modified_output_file = os.path.splitext(output_file)[0] + ".mod.yaml"
processor.save_yaml(containerlab_data, modified_output_file)
print(f"ContainerLab file has been updated: {modified_output_file}")
else:
print("ContainerLab file has not been updated.")

return summary

def main(input_file, output_file, grafana, theme, include_unlinked_nodes=False, no_links=False, layout='vertical', verbose=False, interactive=False):
Expand Down Expand Up @@ -858,7 +884,8 @@ def main(input_file, output_file, grafana, theme, include_unlinked_nodes=False,
diagram.nodes = nodes

if interactive:
interactive_mode(diagram.nodes, styles['icon_to_group_mapping'])
processor = YAMLProcessor()
interactive_mode(diagram.nodes, styles['icon_to_group_mapping'], containerlab_data, input_file, processor)

assign_graphlevels(diagram, verbose=False)
calculate_positions(diagram, layout=layout, verbose=verbose)
Expand Down
49 changes: 49 additions & 0 deletions lib/Yaml_processor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import yaml
import sys

class YAMLProcessor:
class CustomDumper(yaml.SafeDumper):
"""
Custom YAML dumper that adjusts the indentation for lists and maintains certain lists in inline format.
"""
pass

def custom_list_representer(self, dumper, data):
# Check if we are at the specific list under 'links' with 'endpoints'
if len(data) == 2 and isinstance(data[0], str) and ':' in data[0]:
return dumper.represent_sequence(u'tag:yaml.org,2002:seq', data, flow_style=True)
else:
return dumper.represent_sequence(u'tag:yaml.org,2002:seq', data, flow_style=False)

def custom_dict_representer(self, dumper, data):
return dumper.represent_dict(data.items())

def __init__(self):
# Assign custom representers to the CustomDumper class
self.CustomDumper.add_representer(list, self.custom_list_representer)
self.CustomDumper.add_representer(dict, self.custom_dict_representer)

def load_yaml(self, yaml_str):
try:
# Load YAML data
data = yaml.safe_load(yaml_str)
return data

except yaml.YAMLError as e:
print(f"Error loading YAML: {str(e)}")
sys.exit(1)

def save_yaml(self, data, output_file, flow_style=None):
try:
# Save YAML data
with open(output_file, 'w') as file:
if flow_style is None:
yaml.dump(data, file, Dumper=self.CustomDumper, sort_keys=False, default_flow_style=False, indent=2)
else:
yaml.dump(data, file, sort_keys=False, default_flow_style=flow_style, indent=2)

print(f"YAML file saved as '{output_file}'.")

except IOError as e:
print(f"Error saving YAML file: {str(e)}")
sys.exit(1)

0 comments on commit d6bd854

Please sign in to comment.