Project

General

Profile

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

hammer-cli-csv / lib / hammer_cli_csv / content_hosts.rb @ f92e695a

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(--clear-subscriptions), :flag, _('When processing --itemized-subscriptions, clear existing subscriptions first')
16
      option %w(--columns), 'COLUMN_NAMES', _('Comma separated list of column names to export')
17

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

    
34
      def self.help_columns
35
        ['', _('Columns:'),
36
         _(" %{name} - Name of resource") % {:name => NAME},
37
         _(" %{name} - Search for matching names during import (overrides '%{name_col}' column)") % {:name => SEARCH, :name_col => NAME},
38
         _(" %{name} - Organization name") % {:name => ORGANIZATION},
39
         _(" %{name} - Lifecycle environment name") % {:name => ENVIRONMENT},
40
         _(" %{name} - Content view name") % {:name => CONTENTVIEW},
41
         _(" %{name} - Comma separated list of host collection names") % {:name => HOSTCOLLECTIONS},
42
         _(" %{name} - Is a virtual host, %{yes} or %{no}") % {:name => VIRTUAL, :yes => 'Yes', :no => 'No'},
43
         _(" %{name} - Hypervisor host name for virtual hosts") % {:name => GUESTOF},
44
         _(" %{name} - Operating system") % {:name => OPERATINGSYSTEM},
45
         _(" %{name} - Architecture") % {:name => ARCHITECTURE},
46
         _(" %{name} - Number of sockets") % {:name => SOCKETS},
47
         _(" %{name} - Quantity of RAM in bytes") % {:name => RAM},
48
         _(" %{name} - Number of cores") % {:name => CORES},
49
         _(" %{name} - Service Level Agreement value") % {:name => SLA},
50
         _(" %{name} - Comma separated list of products, each of the format \"<sku>|<name>\"") % {:name => PRODUCTS},
51
         _(" %{name} - Comma separated list of subscriptions, each of the format \"<quantity>|<sku>|<name>|<contract>|<account>\"") % {:name => SUBSCRIPTIONS},
52
         _(" %{name} - Subscription name (only applicable for --itemized-subscriptions)") % {:name => SUBS_NAME},
53
         _(" %{name} - Subscription type (only applicable for --itemized-subscriptions)") % {:name => SUBS_TYPE},
54
         _(" %{name} - Subscription quantity (only applicable for --itemized-subscriptions)") % {:name => SUBS_QUANTITY},
55
         _(" %{name} - Subscription SKU (only applicable for --itemized-subscriptions)") % {:name => SUBS_SKU},
56
         _(" %{name} - Subscription contract number (only applicable for --itemized-subscriptions)") % {:name => SUBS_CONTRACT},
57
         _(" %{name} - Subscription account number (only applicable for --itemized-subscriptions)") % {:name => SUBS_ACCOUNT},
58
         _(" %{name} - Subscription start date (only applicable for --itemized-subscriptions)") % {:name => SUBS_START},
59
         _(" %{name} - Subscription end date (only applicable for --itemized-subscriptions)") % {:name => SUBS_END}
60
        ].join("\n")
61
      end
62

    
63
      def column_headers
64
        @column_values = {}
65
        if option_columns.nil?
66
          if ::HammerCLI::Settings.settings[:csv][:columns] &&
67
              ::HammerCLI::Settings.settings[:csv][:columns]['content-hosts'.to_sym] &&
68
              ::HammerCLI::Settings.settings[:csv][:columns]['content-hosts'.to_sym][:export]
69
            @columns = ::HammerCLI::Settings.settings[:csv][:columns]['content-hosts'.to_sym][:export]
70
          else
71
            if option_itemized_subscriptions?
72
              @columns = shared_headers + [SUBS_NAME, SUBS_TYPE, SUBS_QUANTITY, SUBS_SKU,
73
                                           SUBS_CONTRACT, SUBS_ACCOUNT, SUBS_START, SUBS_END, SUBS_GUESTOF]
74
            else
75
              @columns = shared_headers + [SUBSCRIPTIONS]
76
            end
77
          end
78
        else
79
          @columns = option_columns.split(',')
80
        end
81

    
82
        if ::HammerCLI::Settings.settings[:csv][:columns] && ::HammerCLI::Settings.settings[:csv][:columns]['content-hosts'.to_sym][:define]
83
          @column_definitions = ::HammerCLI::Settings.settings[:csv][:columns]['content-hosts'.to_sym][:define]
84
        end
85

    
86
        @columns
87
      end
88

    
89
      def export(csv)
90
        raise _("--clear-subscriptions option only relevant during import") if option_clear_subscriptions?
91

    
92
        if option_itemized_subscriptions?
93
          export_itemized_subscriptions csv
94
        else
95
          export_all csv
96
        end
97
      end
98

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

    
143
      def export_all(csv)
144
        csv << column_headers
145
        iterate_hosts(csv) do |host|
146
          all_subscription_column(host)
147
          shared_columns(host)
148
          custom_columns(host)
149
          columns_to_csv(csv)
150
        end
151
      end
152

    
153
      def import
154
        raise _("--columns option only relevant with --export") unless option_columns.nil?
155

    
156
        @existing = {}
157
        @visited = {}
158
        @hypervisor_guests = {}
159
        @all_subscriptions = {}
160

    
161
        thread_import do |line|
162
          create_from_csv(line)
163
        end
164

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

    
182
      def create_from_csv(line)
183
        return if option_organization && line[ORGANIZATION] != option_organization
184

    
185
        update_existing(line)
186

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

    
206
      private
207

    
208
      def create_named_from_csv(name, line)
209
        if option_itemized_subscriptions?
210
          update_itemized_subscriptions(name, line)
211
        else
212
          update_or_create(name, line)
213
        end
214
      end
215

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

    
219
        print(_("Updating subscriptions for content host '%{name}'...") % {:name => name}) if option_verbose?
220
        host = @api.resource(:hosts).call(:show, {:id => @existing[name]})
221
        remove_existing = clear_subscriptions?(host['name'])
222
        update_subscriptions(host, line, remove_existing)
223
        puts _('done') if option_verbose?
224
      end
225

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

    
260
        hypervisor = hypervisor_from_line(line)
261
        if line[VIRTUAL] == 'Yes' && hypervisor
262
          raise "Content host '#{hypervisor}' not found" if !@existing[hypervisor]
263
          @hypervisor_guests[@existing[hypervisor]] ||= []
264
          @hypervisor_guests[@existing[hypervisor]] << "#{line[ORGANIZATION]}/#{name}"
265
        end
266

    
267
        update_facts(host, line)
268
        update_host_collections(host, line)
269
        update_subscriptions(host, line, true)
270

    
271
        puts _('done') if option_verbose?
272
      end
273

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

    
290
      def update_facts(host, line)
291
        return if host['subscription_facet_attributes'].nil?
292

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

    
304
          facts = facts(host['name'], line, results['facts'])
305

    
306
          request = Net::HTTP::Put.new(uri.request_uri, {'Content-Type' => 'application/json'})
307
          request = authenticate_request(request)
308
          request.body = {:facts => facts}.to_json
309
          response = http.request(request)
310
          JSON.parse(response.body)
311
        end
312

    
313
      end
314

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

    
326
      def os_name_version(operatingsystem)
327
        if operatingsystem.nil?
328
          name = nil
329
          version = nil
330
        elsif operatingsystem.index(' ')
331
          (name, version) = operatingsystem.split(' ')
332
        else
333
          (name, version) = ['RHEL', operatingsystem]
334
        end
335
        [name, version]
336
      end
337

    
338
      def products(line)
339
        return if line[PRODUCTS].nil? || line[PRODUCTS].empty?
340
        CSV.parse_line(line[PRODUCTS]).collect do |product_details|
341
          product = {}
342
          (product['product_id'], product['product_name']) = product_details.split('|')
343
          product['arch'] = line[ARCHITECTURE]
344
          product['version'] = os_name_version(line[OPERATINGSYSTEM])[1]
345
          product
346
        end
347
      end
348

    
349
      def update_subscriptions(host, line, remove_existing)
350
        existing_subscriptions = @api.resource(:host_subscriptions).call(:index, {
351
            'host_id' => host['id'],
352
            'full_results' => true
353
        })['results']
354
        if remove_existing && existing_subscriptions.length != 0
355
          print _("clearing existing subscriptions...") if option_verbose?
356
          existing_subscriptions.map! do |existing_subscription|
357
            subs = [{:id => existing_subscription['id'], :quantity => existing_subscription['quantity_consumed']}]
358
            @api.resource(:host_subscriptions).call(:remove_subscriptions, {
359
              'host_id' => host['id'],
360
              'subscriptions' => subs
361
            })
362
          end
363
          existing_subscriptions = []
364
        end
365

    
366
        if (line[SUBS_SKU].nil? || line[SUBS_SKU].empty?) &&
367
            (line[SUBS_NAME].nil? || line[SUBS_NAME].empty?)
368
          all_in_one_subscription(host, existing_subscriptions, line)
369
        else
370
          single_subscription(host, existing_subscriptions, line)
371
        end
372
      end
373

    
374
      def single_subscription(host, existing_subscriptions, line)
375
        matches = matches_by_sku_and_name([], line[SUBS_SKU], line[SUBS_NAME], existing_subscriptions)
376
        matches = matches_by_hypervisor(matches, line[SUBS_GUESTOF])
377
        unless matches.empty?
378
          print _(" '%{name}' already attached...") % {:name => subscription_name(matches[0])} if option_verbose?
379
          return
380
        end
381

    
382
        matches = get_matching_subscriptions(host['organization_id'],
383
            :host => host, :sku => line[SUBS_SKU], :name => line[SUBS_NAME], :type => line[SUBS_TYPE],
384
            :account => line[SUBS_ACCOUNT], :contract => line[SUBS_CONTRACT],
385
            :quantity => line[SUBS_QUANTITY], :hypervisor => line[SUBS_GUESTOF],
386
            :sla => line[SLA])
387

    
388
        raise _("No matching subscriptions") if matches.empty?
389

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

    
393
        amount = line[SUBS_QUANTITY]
394
        quantity = (amount.nil? || amount.empty? || amount == 'Automatic') ? 0 : amount.to_i
395

    
396
        @api.resource(:host_subscriptions).call(:add_subscriptions, {
397
            'host_id' => host['id'],
398
            'subscriptions' => [{:id => match['id'], :quantity => quantity}]
399
        })
400
      end
401

    
402
      def all_in_one_subscription(host, existing_subscriptions, line)
403
        return if line[SUBSCRIPTIONS].nil? || line[SUBSCRIPTIONS].empty?
404

    
405
        organization_id = foreman_organization(:name => line[ORGANIZATION])
406
        hypervisor = hypervisor_from_line(line)
407

    
408
        subscriptions = CSV.parse_line(line[SUBSCRIPTIONS], {:skip_blanks => true}).collect do |details|
409
          (amount, sku, name, contract, account) = split_subscription_details(details)
410
          matches = get_matching_subscriptions(organization_id,
411
              :name => name, :contract => contract, :account => account, :quantity => amount,
412
              :hypervisor => hypervisor, :sla => line[SLA])
413
          raise "No matching subscription" if matches.empty?
414

    
415
          {
416
            :id => matches[0]['id'],
417
            :quantity => (amount.nil? || amount.empty? || amount == 'Automatic') ? 0 : amount.to_i
418
          }
419
        end
420

    
421
        @api.resource(:host_subscriptions).call(:add_subscriptions, {
422
            'host_id' => host['id'],
423
            'subscriptions' => subscriptions
424
        })
425
      end
426

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

    
452
      def iterate_hosts(csv)
453
        hypervisors = []
454
        hosts = []
455
        @api.resource(:organizations).call(:index, {
456
            'full_results' => true
457
        })['results'].each do |organization|
458
          next if option_organization && organization['name'] != option_organization
459

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

    
494
      def shared_headers
495
        [NAME, ORGANIZATION, ENVIRONMENT, CONTENTVIEW, HOSTCOLLECTIONS, VIRTUAL, GUESTOF,
496
         OPERATINGSYSTEM, ARCHITECTURE, SOCKETS, RAM, CORES, SLA, PRODUCTS]
497
      end
498

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

    
529
        @column_values[NAME] = name
530
        @column_values[ORGANIZATION] = organization_name
531
        @column_values[ENVIRONMENT] = environment
532
        @column_values[CONTENTVIEW] = contentview
533
        @column_values[HOSTCOLLECTIONS] = hostcollections
534
        @column_values[VIRTUAL] = virtual
535
        @column_values[GUESTOF] = hypervisor_host
536
        @column_values[OPERATINGSYSTEM] = operatingsystem
537
        @column_values[ARCHITECTURE] = architecture
538
        @column_values[SOCKETS] = sockets
539
        @column_values[RAM] = ram
540
        @column_values[CORES] = cores
541
        @column_values[SLA] = sla
542
        @column_values[PRODUCTS] = products
543
      end
544

    
545
      def custom_columns(host)
546
        return if @column_definitions.nil?
547
        @column_definitions.each do |definition|
548
          @column_values[definition[:name]] = dig(host, definition[:json])
549
        end
550
      end
551

    
552
      def dig(host, path)
553
        path.inject(host) do |location, key|
554
          location.respond_to?(:keys) ? location[key] : nil
555
        end
556
      end
557

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

    
588
      def columns_to_csv(csv)
589
        if @first_columns_to_csv.nil?
590
          @columns.each do |column|
591
            # rubocop:disable LineLength
592
            if option_export? && !@column_values.key?(column)
593
              $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}
594
            end
595
            # rubocop:enable LineLength
596
          end
597
          @first_columns_to_csv = true
598
        end
599
        csv << @columns.collect do |column|
600
          @column_values[column]
601
        end
602
      end
603

    
604
      def hypervisor_from_line(line)
605
        hypervisor = line[HOST] if !line[HOST].nil? && !line[HOST].empty?
606
        hypervisor ||= line[GUESTOF] if !line[GUESTOF].nil? && !line[GUESTOF].empty?
607
        hypervisor = namify(hypervisor, 1) if !hypervisor.nil? && !hypervisor.empty?
608
        hypervisor
609
      end
610

    
611
      def clear_subscriptions?(name)
612
        if option_clear_subscriptions? && !@visited.include?(name)
613
          @visited[name] = true
614
          true
615
        else
616
          false
617
        end
618
      end
619
    end
620
  end
621
end