Project

General

Profile

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

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

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
            })['results']
106
            if subscriptions.empty?
107
              %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
              columns_to_csv(csv)
112
            else
113
              subscriptions.each do |subscription|
114
                %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
                subscription_type = subscription['product_id'].to_i == 0 ? 'Red Hat' : 'Custom'
120
                subscription_type += ' Guest' unless @column_values[SUBS_GUESTOF].nil?
121
                subscription_type += ' Temporary' if subscription['type'] == 'UNMAPPED_GUEST'
122
                @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
              end
132
            end
133
          else
134
            columns_to_csv(csv)
135
          end
136
        end
137
      end
138

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

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

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

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

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

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

    
180
        update_existing(line)
181

    
182
        count(line[COUNT]).times do |number|
183
          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
          else
194
            name = namify(line[NAME], number)
195
            create_named_from_csv(name, line)
196
          end
197
        end
198
      end
199

    
200
      private
201

    
202
      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
      def update_itemized_subscriptions(name, line)
211
        raise _("Content host '%{name}' must already exist with --itemized-subscriptions") % {:name => name} unless @existing.include? name
212

    
213
        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
        lifecycle_environment_id = lifecycle_environment(line[ORGANIZATION], :name => line[ENVIRONMENT])
221
        content_view_id = katello_contentview(line[ORGANIZATION], :name => line[CONTENTVIEW])
222
        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
            'lifecycle_environment_id' => lifecycle_environment_id,
228
            'content_view_id' => content_view_id
229
          }
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
                'lifecycle_environment_id' => lifecycle_environment_id,
241
                'content_view_id' => content_view_id
242
              },
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
        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
        end
258

    
259
        update_facts(host, line)
260
        update_host_collections(host, line)
261
        update_subscriptions(host, line, true)
262

    
263
        puts _('done') if option_verbose?
264
      end
265

    
266
      def facts(name, line, facts = {})
267
        facts['system.certificate_version'] ||= '3.2'  # Required for auto-attach to work
268
        facts['network.hostname'] = name
269
        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
        facts
280
      end
281

    
282
      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
      def update_host_collections(host, line)
308
        # 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
      end
317

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

    
341
      def update_subscriptions(host, line, remove_existing)
342
        existing_subscriptions = @api.resource(:host_subscriptions).call(:index, {
343
            'host_id' => host['id']
344
        })['results']
345
        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
          @api.resource(:host_subscriptions).call(:remove_subscriptions, {
350
            'host_id' => host['id'],
351
            'subscriptions' => existing_subscriptions
352
          })
353
          existing_subscriptions = []
354
        end
355

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

    
364
      def single_subscription(host, existing_subscriptions, line)
365
        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
          return
370
        end
371

    
372
        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

    
378
        raise _("No matching subscriptions") if matches.empty?
379

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

    
383
        amount = line[SUBS_QUANTITY]
384
        quantity = (amount.nil? || amount.empty? || amount == 'Automatic') ? 0 : amount.to_i
385

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

    
392
      def all_in_one_subscription(host, existing_subscriptions, line)
393
        return if line[SUBSCRIPTIONS].nil? || line[SUBSCRIPTIONS].empty?
394

    
395
        organization_id = foreman_organization(:name => line[ORGANIZATION])
396
        hypervisor = hypervisor_from_line(line)
397

    
398
        subscriptions = CSV.parse_line(line[SUBSCRIPTIONS], {:skip_blanks => true}).collect do |details|
399
          (amount, sku, name, contract, account) = split_subscription_details(details)
400
          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
          {
406
            :id => matches[0]['id'],
407
            :quantity => (amount.nil? || amount.empty? || amount == 'Automatic') ? 0 : amount.to_i
408
          }
409
        end
410

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

    
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

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

    
448
          @api.resource(:hosts).call(:index, {
449
              'full_results' => true,
450
              'search' => option_search,
451
              '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
            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
            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
        [NAME, ORGANIZATION, ENVIRONMENT, CONTENTVIEW, HOSTCOLLECTIONS, VIRTUAL, GUESTOF,
476
         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
        @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
        @column_values[GUESTOF] = hypervisor_host
516
        @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
      end
567

    
568
      def columns_to_csv(csv)
569
        if @first_columns_to_csv.nil?
570
          @columns.each do |column|
571
            # rubocop:disable LineLength
572
            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
            # 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

    
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
    end
590
  end
591
end