Project

General

Profile

Download (24.6 KB) Statistics
| Branch: | Tag: | Revision:

hammer-cli-csv / lib / hammer_cli_csv / content_hosts.rb @ 04be28e1

1 a1aa846b Tom McKay
module HammerCLICsv
2 7bea883f Grant Gainey
  class CsvCommand
3 a77acc4a Tom McKay
    class ContentHostsCommand < BaseCommand
4 780e508f Tom McKay
      include ::HammerCLIForemanTasks::Helper
5 611959a3 Tom McKay
      include ::HammerCLICsv::Utils::Subscriptions
6 780e508f Tom McKay
7 a77acc4a Tom McKay
      command_name 'content-hosts'
8
      desc         'import or export content hosts'
9 7bea883f Grant Gainey
10 c06f1783 Tom McKay
      def self.supported?
11
        true
12
      end
13
14 611959a3 Tom McKay
      option %w(--itemized-subscriptions), :flag, _('Export one subscription per row, only process update subscriptions on import')
15 863472b5 Tom McKay
      option %w(--columns), 'COLUMN_NAMES', _('Comma separated list of column names to export')
16 c06f1783 Tom McKay
17 660e2ae4 Tom McKay
      SEARCH = 'Search'
18 7bea883f Grant Gainey
      ORGANIZATION = 'Organization'
19
      ENVIRONMENT = 'Environment'
20
      CONTENTVIEW = 'Content View'
21 a77acc4a Tom McKay
      HOSTCOLLECTIONS = 'Host Collections'
22 7bea883f Grant Gainey
      VIRTUAL = 'Virtual'
23 afc8539f Tom McKay
      HOST = 'Host'  # deprecated for GUESTOF
24
      GUESTOF = 'Guest of Host'
25 7bea883f Grant Gainey
      OPERATINGSYSTEM = 'OS'
26
      ARCHITECTURE = 'Arch'
27
      SOCKETS = 'Sockets'
28
      RAM = 'RAM'
29
      CORES = 'Cores'
30
      SLA = 'SLA'
31
      PRODUCTS = 'Products'
32
33 863472b5 Tom McKay
      def self.help_columns
34
        ['', _('Columns:'),
35
         _(" %{name} - Name of resource") % {:name => NAME},
36
         _(" %{name} - Search for matching names during import (overrides '%{name_col}' column)") % {:name => SEARCH, :name_col => NAME},
37
         _(" %{name} - Organization name") % {:name => ORGANIZATION},
38
         _(" %{name} - Lifecycle environment name") % {:name => ENVIRONMENT},
39
         _(" %{name} - Content view name") % {:name => CONTENTVIEW},
40
         _(" %{name} - Comma separated list of host collection names") % {:name => HOSTCOLLECTIONS},
41 afc8539f Tom McKay
         _(" %{name} - Is a virtual host, %{yes} or %{no}") % {:name => VIRTUAL, :yes => 'Yes', :no => 'No'},
42
         _(" %{name} - Hypervisor host name for virtual hosts") % {:name => GUESTOF},
43 863472b5 Tom McKay
         _(" %{name} - Operating system") % {:name => OPERATINGSYSTEM},
44
         _(" %{name} - Architecture") % {:name => ARCHITECTURE},
45
         _(" %{name} - Number of sockets") % {:name => SOCKETS},
46
         _(" %{name} - Quantity of RAM in bytes") % {:name => RAM},
47
         _(" %{name} - Number of cores") % {:name => CORES},
48
         _(" %{name} - Service Level Agreement value") % {:name => SLA},
49
         _(" %{name} - Comma separated list of products, each of the format \"<sku>|<name>\"") % {:name => PRODUCTS},
50
         _(" %{name} - Comma separated list of subscriptions, each of the format \"<quantity>|<sku>|<name>|<contract>|<account>\"") % {:name => SUBSCRIPTIONS},
51
         _(" %{name} - Subscription name (only applicable for --itemized-subscriptions)") % {:name => SUBS_NAME},
52
         _(" %{name} - Subscription type (only applicable for --itemized-subscriptions)") % {:name => SUBS_TYPE},
53
         _(" %{name} - Subscription quantity (only applicable for --itemized-subscriptions)") % {:name => SUBS_QUANTITY},
54
         _(" %{name} - Subscription SKU (only applicable for --itemized-subscriptions)") % {:name => SUBS_SKU},
55
         _(" %{name} - Subscription contract number (only applicable for --itemized-subscriptions)") % {:name => SUBS_CONTRACT},
56
         _(" %{name} - Subscription account number (only applicable for --itemized-subscriptions)") % {:name => SUBS_ACCOUNT},
57
         _(" %{name} - Subscription start date (only applicable for --itemized-subscriptions)") % {:name => SUBS_START},
58
         _(" %{name} - Subscription end date (only applicable for --itemized-subscriptions)") % {:name => SUBS_END}
59
        ].join("\n")
60
      end
61
62
      def column_headers
63
        @column_values = {}
64
        if option_columns.nil?
65
          if ::HammerCLI::Settings.settings[:csv][:columns] &&
66
              ::HammerCLI::Settings.settings[:csv][:columns]['content-hosts'.to_sym] &&
67
              ::HammerCLI::Settings.settings[:csv][:columns]['content-hosts'.to_sym][:export]
68
            @columns = ::HammerCLI::Settings.settings[:csv][:columns]['content-hosts'.to_sym][:export]
69
          else
70
            if option_itemized_subscriptions?
71
              @columns = shared_headers + [SUBS_NAME, SUBS_TYPE, SUBS_QUANTITY, SUBS_SKU,
72 afc8539f Tom McKay
                                           SUBS_CONTRACT, SUBS_ACCOUNT, SUBS_START, SUBS_END, SUBS_GUESTOF]
73 863472b5 Tom McKay
            else
74
              @columns = shared_headers + [SUBSCRIPTIONS]
75
            end
76
          end
77
        else
78
          @columns = option_columns.split(',')
79
        end
80
81
        if ::HammerCLI::Settings.settings[:csv][:columns] && ::HammerCLI::Settings.settings[:csv][:columns]['content-hosts'.to_sym][:define]
82
          @column_definitions = ::HammerCLI::Settings.settings[:csv][:columns]['content-hosts'.to_sym][:define]
83
        end
84
85
        @columns
86
      end
87
88 c06f1783 Tom McKay
      def export(csv)
89 611959a3 Tom McKay
        if option_itemized_subscriptions?
90
          export_itemized_subscriptions csv
91 c06f1783 Tom McKay
        else
92
          export_all csv
93 0b978f6e Tom McKay
        end
94
      end
95
96 611959a3 Tom McKay
      def export_itemized_subscriptions(csv)
97 863472b5 Tom McKay
        csv << column_headers
98 c06f1783 Tom McKay
        iterate_hosts(csv) do |host|
99 863472b5 Tom McKay
          shared_columns(host)
100
          custom_columns(host)
101 c06f1783 Tom McKay
          if host['subscription_facet_attributes']
102
            subscriptions = @api.resource(:host_subscriptions).call(:index, {
103
                'organization_id' => host['organization_id'],
104
                'host_id' => host['id']
105
            })['results']
106
            if subscriptions.empty?
107 afc8539f Tom McKay
              %W(#{SUBS_NAME} #{SUBS_TYPE} #{SUBS_QUANTITY} #{SUBS_SKU} #{SUBS_CONTRACT}
108
                 #{SUBS_ACCOUNT} #{SUBS_START} #{SUBS_END} #{SUBS_GUESTOF}).each do |entry|
109
                @column_values[entry] = nil
110
              end
111 863472b5 Tom McKay
              columns_to_csv(csv)
112 5da9eed3 Tom McKay
            else
113 c06f1783 Tom McKay
              subscriptions.each do |subscription|
114 afc8539f Tom McKay
                %W(#{SUBS_NAME} #{SUBS_TYPE} #{SUBS_QUANTITY} #{SUBS_SKU} #{SUBS_CONTRACT}
115
                   #{SUBS_ACCOUNT} #{SUBS_START} #{SUBS_END} #{SUBS_GUESTOF}).each do |entry|
116
                  @column_values[entry] = nil
117
                end
118
                @column_values[SUBS_GUESTOF] = subscription['host']['name'] if subscription['host']
119 c06f1783 Tom McKay
                subscription_type = subscription['product_id'].to_i == 0 ? 'Red Hat' : 'Custom'
120 afc8539f Tom McKay
                subscription_type += ' Guest' unless @column_values[SUBS_GUESTOF].nil?
121 c06f1783 Tom McKay
                subscription_type += ' Temporary' if subscription['type'] == 'UNMAPPED_GUEST'
122 863472b5 Tom McKay
                @column_values[SUBS_NAME] = subscription['product_name']
123
                @column_values[SUBS_TYPE] = subscription_type
124
                @column_values[SUBS_QUANTITY] = subscription['quantity_consumed']
125
                @column_values[SUBS_SKU] = subscription['product_id']
126
                @column_values[SUBS_CONTRACT] = subscription['contract_number']
127
                @column_values[SUBS_ACCOUNT] = subscription['account_number']
128
                @column_values[SUBS_START] = DateTime.parse(subscription['start_date']).strftime('%m/%d/%Y')
129
                @column_values[SUBS_END] = DateTime.parse(subscription['end_date']).strftime('%m/%d/%Y')
130
                columns_to_csv(csv)
131 c06f1783 Tom McKay
              end
132 0b978f6e Tom McKay
            end
133 c06f1783 Tom McKay
          else
134 863472b5 Tom McKay
            columns_to_csv(csv)
135 c06f1783 Tom McKay
          end
136
        end
137
      end
138
139
      def export_all(csv)
140 863472b5 Tom McKay
        csv << column_headers
141 c06f1783 Tom McKay
        iterate_hosts(csv) do |host|
142 863472b5 Tom McKay
          all_subscription_column(host)
143
          shared_columns(host)
144
          custom_columns(host)
145
          columns_to_csv(csv)
146 c1357ce1 Tom McKay
        end
147
      end
148 a1aa846b Tom McKay
149 7bea883f Grant Gainey
      def import
150 863472b5 Tom McKay
        raise _("--columns option only relevant with --export") unless option_columns.nil?
151 9e11b4c5 Tom McKay
152 7bea883f Grant Gainey
        @existing = {}
153 2645da78 Tom McKay
        @hypervisor_guests = {}
154 c06f1783 Tom McKay
        @all_subscriptions = {}
155 a1aa846b Tom McKay
156 7bea883f Grant Gainey
        thread_import do |line|
157 5da9eed3 Tom McKay
          create_from_csv(line)
158 7bea883f Grant Gainey
        end
159 6bc031bd Tom McKay
160 9e11b4c5 Tom McKay
        if !@hypervisor_guests.empty?
161
          print(_('Updating hypervisor and guest associations...')) if option_verbose?
162
          @hypervisor_guests.each do |host_id, guest_ids|
163 5146ac68 Tom McKay
            @api.resource(:hosts).call(:update, {
164 5da9eed3 Tom McKay
              'id' => host_id,
165
              'host' => {
166
                'subscription_facet_attributes' => {
167 c06f1783 Tom McKay
                  'autoheal' => false,
168 5da9eed3 Tom McKay
                  'hypervisor_guest_uuids' => guest_ids
169 5146ac68 Tom McKay
                }
170 5da9eed3 Tom McKay
              }
171 9e11b4c5 Tom McKay
            })
172
          end
173 c4dbc7a3 Tom McKay
          puts _('done') if option_verbose?
174 7bea883f Grant Gainey
        end
175 6bc031bd Tom McKay
      end
176 a1aa846b Tom McKay
177 5da9eed3 Tom McKay
      def create_from_csv(line)
178 2645da78 Tom McKay
        return if option_organization && line[ORGANIZATION] != option_organization
179
180 5da9eed3 Tom McKay
        update_existing(line)
181 a1aa846b Tom McKay
182 02387fb5 Tom McKay
        count(line[COUNT]).times do |number|
183 660e2ae4 Tom McKay
          if  !line[SEARCH].nil? && !line[SEARCH].empty?
184
            search = namify(line[SEARCH], number)
185
            @api.resource(:hosts).call(:index, {
186
                'organization_id' => foreman_organization(:name => line[ORGANIZATION]),
187
                'search' => search
188
            })['results'].each do |host|
189
              if host['subscription_facet_attributes']
190
                create_named_from_csv(host['name'], line)
191
              end
192
            end
193 7bea883f Grant Gainey
          else
194 660e2ae4 Tom McKay
            name = namify(line[NAME], number)
195
            create_named_from_csv(name, line)
196 7bea883f Grant Gainey
          end
197 c06f1783 Tom McKay
        end
198
      end
199 6bc031bd Tom McKay
200 c06f1783 Tom McKay
      private
201 6bc031bd Tom McKay
202 660e2ae4 Tom McKay
      def create_named_from_csv(name, line)
203
        if option_itemized_subscriptions?
204
          update_itemized_subscriptions(name, line)
205
        else
206
          update_or_create(name, line)
207
        end
208
      end
209
210 611959a3 Tom McKay
      def update_itemized_subscriptions(name, line)
211
        raise _("Content host '%{name}' must already exist with --itemized-subscriptions") % {:name => name} unless @existing.include? name
212 6bc031bd Tom McKay
213 c06f1783 Tom McKay
        print(_("Updating subscriptions for content host '%{name}'...") % {:name => name}) if option_verbose?
214
        host = @api.resource(:hosts).call(:show, {:id => @existing[name]})
215
        update_subscriptions(host, line, false)
216
        puts _('done') if option_verbose?
217
      end
218
219
      def update_or_create(name, line)
220 afc8539f Tom McKay
        lifecycle_environment_id = lifecycle_environment(line[ORGANIZATION], :name => line[ENVIRONMENT])
221
        content_view_id = katello_contentview(line[ORGANIZATION], :name => line[CONTENTVIEW])
222 c06f1783 Tom McKay
        if !@existing.include? name
223
          print(_("Creating content host '%{name}'...") % {:name => name}) if option_verbose?
224
          params = {
225
            'name' => name,
226
            'facts' => facts(name, line),
227 afc8539f Tom McKay
            'lifecycle_environment_id' => lifecycle_environment_id,
228
            'content_view_id' => content_view_id
229 c06f1783 Tom McKay
          }
230
          params['installed_products'] = products(line) if line[PRODUCTS]
231
          params['service_level'] = line[SLA] if line[SLA]
232
          host = @api.resource(:host_subscriptions).call(:create, params)
233
          @existing[name] = host['id']
234
        else
235
          print(_("Updating content host '%{name}'...") % {:name => name}) if option_verbose?
236
          params = {
237
            'id' => @existing[name],
238
            'host' => {
239
              'content_facet_attributes' => {
240 afc8539f Tom McKay
                'lifecycle_environment_id' => lifecycle_environment_id,
241
                'content_view_id' => content_view_id
242 c06f1783 Tom McKay
              },
243
              'subscription_facet_attributes' => {
244
                'installed_products' => products(line),
245
                'service_level' => line[SLA]
246
              }
247
            }
248
          }
249
          host = @api.resource(:hosts).call(:update, params)
250
        end
251
252 afc8539f Tom McKay
        hypervisor = hypervisor_from_line(line)
253
        if line[VIRTUAL] == 'Yes' && hypervisor
254
          raise "Content host '#{hypervisor}' not found" if !@existing[hypervisor]
255
          @hypervisor_guests[@existing[hypervisor]] ||= []
256
          @hypervisor_guests[@existing[hypervisor]] << "#{line[ORGANIZATION]}/#{name}"
257 7bea883f Grant Gainey
        end
258 c06f1783 Tom McKay
259 04be28e1 Tom McKay
        update_facts(host, line)
260 c06f1783 Tom McKay
        update_host_collections(host, line)
261
        update_subscriptions(host, line, true)
262
263
        puts _('done') if option_verbose?
264 a1aa846b Tom McKay
      end
265 9d9e4494 Tom McKay
266 04be28e1 Tom McKay
      def facts(name, line, facts = {})
267
        facts['system.certificate_version'] ||= '3.2'  # Required for auto-attach to work
268 8c7ba4af Tom McKay
        facts['network.hostname'] = name
269 04be28e1 Tom McKay
        facts['cpu.core(s)_per_socket'] = line[CORES] if !line[CORES].nil? && !line[CORES].empty?
270
        facts['cpu.cpu_socket(s)'] = line[SOCKETS] if !line[SOCKETS].nil? && !line[SOCKETS].empty?
271
        facts['memory.memtotal'] = line[RAM] if !line[RAM].nil? && !line[RAM].empty?
272
        facts['uname.machine'] = line[ARCHITECTURE] if !line[ARCHITECTURE].nil? && !line[ARCHITECTURE].empty?
273
        (facts['distribution.name'], facts['distribution.version']) = os_name_version(line[OPERATINGSYSTEM]) if !line[OPERATINGSYSTEM].nil? && !line[OPERATINGSYSTEM].empty?
274
        if !line[VIRTUAL].nil? && !line[VIRTUAL].empty?
275
          facts['virt.is_guest'] = line[VIRTUAL] == 'Yes' ? true : false
276
          facts['virt.uuid'] = "#{line[ORGANIZATION]}/#{name}" if facts['virt.uuid'].nil? && facts['virt.is_guest']
277
        end
278
        facts['cpu.cpu(s)'] ||= 1
279 7bea883f Grant Gainey
        facts
280 9d9e4494 Tom McKay
      end
281
282 04be28e1 Tom McKay
      def update_facts(host, line)
283
        return if host['subscription_facet_attributes'].nil?
284
285
        url = "#{@server}/rhsm/consumers/#{host['subscription_facet_attributes']['uuid']}"
286
        uri = URI(url)
287
        nethttp = Net::HTTP.new(uri.host, uri.port)
288
        nethttp.use_ssl = uri.scheme == 'https'
289
        nethttp.verify_mode = OpenSSL::SSL::VERIFY_NONE
290
        nethttp.start do |http|
291
          request = Net::HTTP::Get.new(uri.request_uri)
292
          request = authenticate_request(request)
293
          response = http.request(request)
294
          results = JSON.parse(response.body)
295
296
          facts = facts(host['name'], line, results['facts'])
297
298
          request = Net::HTTP::Put.new(uri.request_uri, {'Content-Type' => 'application/json'})
299
          request = authenticate_request(request)
300
          request.body = {:facts => facts}.to_json
301
          response = http.request(request)
302
          JSON.parse(response.body)
303
        end
304
305
      end
306
307 5da9eed3 Tom McKay
      def update_host_collections(host, line)
308 c06f1783 Tom McKay
        # TODO: http://projects.theforeman.org/issues/16234
309
        # return nil if line[HOSTCOLLECTIONS].nil? || line[HOSTCOLLECTIONS].empty?
310
        # CSV.parse_line(line[HOSTCOLLECTIONS]).each do |hostcollection_name|
311
        #   @api.resource(:host_collections).call(:add_hosts, {
312
        #       'id' => katello_hostcollection(line[ORGANIZATION], :name => hostcollection_name),
313
        #       'host_ids' => [host['id']]
314
        #   })
315
        # end
316 6bc031bd Tom McKay
      end
317
318 fd57822a Tom McKay
      def os_name_version(operatingsystem)
319
        if operatingsystem.nil?
320
          name = nil
321
          version = nil
322
        elsif operatingsystem.index(' ')
323
          (name, version) = operatingsystem.split(' ')
324
        else
325
          (name, version) = ['RHEL', operatingsystem]
326
        end
327
        [name, version]
328
      end
329
330 7bea883f Grant Gainey
      def products(line)
331 afc8539f Tom McKay
        return if line[PRODUCTS].nil? || line[PRODUCTS].empty?
332 5da9eed3 Tom McKay
        CSV.parse_line(line[PRODUCTS]).collect do |product_details|
333 7bea883f Grant Gainey
          product = {}
334 5da9eed3 Tom McKay
          (product['product_id'], product['product_name']) = product_details.split('|')
335 fd57822a Tom McKay
          product['arch'] = line[ARCHITECTURE]
336
          product['version'] = os_name_version(line[OPERATINGSYSTEM])[1]
337 7bea883f Grant Gainey
          product
338
        end
339 9d9e4494 Tom McKay
      end
340
341 c06f1783 Tom McKay
      def update_subscriptions(host, line, remove_existing)
342 5146ac68 Tom McKay
        existing_subscriptions = @api.resource(:host_subscriptions).call(:index, {
343 5da9eed3 Tom McKay
            'host_id' => host['id']
344 fd57822a Tom McKay
        })['results']
345 c06f1783 Tom McKay
        if remove_existing && existing_subscriptions.length != 0
346
          existing_subscriptions.map! do |existing_subscription|
347
            {:id => existing_subscription['id'], :quantity => existing_subscription['quantity_consumed']}
348
          end
349 5146ac68 Tom McKay
          @api.resource(:host_subscriptions).call(:remove_subscriptions, {
350 5da9eed3 Tom McKay
            'host_id' => host['id'],
351 5146ac68 Tom McKay
            'subscriptions' => existing_subscriptions
352 fd57822a Tom McKay
          })
353 c06f1783 Tom McKay
          existing_subscriptions = []
354
        end
355
356 afc8539f Tom McKay
        if (line[SUBS_SKU].nil? || line[SUBS_SKU].empty?) &&
357
            (line[SUBS_NAME].nil? || line[SUBS_NAME].empty?)
358 c06f1783 Tom McKay
          all_in_one_subscription(host, existing_subscriptions, line)
359
        else
360
          single_subscription(host, existing_subscriptions, line)
361 fd57822a Tom McKay
        end
362 c06f1783 Tom McKay
      end
363
364
      def single_subscription(host, existing_subscriptions, line)
365 afc8539f Tom McKay
        matches = matches_by_sku_and_name([], line[SUBS_SKU], line[SUBS_NAME], existing_subscriptions)
366
        matches = matches_by_hypervisor(matches, line[SUBS_GUESTOF])
367
        unless matches.empty?
368
          print _(" '%{name}' already attached...") % {:name => subscription_name(matches[0])} if option_verbose?
369 c06f1783 Tom McKay
          return
370
        end
371
372 afc8539f Tom McKay
        matches = get_matching_subscriptions(host['organization_id'],
373
            :host => host, :sku => line[SUBS_SKU], :name => line[SUBS_NAME], :type => line[SUBS_TYPE],
374
            :account => line[SUBS_ACCOUNT], :contract => line[SUBS_CONTRACT],
375
            :quantity => line[SUBS_QUANTITY], :hypervisor => line[SUBS_GUESTOF],
376
            :sla => line[SLA])
377 fd57822a Tom McKay
378 c06f1783 Tom McKay
        raise _("No matching subscriptions") if matches.empty?
379
380
        match = matches[0]
381 afc8539f Tom McKay
        print _(" attaching '%{name}'...") % {:name => subscription_name(match)} if option_verbose?
382 c06f1783 Tom McKay
383 a0be43ee Tom McKay
        amount = line[SUBS_QUANTITY]
384
        quantity = (amount.nil? || amount.empty? || amount == 'Automatic') ? 0 : amount.to_i
385
386 c06f1783 Tom McKay
        @api.resource(:host_subscriptions).call(:add_subscriptions, {
387
            'host_id' => host['id'],
388 a0be43ee Tom McKay
            'subscriptions' => [{:id => match['id'], :quantity => quantity}]
389 c06f1783 Tom McKay
        })
390
      end
391
392
      def all_in_one_subscription(host, existing_subscriptions, line)
393 fd57822a Tom McKay
        return if line[SUBSCRIPTIONS].nil? || line[SUBSCRIPTIONS].empty?
394
395 afc8539f Tom McKay
        organization_id = foreman_organization(:name => line[ORGANIZATION])
396
        hypervisor = hypervisor_from_line(line)
397
398 fd57822a Tom McKay
        subscriptions = CSV.parse_line(line[SUBSCRIPTIONS], {:skip_blanks => true}).collect do |details|
399 611959a3 Tom McKay
          (amount, sku, name, contract, account) = split_subscription_details(details)
400 afc8539f Tom McKay
          matches = get_matching_subscriptions(organization_id,
401
              :name => name, :contract => contract, :account => account, :quantity => amount,
402
              :hypervisor => hypervisor, :sla => line[SLA])
403
          raise "No matching subscription" if matches.empty?
404
405 fd57822a Tom McKay
          {
406 afc8539f Tom McKay
            :id => matches[0]['id'],
407 2645da78 Tom McKay
            :quantity => (amount.nil? || amount.empty? || amount == 'Automatic') ? 0 : amount.to_i
408 fd57822a Tom McKay
          }
409 7bea883f Grant Gainey
        end
410 fd57822a Tom McKay
411 5146ac68 Tom McKay
        @api.resource(:host_subscriptions).call(:add_subscriptions, {
412 5da9eed3 Tom McKay
            'host_id' => host['id'],
413 fd57822a Tom McKay
            'subscriptions' => subscriptions
414
        })
415 9d9e4494 Tom McKay
      end
416 5da9eed3 Tom McKay
417
      def update_existing(line)
418
        if !@existing[line[ORGANIZATION]]
419
          @existing[line[ORGANIZATION]] = true
420
          # Fetching all content hosts can be too slow and times so page
421
          # http://projects.theforeman.org/issues/6307
422
          total = @api.resource(:hosts).call(:index, {
423
              'organization_id' => foreman_organization(:name => line[ORGANIZATION]),
424
              'per_page' => 1
425
          })['total'].to_i
426
          (total / 20 + 1).to_i.times do |page|
427
            @api.resource(:hosts).call(:index, {
428
                'organization_id' => foreman_organization(:name => line[ORGANIZATION]),
429
                'page' => page + 1,
430
                'per_page' => 20
431
            })['results'].each do |host|
432
              if host['subscription_facet_attributes']
433
                @existing[host['name']] = host['id']
434
              end
435
            end
436
          end
437
        end
438
      end
439 c06f1783 Tom McKay
440
      def iterate_hosts(csv)
441
        hypervisors = []
442
        hosts = []
443 afc8539f Tom McKay
        @api.resource(:organizations).call(:index, {
444
            'full_results' => true
445
        })['results'].each do |organization|
446 c06f1783 Tom McKay
          next if option_organization && organization['name'] != option_organization
447
448
          @api.resource(:hosts).call(:index, {
449 afc8539f Tom McKay
              'full_results' => true,
450 6498291e Tom McKay
              'search' => option_search,
451 c06f1783 Tom McKay
              'organization_id' => foreman_organization(:name => organization['name'])
452
          })['results'].each do |host|
453
            host = @api.resource(:hosts).call(:show, {
454
                'id' => host['id']
455
            })
456
            host['facts'] ||= {}
457 660e2ae4 Tom McKay
            unless host['subscription_facet_attributes'].nil?
458
              if host['subscription_facet_attributes']['virtual_guests'].empty?
459
                hosts.push(host)
460
              else
461
                hypervisors.push(host)
462
              end
463 c06f1783 Tom McKay
            end
464
          end
465
        end
466
        hypervisors.each do |host|
467
          yield host
468
        end
469
        hosts.each do |host|
470
          yield host
471
        end
472
      end
473
474
      def shared_headers
475 afc8539f Tom McKay
        [NAME, ORGANIZATION, ENVIRONMENT, CONTENTVIEW, HOSTCOLLECTIONS, VIRTUAL, GUESTOF,
476 c06f1783 Tom McKay
         OPERATINGSYSTEM, ARCHITECTURE, SOCKETS, RAM, CORES, SLA, PRODUCTS]
477
      end
478
479
      def shared_columns(host)
480
        name = host['name']
481
        organization_name = host['organization_name']
482
        if host['content_facet_attributes']
483
          environment = host['content_facet_attributes']['lifecycle_environment']['name']
484
          contentview = host['content_facet_attributes']['content_view']['name']
485
          hostcollections = export_column(host['content_facet_attributes'], 'host_collections', 'name')
486
        else
487
          environment = nil
488
          contentview = nil
489
          hostcollections = nil
490
        end
491
        if host['subscription_facet_attributes']
492
          hypervisor_host = host['subscription_facet_attributes']['virtual_host'].nil? ? nil : host['subscription_facet_attributes']['virtual_host']['name']
493
          products = export_column(host['subscription_facet_attributes'], 'installed_products') do |product|
494
            "#{product['productId']}|#{product['productName']}"
495
          end
496
        else
497
          hypervisor_host = nil
498
          products = nil
499
        end
500
        virtual = host['facts']['virt::is_guest'] == 'true' ? 'Yes' : 'No'
501
        operatingsystem = host['facts']['distribution::name'] if host['facts']['distribution::name']
502
        operatingsystem += " #{host['facts']['distribution::version']}" if host['facts']['distribution::version']
503
        architecture = host['facts']['uname::machine']
504
        sockets = host['facts']['cpu::cpu_socket(s)']
505
        ram = host['facts']['memory::memtotal']
506
        cores = host['facts']['cpu::core(s)_per_socket'] || 1
507
        sla = ''
508
509 863472b5 Tom McKay
        @column_values[NAME] = name
510
        @column_values[ORGANIZATION] = organization_name
511
        @column_values[ENVIRONMENT] = environment
512
        @column_values[CONTENTVIEW] = contentview
513
        @column_values[HOSTCOLLECTIONS] = hostcollections
514
        @column_values[VIRTUAL] = virtual
515 afc8539f Tom McKay
        @column_values[GUESTOF] = hypervisor_host
516 863472b5 Tom McKay
        @column_values[OPERATINGSYSTEM] = operatingsystem
517
        @column_values[ARCHITECTURE] = architecture
518
        @column_values[SOCKETS] = sockets
519
        @column_values[RAM] = ram
520
        @column_values[CORES] = cores
521
        @column_values[SLA] = sla
522
        @column_values[PRODUCTS] = products
523
      end
524
525
      def custom_columns(host)
526
        return if @column_definitions.nil?
527
        @column_definitions.each do |definition|
528
          @column_values[definition[:name]] = dig(host, definition[:json])
529
        end
530
      end
531
532
      def dig(host, path)
533
        path.inject(host) do |location, key|
534
          location.respond_to?(:keys) ? location[key] : nil
535
        end
536
      end
537
538
      def all_subscription_column(host)
539
        if host['subscription_facet_attributes'] &&
540
           (@columns.include?(SUBS_NAME) ||
541
            @columns.include?(SUBSCRIPTIONS) ||
542
            @columns.include?(SUBS_TYPE) ||
543
            @columns.include?(SUBS_QUANTITY) ||
544
            @columns.include?(SUBS_SKU) ||
545
            @columns.include?(SUBS_CONTRACT) ||
546
            @columns.include?(SUBS_ACCOUNT) ||
547
            @columns.include?(SUBS_START) ||
548
            @columns.include?(SUBS_END)
549
           )
550
          subscriptions = CSV.generate do |column|
551
            column << @api.resource(:host_subscriptions).call(:index, {
552
                'organization_id' => host['organization_id'],
553
                'host_id' => host['id']
554
            })['results'].collect do |subscription|
555
              "#{subscription['quantity_consumed']}"\
556
              "|#{subscription['product_id']}"\
557
              "|#{subscription['product_name']}"\
558
              "|#{subscription['contract_number']}|#{subscription['account_number']}"
559
            end
560
          end
561
          subscriptions.delete!("\n")
562
        else
563
          subscriptions = nil
564
        end
565
        @column_values[SUBSCRIPTIONS] = subscriptions
566 c06f1783 Tom McKay
      end
567
568 863472b5 Tom McKay
      def columns_to_csv(csv)
569
        if @first_columns_to_csv.nil?
570
          @columns.each do |column|
571
            # rubocop:disable LineLength
572 afc8539f Tom McKay
            if option_export? && !@column_values.key?(column)
573
              $stderr.puts  _("Warning: Column '%{name}' does not match any field, be sure to check spelling. A full list of supported columns are available with 'hammer csv content-hosts --help'") % {:name => column}
574
            end
575 863472b5 Tom McKay
            # rubocop:enable LineLength
576
          end
577
          @first_columns_to_csv = true
578
        end
579
        csv << @columns.collect do |column|
580
          @column_values[column]
581
        end
582
      end
583 afc8539f Tom McKay
584
      def hypervisor_from_line(line)
585
        hypervisor = line[HOST] if !line[HOST].nil? && !line[HOST].empty?
586
        hypervisor ||= line[GUESTOF] if !line[GUESTOF].nil? && !line[GUESTOF].empty?
587
        hypervisor
588
      end
589 9d9e4494 Tom McKay
    end
590 a1aa846b Tom McKay
  end
591
end