Project

General

Profile

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

hammer-cli-csv / lib / hammer_cli_csv / content_hosts.rb @ 4a6b2238

1
module HammerCLICsv
2
  class CsvCommand
3
    class ContentHostsCommand < BaseCommand
4
      include ::HammerCLIForemanTasks::Helper
5
      include ::HammerCLICsv::Utils::Subscriptions
6

    
7
      command_name 'content-hosts'
8
      desc         'import or export content hosts'
9

    
10
      def self.supported?
11
        true
12
      end
13

    
14
      option %w(--itemized-subscriptions), :flag, _('Export one subscription per row, only process update subscriptions on import')
15
      option %w(--columns), 'COLUMN_NAMES', _('Comma separated list of column names to export')
16

    
17
      SEARCH = 'Search'
18
      ORGANIZATION = 'Organization'
19
      ENVIRONMENT = 'Environment'
20
      CONTENTVIEW = 'Content View'
21
      HOSTCOLLECTIONS = 'Host Collections'
22
      VIRTUAL = 'Virtual'
23
      HOST = 'Host'  # deprecated for GUESTOF
24
      GUESTOF = 'Guest of Host'
25
      OPERATINGSYSTEM = 'OS'
26
      ARCHITECTURE = 'Arch'
27
      SOCKETS = 'Sockets'
28
      RAM = 'RAM'
29
      CORES = 'Cores'
30
      SLA = 'SLA'
31
      PRODUCTS = 'Products'
32

    
33
      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
         _(" %{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
         _(" %{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
                                           SUBS_CONTRACT, SUBS_ACCOUNT, SUBS_START, SUBS_END, SUBS_GUESTOF]
73
            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
      def export(csv)
89
        if option_itemized_subscriptions?
90
          export_itemized_subscriptions csv
91
        else
92
          export_all csv
93
        end
94
      end
95

    
96
      def export_itemized_subscriptions(csv)
97
        csv << column_headers
98
        iterate_hosts(csv) do |host|
99
          shared_columns(host)
100
          custom_columns(host)
101
          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
                'full_results' => true
106
            })['results']
107
            if subscriptions.empty?
108
              %W(#{SUBS_NAME} #{SUBS_TYPE} #{SUBS_QUANTITY} #{SUBS_SKU} #{SUBS_CONTRACT}
109
                 #{SUBS_ACCOUNT} #{SUBS_START} #{SUBS_END} #{SUBS_GUESTOF}).each do |entry|
110
                @column_values[entry] = nil
111
              end
112
              columns_to_csv(csv)
113
            else
114
              subscriptions.each do |subscription|
115
                %W(#{SUBS_NAME} #{SUBS_TYPE} #{SUBS_QUANTITY} #{SUBS_SKU} #{SUBS_CONTRACT}
116
                   #{SUBS_ACCOUNT} #{SUBS_START} #{SUBS_END} #{SUBS_GUESTOF}).each do |entry|
117
                  @column_values[entry] = nil
118
                end
119
                @column_values[SUBS_GUESTOF] = subscription['host']['name'] if subscription['host']
120
                subscription_type = subscription['product_id'].to_i == 0 ? 'Red Hat' : 'Custom'
121
                subscription_type += ' Guest' unless @column_values[SUBS_GUESTOF].nil?
122
                subscription_type += ' Temporary' if subscription['type'] == 'UNMAPPED_GUEST'
123
                @column_values[SUBS_NAME] = subscription['product_name']
124
                @column_values[SUBS_TYPE] = subscription_type
125
                @column_values[SUBS_QUANTITY] = subscription['quantity_consumed']
126
                @column_values[SUBS_SKU] = subscription['product_id']
127
                @column_values[SUBS_CONTRACT] = subscription['contract_number']
128
                @column_values[SUBS_ACCOUNT] = subscription['account_number']
129
                @column_values[SUBS_START] = DateTime.parse(subscription['start_date']).strftime('%m/%d/%Y')
130
                @column_values[SUBS_END] = DateTime.parse(subscription['end_date']).strftime('%m/%d/%Y')
131
                columns_to_csv(csv)
132
              end
133
            end
134
          else
135
            columns_to_csv(csv)
136
          end
137
        end
138
      end
139

    
140
      def export_all(csv)
141
        csv << column_headers
142
        iterate_hosts(csv) do |host|
143
          all_subscription_column(host)
144
          shared_columns(host)
145
          custom_columns(host)
146
          columns_to_csv(csv)
147
        end
148
      end
149

    
150
      def import
151
        raise _("--columns option only relevant with --export") unless option_columns.nil?
152

    
153
        @existing = {}
154
        @hypervisor_guests = {}
155
        @all_subscriptions = {}
156

    
157
        thread_import do |line|
158
          create_from_csv(line)
159
        end
160

    
161
        if !@hypervisor_guests.empty?
162
          print(_('Updating hypervisor and guest associations...')) if option_verbose?
163
          @hypervisor_guests.each do |host_id, guest_ids|
164
            @api.resource(:hosts).call(:update, {
165
              'id' => host_id,
166
              'host' => {
167
                'subscription_facet_attributes' => {
168
                  'autoheal' => false,
169
                  'hypervisor_guest_uuids' => guest_ids
170
                }
171
              }
172
            })
173
          end
174
          puts _('done') if option_verbose?
175
        end
176
      end
177

    
178
      def create_from_csv(line)
179
        return if option_organization && line[ORGANIZATION] != option_organization
180

    
181
        update_existing(line)
182

    
183
        count(line[COUNT]).times do |number|
184
          if  !line[SEARCH].nil? && !line[SEARCH].empty?
185
            search = namify(line[SEARCH], number)
186
            @api.resource(:hosts).call(:index, {
187
                'organization_id' => foreman_organization(:name => line[ORGANIZATION]),
188
                'search' => search,
189
                'per_page' => 999999
190
            })['results'].each do |host|
191
              if host['subscription_facet_attributes']
192
                create_named_from_csv(host['name'], line)
193
              end
194
            end
195
          else
196
            name = namify(line[NAME], number)
197
            create_named_from_csv(name, line)
198
          end
199
        end
200
      end
201

    
202
      private
203

    
204
      def create_named_from_csv(name, line)
205
        if option_itemized_subscriptions?
206
          update_itemized_subscriptions(name, line)
207
        else
208
          update_or_create(name, line)
209
        end
210
      end
211

    
212
      def update_itemized_subscriptions(name, line)
213
        raise _("Content host '%{name}' must already exist with --itemized-subscriptions") % {:name => name} unless @existing.include? name
214

    
215
        print(_("Updating subscriptions for content host '%{name}'...") % {:name => name}) if option_verbose?
216
        host = @api.resource(:hosts).call(:show, {:id => @existing[name]})
217
        update_subscriptions(host, line, false)
218
        puts _('done') if option_verbose?
219
      end
220

    
221
      def update_or_create(name, line)
222
        lifecycle_environment_id = lifecycle_environment(line[ORGANIZATION], :name => line[ENVIRONMENT])
223
        content_view_id = katello_contentview(line[ORGANIZATION], :name => line[CONTENTVIEW])
224
        if !@existing.include? name
225
          print(_("Creating content host '%{name}'...") % {:name => name}) if option_verbose?
226
          params = {
227
            'name' => name,
228
            'facts' => facts(name, line),
229
            'lifecycle_environment_id' => lifecycle_environment_id,
230
            'content_view_id' => content_view_id
231
          }
232
          params['installed_products'] = products(line) if line[PRODUCTS]
233
          params['service_level'] = line[SLA] if line[SLA]
234
          host = @api.resource(:host_subscriptions).call(:create, params)
235
          @existing[name] = host['id']
236
        else
237
          print(_("Updating content host '%{name}'...") % {:name => name}) if option_verbose?
238
          params = {
239
            'id' => @existing[name],
240
            'host' => {
241
              'content_facet_attributes' => {
242
                'lifecycle_environment_id' => lifecycle_environment_id,
243
                'content_view_id' => content_view_id
244
              },
245
              'subscription_facet_attributes' => {
246
                'installed_products' => products(line),
247
                'service_level' => line[SLA]
248
              }
249
            }
250
          }
251
          host = @api.resource(:hosts).call(:update, params)
252
        end
253

    
254
        hypervisor = hypervisor_from_line(line)
255
        if line[VIRTUAL] == 'Yes' && hypervisor
256
          raise "Content host '#{hypervisor}' not found" if !@existing[hypervisor]
257
          @hypervisor_guests[@existing[hypervisor]] ||= []
258
          @hypervisor_guests[@existing[hypervisor]] << "#{line[ORGANIZATION]}/#{name}"
259
        end
260

    
261
        update_facts(host, line)
262
        update_host_collections(host, line)
263
        update_subscriptions(host, line, true)
264

    
265
        puts _('done') if option_verbose?
266
      end
267

    
268
      def facts(name, line, facts = {})
269
        facts['system.certificate_version'] ||= '3.2'  # Required for auto-attach to work
270
        facts['network.hostname'] = name
271
        facts['cpu.core(s)_per_socket'] = line[CORES] if !line[CORES].nil? && !line[CORES].empty?
272
        facts['cpu.cpu_socket(s)'] = line[SOCKETS] if !line[SOCKETS].nil? && !line[SOCKETS].empty?
273
        facts['memory.memtotal'] = line[RAM] if !line[RAM].nil? && !line[RAM].empty?
274
        facts['uname.machine'] = line[ARCHITECTURE] if !line[ARCHITECTURE].nil? && !line[ARCHITECTURE].empty?
275
        (facts['distribution.name'], facts['distribution.version']) = os_name_version(line[OPERATINGSYSTEM]) if !line[OPERATINGSYSTEM].nil? && !line[OPERATINGSYSTEM].empty?
276
        if !line[VIRTUAL].nil? && !line[VIRTUAL].empty?
277
          facts['virt.is_guest'] = line[VIRTUAL] == 'Yes' ? true : false
278
          facts['virt.uuid'] = "#{line[ORGANIZATION]}/#{name}" if facts['virt.uuid'].nil? && facts['virt.is_guest']
279
        end
280
        facts['cpu.cpu(s)'] ||= 1
281
        facts
282
      end
283

    
284
      def update_facts(host, line)
285
        return if host['subscription_facet_attributes'].nil?
286

    
287
        url = "#{@server}/rhsm/consumers/#{host['subscription_facet_attributes']['uuid']}"
288
        uri = URI(url)
289
        nethttp = Net::HTTP.new(uri.host, uri.port)
290
        nethttp.use_ssl = uri.scheme == 'https'
291
        nethttp.verify_mode = OpenSSL::SSL::VERIFY_NONE
292
        nethttp.start do |http|
293
          request = Net::HTTP::Get.new(uri.request_uri)
294
          request = authenticate_request(request)
295
          response = http.request(request)
296
          results = JSON.parse(response.body)
297

    
298
          facts = facts(host['name'], line, results['facts'])
299

    
300
          request = Net::HTTP::Put.new(uri.request_uri, {'Content-Type' => 'application/json'})
301
          request = authenticate_request(request)
302
          request.body = {:facts => facts}.to_json
303
          response = http.request(request)
304
          JSON.parse(response.body)
305
        end
306

    
307
      end
308

    
309
      def update_host_collections(host, line)
310
        # TODO: http://projects.theforeman.org/issues/16234
311
        # return nil if line[HOSTCOLLECTIONS].nil? || line[HOSTCOLLECTIONS].empty?
312
        # CSV.parse_line(line[HOSTCOLLECTIONS]).each do |hostcollection_name|
313
        #   @api.resource(:host_collections).call(:add_hosts, {
314
        #       'id' => katello_hostcollection(line[ORGANIZATION], :name => hostcollection_name),
315
        #       'host_ids' => [host['id']]
316
        #   })
317
        # end
318
      end
319

    
320
      def os_name_version(operatingsystem)
321
        if operatingsystem.nil?
322
          name = nil
323
          version = nil
324
        elsif operatingsystem.index(' ')
325
          (name, version) = operatingsystem.split(' ')
326
        else
327
          (name, version) = ['RHEL', operatingsystem]
328
        end
329
        [name, version]
330
      end
331

    
332
      def products(line)
333
        return if line[PRODUCTS].nil? || line[PRODUCTS].empty?
334
        CSV.parse_line(line[PRODUCTS]).collect do |product_details|
335
          product = {}
336
          (product['product_id'], product['product_name']) = product_details.split('|')
337
          product['arch'] = line[ARCHITECTURE]
338
          product['version'] = os_name_version(line[OPERATINGSYSTEM])[1]
339
          product
340
        end
341
      end
342

    
343
      def update_subscriptions(host, line, remove_existing)
344
        existing_subscriptions = @api.resource(:host_subscriptions).call(:index, {
345
            'host_id' => host['id'],
346
            'full_results' => true
347
        })['results']
348
        if remove_existing && existing_subscriptions.length != 0
349
          existing_subscriptions.map! do |existing_subscription|
350
            {:id => existing_subscription['id'], :quantity => existing_subscription['quantity_consumed']}
351
          end
352
          @api.resource(:host_subscriptions).call(:remove_subscriptions, {
353
            'host_id' => host['id'],
354
            'subscriptions' => existing_subscriptions
355
          })
356
          existing_subscriptions = []
357
        end
358

    
359
        if (line[SUBS_SKU].nil? || line[SUBS_SKU].empty?) &&
360
            (line[SUBS_NAME].nil? || line[SUBS_NAME].empty?)
361
          all_in_one_subscription(host, existing_subscriptions, line)
362
        else
363
          single_subscription(host, existing_subscriptions, line)
364
        end
365
      end
366

    
367
      def single_subscription(host, existing_subscriptions, line)
368
        matches = matches_by_sku_and_name([], line[SUBS_SKU], line[SUBS_NAME], existing_subscriptions)
369
        matches = matches_by_hypervisor(matches, line[SUBS_GUESTOF])
370
        unless matches.empty?
371
          print _(" '%{name}' already attached...") % {:name => subscription_name(matches[0])} if option_verbose?
372
          return
373
        end
374

    
375
        matches = get_matching_subscriptions(host['organization_id'],
376
            :host => host, :sku => line[SUBS_SKU], :name => line[SUBS_NAME], :type => line[SUBS_TYPE],
377
            :account => line[SUBS_ACCOUNT], :contract => line[SUBS_CONTRACT],
378
            :quantity => line[SUBS_QUANTITY], :hypervisor => line[SUBS_GUESTOF],
379
            :sla => line[SLA])
380

    
381
        raise _("No matching subscriptions") if matches.empty?
382

    
383
        match = matches[0]
384
        print _(" attaching '%{name}'...") % {:name => subscription_name(match)} if option_verbose?
385

    
386
        amount = line[SUBS_QUANTITY]
387
        quantity = (amount.nil? || amount.empty? || amount == 'Automatic') ? 0 : amount.to_i
388

    
389
        @api.resource(:host_subscriptions).call(:add_subscriptions, {
390
            'host_id' => host['id'],
391
            'subscriptions' => [{:id => match['id'], :quantity => quantity}]
392
        })
393
      end
394

    
395
      def all_in_one_subscription(host, existing_subscriptions, line)
396
        return if line[SUBSCRIPTIONS].nil? || line[SUBSCRIPTIONS].empty?
397

    
398
        organization_id = foreman_organization(:name => line[ORGANIZATION])
399
        hypervisor = hypervisor_from_line(line)
400

    
401
        subscriptions = CSV.parse_line(line[SUBSCRIPTIONS], {:skip_blanks => true}).collect do |details|
402
          (amount, sku, name, contract, account) = split_subscription_details(details)
403
          matches = get_matching_subscriptions(organization_id,
404
              :name => name, :contract => contract, :account => account, :quantity => amount,
405
              :hypervisor => hypervisor, :sla => line[SLA])
406
          raise "No matching subscription" if matches.empty?
407

    
408
          {
409
            :id => matches[0]['id'],
410
            :quantity => (amount.nil? || amount.empty? || amount == 'Automatic') ? 0 : amount.to_i
411
          }
412
        end
413

    
414
        @api.resource(:host_subscriptions).call(:add_subscriptions, {
415
            'host_id' => host['id'],
416
            'subscriptions' => subscriptions
417
        })
418
      end
419

    
420
      def update_existing(line)
421
        if !@existing[line[ORGANIZATION]]
422
          @existing[line[ORGANIZATION]] = true
423
          # Fetching all content hosts can be too slow and times so page
424
          # http://projects.theforeman.org/issues/6307
425
          total = @api.resource(:hosts).call(:index, {
426
              'organization_id' => foreman_organization(:name => line[ORGANIZATION]),
427
              'per_page' => 1
428
          })['total'].to_i
429
          (total / 20 + 1).to_i.times do |page|
430
            @api.resource(:hosts).call(:index, {
431
                'organization_id' => foreman_organization(:name => line[ORGANIZATION]),
432
                'page' => page + 1,
433
                'per_page' => 20
434
            })['results'].each do |host|
435
              if host['subscription_facet_attributes']
436
                @existing[host['name']] = host['id']
437
              end
438
            end
439
          end
440
        end
441
      end
442

    
443
      def iterate_hosts(csv)
444
        hypervisors = []
445
        hosts = []
446
        @api.resource(:organizations).call(:index, {
447
            'full_results' => true
448
        })['results'].each do |organization|
449
          next if option_organization && organization['name'] != option_organization
450

    
451
          total = @api.resource(:hosts).call(:index, {
452
              'organization_id' => foreman_organization(:name => organization['name']),
453
              'search' => option_search,
454
              'per_page' => 1
455
          })['total'].to_i
456
          (total / 20 + 1).to_i.times do |page|
457
            @api.resource(:hosts).call(:index, {
458
                'page' => page + 1,
459
                'per_page' => 20,
460
                'search' => option_search,
461
                'organization_id' => foreman_organization(:name => organization['name'])
462
            })['results'].each do |host|
463
              host = @api.resource(:hosts).call(:show, {
464
                  'id' => host['id']
465
              })
466
              host['facts'] ||= {}
467
              unless host['subscription_facet_attributes'].nil?
468
                if host['subscription_facet_attributes']['virtual_guests'].empty?
469
                  hosts.push(host)
470
                else
471
                  hypervisors.push(host)
472
                end
473
              end
474
            end
475
          end
476
        end
477
        hypervisors.each do |host|
478
          yield host
479
        end
480
        hosts.each do |host|
481
          yield host
482
        end
483
      end
484

    
485
      def shared_headers
486
        [NAME, ORGANIZATION, ENVIRONMENT, CONTENTVIEW, HOSTCOLLECTIONS, VIRTUAL, GUESTOF,
487
         OPERATINGSYSTEM, ARCHITECTURE, SOCKETS, RAM, CORES, SLA, PRODUCTS]
488
      end
489

    
490
      def shared_columns(host)
491
        name = host['name']
492
        organization_name = host['organization_name']
493
        if host['content_facet_attributes']
494
          environment = host['content_facet_attributes']['lifecycle_environment']['name']
495
          contentview = host['content_facet_attributes']['content_view']['name']
496
          hostcollections = export_column(host['content_facet_attributes'], 'host_collections', 'name')
497
        else
498
          environment = nil
499
          contentview = nil
500
          hostcollections = nil
501
        end
502
        if host['subscription_facet_attributes']
503
          hypervisor_host = host['subscription_facet_attributes']['virtual_host'].nil? ? nil : host['subscription_facet_attributes']['virtual_host']['name']
504
          products = export_column(host['subscription_facet_attributes'], 'installed_products') do |product|
505
            "#{product['productId']}|#{product['productName']}"
506
          end
507
        else
508
          hypervisor_host = nil
509
          products = nil
510
        end
511
        virtual = host['facts']['virt::is_guest'] == 'true' ? 'Yes' : 'No'
512
        operatingsystem = host['facts']['distribution::name'] if host['facts']['distribution::name']
513
        operatingsystem += " #{host['facts']['distribution::version']}" if host['facts']['distribution::version']
514
        architecture = host['facts']['uname::machine']
515
        sockets = host['facts']['cpu::cpu_socket(s)']
516
        ram = host['facts']['memory::memtotal']
517
        cores = host['facts']['cpu::core(s)_per_socket'] || 1
518
        sla = ''
519

    
520
        @column_values[NAME] = name
521
        @column_values[ORGANIZATION] = organization_name
522
        @column_values[ENVIRONMENT] = environment
523
        @column_values[CONTENTVIEW] = contentview
524
        @column_values[HOSTCOLLECTIONS] = hostcollections
525
        @column_values[VIRTUAL] = virtual
526
        @column_values[GUESTOF] = hypervisor_host
527
        @column_values[OPERATINGSYSTEM] = operatingsystem
528
        @column_values[ARCHITECTURE] = architecture
529
        @column_values[SOCKETS] = sockets
530
        @column_values[RAM] = ram
531
        @column_values[CORES] = cores
532
        @column_values[SLA] = sla
533
        @column_values[PRODUCTS] = products
534
      end
535

    
536
      def custom_columns(host)
537
        return if @column_definitions.nil?
538
        @column_definitions.each do |definition|
539
          @column_values[definition[:name]] = dig(host, definition[:json])
540
        end
541
      end
542

    
543
      def dig(host, path)
544
        path.inject(host) do |location, key|
545
          location.respond_to?(:keys) ? location[key] : nil
546
        end
547
      end
548

    
549
      def all_subscription_column(host)
550
        if host['subscription_facet_attributes'] &&
551
           (@columns.include?(SUBS_NAME) ||
552
            @columns.include?(SUBSCRIPTIONS) ||
553
            @columns.include?(SUBS_TYPE) ||
554
            @columns.include?(SUBS_QUANTITY) ||
555
            @columns.include?(SUBS_SKU) ||
556
            @columns.include?(SUBS_CONTRACT) ||
557
            @columns.include?(SUBS_ACCOUNT) ||
558
            @columns.include?(SUBS_START) ||
559
            @columns.include?(SUBS_END)
560
           )
561
          subscriptions = CSV.generate do |column|
562
            column << @api.resource(:host_subscriptions).call(:index, {
563
                'organization_id' => host['organization_id'],
564
                'host_id' => host['id']
565
            })['results'].collect do |subscription|
566
              "#{subscription['quantity_consumed']}"\
567
              "|#{subscription['product_id']}"\
568
              "|#{subscription['product_name']}"\
569
              "|#{subscription['contract_number']}|#{subscription['account_number']}"
570
            end
571
          end
572
          subscriptions.delete!("\n")
573
        else
574
          subscriptions = nil
575
        end
576
        @column_values[SUBSCRIPTIONS] = subscriptions
577
      end
578

    
579
      def columns_to_csv(csv)
580
        if @first_columns_to_csv.nil?
581
          @columns.each do |column|
582
            # rubocop:disable LineLength
583
            if option_export? && !@column_values.key?(column)
584
              $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}
585
            end
586
            # rubocop:enable LineLength
587
          end
588
          @first_columns_to_csv = true
589
        end
590
        csv << @columns.collect do |column|
591
          @column_values[column]
592
        end
593
      end
594

    
595
      def hypervisor_from_line(line)
596
        hypervisor = line[HOST] if !line[HOST].nil? && !line[HOST].empty?
597
        hypervisor ||= line[GUESTOF] if !line[GUESTOF].nil? && !line[GUESTOF].empty?
598
        hypervisor
599
      end
600
    end
601
  end
602
end