aws-edges 0.8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|