Explorar o código

Merge branch 'master' of ssh://gogs.mague.com:7022/Shokunin/neo-infra

Christian Mague %!s(int64=7) %!d(string=hai) anos
pai
achega
ab3db2d298

+ 1 - 0
.gitignore

@@ -4,3 +4,4 @@ config.yaml.private.*
 data/*
 vendor/*
 .bundle
+tmp_notes

+ 8 - 0
config.ru

@@ -15,3 +15,11 @@ end
 map '/load' do
   run Dataloader
 end
+
+map '/view' do
+  run Views
+end
+
+map '/search' do
+  run Search
+end

+ 208 - 12
lib/neoinfra/aws.rb

@@ -5,11 +5,20 @@ require 'regions'
 require 'mime-types'
 require 'fog-aws'
 require 's3'
+require 'date'
+require 'ipaddr'
 require 'neo4j'
 require 'rds'
+require 'lambdas'
 require 'neoinfra/config'
 require 'neoinfra/cloudwatch'
 
+RFC_1918 = [
+  IPAddr.new('10.0.0.0/8'),
+  IPAddr.new('172.16.0.0/12'),
+  IPAddr.new('192.168.0.0/16'),
+].freeze
+
 # NeoInfra Account information
 module NeoInfra
   # Provide informations about the accounts available
@@ -48,8 +57,12 @@ module NeoInfra
         aws_secret_access_key: account[:secret],
         region: region
       }
-      conn = Fog::Compute.new(base_conf)
-      conn.describe_availability_zones.data[:body]['availabilityZoneInfo'].collect { |x| x['zoneName'] }
+      begin
+        conn = Fog::Compute.new(base_conf)
+        conn.describe_availability_zones.data[:body]['availabilityZoneInfo'].collect { |x| x['zoneName'] }
+      rescue Exception => e
+        puts "Zone couldn't load region #{region}: #{e.message}"
+      end
     end
 
     def load_regions
@@ -68,6 +81,21 @@ module NeoInfra
       end
     end
 
+    def list_buckets
+      buckets = []
+      Bucket.all.order('n.size DESC').each do |b|
+        buckets <<  {
+          'name'       => b.name,
+          'size'       => b.size,
+          'versioning' => b.versioning,
+          'creation'   => b.creation,
+          'region'     => b.region.region,
+          'owner'      => b.owner.name
+        }
+      end
+      return buckets
+    end
+
     def load_buckets
       cw = NeoInfra::Cloudwatch.new
       @cfg.accounts.each do |account|
@@ -79,8 +107,17 @@ module NeoInfra
         s = Fog::Storage.new(base_conf)
         s.directories.each do |bucket|
           next unless Bucket.where(name: bucket.key).empty?
+          begin
+            vers = bucket.versioning?.to_s
+            crea =  bucket.creation_date.to_s
+          rescue
+            vers = "unknown"
+            crea = "unknown"
+          end
           b = Bucket.new(
             name: bucket.key,
+            versioning: vers,
+            creation: crea,
             size: cw.get_bucket_size(account[:key], account[:secret], bucket.location, bucket.key)
           )
           b.save
@@ -99,19 +136,178 @@ module NeoInfra
         }
         self.regions.each do |region|
           region_conf = { region: region }
-          conn = Fog::Compute.new(region_conf.merge(base_conf))
+          begin
+            conn = Fog::Compute.new(region_conf.merge(base_conf))
+          rescue
+            puts "Error loading security groups for region #{region}"
+            next
+          end
           conn.security_groups.all.each do |grp|
-            next unless SecurityGroup.where(sg_id: grp.group_id).empty?
-            g = SecurityGroup.new(
-              sg_id: grp.group_id,
-              name: grp.name,
-              description: grp.description,
-            )
-            g.save
-            SecurityGroupOwner.create(from_node: g, to_node:  AwsAccount.where(account_id: grp.owner_id).first)
-            SecurityGroupVpc.create(from_node: g, to_node:  Vpc.where(vpc_id: grp.vpc_id).first)
+            if SecurityGroup.where(sg_id: grp.group_id).empty?
+              g = SecurityGroup.new(
+                sg_id: grp.group_id,
+                name: grp.name,
+                description: grp.description,
+              )
+              g.save
+              SecurityGroupOwner.create(from_node: g, to_node:  AwsAccount.where(account_id: grp.owner_id).first)
+              SecurityGroupVpc.create(from_node: g, to_node:  Vpc.where(vpc_id: grp.vpc_id).first)
+            end
+            grp.ip_permissions.each do |iprule|
+                  if iprule['ipProtocol'] != "-1"
+                  iprule['ipRanges'].each do |r|
+                    if iprule['toPort'] == -1
+                      to_port = 65535
+                    else
+                      to_port = iprule['toPort']
+                    end
+                    if iprule['fromPort'] == -1
+                      from_port = 0
+                    else
+                      from_port = iprule['fromPort']
+                    end
+                    if IpRules.where(
+                      cidr_block: r['cidrIp'],
+                      direction: 'ingress',
+                      proto: iprule['ipProtocol'],
+                      to_port: to_port,
+                      from_port: from_port,
+                    ).empty?
+                      rl = IpRules.new(
+                        cidr_block: r['cidrIp'],
+                        direction: 'ingress',
+                        proto: iprule['ipProtocol'],
+                        to_port: to_port,
+                        from_port: from_port,
+                        private: RFC_1918.any? { |rfc| rfc.include?(IPAddr.new(r['cidrIp']))}
+                      )
+                      rl.save
+                    end
+                    # TODO: remove duplicate Relationships
+                    SecurityGroupsIpRules.create(
+                      from_node: SecurityGroup.where(sg_id: grp.group_id).first,
+                      to_node: IpRules.where(
+                        cidr_block: r['cidrIp'],
+                        direction: 'ingress',
+                        proto: iprule['ipProtocol'],
+                        to_port: to_port,
+                        from_port: from_port,
+                        private: RFC_1918.any? { |rfc| rfc.include?(IPAddr.new(r['cidrIp']))}
+                      ).first
+                    )
+                  end
+                end
+              end
+              #
+          end
+        end
+      end
+    end
+
+    def list_lambdas
+      lambdas = []
+      Lambda.all.each do |l|
+        lambdas <<  {
+          'name'           => l.name,
+          'runtime'        => l.runtime,
+          'handler'        => l.handler,
+          'lambda_timeout' => l.lambda_timeout,
+          'memorysize'     => l.memorysize,
+          'last_modified'  => l.last_modified,
+          'region'         => l.region.region,
+          'owner'          => l.owner.name
+        }
+      end
+      return lambdas
+    end
+
+    def load_lambda
+      @cfg.accounts.each do |account|
+        base_conf = {
+          aws_access_key_id: account[:key],
+          aws_secret_access_key: account[:secret]
+        }
+        self.regions.each do |region|
+          region_conf = { region: region }
+          begin
+            lambdas = Fog::AWS::Lambda.new(region_conf.merge(base_conf))
+            lambdas.list_functions.data[:body]['Functions'].each do |f|
+              next unless Lambda.where(name: f['FunctionArn']).empty?
+              l = Lambda.new(
+                  name:             f['FunctionName'],
+                  runtime:          f['Runtime'],
+                  lambda_timeout:   f['Timeout'],
+                  handler:          f['Handler'],
+                  memorysize:       f['MemorySize'],
+                  arn:              f['FunctionArn'],
+                  codesize:         f['CodeSize'],
+                  last_modified:    f['LastModified'],
+              )
+              l.save
+              LambdaAccount.create(from_node: l, to_node: AwsAccount.where(name: account[:name]).first)
+              LambdaRegion.create(from_node: l, to_node: Region.where(region: region).first)
+            end
+          rescue Exception => e
+            puts "Error with #{region}: #{e.message}"
+            next
+          end
+        end
+      end
+    end
+
+    def list_dynamos
+      dynamos = []
+      Dynamo.all.order('n.sizebytes DESC').each do |d|
+        dynamos <<  {
+          'name'       => d.name,
+          'size'       => d.sizebytes,
+          'itemcount'  => d.itemcount,
+          'status'     => d.status,
+          'creation'   => d.creation,
+          'region'     => d.region.region,
+          'owner'      => d.owner.name
+        }
+      end
+      return dynamos
+    end
+
+    def load_dynamo
+      @cfg.accounts.each do |account|
+        base_conf = {
+          aws_access_key_id: account[:key],
+          aws_secret_access_key: account[:secret]
+        }
+        self.regions.each do |region|
+          region_conf = { region: region }
+          begin
+            dyns = Fog::AWS::DynamoDB.new(region_conf.merge(base_conf))
+            dyns.list_tables.data[:body]["TableNames"].each do |table|
+              tb = dyns.describe_table(table).data[:body]['Table']
+              next unless Dynamo.where(name: table['TableId']).empty?
+              d = Dynamo.new(
+                  tableid: 	tb['TableId'],
+                  name: 	tb['TableName'],
+                  creation: Time.at(tb['CreationDateTime']).to_datetime.strftime("%F %H:%M:%S %Z"),
+                  arn: 		tb['TableArn'],
+                  itemcount: 	tb['ItemCount'],
+                  sizebytes: 	tb['TableSizeBytes'],
+                  status: 	tb['TableStatus'],
+                  readcap: 	tb['ProvisionedThroughput']['ReadCapacityUnits'],
+                  writecap: 	tb['ProvisionedThroughput']['WriteCapacityUnits'],
+                  capdecreases: tb['ProvisionedThroughput']['NumberOfDecreasesToday'],
+              )
+              d.save
+              DynamoAccount.create(from_node: d, to_node: AwsAccount.where(name: account[:name]).first)
+              DynamoRegion.create(from_node: d, to_node: Region.where(region: region).first)
+            end
+          rescue Exception => e
+            puts "Could not list Dynamos for region: #{region}: #{e.message}"
+            next
           end
         end
+        #dyns.list_tables.each do |table|
+        #  p table
+        #end
       end
     end
 

+ 42 - 3
lib/neoinfra/nodes.rb

@@ -11,11 +11,45 @@ require 'fog-aws'
 module NeoInfra
   # Provide informations about the accounts available
   class Nodes
-    def load_nodes
-      aws = NeoInfra::Aws.new
+
+    def initialize
       @cfg = NeoInfra::Config.new
       neo4j_url = "http://#{@cfg.neo4j[:host]}:#{@cfg.neo4j[:port]}"
       Neo4j::Session.open(:server_db, neo4j_url)
+    end
+
+    def display_node(node_id)
+      n = Node.where(node_id: node_id).first
+      return {
+        "Name"          => n.name,
+        "IP"            => n.ip,
+        "State"         => n.state,
+        "AMI"           => n.ami,
+        "Public_IP"     => n.public_ip,
+        "AZ"            => n.az.az,
+        "Account"       => n.account.name,
+        "Size"          => n.size,
+        "Subnet"        => n.subnet.name,
+        "VPC"           => n.subnet.subnet.name,
+        "SSH-Key"       => n.sshkey.name,
+        "SecurityGroup" => n.node_sg.name,
+      }
+    end
+
+    def search_nodes_by_ip(ip)
+      if Node.where(ip: ip).length > 0
+	display_node(Node.where(ip: ip).first.node_id)
+      else
+	display_node(Node.where(public_ip: ip).first.node_id)
+      end
+    end
+
+    def search_nodes_by_node_id(node_id)
+      display_node(Node.where(node_id: node_id).first.node_id)
+    end
+
+    def load_nodes
+      aws = NeoInfra::Aws.new
 
       @cfg.accounts.each do |account|
         base_conf = {
@@ -25,7 +59,12 @@ module NeoInfra
         }
         aws.regions.each do |region|
           region_conf = { region: region }
-          new_conn = Fog::Compute.new(region_conf.merge(base_conf))
+          begin
+            new_conn = Fog::Compute.new(region_conf.merge(base_conf))
+          rescue
+            puts "Error loading nodes in region: #{region}"
+            next
+          end
           new_conn.servers.all.each do |ec2|
             if SshKey.where(name: ec2.key_name).empty?
               s = SshKey.new(

+ 21 - 9
lib/neoinfra/vpcs.rb

@@ -10,22 +10,29 @@ require 'neo4j'
 module NeoInfra
   # Provide informations about the accounts available
   class Vpcs
+
+    def initialize
+      @cfg = NeoInfra::Config.new
+      neo4j_url = "http://#{@cfg.neo4j[:host]}:#{@cfg.neo4j[:port]}"
+      Neo4j::Session.open(:server_db, neo4j_url)
+    end
+
     def non_default_vpc_count
-      p Vpc.all
-      21
+      Vpc.all.collect{|x| x.default}.select{|y| y == "false"}.length
     end
 
     def default_vpc_count
-      22
+      Vpc.all.collect{|x| x.default}.select{|y| y == "true"}.length
+    end
+
+    def list_vpcs
+      node_counts = Hash.new(0)
+      Node.all.each{|x| node_counts[x.subnet.subnet.name]+=1}
+      Vpc.all.collect{|x| {'nodes' => node_counts[x.name], 'vpc_id' => x.vpc_id, 'name'=>x.name, 'region' => x.region.region, 'owner' => x.owned.name, 'cidr' => x.cidr, 'default' => x.default} }.select{ |y| y['default'] == "false"}.sort_by{|h| h['nodes']}.reverse
     end
 
     def load
       aws = NeoInfra::Aws.new
-      @cfg = NeoInfra::Config.new
-
-      neo4j_url = "http://#{@cfg.neo4j[:host]}:#{@cfg.neo4j[:port]}"
-      Neo4j::Session.open(:server_db, neo4j_url)
-
       @cfg.accounts.each do |account|
         base_conf = {
           provider: 'AWS',
@@ -34,7 +41,12 @@ module NeoInfra
         }
         aws.regions.each do |region|
           region_conf = { region: region }
-          new_conn = Fog::Compute.new(region_conf.merge(base_conf))
+          begin
+            new_conn = Fog::Compute.new(region_conf.merge(base_conf))
+          rescue
+            puts "Error loading VPCs in region: #{region}"
+            next
+          end
           # Get VPCs
           new_conn.vpcs.all.each do |vpc|
             next unless Vpc.where(vpc_id: vpc.id).empty?

+ 34 - 0
models/lambdas.rb

@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'neo4j'
+
+# Information on Lambda
+class Lambda
+  include Neo4j::ActiveNode
+  property :name
+  property :runtime
+  property :lambda_timeout
+  property :handler
+  property :memorysize
+  property :arn
+  property :codesize
+  property :last_modified
+  has_one :out, :region, rel_class: :LambdaRegion
+  has_one :out, :owner, rel_class: :LambdaAccount
+end
+
+# Map Lambda to Region
+class LambdaRegion
+  include Neo4j::ActiveRel
+  from_class :Lambda
+  to_class :Region
+  type :region
+end
+
+# Map Lambda to Region
+class LambdaAccount
+  include Neo4j::ActiveRel
+  from_class :Lambda
+  to_class :AwsAccount
+  type :owner
+end

+ 11 - 2
models/nodes.rb

@@ -26,7 +26,7 @@ class SecurityGroup
   property :description
   has_one  :out, :sg_owner, rel_class: :SecurityGroupOwner
   has_one  :out, :sg_vpc, rel_class: :SecurityGroupVpc
-#  has_many :out, :ip_rules, rel_class: :SecurityGroupsIpRules
+  has_many :out, :ip_rules, rel_class: :SecurityGroupsIpRules
 #  has_many :out, :sg_rules, rel_class: :SecurityGroupsSgRules
 end
 
@@ -56,7 +56,16 @@ class IpRules
   property :cidr_block
   property :direction
   property :proto
-  property :start_port
+  property :from_port
+  property :to_port
+  property :private
+end
+
+class SecurityGroupsIpRules
+  include Neo4j::ActiveRel
+  from_class :SecurityGroup
+  to_class :IpRules
+  type :ip_rules
 end
 
 # SSH key class

+ 31 - 1
models/rds.rb

@@ -25,10 +25,40 @@ class RdsAz
   type :az
 end
 
-# Map Rds to Region
 class RdsAccount
   include Neo4j::ActiveRel
   from_class :Rds
   to_class :AwsAccount
   type :owner
 end
+
+class Dynamo
+  include Neo4j::ActiveNode
+  property :tableid, constraint: :unique
+  property :name
+  property :creation
+  property :arn
+  property :itemcount
+  property :sizebytes
+  property :status
+  property :readcap
+  property :writecap
+  property :capdecreases
+  has_one :out, :owner, rel_class: :DynamoAccount
+  has_one :out, :region, rel_class: :DynamoRegion
+end
+
+class DynamoAccount
+  include Neo4j::ActiveRel
+  from_class :Dynamo
+  to_class :AwsAccount
+  type :owner
+end
+
+class DynamoRegion
+  include Neo4j::ActiveRel
+  from_class :Dynamo
+  to_class :Region
+  type :region
+end
+

+ 2 - 0
models/s3.rb

@@ -6,6 +6,8 @@ require 'neo4j'
 class Bucket
   include Neo4j::ActiveNode
   property :name, constraint: :unique
+  property :versioning
+  property :creation
   property :size
   has_one :out, :region, rel_class: :BucketRegion
   has_one :out, :owner, rel_class: :BucketAccount

+ 16 - 1
tasks/load_data.rake

@@ -50,6 +50,21 @@ namespace :load_data do
     j.load_security_groups
   end
 
+  desc 'Load Dynamo'
+  task :dynamo do
+    puts 'loading Dynamo'
+    j = NeoInfra::Aws.new
+    j.load_dynamo
+  end
+
+
+  desc 'Load Lambdas'
+  task :lambda do
+    puts 'loading Lambdas'
+    j = NeoInfra::Aws.new
+    j.load_lambda
+  end
+
   desc 'Load Everything'
-  task all: %i[accounts regions vpcs buckets security_groups nodes rds]
+  task all: %i[accounts regions vpcs buckets security_groups nodes rds dynamo lambda]
 end

+ 1 - 2
web/controllers/dataloader.rb

@@ -34,8 +34,7 @@ class Dataloader < Sinatra::Base
     j = NeoInfra::Aws.new
     j.load_regions
     status 200
-    # {}"Loaded #{j.region_count} regions, #{j.az_count} availablity zones"
-    'suck'
+    "Loaded #{j.region_count} regions, #{j.az_count} availablity zones"
   end
 
   get '/vpcs' do

+ 39 - 0
web/controllers/search.rb

@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+lib_dir = File.join(File.dirname(File.expand_path(__FILE__)), '..', '..', 'lib')
+$LOAD_PATH.unshift(lib_dir) unless
+  $LOAD_PATH.include?(lib_dir) || $LOAD_PATH.include?(lib_dir)
+
+require 'json'
+require 'neoinfra'
+require 'sinatra'
+require 'sinatra/base'
+require 'sinatra/respond_to'
+
+# Handle loading data into the graph db
+class Search < Sinatra::Base
+  register Sinatra::RespondTo
+  set :views, File.join(File.dirname(__FILE__), '..', '/views')
+
+  post '/all' do
+    if params['search'] =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/
+      n = NeoInfra::Nodes.new
+      respond_to do |wants|
+        wants.html {
+          erb :view_node,
+          :layout => :base_layout,
+          :locals => {:node => n.search_nodes_by_ip(params['search'])}
+        }
+    end
+    elsif params['search'] =~ /i-[a-f0-9]{6,20}/
+      n = NeoInfra::Nodes.new
+      respond_to do |wants|
+        wants.html {
+          erb :view_node,
+          :layout => :base_layout,
+          :locals => {:node => n.search_nodes_by_node_id(params['search'])}
+        }
+    end
+    end
+  end
+end

+ 63 - 0
web/controllers/views.rb

@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+lib_dir = File.join(File.dirname(File.expand_path(__FILE__)), '..', '..', 'lib')
+$LOAD_PATH.unshift(lib_dir) unless
+  $LOAD_PATH.include?(lib_dir) || $LOAD_PATH.include?(lib_dir)
+
+require 'json'
+require 'neoinfra'
+require 'sinatra'
+require 'sinatra/base'
+require 'sinatra/respond_to'
+
+# Handle loading data into the graph db
+class Views < Sinatra::Base
+  register Sinatra::RespondTo
+  set :views, File.join(File.dirname(__FILE__), '..', '/views')
+
+  get '/vpcs' do
+    w = NeoInfra::Vpcs.new
+    respond_to do |wants|
+      wants.html {
+        erb :view_vpcs,
+        :layout => :base_layout,
+        :locals => {:vpcs => w.list_vpcs}
+      }
+    end
+  end
+
+  get '/buckets' do
+    j = NeoInfra::Aws.new
+    respond_to do |wants|
+      wants.html {
+        erb :view_buckets,
+        :layout => :base_layout,
+        :locals => {:buckets => j.list_buckets}
+      }
+    end
+  end
+
+  get '/dynamos' do
+    j = NeoInfra::Aws.new
+    respond_to do |wants|
+      wants.html {
+        erb :view_dynamos,
+        :layout => :base_layout,
+        :locals => {:dynamos => j.list_dynamos}
+      }
+    end
+  end
+
+  get '/lambdas' do
+    j = NeoInfra::Aws.new
+    respond_to do |wants|
+      wants.html {
+        erb :view_lambdas,
+        :layout => :base_layout,
+        :locals => {:lambdas => j.list_lambdas}
+      }
+    end
+  end
+
+
+end

+ 7 - 3
web/views/base_layout.html.erb

@@ -5,6 +5,7 @@
     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
     <meta name="description" content="">
     <meta name="author" content="">
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
     <link rel="icon" href="../../favicon.ico">
     <script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
 
@@ -22,7 +23,6 @@
       <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
         <span class="navbar-toggler-icon"></span>
       </button>
-      <a class="navbar-brand" href="#">Navbar</a>
 
       <div class="collapse navbar-collapse" id="navbarsExampleDefault">
         <ul class="navbar-nav mr-auto">
@@ -32,13 +32,17 @@
           <li class="nav-item dropdown">
             <a class="nav-link dropdown-toggle" id="dropdown01" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Views</a>
             <div class="dropdown-menu" aria-labelledby="dropdown01">
+              <a class="dropdown-item" href="/view/vpcs">VPCs</a>
+              <a class="dropdown-item" href="/view/buckets">S3 Buckets</a>
+              <a class="dropdown-item" href="/view/dynamos">Dyanmos</a>
+              <a class="dropdown-item" href="/view/lambdas">Labmda Functions</a>
               <a class="dropdown-item" href="/audit/tags">Tag Audit</a>
               <a class="dropdown-item" href="/load/all">Load Data</a>
             </div>
           </li>
         </ul>
-        <form class="form-inline my-2 my-lg-0">
-          <input class="form-control mr-sm-2" type="text" placeholder="Search">
+        <form class="form-inline my-2 my-lg-0" action="/search/all" method="POST">
+          <input class="form-control mr-sm-2" type="text" name="search" placeholder="Search">
           <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
         </form>
       </div>

+ 40 - 0
web/views/view_buckets.html.erb

@@ -0,0 +1,40 @@
+<h2 class="title"><br><br><center>S3 Buckets</center></h2>
+
+<div class="container">
+  <table class="table table-hover">
+    <thead>
+      <tr>
+        <th>Name</th>
+        <th>Size</th>
+        <th>Region</th>
+        <th>Owner</th>
+        <th>Versioning</th>
+        <th>Creation</th>
+      </tr>
+    </thead>
+    <tbody>
+<% buckets.each do |bucket| %>
+<tr>
+<td><%= bucket['name'] %></td>
+<% if bucket['size'] < 0 %>
+<td>Unknown</td>
+<% else %>
+<td><%= (bucket['size']/1000000000).round(2) %> Gb</td>
+<% end %>
+<td><%= bucket['region']%></td>
+<td><%= bucket['owner'] %></td>
+<td>
+  <% if bucket['versioning'] == "true" %>
+    <i class="fa fa-check-circle" aria-hidden="true"></i>
+  <% elsif bucket['versioning'] == "false" %>
+    <i class="fa fa-times-circle" aria-hidden="true"></i>
+  <% else %>
+    <i class="fa fa-question-circle" aria-hidden="true"></i>
+  <% end %>
+</td>
+<td><%= bucket['creation'] %></td>
+</tr>
+<% end %>
+    </tbody>
+  </table>
+</div>

+ 32 - 0
web/views/view_dynamos.html.erb

@@ -0,0 +1,32 @@
+<h2 class="title"><br><br><center>Dynamo Tables</center></h2>
+
+<div class="container">
+  <table class="table table-hover">
+    <thead>
+      <tr>
+        <th>Name</th>
+        <th>Owner</th>
+        <th>Region</th>
+        <th>Size</th>
+        <th>Items</th>
+        <th>Creation</th>
+      </tr>
+    </thead>
+    <tbody>
+<% dynamos.each do |dynamo| %>
+<tr>
+<td><%= dynamo['name'] %></td>
+<td><%= dynamo['owner'] %></td>
+<td><%= dynamo['region'] %></td>
+<% if dynamo['size'] < 0 %>
+<td>Unknown</td>
+<% else %>
+<td><%= (dynamo['size']/1000000).round(2) %> Mb</td>
+<% end %>
+<td><%= dynamo['itemcount']%></td>
+<td><%= dynamo['creation'] %></td>
+</tr>
+<% end %>
+    </tbody>
+  </table>
+</div>

+ 30 - 0
web/views/view_lambdas.html.erb

@@ -0,0 +1,30 @@
+<h2 class="title"><br><br><center>Lambda Functions</center></h2>
+
+<div class="container">
+  <table class="table table-hover">
+    <thead>
+      <tr>
+        <th>Name</th>
+        <th>Owner</th>
+        <th>Region</th>
+        <th>Handler</th>
+        <th>Memory</th>
+        <th>Timeout</th>
+        <th>Modified</th>
+      </tr>
+    </thead>
+    <tbody>
+<% lambdas.each do |lambda| %>
+<tr>
+<td><%= lambda['name'] %></td>
+<td><%= lambda['owner'] %></td>
+<td><%= lambda['region'] %></td>
+<td><%= lambda['handler'] %></td>
+<td><%= lambda['memorysize'] %></td>
+<td><%= lambda['lambda_timeout']%></td>
+<td><%= lambda['last_modified'] %></td>
+</tr>
+<% end %>
+    </tbody>
+  </table>
+</div>

+ 20 - 0
web/views/view_node.html.erb

@@ -0,0 +1,20 @@
+<h2 class="title"><br><br><center>Node Information</center><br><br></h2>
+<div class="container">
+ <table class="table table-hover">
+<tbody>
+<tr> <td>Name</td> <td><%= node['Name'] %></td></tr>
+<tr><td>IP</td><td><%= node['IP'] %></td></tr>
+<tr><td>Public IP</td><td><%= node['Public_IP'] %></td></tr>
+<tr><td>Account</td><td><%= node['Account'] %></td></tr>
+<tr><td>Size</td><td><%= node['Size'] %></td></tr>
+<tr><td>Subnet</td><td><%= node['Subnet'] %></td></tr>
+<tr><td>AZ</td><td><%= node['AZ'] %></td></tr>
+<tr><td>VPC</td><td><%= node['VPC'] %></td></tr>
+<tr><td>SSH Key</td><td><%= node['SSH-Key'] %></td></tr>
+<tr><td>AMI</td><td><%= node['AMI'] %></td></tr>
+<tr><td>State</td><td><%= node['State'] %></td></tr>
+<tr><td>Security Group</td><td><%= node['SecurityGroup'] %></td></tr>
+    </tbody>
+  </table>
+
+</div>

+ 30 - 0
web/views/view_vpcs.html.erb

@@ -0,0 +1,30 @@
+<h2 class="title"><br><br><center>VPCs</center></h2>
+
+<div class="container">
+  <table class="table table-hover">
+    <thead>
+      <tr>
+        <th>Name</th>
+        <th>Nodes</th>
+        <th>Account</th>
+        <th>Region</th>
+        <th>CIDR</th>
+        <th>Default</th>
+        <th>VPC-ID</th>
+      </tr>
+    </thead>
+    <tbody>
+<% vpcs.each do |vpc| %>
+<tr>
+<td><%= vpc['name'] %></td>
+<td><%= vpc['nodes'] %></td>
+<td><%= vpc['owner'] %></td>
+<td><%= vpc['region'] %></td>
+<td><%= vpc['cidr'] %></td>
+<td><%= vpc['default'] %></td>
+<td><%= vpc['vpc_id'] %></td>
+</tr>
+<% end %>
+    </tbody>
+  </table>
+</div>