summaryrefslogtreecommitdiff
path: root/lib/leap_cli/commands/list.rb
blob: b8d7739e2b864bf82f1d262c9f2649b1716537e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
require 'command_line_reporter'

module LeapCli; module Commands

  desc 'List nodes and their classifications'
  long_desc 'Prints out a listing of nodes, services, or tags. ' +
            'If present, the FILTER can be a list of names of nodes, services, or tags. ' +
            'If the name is prefixed with +, this acts like an AND condition. ' +
            "For example:\n\n" +
            "`leap list node1 node2` matches all nodes named \"node1\" OR \"node2\"\n\n" +
            "`leap list openvpn +local` matches all nodes with service \"openvpn\" AND tag \"local\""

  arg_name 'FILTER', :optional => true
  command :list do |c|
    c.flag 'print', :desc => 'What attributes to print (optional)'
    c.switch 'disabled', :desc => 'Include disabled nodes in the list.', :negatable => false
    c.action do |global_options,options,args|
      # don't rely on default manager(), because we want to pass custom options to load()
      manager = LeapCli::Config::Manager.new
      if global_options[:color]
        colors = ['cyan', 'white']
      else
        colors = [nil, nil]
      end
      puts
      manager.load(:include_disabled => options['disabled'], :continue_on_error => true)
      if options['print']
        print_node_properties(manager.filter(args), options['print'])
      else
        if args.any?
          NodeTable.new(manager.filter(args), colors).run
        else
          environment = LeapCli.leapfile.environment || '_all_'
          TagTable.new('SERVICES', manager.env(environment).services, colors).run
          TagTable.new('TAGS', manager.env(environment).tags, colors).run
          NodeTable.new(manager.filter(), colors).run
        end
      end
    end
  end

  private

  def self.print_node_properties(nodes, properties)
    properties = properties.split(',')
    max_width = nodes.keys.inject(0) {|max,i| [i.size,max].max}
    nodes.each_node do |node|
      value = properties.collect{|prop|
        if node[prop].nil?
          "null"
        elsif node[prop] == ""
          "empty"
        else
          node[prop]
        end
      }.join(', ')
      printf("%#{max_width}s  %s\n", node.name, value)
    end
    puts
  end

  class TagTable
    include CommandLineReporter
    def initialize(heading, tag_list, colors)
      @heading = heading
      @tag_list = tag_list
      @colors = colors
    end
    def run
      tags = @tag_list.keys.select{|tag| tag !~ /^_/}.sort # sorted list of tags, excluding _partials
      max_width = [20, (tags+[@heading]).inject(0) {|max,i| [i.size,max].max}].max
      table :border => false do
        row :color => @colors[0]  do
          column @heading, :align => 'right', :width => max_width
          column "NODES", :width => HighLine::SystemExtensions.terminal_size.first - max_width - 2, :padding => 2
        end
        tags.each do |tag|
          next if @tag_list[tag].node_list.empty?
          row :color => @colors[1] do
            column tag
            column @tag_list[tag].node_list.keys.sort.join(', ')
          end
        end
      end
      vertical_spacing
    end
  end

  #
  # might be handy: HighLine::SystemExtensions.terminal_size.first
  #
  class NodeTable
    include CommandLineReporter
    def initialize(node_list, colors)
      @node_list = node_list
      @colors = colors
    end
    def run
      rows = @node_list.keys.sort.collect do |node_name|
        [node_name, @node_list[node_name].services.sort.join(', '), @node_list[node_name].tags.sort.join(', ')]
      end
      unless rows.any?
        puts Paint["no results", :red]
        puts
        return
      end
      padding = 2
      max_node_width    = [20, (rows.map{|i|i[0]} + ["NODES"]   ).inject(0) {|max,i| [i.size,max].max}].max
      max_service_width = (rows.map{|i|i[1]} + ["SERVICES"]).inject(0) {|max,i| [i.size+padding+padding,max].max}
      max_tag_width     = (rows.map{|i|i[2]} + ["TAGS"]    ).inject(0) {|max,i| [i.size,max].max}
      table :border => false do
        row :color => @colors[0]  do
          column "NODES", :align => 'right', :width => max_node_width
          column "SERVICES", :width => max_service_width, :padding => 2
          column "TAGS", :width => max_tag_width
        end
        rows.each do |r|
          row :color => @colors[1] do
            column r[0]
            column r[1]
            column r[2]
          end
        end
      end
      vertical_spacing
    end
  end

end; end