Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to have overlapping clusters #852

Open
samuelkdavis-vector opened this issue Feb 12, 2023 · 8 comments
Open

How to have overlapping clusters #852

samuelkdavis-vector opened this issue Feb 12, 2023 · 8 comments

Comments

@samuelkdavis-vector
Copy link

image

I would like to have overlapping groups/clusters. I cannot find an example of how to do this. In the linked example - the Public Subnet is in Availability Zone A, but also in a separate group for all public subnets sharing a route table

@kevinvalleau
Copy link

I tried something like this, like mentioned in #111 :

from diagrams import Diagram, Cluster, Edge, Node

#graph_attr = {
 #   "layout":"neato",
  #  }
graph_attr = {
    "layout":"neato",
    "compound":"true",
    "splines":"spline",
    }

scaling_clus_attr = {
    "bgcolor":"transparent",
    "pencolor":"blue",
    "penwidth":"4.0",
    "fillcolor" : "green"
    }

with Diagram("\n\nOverlapping Clusters", show=False, graph_attr=graph_attr) as diag:

    with Cluster("SubnetA","TB",{"bgcolor": "green"}):
        A_UpLf = Node("", shape="plaintext", pin="true", pos="0,4")
        A_LwRt = Node("", shape="plaintext", pin="true", pos="4,0")
    with Cluster("SubnetB","TB"):
        B_UpLf = Node("", shape="plaintext", pin="true", pos="6,4")
        B_LwRt = Node("", shape="plaintext", pin="true", pos="10,0")
    with Cluster("Scaling Set", "LR", graph_attr=scaling_clus_attr):
        SS_UpLf = Node("", shape="plaintext", pin="true", pos="2,3")
        SS_LwRt = Node("", shape="plaintext", pin="true", pos="8,1")
        n1 = Node("node 1", shape="circle", labelloc="c", pin="true", pos="3,2")
        n1 = Node("node 2", shape="box",    labelloc="c", pin="true", pos="7,2")

But it doesn't work. I just get a circle, a square and a text, with no background.
image

@clayms
Copy link

clayms commented Feb 15, 2023

@kevinvalleau
Your code works on my system. Likely a version difference issue.

$ dot -V
dot - graphviz version 7.1.0 (0)

$ pip freeze | grep -E 'diagrams|graphviz'
diagrams==0.20.0
graphviz==0.16

$ hostnamectl | grep Kernel
Kernel: Linux 6.1.7-arch1-1

image

@kevinvalleau
Copy link

kevinvalleau commented Feb 15, 2023

dot - graphviz version 7.1.0 (20230121.1956)`
diagrams==0.23.3
graphviz==0.20.1

And it still does not work but I am on Windows. I'll try with WSL2.

=> Works on a WSL2 debian.

=> After a few tests, it seems clusters are not working with neato on Windows with my setup.
The pure graphviz code here https://graphviz.readthedocs.io/en/stable/examples.html#cluster-py works.
But even a small code with one cluster with diagrams does not work.

@samuelkdavis-vector
Copy link
Author

Can this only be done with exact positioning?

The reason I wanted to use diagrams as code was to gain the benefit of clusters automatic grouping. Pixel perfect diagramming is quite painful when adding an item into an availability zone in a networking diagram which requires resizing subnets, vpcs, accounts, and any arbitrary cross-cutting grouping.

This solution would remove the need for pixel perfect drag-drop resizing, but would require specifically defining where each individual item appears in a grid.

@clayms
Copy link

clayms commented Feb 15, 2023

@samuelkdavis-vector

Can this only be done with exact positioning?
I don't think so. I am pretty sure this is not a limitation of this library, but a limitation of the dot rendering engine.

@clayms
Copy link

clayms commented Feb 15, 2023

have a look at
https://graphviz.org/docs/layouts/

@Anddd7
Copy link

Anddd7 commented Dec 15, 2023

I use a loop to calculate the pos for each subnet, looks fine ... But it's hard to add nodes - you need to calculate the pos as well. 😢

I tried 2 ways, both big effort ...

  • Break the loops, reuse (x,y) to calculate the relative position for each node inside a cluster
  • Store the Cluster and its metadata into a map, build all subnets in loop, but define the node outside of it.

image

graph_attr = {"layout": "neato", "compound": "true", "splines": "spline"}
with Diagram("dist/demo", show=False, graph_attr=graph_attr) as diag:
    # input
    subnet_area = 3
    az_count = 3
    az_names = ["1a", "1b", "1c", "1d", "1e", "1f"]
    layer_count = 3
    layer_types = [Cluster, Cluster, Cluster]
    # layer_types = [ClusterPublicSubnet, ClusterPrivateSubnet, ClusterIntraSubnet]
    layer_names = ["public", "private", "intra"]

    # param
    spacer = 2.5  # 直径 1.9
    vpc_padding = 1
    az_padding = 0.4
    subnet_padding = 0.2
    vpc_width = subnet_area * az_count + spacer * (az_count - 1)
    vpc_height = subnet_area * layer_count + spacer * (layer_count - 1)

    # cluster cache, add nodes later
    subnets = [[{}] * layer_count for _ in range(az_count)]

    with ClusterVPC("vpc") as vpc:
        p_tl = Node("", shape="none", pin="true", pos=f"{-vpc_padding},{vpc_height+vpc_padding}")
        p_br = Node("", shape="none", pin="true", pos=f"{vpc_width+vpc_padding},{-vpc_padding}")

    for ia in range(az_count):
        x = (subnet_area + spacer) * ia
        y = 0
        for il, type in enumerate(layer_types):
            y = (subnet_area + spacer) * il
            name = f"sn-{layer_names[il]}-{az_names[ia]}"
            with type(name) as instance:
                p_tl = Node("", shape="none", pin="true", pos=f"{x},{y+subnet_area}")
                p_br = Node("", shape="none", pin="true", pos=f"{x+subnet_area},{y}")

                subnets[ia][il] = {"subnet": instance, "x": x + subnet_padding, "y": y + subnet_padding}

        with Cluster(
            f"az-{az_names[ia]}",
            graph_attr={
                "bgcolor": "transparent",
                "penwidth": "2.0",
                "style": "rounded,dashed",
            },
        ):
            p_tl = Node("", shape="none", pin="true", pos=f"{x-az_padding},{y+subnet_area+az_padding}")
            p_br = Node("", shape="none", pin="true", pos=f"{x+subnet_area+az_padding},{-az_padding}")

@Anddd7
Copy link

Anddd7 commented Dec 15, 2023

optimize a litter bit, looks better 😄

image

    with vpc:
        igw = InternetGateway(pin="true", pos=f"{vpc_width/2},{-3}")
    with subnets[1][0]["subnet"]:
        x, y = subnets[1][0]["x"], subnets[1][0]["y"]
        nat = NATGateway(pin="true", pos=f"{x},{y}")
        elb = ElasticLoadBalancing(pin="true", pos=f"{x+spacer},{y}")
    with subnets[1][1]["subnet"]:
        x, y = subnets[1][1]["x"], subnets[1][1]["y"]
        eks = EKS(pin="true", pos=f"{x},{y}")
    with subnets[1][2]["subnet"]:
        x, y = subnets[1][2]["x"], subnets[1][2]["y"]
        rds = RDSInstance(pin="true", pos=f"{x},{y}")

    igw >> elb >> eks >> rds
    eks >> nat >> igw

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants