summaryrefslogtreecommitdiff
path: root/lib/leap_cli/commands/list.rb
blob: bfb0780873a1b7950a434cb0442dfe76203193ee (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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
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,:ls] 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, nil]
      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|
        prop_value = node[prop]
        if prop_value.nil?
          "null"
        elsif prop_value == ""
          "empty"
        elsif prop_value.is_a? LeapCli::Config::Object
          node[prop].dump_json(:format => :compact) # TODO: add option of getting pre-evaluation values.
        else
          prop_value.to_s
        end
      }.join(', ')
      printf("%#{max_width}s  %s\n", node.name, value)
    end
    puts
  end

  class Table
    def table
      @rows = []
      @row_options = []
      @column_widths = [20] # first column at least 20
      @column_options = []
      @current_row = 0
      @current_column = 0
      yield
    end

    def row(options=nil)
      @current_column = 0
      @row_options[@current_row] ||= options
      yield
      @current_row += 1
    end

    def column(str, options=nil)
      @rows[@current_row] ||= []
      @rows[@current_row][@current_column] = str
      @column_widths[@current_column] = [str.length, @column_widths[@current_column]||0].max
      @column_options[@current_column] ||= options
      @current_column += 1
    end

    def draw_table
      @rows.each_with_index do |row, i|
        color = (@row_options[i]||{})[:color]
        row.each_with_index do |column, j|
          align = (@column_options[j]||{})[:align] || "left"
          width = @column_widths[j]
          if color
            str = LeapCli.logger.colorize(column, color)
            extra_width = str.length - column.length
          else
            str = column
            extra_width = 0
          end
          if align == "right"
            printf "  %#{width+extra_width}s" % str
          else
            printf "  %-#{width+extra_width}s" % str
          end
        end
        puts
      end
      puts
    end
  end

  class TagTable < Table
    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
      table do
        row(color: @colors[0]) do
          column @heading, align: 'right'
          column "NODES"
        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
      draw_table
    end
  end

  class NodeTable < Table
    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 " = " + LeapCli.logger.colorize("no results", :red)
        puts
        return
      end
      table do
        row(color: @colors[0]) do
          column "NODES", align: 'right'
          column "SERVICES"
          column "TAGS"
        end
        rows.each do |r|
          row(color: @colors[1]) do
            column r[0]
            column r[1]
            column r[2]
          end
        end
      end
      draw_table
    end
  end

end; end