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

#90 #279 branded clusters [WIP] #407

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
39 changes: 36 additions & 3 deletions diagrams/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ def getcluster():
def setcluster(cluster):
__cluster.set(cluster)

def new_init(cls, init):
def reset_init(*args, **kwargs):
cls.__init__ = init
return reset_init

class Diagram:
__directions = ("TB", "BT", "LR", "RL")
Expand Down Expand Up @@ -205,14 +209,22 @@ class Cluster:
"fontsize": "12",
}

_icon = None
_icon_size = 0

# fmt: on

# FIXME:
# Cluster direction does not work now. Graphviz couldn't render
# correctly for a subgraph that has a different rank direction.
def __init__(
self, label: str = "cluster", direction: str = "LR", graph_attr: dict = {},
):
self,
label: str = "cluster",
direction: str = "LR",
graph_attr: dict = {},
icon: object = None,
icon_size: int = 30
):
"""Cluster represents a cluster context.

:param label: Cluster label.
Expand All @@ -221,13 +233,26 @@ def __init__(
"""
self.label = label
self.name = "cluster_" + self.label
if not self._icon:
self.icon = icon
dan-ash marked this conversation as resolved.
Show resolved Hide resolved
if not self._icon_size:
self._icon_size = icon_size

self.dot = Digraph(self.name)

# Set attributes.
for k, v in self._default_graph_attrs.items():
self.dot.graph_attr[k] = v
self.dot.graph_attr["label"] = self.label

# if an icon is set, try to find and instantiate a Node without calling __init__()
# then find it's icon by calling _load_icon()
if self._icon:
_node = self._icon(_no_init=True)
if isinstance(_node,Node):
self._icon_label = '<<TABLE border="0"><TR><TD fixedsize="true" width="' + str(self._icon_size) +'" height="' + str(self._icon_size) +'"><IMG SRC="' + _node._load_icon() + '"></IMG></TD><TD>' + self.label + '</TD></TR></TABLE>>'
self.dot.graph_attr["label"] = self._icon_label
else:
self.dot.graph_attr["label"] = self.label

if not self._validate_direction(direction):
raise ValueError(f'"{direction}" is not a valid direction')
Expand Down Expand Up @@ -284,6 +309,14 @@ class Node:

_height = 1.9

def __new__(cls, *args, **kwargs):
instance = object.__new__(cls)
lazy = kwargs.pop('_no_init', False)
if not lazy:
return instance
cls.__init__ = new_init(cls, cls.__init__)
return instance

def __init__(self, label: str = "", **attrs: Dict):
"""Node represents a system component.

Expand Down
2 changes: 1 addition & 1 deletion diagrams/aws/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
AWS provides a set of services for Amazon Web Service provider.
"""

from diagrams import Node
from diagrams import Node, Cluster


class _AWS(Node):
Expand Down
104 changes: 104 additions & 0 deletions diagrams/aws/cluster.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
from diagrams import Cluster
from diagrams.aws.compute import EC2, ApplicationAutoScaling
from diagrams.aws.network import VPC, PrivateSubnet, PublicSubnet

class Region(Cluster):
# fmt: off
_default_graph_attrs = {
"shape": "box",
"style": "dotted",
"labeljust": "l",
"pencolor": "#AEB6BE",
"fontname": "Sans-Serif",
"fontsize": "12",
}
# fmt: on

class AvailabilityZone(Cluster):
# fmt: off
_default_graph_attrs = {
"shape": "box",
"style": "dashed",
"labeljust": "l",
"pencolor": "#27a0ff",
"fontname": "sans-serif",
"fontsize": "12",
}
# fmt: on

class VirtualPrivateCloud(Cluster):
# fmt: off
_default_graph_attrs = {
"shape": "box",
"style": "",
"labeljust": "l",
"pencolor": "#00D110",
"fontname": "sans-serif",
"fontsize": "12",
}
# fmt: on
_icon = VPC

class PrivateSubnet(Cluster):
# fmt: off
_default_graph_attrs = {
"shape": "box",
"style": "",
"labeljust": "l",
"pencolor": "#329CFF",
"fontname": "sans-serif",
"fontsize": "12",
}
# fmt: on
_icon = PrivateSubnet

class PublicSubnet(Cluster):
# fmt: off
_default_graph_attrs = {
"shape": "box",
"style": "",
"labeljust": "l",
"pencolor": "#00D110",
"fontname": "sans-serif",
"fontsize": "12",
}
# fmt: on
_icon = PublicSubnet

class SecurityGroup(Cluster):
# fmt: off
_default_graph_attrs = {
"shape": "box",
"style": "dashed",
"labeljust": "l",
"pencolor": "#FF361E",
"fontname": "Sans-Serif",
"fontsize": "12",
}
# fmt: on

class AutoScalling(Cluster):
# fmt: off
_default_graph_attrs = {
"shape": "box",
"style": "dashed",
"labeljust": "l",
"pencolor": "#FF7D1E",
"fontname": "Sans-Serif",
"fontsize": "12",
}
# fmt: on
_icon = ApplicationAutoScaling

class EC2Contents(Cluster):
# fmt: off
_default_graph_attrs = {
"shape": "box",
"style": "",
"labeljust": "l",
"pencolor": "#FFB432",
"fontname": "Sans-Serif",
"fontsize": "12",
}
# fmt: on
_icon = EC2
15 changes: 15 additions & 0 deletions diagrams/onprem/cluster.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from diagrams import Cluster
from diagrams.onprem.compute import Server

class ServerContents(Cluster):
# fmt: off
_default_graph_attrs = {
"shape": "box",
"style": "rounded,dotted",
"labeljust": "l",
"pencolor": "#A0A0A0",
"fontname": "Sans-Serif",
"fontsize": "12",
}
# fmt: on
_icon = Server
25 changes: 24 additions & 1 deletion docs/guides/cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,29 @@ with Diagram("Event Processing", show=False):
handlers >> dw
```

## Clusters with icons in the label

You can add a Node icon before the cluster label (and specify its size as well). You need to import the used Node class first.

```python
from diagrams import Cluster, Diagram
from diagrams.aws.compute import ECS
from diagrams.aws.database import RDS, Aurora
from diagrams.aws.network import Route53, VPC

with Diagram("Simple Web Service with DB Cluster", show=False):
dns = Route53("dns")
web = ECS("service")

with Cluster(label='VPC',icon=VPC):
with Cluster("DB Cluster",icon=Aurora,icon_size=30):
db_master = RDS("master")
db_master - [RDS("slave1"),
RDS("slave2")]

dns >> web >> db_master
```

![event processing diagram](/img/event_processing_diagram.png)

> There is no depth limit of nesting. Feel free to create nested clusters as deep as you want.
> There is no depth limit of nesting. Feel free to create nested clusters as deep as you want.
Empty file added examples/__init__.py
Empty file.
Binary file added examples/aws.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions examples/aws.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from diagrams import Diagram, Edge
from diagrams.aws.cluster import *
from diagrams.aws.compute import EC2
from diagrams.onprem.container import Docker
from diagrams.onprem.cluster import *
from diagrams.aws.network import ELB

with Diagram(name="", direction="TB", show=True):
with Cluster("AWS"):
with Region("eu-west-1"):
with AvailabilityZone("eu-west-1a"):
with VirtualPrivateCloud(""):
with PrivateSubnet("Private"):
with SecurityGroup("web sg"):
with AutoScalling(""):
with EC2Contents("A"):
d1 = Docker("Container")
with ServerContents("A1"):
d2 = Docker("Container")

with PublicSubnet("Public"):
with SecurityGroup("elb sg"):
lb = ELB()

lb >> Edge(forward=True, reverse=True) >> d1
lb >> Edge(forward=True, reverse=True) >> d2