aws-edges 0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +644 -0
- data/README.md +163 -0
- data/Rakefile +0 -0
- data/SHA256 +1 -0
- data/aws-edges.gemspec +17 -0
- data/bin/aws-edges +113 -0
- data/examples/config.yml +34 -0
- data/examples/iam.yml +23 -0
- data/lib/aws-edges.rb +19 -0
- data/lib/aws-edges/config.rb +166 -0
- data/lib/aws-edges/ec2.rb +53 -0
- data/lib/aws-edges/graph.rb +190 -0
- data/lib/aws-edges/iam.rb +91 -0
- data/lib/aws-edges/rds.rb +32 -0
- data/lib/aws-edges/redshift.rb +40 -0
- data/lib/aws-edges/subnet.rb +21 -0
- data/lib/aws-edges/version.rb +5 -0
- data/lib/aws-edges/vpc.rb +19 -0
- metadata +102 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
module AWSEdges
|
2
|
+
class EC2
|
3
|
+
attr_reader :nodes
|
4
|
+
|
5
|
+
def initialize(describe_instances)
|
6
|
+
@nodes = Array.new
|
7
|
+
describe_instances[:reservation_set].each{|r| r[:instances_set].each{|i|
|
8
|
+
@nodes.push({
|
9
|
+
:instance_id => i[:instance_id],
|
10
|
+
:instance_type => i[:instance_type],
|
11
|
+
:kernel_id => i[:kernel_id],
|
12
|
+
:ramdisk_id => i[:ramdisk_id],
|
13
|
+
:architecture => i[:architecture],
|
14
|
+
:ebs_optimized => i[:ebs_optimized],
|
15
|
+
:root_device_type => i[:root_device_type],
|
16
|
+
:root_device_name => i[:root_device_name],
|
17
|
+
:virtualization_type => i[:virtualization_type],
|
18
|
+
:hypervisor => i[:hypervisor],
|
19
|
+
:source_dest_check => i[:source_dest_check],
|
20
|
+
:image_id => i[:image_id],
|
21
|
+
:vpc_id => i[:vpc_id],
|
22
|
+
:subnet_id => i[:subnet_id],
|
23
|
+
:public_dns_name => i[:dns_name],
|
24
|
+
:public_ip_address => i[:ip_address],
|
25
|
+
:private_dns_name => i[:private_dns_name],
|
26
|
+
:private_ip_address => i[:private_ip_address],
|
27
|
+
:availability_zone => i[:placement][:availability_zone],
|
28
|
+
:security_groups => []
|
29
|
+
})
|
30
|
+
|
31
|
+
security_groups = Array.new
|
32
|
+
i[:group_set].each{|g|
|
33
|
+
security_groups.push({
|
34
|
+
:group_name => g[:group_name],
|
35
|
+
:group_id => g[:group_id]
|
36
|
+
})
|
37
|
+
}
|
38
|
+
@nodes[@nodes.length - 1][:security_groups] = security_groups
|
39
|
+
}}
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.supported_fields
|
43
|
+
[
|
44
|
+
"instance_id", "kernel_id", "ramdisk_id", "architecture",
|
45
|
+
"ebs_optimized", "root_device_type", "root_device_name",
|
46
|
+
"virtualization_type", "hypervisor", "source_dest_check", "image_id", "vpc_id",
|
47
|
+
"subnet_id", "public_dns_name", "public_ip_address", "private_dns_name",
|
48
|
+
"private_ip_address", "availability_zone", "security_groups-group_name",
|
49
|
+
"security_groups-group_id"
|
50
|
+
]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
# TODO -list
|
2
|
+
# add edge shape/color attribute support
|
3
|
+
# add node (ie path) color/shape support
|
4
|
+
# allow control of node_attribs << filled
|
5
|
+
# add xref support for edge nodes (eg:)
|
6
|
+
# - ec2_private_ip_address
|
7
|
+
# - vpc_cidr_block
|
8
|
+
# change edge to node if point is missing
|
9
|
+
# add support for nested values
|
10
|
+
# - redshift_cluster_nodes-*
|
11
|
+
|
12
|
+
module AWSEdges
|
13
|
+
class Graph
|
14
|
+
attr_reader :format
|
15
|
+
|
16
|
+
def initialize(config_data, aws_data)
|
17
|
+
@config = config_data
|
18
|
+
@aws = aws_data
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Generate the graphviz digraph statement for edges
|
23
|
+
#
|
24
|
+
def map_edges(from, from_color, from_shape, to, to_color, to_shape)
|
25
|
+
cmd_string = ""
|
26
|
+
from_prefix, from_node = $1, $2 if from =~ /^(\w+?)_(.+)/
|
27
|
+
to_prefix, to_node = $1, $2 if to =~ /^(\w+?)_(.+)/
|
28
|
+
@aws[:"#{from_prefix}"].each do |node|
|
29
|
+
if from_node.include?('-')
|
30
|
+
(parent, child) = from_node.split('-')
|
31
|
+
node[:"#{parent}"].each do |i|
|
32
|
+
cmd_string += " edge " + '"' +
|
33
|
+
i[:"#{child}"].to_s + '","' +
|
34
|
+
node[:"#{to_node}"].to_s + '";'
|
35
|
+
|
36
|
+
unless from_color.nil?
|
37
|
+
unless from_color.empty?
|
38
|
+
cmd_string += assign_color_attribute(from_color, i[:"#{child}"])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
unless from_shape.nil?
|
43
|
+
unless from_shape.empty?
|
44
|
+
cmd_string += assign_shape_attribute(from_shape, i[:"#{child}"])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
unless to_color.nil?
|
49
|
+
unless to_color.empty?
|
50
|
+
cmd_string += assign_color_attribute(to_color, node[:"#{to_node}"])
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
unless to_shape.nil?
|
55
|
+
unless to_shape.empty?
|
56
|
+
cmd_string += assign_shape_attribute(to_shape, node[:"#{to_node}"])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end unless node[:"#{parent}"].nil?
|
61
|
+
elsif to_node.include?('-')
|
62
|
+
(parent, child) = to_node.split('-')
|
63
|
+
node[:"#{parent}"].each do |i|
|
64
|
+
cmd_string += " edge " + '"' +
|
65
|
+
node[:"#{from_node}"].to_s + '","' +
|
66
|
+
i[:"#{child}"].to_s + '";'
|
67
|
+
|
68
|
+
unless from_color.nil?
|
69
|
+
unless from_color.empty?
|
70
|
+
cmd_string += assign_color_attribute(from_color, node[:"#{from_node}"])
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
unless from_shape.nil?
|
75
|
+
unless from_shape.empty?
|
76
|
+
cmd_string += assign_shape_attribute(from_shape, node[:"#{from_node}"])
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
unless to_color.nil?
|
81
|
+
unless to_color.empty?
|
82
|
+
cmd_string += assign_color_attribute(to_color, i[:"#{child}"])
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
unless to_shape.nil?
|
87
|
+
unless to_shape.empty?
|
88
|
+
cmd_string += assign_shape_attribute(to_shape, i[:"#{child}"])
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end unless node[:"#{parent}"].nil?
|
93
|
+
else
|
94
|
+
cmd_string += " edge " + '"' +
|
95
|
+
node[:"#{from_node}"].to_s + '","' +
|
96
|
+
node[:"#{to_node}"].to_s + '";'
|
97
|
+
|
98
|
+
unless from_color.nil?
|
99
|
+
unless from_color.empty?
|
100
|
+
cmd_string += assign_color_attribute(from_color, node[:"#{from_node}"])
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
unless from_shape.nil?
|
105
|
+
unless from_shape.empty?
|
106
|
+
cmd_string += assign_shape_attribute(from_shape, node[:"#{from_node}"])
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
unless to_color.nil?
|
111
|
+
unless to_color.empty?
|
112
|
+
cmd_string += assign_color_attribute(to_color, node[:"#{to_node}"])
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
unless to_shape.nil?
|
117
|
+
unless to_shape.empty?
|
118
|
+
cmd_string += assign_shape_attribute(to_shape, node[:"#{to_node}"])
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
124
|
+
cmd_string
|
125
|
+
end
|
126
|
+
|
127
|
+
##
|
128
|
+
# TODO: assign colors
|
129
|
+
# all eg: edge_attribs << orange
|
130
|
+
def assign_color_attribute(color, edge)
|
131
|
+
return "#{color} << node(\"#{edge}\");"
|
132
|
+
end
|
133
|
+
|
134
|
+
##
|
135
|
+
# TODO: assign shapes
|
136
|
+
# all eg: edge_attribs << triangle
|
137
|
+
def assign_shape_attribute(shape, edge)
|
138
|
+
return "#{shape} << node(\"#{edge}\");"
|
139
|
+
end
|
140
|
+
|
141
|
+
##
|
142
|
+
# Dynamically create graphviz digraph statements
|
143
|
+
# based on the config file input
|
144
|
+
def generate_graph_statements(config)
|
145
|
+
cmd_string = ""
|
146
|
+
config.each do |key,value|
|
147
|
+
if key == "cluster"
|
148
|
+
cmd_string += "cluster \"#{value['label']}\" do "
|
149
|
+
cmd_string += "label \"#{value['label']}\";"
|
150
|
+
cmd_string += generate_graph_statements(value)
|
151
|
+
cmd_string += " end;"
|
152
|
+
elsif key == "edges"
|
153
|
+
value.each do |e|
|
154
|
+
cmd_string += map_edges(
|
155
|
+
e['from'], e['from_color'], e['from_shape'],
|
156
|
+
e['to'], e['to_color'], e['to_shape']
|
157
|
+
)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
cmd_string
|
162
|
+
end
|
163
|
+
|
164
|
+
##
|
165
|
+
# Main method for creating graphviz digraphs from the
|
166
|
+
# parsed config file
|
167
|
+
def create_graph()
|
168
|
+
##
|
169
|
+
# default to png output format
|
170
|
+
# for a full list: http://www.graphviz.org/doc/info/output.html
|
171
|
+
format = @config.delete('save_as')
|
172
|
+
format = "png" unless format
|
173
|
+
graph_name = @config.delete('name').gsub(/\s+/,'_')
|
174
|
+
rotate_layout = true if @config['rotate'] == true
|
175
|
+
cmds = generate_graph_statements(@config)
|
176
|
+
begin
|
177
|
+
digraph do
|
178
|
+
node_attribs << filled
|
179
|
+
rotate if rotate_layout
|
180
|
+
boxes
|
181
|
+
eval cmds
|
182
|
+
save "#{graph_name}", "#{format}"
|
183
|
+
end
|
184
|
+
rescue Exception => e
|
185
|
+
puts "Failed to create the graph: #{e}"
|
186
|
+
exit 1
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module AWSEdges
|
2
|
+
class IAM
|
3
|
+
attr_reader :nodes
|
4
|
+
|
5
|
+
def initialize(iam)
|
6
|
+
@nodes = Array.new
|
7
|
+
|
8
|
+
# get IAM groups
|
9
|
+
iam.list_groups[:groups].each{|g|
|
10
|
+
@nodes.push({
|
11
|
+
:group_name => g[:group_name],
|
12
|
+
:group_id => g[:group_id],
|
13
|
+
:group_arn => g[:arn],
|
14
|
+
:group_create_date => g[:create_date],
|
15
|
+
:users => [],
|
16
|
+
:group_policies => []
|
17
|
+
})
|
18
|
+
|
19
|
+
# get group members
|
20
|
+
assigned_users = Array.new
|
21
|
+
iam.get_group(options = {:group_name => g[:group_name]})[:users].each {|u|
|
22
|
+
assigned_users.push({
|
23
|
+
:user_name => u[:user_name],
|
24
|
+
:user_id => u[:user_id],
|
25
|
+
:arn => u[:arn],
|
26
|
+
:path => u[:path],
|
27
|
+
:create_date => u[:create_date],
|
28
|
+
})
|
29
|
+
}
|
30
|
+
@nodes[@nodes.length - 1][:users] = assigned_users
|
31
|
+
|
32
|
+
# get group policies
|
33
|
+
assigned_policies = Array.new
|
34
|
+
iam.list_group_policies(options = {:group_name => g[:group_name]})[:policy_names].each {|p|
|
35
|
+
assigned_policies.push({
|
36
|
+
:policy_name => p
|
37
|
+
})
|
38
|
+
}
|
39
|
+
@nodes[@nodes.length - 1][:group_policies] = assigned_policies
|
40
|
+
}
|
41
|
+
|
42
|
+
# get IAM users
|
43
|
+
iam.list_users[:users].each{|u|
|
44
|
+
@nodes.push({
|
45
|
+
:user_name => u[:user_name],
|
46
|
+
:user_id => u[:user_id],
|
47
|
+
:user_arn => u[:arn],
|
48
|
+
:user_path => u[:path],
|
49
|
+
:user_create_date => u[:create_date],
|
50
|
+
:groups => [],
|
51
|
+
:user_policies => []
|
52
|
+
})
|
53
|
+
|
54
|
+
# get membership
|
55
|
+
assigned_groups = Array.new
|
56
|
+
iam.list_groups_for_user(options = {:user_name => u[:user_name]})[:groups].each {|g|
|
57
|
+
assigned_groups.push({
|
58
|
+
:path => g[:path],
|
59
|
+
:group_name => g[:group_name],
|
60
|
+
:group_id => g[:group_id],
|
61
|
+
:arn => g[:arn],
|
62
|
+
:create_date => g[:create_date]
|
63
|
+
})
|
64
|
+
}
|
65
|
+
@nodes[@nodes.length - 1][:groups] = assigned_groups
|
66
|
+
|
67
|
+
# get user policies
|
68
|
+
assigned_policies = Array.new
|
69
|
+
iam.list_user_policies(options = {:user_name => u[:user_name]})[:policy_names].each {|p|
|
70
|
+
assigned_policies.push({
|
71
|
+
:policy_name => p
|
72
|
+
})
|
73
|
+
}
|
74
|
+
@nodes[@nodes.length - 1][:user_policies] = assigned_policies
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.supported_fields
|
79
|
+
[ "group_path", "group_name", "group_id", "group_arn",
|
80
|
+
"group_create_date", "users-path", "users-user_name",
|
81
|
+
"users-user_id", "users-arn", "users-create_date",
|
82
|
+
"group_policies-policy_name",
|
83
|
+
"user_path", "user_name", "user_id", "user_arn",
|
84
|
+
"user_create_date", "groups-path", "groups-group_name",
|
85
|
+
"groups-group_id", "groups-arn", "groups-create_date",
|
86
|
+
"user_policies-policy_name"
|
87
|
+
]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module AWSEdges
|
2
|
+
class RDS
|
3
|
+
attr_reader :nodes
|
4
|
+
|
5
|
+
def initialize(describe_db_instances)
|
6
|
+
@nodes = Array.new
|
7
|
+
describe_db_instances[:db_instances].each{|i|
|
8
|
+
@nodes.push({
|
9
|
+
:db_name => i[:db_name],
|
10
|
+
:db_engine => i[:engine],
|
11
|
+
:vpc_id => i[:db_subnet_group][:vpc_id],
|
12
|
+
:subnet_group_name => i[:db_subnet_group][:db_subnet_group_name],
|
13
|
+
:availability_zone => i[:availability_zone],
|
14
|
+
:secondary_availability_zone => i[:secondary_availability_zone],
|
15
|
+
:multi_az => i[:multi_az],
|
16
|
+
:db_engine_version => i[:engine_version],
|
17
|
+
:iops => i[:iops],
|
18
|
+
:publicly_accessible => i[:publicly_accessible]
|
19
|
+
})
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.supported_fields
|
24
|
+
[
|
25
|
+
"db_name", "db_engine", "vpc_id",
|
26
|
+
"subnet_group_name", "availability_zone",
|
27
|
+
"secondary_availability_zone", "multi_az",
|
28
|
+
"db_engine_version", "iops", "publicly_accessible"
|
29
|
+
]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module AWSEdges
|
2
|
+
class Redshift
|
3
|
+
attr_reader :nodes
|
4
|
+
|
5
|
+
def initialize(describe_clusters)
|
6
|
+
@nodes = Array.new
|
7
|
+
describe_clusters[:clusters].each{|c|
|
8
|
+
@nodes.push({
|
9
|
+
:db_name => c[:db_name],
|
10
|
+
:vpc_id => c[:vpc_id],
|
11
|
+
:availability_zone => c[:availability_zone],
|
12
|
+
:subnet_group_name => c[:cluster_subnet_group_name],
|
13
|
+
:publicly_accessible => c[:publicly_accessible],
|
14
|
+
:cluster_version => c[:cluster_version],
|
15
|
+
:encrypted => c[:encrypted],
|
16
|
+
:cluster_nodes => []
|
17
|
+
})
|
18
|
+
|
19
|
+
cluster_nodes = Array.new
|
20
|
+
c[:cluster_nodes].each{|n|
|
21
|
+
cluster_nodes.push({
|
22
|
+
:node_role => n[:role_name],
|
23
|
+
:public_ip_address => n[:public_ip_address],
|
24
|
+
:private_ip_address => n[:private_ip_address]
|
25
|
+
})
|
26
|
+
}
|
27
|
+
@nodes[@nodes.length - 1][:cluster_nodes] = cluster_nodes
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.supported_fields
|
32
|
+
[
|
33
|
+
"db_name", "vpc_id", "availability_zone",
|
34
|
+
"subnet_group_name", "publicly_accessible", "cluster_version",
|
35
|
+
"encrypted", "cluster_nodes-node_role",
|
36
|
+
"cluster_nodes-public_ip_address", "cluster_nodes-private_ip_address"
|
37
|
+
]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module AWSEdges
|
2
|
+
class Subnet
|
3
|
+
attr_reader :nodes
|
4
|
+
|
5
|
+
def initialize(describe_subnets)
|
6
|
+
@nodes = Array.new
|
7
|
+
describe_subnets[:subnet_set].each{|s|
|
8
|
+
@nodes.push({
|
9
|
+
:vpc_id => s[:vpc_id],
|
10
|
+
:cidr_block => s[:cidr_block],
|
11
|
+
:availability_zone => s[:availability_zone],
|
12
|
+
:subnet_id => s[:subnet_id]
|
13
|
+
})
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.supported_fields
|
18
|
+
[ "vpc_id", "cidr_block", "availability_zone", "subnet_id" ]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module AWSEdges
|
2
|
+
class VPC
|
3
|
+
attr_reader :nodes
|
4
|
+
|
5
|
+
def initialize(describe_vpcs)
|
6
|
+
@nodes = Array.new
|
7
|
+
describe_vpcs[:vpc_set].each{|v|
|
8
|
+
@nodes.push({
|
9
|
+
:vpc_id => v[:vpc_id],
|
10
|
+
:cidr_block => v[:cidr_block]
|
11
|
+
})
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.supported_fields
|
16
|
+
[ "vpc_id", "cidr_block" ]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|