LoginSignup
12
11

More than 5 years have passed since last update.

gvizでAWSの構成図を動的に作成する

Posted at

仕様

  • VPC + Subnet + EC2 + ELBを表示
  • インスタンスは指定した属性・タグを表示
  • ELBは参加しているEC2のInService/OutOfServiceが分かるように色分け(Inservice=青, OutofService=赤)
  • VPCごとに画像を出力

出力結果

AWS構成

  • VPCは1つ("dev")
  • Subnetは2つ("sub1" , "sub2")
  • EC2は3つ("dev1", "dev2", "dev3")
    • タグは Name, status, role
  • ELBは1つ("elb1")

20160229_003815.jpg

出力結果

dev.png

セットアップ

sudo yum install -y graphviz
gem install gviz

コード

# -*- coding: utf-8 -*-
require 'json'
require 'gviz'

DISPLAY_INSTANCE_TAG  = ['Name', 'status', 'role']
DISPLAY_INSTANCE_ATTR = ['PrivateIpAddress']

def get_tag_value(tags, key)
  return nil if tags.nil? or tags == []
  tags.each do |k, v|
    next unless k
    return k['Value'] if k['Key'].downcase == key.downcase
  end
  nil
end

def get_vpc_info
  ret = {}
  JSON.parse(`aws ec2 describe-vpcs`)['Vpcs'].each do |vpc|
    ret[vpc['VpcId']] = { 'name' => get_tag_value(vpc['Tags'], "name"), 'vpc_id' => vpc['VpcId'], 'subnets' => [], 'elbs' => [] }
  end
  ret
end

def get_subnet_info
  ret = {}
  JSON.parse(`aws ec2 describe-subnets`)['Subnets'].each do |subnet|
    name = get_tag_value(subnet['Tags'], "name")
    ret[subnet['SubnetId']] = { 'name' => get_tag_value(subnet['Tags'], "name"), 'subnet_id' => subnet['SubnetId'], 'vpc_id' => subnet['VpcId'], 'instances' => [] }
  end
  ret
end

def get_instance_info
  ret = {}
  JSON.parse(`aws ec2 describe-instances`)['Reservations'].each do |i|
    i['Instances'].each do |inst|
      attr = {'instance_id' => inst['InstanceId'], 'subnet_id' => inst['SubnetId']}
      # 表示させたいタグを取得&設定
      DISPLAY_INSTANCE_TAG.each do |tag|
        attr.merge!(tag => get_tag_value(inst['Tags'], tag))
      end
      # 表示させたい属性を取得&設定
      DISPLAY_INSTANCE_ATTR.each do |attr_key|
        attr.merge!(attr_key => inst[attr_key])
      end
      ret[inst['InstanceId']] = attr
    end
  end
  ret
end


def get_elb_info
  ret = {}
  JSON.parse(`aws elb describe-load-balancers`)['LoadBalancerDescriptions'].each do |e|
    instance_ids = []
    e['Instances'].select {|inst| instance_ids << inst['InstanceId'] if inst['InstanceId'] }
    ret[e['LoadBalancerName']] = { 'name' => e['LoadBalancerName'], 'vpc_id' => e['VPCId'], 'instances' => instance_ids, 'health' => {} }
  end
  ret
end

def get_elb_health(elb_name)
  ret = {}
  JSON.parse(`aws elb describe-instance-health --load-balancer-name #{elb_name}`)['InstanceStates'].each do |e|
    ret[e['InstanceId']] =  e['State']
  end
  ret
end

# VPC情報を取得
vpc = get_vpc_info
# Subnet情報の取得
subnet = get_subnet_info
# VPC情報にSubnetを追加
subnet.each do |subnet_id, v|
  next unless vpc[v['vpc_id']]
  vpc[v['vpc_id']]['subnets'] << subnet_id
end
# EC2情報を取得
instance = get_instance_info
# Subnet情報にEC2を追加
instance.each do |inst_id, v|
  next unless subnet[v['subnet_id']]
  subnet[v['subnet_id']]['instances'] << inst_id
end
# ELB情報を取得
elb = get_elb_info
# VPC情報にELBを追加
elb.each do |elb_name, v|
  next unless vpc[v['vpc_id']]
  vpc[v['vpc_id']]['elbs'] << elb_name
  v['health'] = get_elb_health(elb_name)
end

vpc.each do |vpc_id, v|
  Graph do
   subgraph do
      global label:"#{vpc[vpc_id]['name']}"

      v['subnets'].each_with_index do |subnet_id, i|
        subgraph("cluster#{i+1}") do
          global label:"#{subnet[subnet_id]['name']}"
          subnet[subnet_id]['instances'].each do |inst_id|

            label = "{ "
            DISPLAY_INSTANCE_TAG.each_with_index do |tag, i|
              label += " | " if i > 0
              label += instance[inst_id][tag] ? instance[inst_id][tag] : ""
            end
            DISPLAY_INSTANCE_ATTR.each_with_index do |at, i|
              label += " | "
              label += instance[inst_id][at] ? instance[inst_id][at] : ""
            end
            label += " }"
            node:"#{instance[inst_id]['Name'].gsub("-","")}",
            :shape => :Mrecord,
            :style => :filled,
            :fillcolor => :yellow,
            :label => label
          end
        end
      end

      v['elbs'].each do |elb_name|
        node:"#{elb_name.gsub("-","")}"

        instance_names = []
        elb[elb_name]['instances'].each do |inst_id|
          instance_names << instance[inst_id]['Name'].gsub("-","")

          color = elb[elb_name]['health'][inst_id] == 'InService' ? "blue" : "red"
          edge :"#{elb_name.gsub("-","")}_#{instance[inst_id]['Name'].gsub("-","")}", color:"#{color}"
        end
        route :"#{elb_name.gsub("-","")}" => instance_names
      end
    end
    save(:"#{v['name']}", :png)
  end
  `dot -Tpng #{v['name']}.dot -o #{v['name']}.png`
end

12
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
11