Project

General

Profile

Revision afc8539f

Added by Thomas McKay almost 5 years ago

fixes #17505, #17052 - refine search for subs in act-keys and content-hosts

View differences:

lib/hammer_cli_csv/activation_keys.rb
34 34
        csv << shared_headers + [Utils::Subscriptions::SUBS_NAME, Utils::Subscriptions::SUBS_TYPE,
35 35
                                 Utils::Subscriptions::SUBS_QUANTITY, Utils::Subscriptions::SUBS_SKU,
36 36
                                 Utils::Subscriptions::SUBS_CONTRACT, Utils::Subscriptions::SUBS_ACCOUNT,
37
                                 Utils::Subscriptions::SUBS_START, Utils::Subscriptions::SUBS_END]
37
                                 Utils::Subscriptions::SUBS_START, Utils::Subscriptions::SUBS_END,
38
                                 Utils::Subscriptions::SUBS_GUESTOF]
38 39
        iterate_activationkeys(csv) do |activationkey|
39 40
          columns = shared_columns(activationkey)
40 41
          @api.resource(:subscriptions).call(:index, {
......
42 43
              'activation_key_id' => activationkey['id']
43 44
          })['results'].collect do |subscription|
44 45
            subscription_type = subscription['product_id'].to_i == 0 ? 'Red Hat' : 'Custom'
45
            subscription_type += ' Guest' if subscription['type'] == 'STACK_DERIVED'
46
            hypervisor = subscription['host']['name'] if subscription['host']
47
            subscription_type += ' Guest' unless hypervisor.nil?
46 48
            subscription_type += ' Temporary' if subscription['type'] == 'UNMAPPED_GUEST'
47 49
            amount = (subscription['quantity_attached'].nil? || subscription['quantity_attached'] < 1) ? 'Automatic' : subscription['quantity_attached']
48 50
            csv << columns +
49 51
              [subscription['product_name'], subscription_type, amount,
50 52
               subscription['product_id'], subscription['contract_number'], subscription['account_number'],
51 53
               DateTime.parse(subscription['start_date']),
52
               DateTime.parse(subscription['end_date'])]
54
               DateTime.parse(subscription['end_date']),
55
               hypervisor]
53 56
          end
54 57
        end
55 58
      end
......
191 194
      end
192 195

  
193 196
      def single_subscription(activationkey, existing_subscriptions, line)
194
        already_attached = false
195
        if line[Utils::Subscriptions::SUBS_SKU]
196
          already_attached = existing_subscriptions.detect do |subscription|
197
            line[Utils::Subscriptions::SUBS_SKU] == subscription['product_id']
198
          end
199
        elsif line[Utils::Subscriptions::SUBS_NAME]
200
          already_attached = existing_subscriptions.detect do |subscription|
201
            line[Utils::Subscriptions::SUBS_NAME] == subscription['name']
202
          end
203
        end
204
        if already_attached
205
          print _(" '%{name}' already attached...") % {:name => already_attached['name']}
197
        matches = matches_by_sku_and_name([], line[SUBS_SKU], line[SUBS_NAME], existing_subscriptions)
198
        matches = matches_by_hypervisor(matches, line[SUBS_GUESTOF])
199
        unless matches.empty?
200
          print _(" '%{name}' already attached...") % {:name => subscription_name(matches[0])} if option_verbose?
206 201
          return
207 202
        end
208 203

  
209
        available_subscriptions = @api.resource(:subscriptions).call(:index, {
210
                                                                       'organization_id' => activationkey['organization']['id'],
211
                                                                       'activation_key_id' => activationkey['id'],
212
                                                                       'available_for' => 'activation_key'
213
                                                                     })['results']
214

  
215
        matches = matches_by_sku_and_name([], line, available_subscriptions)
216
        matches = matches_by_type(matches, line)
217
        matches = matches_by_account(matches, line)
218
        matches = matches_by_contract(matches, line)
219
        matches = matches_by_quantity(matches, line)
204
        matches = get_matching_subscriptions(activationkey['organization']['id'],
205
            :activation_key => activationkey, :sku => line[SUBS_SKU], :name => line[SUBS_NAME],
206
            :type => line[SUBS_TYPE], :account => line[SUBS_ACCOUNT],
207
            :contract => line[SUBS_CONTRACT], :hypervisor => hypervisor_from_line(line))
220 208

  
221 209
        raise _("No matching subscriptions") if matches.empty?
222

  
223 210
        match = matches[0]
224

  
225
        match = match_with_quantity_to_attach(match, line)
211
        match = match_with_quantity_to_attach(match, line[SUBS_QUANTITY])
226 212

  
227 213
        if option_verbose?
228 214
          print _(" attaching %{quantity} of '%{name}'...") % {
229
            :name => match['name'], :quantity => match['quantity']
215
            :name => subscription_name(match), :quantity => match['quantity']
230 216
          }
231 217
        end
232 218

  
......
239 225
      def all_in_one_subscription(activationkey, existing_subscriptions, line)
240 226
        return if line[SUBSCRIPTIONS].nil? || line[SUBSCRIPTIONS].empty?
241 227

  
228
        organization_id = foreman_organization(:name => line[ORGANIZATION])
229
        hypervisor = hypervisor_from_line(line)
230

  
242 231
        subscriptions = CSV.parse_line(line[SUBSCRIPTIONS], {:skip_blanks => true}).collect do |details|
243 232
          (amount, sku, name, contract, account) = split_subscription_details(details)
233
          matches = get_matching_subscriptions(organization_id,
234
              :name => name, :contract => contract, :account => account, :quantity => amount,
235
              :hypervisor => hypervisor)
236
          raise _("No matching subscriptions") if matches.empty?
237

  
244 238
          {
245
            :id => get_subscription(line[ORGANIZATION], :name => name),
239
            :id => matches[0]['id'],
246 240
            :quantity => (amount.nil? || amount == 'Automatic') ? 0 : amount.to_i
247 241
          }
248 242
        end
......
288 282
          end
289 283
        end
290 284
      end
285

  
286
      def hypervisor_from_line(line)
287
        hypervisor = line[SUBS_GUESTOF] if !line[SUBS_GUESTOF].nil? && !line[SUBS_GUESTOF].empty?
288
        hypervisor
289
      end
291 290
    end
292 291
  end
293 292
end
lib/hammer_cli_csv/base.rb
146 146
            lines.each do |line|
147 147
              next if !line[name_column || NAME].nil? && line[name_column || NAME][0] == '#'
148 148
              begin
149
                logger.debug(line)
149 150
                yield line
150 151
              rescue RuntimeError => e
151 152
                message = "#{e}\n#{line}"
lib/hammer_cli_csv/content_hosts.rb
20 20
      CONTENTVIEW = 'Content View'
21 21
      HOSTCOLLECTIONS = 'Host Collections'
22 22
      VIRTUAL = 'Virtual'
23
      HOST = 'Host'
23
      HOST = 'Host'  # deprecated for GUESTOF
24
      GUESTOF = 'Guest of Host'
24 25
      OPERATINGSYSTEM = 'OS'
25 26
      ARCHITECTURE = 'Arch'
26 27
      SOCKETS = 'Sockets'
......
37 38
         _(" %{name} - Lifecycle environment name") % {:name => ENVIRONMENT},
38 39
         _(" %{name} - Content view name") % {:name => CONTENTVIEW},
39 40
         _(" %{name} - Comma separated list of host collection names") % {:name => HOSTCOLLECTIONS},
40
         _(" %{name} - Is a virtual host, %{yes} or %{no}") % {:name => NAME, :yes => 'Yes', :no => 'No'},
41
         _(" %{name} - Hypervisor host name for virtual hosts") % {:name => HOST},
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},
42 43
         _(" %{name} - Operating system") % {:name => OPERATINGSYSTEM},
43 44
         _(" %{name} - Architecture") % {:name => ARCHITECTURE},
44 45
         _(" %{name} - Number of sockets") % {:name => SOCKETS},
......
68 69
          else
69 70
            if option_itemized_subscriptions?
70 71
              @columns = shared_headers + [SUBS_NAME, SUBS_TYPE, SUBS_QUANTITY, SUBS_SKU,
71
                                           SUBS_CONTRACT, SUBS_ACCOUNT, SUBS_START, SUBS_END]
72
                                           SUBS_CONTRACT, SUBS_ACCOUNT, SUBS_START, SUBS_END, SUBS_GUESTOF]
72 73
            else
73 74
              @columns = shared_headers + [SUBSCRIPTIONS]
74 75
            end
......
97 98
        iterate_hosts(csv) do |host|
98 99
          shared_columns(host)
99 100
          custom_columns(host)
100
          @column_values[SUBS_NAME] = nil
101
          @column_values[SUBS_TYPE] = nil
102
          @column_values[SUBS_QUANTITY] = nil
103
          @column_values[SUBS_SKU] = nil
104
          @column_values[SUBS_CONTRACT] = nil
105
          @column_values[SUBS_ACCOUNT] = nil
106
          @column_values[SUBS_START] = nil
107
          @column_values[SUBS_END] = nil
108 101
          if host['subscription_facet_attributes']
109 102
            subscriptions = @api.resource(:host_subscriptions).call(:index, {
110 103
                'organization_id' => host['organization_id'],
111 104
                'host_id' => host['id']
112 105
            })['results']
113 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
114 111
              columns_to_csv(csv)
115 112
            else
116 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']
117 119
                subscription_type = subscription['product_id'].to_i == 0 ? 'Red Hat' : 'Custom'
118
                subscription_type += ' Guest' if subscription['type'] == 'STACK_DERIVED'
120
                subscription_type += ' Guest' unless @column_values[SUBS_GUESTOF].nil?
119 121
                subscription_type += ' Temporary' if subscription['type'] == 'UNMAPPED_GUEST'
120 122
                @column_values[SUBS_NAME] = subscription['product_name']
121 123
                @column_values[SUBS_TYPE] = subscription_type
......
229 231
      end
230 232

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

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

  
268 273
        update_host_collections(host, line)
......
310 315
      end
311 316

  
312 317
      def products(line)
313
        return nil if !line[PRODUCTS]
318
        return if line[PRODUCTS].nil? || line[PRODUCTS].empty?
314 319
        CSV.parse_line(line[PRODUCTS]).collect do |product_details|
315 320
          product = {}
316 321
          (product['product_id'], product['product_name']) = product_details.split('|')
......
335 340
          existing_subscriptions = []
336 341
        end
337 342

  
338
        if line[SUBS_NAME].nil? && line[SUBS_SKU].nil?
343
        if (line[SUBS_SKU].nil? || line[SUBS_SKU].empty?) &&
344
            (line[SUBS_NAME].nil? || line[SUBS_NAME].empty?)
339 345
          all_in_one_subscription(host, existing_subscriptions, line)
340 346
        else
341 347
          single_subscription(host, existing_subscriptions, line)
......
343 349
      end
344 350

  
345 351
      def single_subscription(host, existing_subscriptions, line)
346
        if !line[SUBS_SKU].nil? && !line[SUBS_SKU].empty? &&
347
            !line[SUBS_NAME].nil? && !line[SUBS_NAME].empty?
348
          return
349
        end
350

  
351
        already_attached = false
352
        if !line[SUBS_SKU].nil? && !line[SUBS_SKU].empty?
353
          already_attached = existing_subscriptions.detect do |subscription|
354
            line[SUBS_SKU] == subscription['product_id']
355
          end
356
        elsif !line[SUBS_NAME].nil? && !line[SUBS_NAME].empty?
357
          already_attached = existing_subscriptions.detect do |subscription|
358
            line[SUBS_NAME] == subscription['name']
359
          end
360
        end
361
        if already_attached
362
          print _(" '%{name}' already attached...") % {:name => already_attached['name']}
352
        matches = matches_by_sku_and_name([], line[SUBS_SKU], line[SUBS_NAME], existing_subscriptions)
353
        matches = matches_by_hypervisor(matches, line[SUBS_GUESTOF])
354
        unless matches.empty?
355
          print _(" '%{name}' already attached...") % {:name => subscription_name(matches[0])} if option_verbose?
363 356
          return
364 357
        end
365 358

  
366
        available_subscriptions = @api.resource(:subscriptions).call(:index, {
367
          'organization_id' => host['organization_id'],
368
          'host_id' => host['id'],
369
          'available_for' => 'host',
370
          'match_host' => true
371
        })['results']
372

  
373
        matches = matches_by_sku_and_name([], line, available_subscriptions)
374
        matches = matches_by_type(matches, line)
375
        matches = matches_by_account(matches, line)
376
        matches = matches_by_contract(matches, line)
377
        matches = matches_by_quantity(matches, line)
359
        matches = get_matching_subscriptions(host['organization_id'],
360
            :host => host, :sku => line[SUBS_SKU], :name => line[SUBS_NAME], :type => line[SUBS_TYPE],
361
            :account => line[SUBS_ACCOUNT], :contract => line[SUBS_CONTRACT],
362
            :quantity => line[SUBS_QUANTITY], :hypervisor => line[SUBS_GUESTOF],
363
            :sla => line[SLA])
378 364

  
379 365
        raise _("No matching subscriptions") if matches.empty?
380 366

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

  
384 370
        amount = line[SUBS_QUANTITY]
385 371
        quantity = (amount.nil? || amount.empty? || amount == 'Automatic') ? 0 : amount.to_i
......
393 379
      def all_in_one_subscription(host, existing_subscriptions, line)
394 380
        return if line[SUBSCRIPTIONS].nil? || line[SUBSCRIPTIONS].empty?
395 381

  
382
        organization_id = foreman_organization(:name => line[ORGANIZATION])
383
        hypervisor = hypervisor_from_line(line)
384

  
396 385
        subscriptions = CSV.parse_line(line[SUBSCRIPTIONS], {:skip_blanks => true}).collect do |details|
397 386
          (amount, sku, name, contract, account) = split_subscription_details(details)
387
          matches = get_matching_subscriptions(organization_id,
388
              :name => name, :contract => contract, :account => account, :quantity => amount,
389
              :hypervisor => hypervisor, :sla => line[SLA])
390
          raise "No matching subscription" if matches.empty?
391

  
398 392
          {
399
            :id => get_subscription(line[ORGANIZATION], :name => name, :contract => contract,
400
                                                        :account => account),
393
            :id => matches[0]['id'],
401 394
            :quantity => (amount.nil? || amount.empty? || amount == 'Automatic') ? 0 : amount.to_i
402 395
          }
403 396
        end
......
434 427
      def iterate_hosts(csv)
435 428
        hypervisors = []
436 429
        hosts = []
437
        @api.resource(:organizations).call(:index, {:per_page => 999999})['results'].each do |organization|
430
        @api.resource(:organizations).call(:index, {
431
            'full_results' => true
432
        })['results'].each do |organization|
438 433
          next if option_organization && organization['name'] != option_organization
439 434

  
440 435
          @api.resource(:hosts).call(:index, {
441
              'per_page' => 999999,
436
              'full_results' => true,
442 437
              'search' => option_search,
443 438
              'organization_id' => foreman_organization(:name => organization['name'])
444 439
          })['results'].each do |host|
......
464 459
      end
465 460

  
466 461
      def shared_headers
467
        [NAME, ORGANIZATION, ENVIRONMENT, CONTENTVIEW, HOSTCOLLECTIONS, VIRTUAL, HOST,
462
        [NAME, ORGANIZATION, ENVIRONMENT, CONTENTVIEW, HOSTCOLLECTIONS, VIRTUAL, GUESTOF,
468 463
         OPERATINGSYSTEM, ARCHITECTURE, SOCKETS, RAM, CORES, SLA, PRODUCTS]
469 464
      end
470 465

  
......
504 499
        @column_values[CONTENTVIEW] = contentview
505 500
        @column_values[HOSTCOLLECTIONS] = hostcollections
506 501
        @column_values[VIRTUAL] = virtual
507
        @column_values[HOST] = hypervisor_host
502
        @column_values[GUESTOF] = hypervisor_host
508 503
        @column_values[OPERATINGSYSTEM] = operatingsystem
509 504
        @column_values[ARCHITECTURE] = architecture
510 505
        @column_values[SOCKETS] = sockets
......
561 556
        if @first_columns_to_csv.nil?
562 557
          @columns.each do |column|
563 558
            # rubocop:disable LineLength
564
            $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} unless @column_values.key?(column)
559
            if option_export? && !@column_values.key?(column)
560
              $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}
561
            end
565 562
            # rubocop:enable LineLength
566 563
          end
567 564
          @first_columns_to_csv = true
......
570 567
          @column_values[column]
571 568
        end
572 569
      end
570

  
571
      def hypervisor_from_line(line)
572
        hypervisor = line[HOST] if !line[HOST].nil? && !line[HOST].empty?
573
        hypervisor ||= line[GUESTOF] if !line[GUESTOF].nil? && !line[GUESTOF].empty?
574
        hypervisor
575
      end
573 576
    end
574 577
  end
575 578
end
lib/hammer_cli_csv/utils/subscriptions.rb
10 10
      SUBS_ACCOUNT = 'Subscription Account'
11 11
      SUBS_START = 'Subscription Start'
12 12
      SUBS_END = 'Subscription End'
13
      SUBS_GUESTOF = 'Subscription Guest'
13 14

  
14 15
      def get_all_subscriptions(organization)
15 16
        @api.resource(:subscriptions).call(:index, {
16
            :per_page => 999999,
17
            'full_results' => true,
17 18
            'organization_id' => foreman_organization(:name => organization)
18 19
        })['results']
19 20
      end
20 21

  
21
      def get_subscription(organization, options = {})
22
        @subscriptions ||= {}
23
        @subscriptions[organization] ||= {}
24

  
25
        if options[:name]
26
          return nil if options[:name].nil? || options[:name].empty?
27
          options[:id] = @subscriptions[organization][options[:name]]
28
          if !options[:id]
29
            results = @api.resource(:subscriptions).call(:index, {
30
                :per_page => 999999,
31
                'organization_id' => foreman_organization(:name => organization),
32
                'search' => "name = \"#{options[:name]}\""
33
            })
34
            raise "No subscriptions match '#{options[:name]}'" if results['subtotal'] == 0
35
            raise "Too many subscriptions match '#{options[:name]}'" if results['subtotal'] > 1
36
            subscription = results['results'][0]
37
            @subscriptions[organization][options[:name]] = subscription['id']
38
            options[:id] = @subscriptions[organization][options[:name]]
39
            raise "Subscription '#{options[:name]}' not found" if !options[:id]
40
          end
41
          result = options[:id]
22
      def get_matching_subscriptions(organization_id, options = {})
23
        logger.debug("get_matching_subscriptions: #{options}")
24
        if options[:host]
25
          available_subscriptions = @api.resource(:subscriptions).call(:index, {
26
            'full_results' => true,
27
            'organization_id' => organization_id,
28
            'host_id' => options[:host]['id'],
29
            'available_for' => 'host',
30
            'match_host' => true
31
          })['results']
32
        elsif options[:activation_key]
33
          available_subscriptions = @api.resource(:subscriptions).call(:index, {
34
            'full_results' => true,
35
            'organization_id' => organization_id,
36
            'activation_key_id' => options[:activation_key]['id'],
37
            'available_for' => 'activation_key'
38
          })['results']
42 39
        else
43
          return nil if options[:id].nil?
44
          options[:name] = @subscriptions.key(options[:id])
45
          if !options[:name]
46
            subscription = @api.resource(:subscriptions).call(:show, {'id' => options[:id]})
47
            raise "Subscription '#{options[:name]}' not found" if !subscription || subscription.empty?
48
            options[:name] = subscription['name']
49
            @subscriptions[options[:name]] = options[:id]
50
          end
51
          result = options[:name]
40
          available_subscriptions = @api.resource(:subscriptions).call(:index, {
41
              'full_results' => true,
42
              'organization_id' => organization_id
43
          })['results']
52 44
        end
53 45

  
54
        result
46
        debug_subscriptions('available_subscriptions', available_subscriptions)
47
        matches = matches_by_sku_and_name([], options[:sku], options[:name], available_subscriptions)
48
        matches = matches_by_type(matches, options[:type])
49
        matches = matches_by_hypervisor(matches, options[:hypervisor])
50
        matches = matches_by_account(matches, options[:account])
51
        matches = matches_by_contract(matches, options[:contract])
52
        matches = matches_by_sla(matches, options[:sla])
53
        matches = matches_by_quantity(matches, options[:quantity]) unless options[:activation_key]
54

  
55
        matches
55 56
      end
56 57

  
57
      def matches_by_sku_and_name(matches, line, subscriptions)
58
        if line[SUBS_SKU]
58
      def matches_by_sku_and_name(matches, subs_sku, subs_name, subscriptions)
59
        return matches if subscriptions.empty?
60

  
61
        if subs_sku
59 62
          matches = subscriptions.select do |subscription|
60
            line[SUBS_SKU] == subscription['product_id']
63
            subs_sku == subscription['product_id']
61 64
          end
62
          raise _("No subscriptions match SKU '%{sku}'") % {:sku => line[SUBS_SKU]} if matches.empty?
63
        elsif line[SUBS_NAME]
65
          raise _("No subscriptions match SKU '%{sku}'") % {:sku => subs_sku} if matches.empty?
66
        elsif subs_name
64 67
          matches = subscriptions.select do |subscription|
65
            line[SUBS_NAME] == subscription['name']
68
            subs_name == subscription['name']
69
          end
70
        end
71
        debug_subscriptions("matches_by_sku_and_name: #{subs_sku}|#{subs_name}", matches)
72
        matches
73
      end
74

  
75
      def matches_by_hypervisor(matches, subs_hypervisor)
76
        return matches if matches.empty?
77

  
78
        if !subs_hypervisor.nil? && !subs_hypervisor.empty?
79
          matches.select! do |subscription|
80
            !subscription['host'].nil? && subscription['host']['name'] == subs_hypervisor
81
          end
82
          if matches.empty? && subs_hypervisor =~ /virt-who-/
83
            subs_hypervisor = subs_hypervisor.split('-')[2..-2].join('-')
84
            matches.select! do |subscription|
85
              !subscription['host'].nil? && subscription['host']['name'] == subs_hypervisor
86
            end
87
          end
88
        else
89
          matches.select! do |subscription|
90
            subscription['host'].nil?
91
          end
92
        end
93
        debug_subscriptions("matches_by_hypervisor: #{subs_hypervisor}", matches)
94
        matches
95
      end
96

  
97
      def matches_by_sla(matches, subs_sla)
98
        return matches if matches.empty?
99

  
100
        if !subs_sla.nil? && !subs_sla.empty?
101
          found = matches.select do |subscription|
102
            subscription['sla'] == subs_sla
66 103
          end
67
          raise _("No subscriptions match name '%{name}'") % {:name => line[SUBS_NAME]} if matches.empty?
104
          # Fallback to subscriptions w/o sla set
105
          if found.empty?
106
            found = matches.select do |subscription|
107
              subscription['sla'].nil? || subscription['sla'].empty?
108
            end
109
          end
110
          matches = found
68 111
        end
112
        debug_subscriptions("matches_by_sla: #{subs_sla}", matches)
69 113
        matches
70 114
      end
71 115

  
72
      def matches_by_type(matches, line)
73
        if line[SUBS_TYPE] == 'Red Hat' || line[SUBS_TYPE] == 'Custom'
74
          matches = matches.select do |subscription|
116
      def matches_by_type(matches, subs_type)
117
        return matches if matches.empty?
118

  
119
        if subs_type == 'Red Hat' || subs_type == 'Custom'
120
          matches.select! do |subscription|
75 121
            subscription['type'] == 'NORMAL'
76 122
          end
77
        elsif line[SUBS_TYPE] == 'Red Hat Guest'
78
          matches = matches.select do |subscription|
79
            subscription['type'] == 'STACK_DERIVED'
123
        elsif subs_type == 'Red Hat Guest'
124
          matches.select! do |subscription|
125
            !subscription['host'].nil? && !subscription['host'].empty?
80 126
          end
81
        elsif line[SUBS_TYPE] == 'Red Hat Temporary'
82
          matches = matches.select do |subscription|
127
        elsif subs_type == 'Red Hat Temporary'
128
          matches.select! do |subscription|
83 129
            subscription['type'] == 'UNMAPPED_GUEST'
84 130
          end
85 131
        end
86
        raise _("No subscriptions match type '%{type}'") % {:type => line[SUBS_TYPE]} if matches.empty?
132
        debug_subscriptions("matches_type: #{subs_type}", matches)
87 133
        matches
88 134
      end
89 135

  
90
      def matches_by_account(matches, line)
91
        if matches.length > 1 && line[SUBS_ACCOUNT]
136
      def matches_by_account(matches, subs_account)
137
        return matches if matches.empty?
138

  
139
        if matches.length > 1 && subs_account
92 140
          refined = matches.select do |subscription|
93
            line[SUBS_ACCOUNT] == subscription['account_number']
141
            subs_account == subscription['account_number']
94 142
          end
95 143
          matches = refined unless refined.empty?
96 144
        end
145
        debug_subscriptions("matches_by_account: #{subs_account}", matches)
97 146
        matches
98 147
      end
99 148

  
100
      def matches_by_contract(matches, line)
101
        if matches.length > 1 && line[SUBS_CONTRACT]
149
      def matches_by_contract(matches, subs_contract)
150
        return matches if matches.empty?
151

  
152
        if matches.length > 1 && subs_contract
102 153
          refined = matches.select do |subscription|
103
            line[SUBS_CONTRACT] == subscription['contract_number']
154
            subs_contract == subscription['contract_number']
104 155
          end
105 156
          matches = refined unless refined.empty?
106 157
        end
158
        debug_subscriptions("matches_by_contract: #{subs_contract}", matches)
107 159
        matches
108 160
      end
109 161

  
110
      def matches_by_quantity(matches, line)
111
        if line[SUBS_QUANTITY] && line[SUBS_QUANTITY] != 'Automatic'
162
      def matches_by_quantity(matches, subs_quantity)
163
        return matches if matches.empty?
164

  
165
        matches.select! do |subscription|
166
          subscription['available'] != 0
167
        end
168

  
169
        if !subs_quantity.nil? && !subs_quantity.empty? && subs_quantity != 'Automatic'
170
          subs_quantity = subs_quantity.to_i
112 171
          refined = matches.select do |subscription|
113
            subscription['available'] == -1 || line[SUBS_QUANTITY].to_i <= subscription['available']
172
            subscription['available'] == -1 || subs_quantity <= subscription['available']
114 173
          end
115 174
          raise _("No '%{name}' subscription with quantity %{quantity} or more available") %
116
            {:name => matches[0]['name'], :quantity => line[SUBS_QUANTITY]} if refined.empty?
175
            {:name => matches[0]['name'], :quantity => subs_quantity} if refined.empty?
117 176
          matches = refined
118 177
        end
178
        debug_subscriptions("matches_by_quantity: #{subs_quantity}", matches)
119 179
        matches
120 180
      end
121 181

  
122
      def match_with_quantity_to_attach(match, line)
123
        if line[SUBS_QUANTITY] && line[SUBS_QUANTITY] != 'Automatic' && !line[SUBS_QUANTITY].empty?
124
          match['quantity'] = line[SUBS_QUANTITY]
182
      def match_with_quantity_to_attach(match, subs_quantity)
183
        if subs_quantity && subs_quantity != 'Automatic' && !subs_quantity.empty?
184
          match['quantity'] = subs_quantity
125 185
        else
126 186
          match['quantity'] = -1
127 187
        end
128 188
        match
129 189
      end
130 190

  
191
      def subscription_name(subscription)
192
        if subscription['host'].nil?
193
          subscription['name']
194
        else
195
          "#{subscription['name']} - Guest of #{subscription['host']['name']}"
196
        end
197
      end
198

  
131 199
      # Subscription amount, SKU, name, contract number, and account number separated by '|'
132 200
      # or simply the subscription name.
133 201
      def split_subscription_details(details)
134 202
        details = details.split('|')
135 203
        details.length == 1 ? ['Automatic', nil, details[0], nil, nil] : details
136 204
      end
205

  
206
      def debug_subscriptions(description, subscriptions)
207
        logger.debug(description)
208
        subscriptions.each do |subscription|
209
          logger.debug "#{subscription['quantity_consumed']}"\
210
                       "|#{subscription['product_id']}"\
211
                       "|#{subscription['product_name']}"\
212
                       "|#{subscription['contract_number']}|#{subscription['account_number']}"
213
        end
214
      end
137 215
    end
138 216
  end
139 217
end
test/csv_test_helper.rb
101 101
  }
102 102
  lines = stdout.split("\n")
103 103
  if lines.length == 5
104
    id = stdout.split("\n")[3].split(" ")[0]
104
    id = lines[3].split(" ")[0]
105 105
    stdout,stderr = capture {
106 106
      hammer.run(%W(host delete --id #{id}))
107 107
    }
test/data/setup/activation-keys-itemized.csv
1
Name,Organization,Description,Limit,Environment,Content View,Host Collections,Auto-Attach,Service Level,Release Version,Subscription Name,Subscription Type,Subscription Quantity,Subscription SKU,Subscription Contract,Subscription Account,Subscription Start,Subscription End,Subscription Guest
2
testakeyitemized,Test Corporation,,,,Default Organization View,"",Yes,,,"Red Hat Enterprise Linux Server, Standard (Physical or Virtual Nodes)",Red Hat,Automatic,RH00004,10999113,5700573,2016-06-20T04:00:00+00:00,2017-06-20T03:59:59+00:00,
3
testakeyitemized,Test Corporation,,,,Default Organization View,"",Yes,,,"Red Hat Enterprise Linux for Virtual Datacenters, Standard",Red Hat,1,RH00002,10999110,5700573,2016-06-20T04:00:00+00:00,2017-06-20T03:59:59+00:00,
test/data/setup/activation-keys.csv
1 1
Name,Organization,Description,Limit,Environment,Content View,Host Collections,Auto-Attach,Service Level,Release Version,Subscriptions
2
testakey1,Test Corporation,,,,Default Organization View,"",Yes,,,"""1|RH00002|Red Hat Enterprise Linux for Virtual Datacenters, Standard||1583473"""
2
testakey,Test Corporation,,,,Default Organization View,"",Yes,,,"""1|RH00002|Red Hat Enterprise Linux for Virtual Datacenters, Standard||1583473"""
test/resources/activation_keys_test.rb
32 32
      start_vcr
33 33
      set_user 'admin'
34 34

  
35
      @name = "testakey2"
35
      name = "testakey1"
36 36

  
37 37
      file = Tempfile.new('activation_keys_test')
38
      file.write("Name,Organization,Description,Limit,Environment,Content View,Host Collections,Auto-Attach,Service Level,Release Version,Subscriptions\n")
39
      file.write("#{@name},Test Corporation,,,,Default Organization View,"",No,,,\n")
38
      file.write <<-EOF
39
Name,Organization,Description,Limit,Environment,Content View,Host Collections,Auto-Attach,Service Level,Release Version,Subscriptions
40
#{name},Test Corporation,,,,Default Organization View,"",No,,,
41
EOF
40 42
      file.rewind
41 43

  
42 44
      stdout,stderr = capture {
43 45
        hammer.run(%W{--reload-cache csv activation-keys --verbose --file #{file.path}})
44 46
      }
45 47
      assert_equal '', stderr
46
      assert_equal stdout[0..-2], "Creating activation key '#{@name}'...done"
48
      assert_equal stdout[0..-2], "Creating activation key '#{name}'...done"
47 49

  
48 50
      file.rewind
49 51

  
......
51 53
        hammer.run(%W{--reload-cache csv activation-keys --verbose --file #{file.path}})
52 54
      }
53 55
      assert_equal '', stderr
54
      assert_equal stdout[0..-2], "Updating activation key '#{@name}'...done"
56
      assert_equal stdout[0..-2], "Updating activation key '#{name}'...done"
55 57
      file.unlink
56 58

  
57
      stdout,stderr = capture {
58
        hammer.run(%W(--reload-cache activation-key list --organization Test\ Corporation --search name=#{@name}))
59
      }
60
      assert_equal '', stderr
61
      assert_equal stdout.split("\n").length, 5
62

  
63
      id = stdout.split("\n")[3].split(" ")[0]
64
      stdout,stderr = capture {
65
        hammer.run(%W(--reload-cache activation-key delete --id #{id}))
66
      }
67

  
68
      # Cleanup for subsequent test runs
69
      capture {
70
        hammer.run(%W{activation-key delete --organization-label testcorp --name #{@name}})
71
      }
59
      activation_key_delete(name)
72 60

  
73 61
      stop_vcr
74 62
    end
75 63

  
76 64
    def test_itemized_create_and_update
77 65
      start_vcr
78
      _stdout,stderr = capture {
79
        hammer.run(%W{--reload-cache csv activation-keys --verbose --file test/data/setup/activation-keys.csv})
80
      }
81
      assert_equal '', stderr
82

  
83 66
      set_user 'admin'
84 67

  
85
      name = "testakey1"
68
      name = "testakey2"
86 69
      sub_name = "Red Hat Enterprise Linux Server, Standard (Physical or Virtual Nodes)"
87 70
      quantity = 1
71
      activation_key_create(name)
88 72

  
89 73
      file = Tempfile.new('activation_keys_test')
90
      file.write("Name,Organization,Description,Limit,Environment,Content View,Host\
91
                  Collections,Auto-Attach,Service Level,Release Version,Subscription\
92
                  Name,Subscription Type,Subscription Quantity,Subscription SKU,Subscription\
93
                  Contract,Subscription Account,Subscription Start,Subscription End\n")
94
      file.write("#{name},Test Corporation,,,,Default Organization View,\"\",Yes,,,\"#{sub_name}\",Red\
95
                  Hat,#{quantity},RH00004,,1583473,2016-11-10T05:00:00.000+0000,2017-11-10T04:59:59.000+0000")
96

  
74
      # rubocop:disable LineLength
75
      file.write <<-EOF
76
Name,Organization,Description,Limit,Environment,Content View,Host Collections,Auto-Attach,Service Level,Release Version,Subscription Name,Subscription Type,Subscription Quantity,Subscription SKU,Subscription Contract,Subscription Account,Subscription Start,Subscription End
77
#{name},Test Corporation,,,,Default Organization View,\"\",Yes,,,\"#{sub_name}\",Red Hat,#{quantity},RH00004,,1583473,2016-11-10T05:00:00.000+0000,2017-11-10T04:59:59.000+0000
78
EOF
79
      # rubocop:enable LineLength
97 80
      file.rewind
98 81

  
99 82
      # Attaching an integer quantity of a subscription
......
112 95
      assert_equal '', stderr
113 96
      assert_equal stdout[0..-2], "Updating subscriptions for activation key '#{name}'... '#{sub_name}' already attached...done"
114 97

  
115
      # Attaching automatic quantity with Automatic in quantity field
116
      stdout,stderr = capture {
117
        hammer.run(%W{--reload-cache csv activation-keys --verbose --file test/data/setup/activation-keys.csv})
118
      }
98
      activation_key_delete(name)
119 99

  
120
      file.rewind
100
      stop_vcr
101
    end
121 102

  
122
      file.write("Name,Organization,Description,Limit,Environment,Content View,Host\
123
                  Collections,Auto-Attach,Service Level,Release Version,Subscription\
124
                  Name,Subscription Type,Subscription Quantity,Subscription SKU,Subscription\
125
                  Contract,Subscription Account,Subscription Start,Subscription End\n")
126
      file.write("#{name},Test Corporation,,,,Default Organization View,\"\",Yes,,,\"#{sub_name}\",Red\
127
                  Hat,Automatic,RH00004,,1583473,2016-11-10T05:00:00.000+0000,2017-11-10T04:59:59.000+0000")
103
    def test_itemized_update_automatic_quantity
104
      start_vcr
105
      set_user 'admin'
106

  
107
      name = "testakey3"
108
      sub_name = "Red Hat Enterprise Linux Server, Standard (Physical or Virtual Nodes)"
109
      quantity = "Automatic"
110
      activation_key_create(name)
128 111

  
112
      file = Tempfile.new('activation_keys_test')
113
      # rubocop:disable LineLength
114
      file.write <<-EOF
115
Name,Organization,Description,Limit,Environment,Content View,Host Collections,Auto-Attach,Service Level,Release Version,Subscription Name,Subscription Type,Subscription Quantity,Subscription SKU,Subscription Contract,Subscription Account,Subscription Start,Subscription End
116
#{name},Test Corporation,,,,Default Organization View,\"\",Yes,,,\"#{sub_name}\",Red Hat,#{quantity},RH00004,,1583473,2016-11-10T05:00:00.000+0000,2017-11-10T04:59:59.000+0000
117
EOF
118
      # rubocop:enable LineLength
129 119
      file.rewind
130 120

  
131 121
      stdout,stderr = capture {
......
134 124
      assert_equal '', stderr
135 125
      assert_equal stdout[0..-2], "Updating subscriptions for activation key '#{name}'... attaching -1 of '#{sub_name}'...done"
136 126

  
137
      # Attaching automatic quantity with nothing in quantity field
138
      stdout,stderr = capture {
139
        hammer.run(%W{--reload-cache csv activation-keys --verbose --file test/data/setup/activation-keys.csv})
140
      }
127
      activation_key_delete(name)
141 128

  
142
      file.rewind
129
      stop_vcr
130
    end
143 131

  
144
      file.write("Name,Organization,Description,Limit,Environment,Content View,Host\
145
                  Collections,Auto-Attach,Service Level,Release Version,Subscription\
146
                  Name,Subscription Type,Subscription Quantity,Subscription SKU,Subscription\
147
                  Contract,Subscription Account,Subscription Start,Subscription End\n")
148
      file.write("#{name},Test Corporation,,,,Default Organization View,\"\",Yes,,,\"#{sub_name}\",Red\
149
                  Hat,,RH00004,,1583473,2016-11-10T05:00:00.000+0000,2017-11-10T04:59:59.000+0000")
132
    def test_itemized_update_blank_quantity
133
      start_vcr
134
      set_user 'admin'
135

  
136
      name = "testakey4"
137
      sub_name = "Red Hat Enterprise Linux Server, Standard (Physical or Virtual Nodes)"
138
      quantity = ""
139
      activation_key_create(name)
150 140

  
141
      file = Tempfile.new('activation_keys_test')
142
      # rubocop:disable LineLength
143
      file.write <<-EOF
144
Name,Organization,Description,Limit,Environment,Content View,Host Collections,Auto-Attach,Service Level,Release Version,Subscription Name,Subscription Type,Subscription Quantity,Subscription SKU,Subscription Contract,Subscription Account,Subscription Start,Subscription End
145
#{name},Test Corporation,,,,Default Organization View,\"\",Yes,,,\"#{sub_name}\",Red Hat,#{quantity},RH00004,,1583473,2016-11-10T05:00:00.000+0000,2017-11-10T04:59:59.000+0000
146
EOF
147
      # rubocop:enable LineLength
151 148
      file.rewind
152 149

  
153 150
      stdout,stderr = capture {
......
156 153
      assert_equal '', stderr
157 154
      assert_equal stdout[0..-2], "Updating subscriptions for activation key '#{name}'... attaching -1 of '#{sub_name}'...done"
158 155

  
159
      # Attaching automatic quantity with "" in quantity field
160
      stdout,stderr = capture {
161
        hammer.run(%W{--reload-cache csv activation-keys --verbose --file test/data/setup/activation-keys.csv})
162
      }
163

  
164
      file.rewind
156
      activation_key_delete(name)
165 157

  
166
      file.write("Name,Organization,Description,Limit,Environment,Content View,Host\
167
                  Collections,Auto-Attach,Service Level,Release Version,Subscription\
168
                  Name,Subscription Type,Subscription Quantity,Subscription SKU,Subscription\
169
                  Contract,Subscription Account,Subscription Start,Subscription End\n")
170
      file.write("#{name},Test Corporation,,,,Default Organization View,\"\",Yes,,,\"#{sub_name}\",Red\
171
                  Hat,\"\",RH00004,,1583473,2016-11-10T05:00:00.000+0000,2017-11-10T04:59:59.000+0000")
158
      stop_vcr
159
    end
172 160

  
161
    def activation_key_create(name)
162
      file = Tempfile.new('activation_keys_test')
163
      # rubocop:disable LineLength
164
      file.write <<-EOF
165
Name,Organization,Description,Limit,Environment,Content View,Host Collections,Auto-Attach,Service Level,Release Version,Subscriptions
166
#{name},Test Corporation,,,,Default Organization View,"",Yes,,,
167
EOF
168
      # rubocop:enable LineLength
173 169
      file.rewind
174

  
175 170
      stdout,stderr = capture {
176
        hammer.run(%W{--reload-cache csv activation-keys --verbose --itemized-subscriptions --file #{file.path}})
171
        hammer.run(%W{--reload-cache csv activation-keys --verbose --file #{file.path}})
177 172
      }
178 173
      assert_equal '', stderr
179
      assert_equal stdout[0..-2], "Updating subscriptions for activation key '#{name}'... attaching -1 of '#{sub_name}'...done"
180

  
181
      file.unlink
174
      assert_equal stdout[0..-2], "Creating activation key '#{name}'...done"
175
    end
182 176

  
183
      stop_vcr
177
    def activation_key_delete(name)
178
      stdout,stderr = capture {
179
        hammer.run(%W(activation-key list --organization Test\ Corporation --search name=#{name}))
180
      }
181
      lines = stdout.split("\n")
182
      if lines.length == 5
183
        id = lines[3].split(" ")[0]
184
        stdout,stderr = capture {
185
          hammer.run(%W(activation-key delete --organization Test\ Corporation --id #{id}))
186
        }
187
      end
184 188
    end
185 189
  end
186 190
end
test/resources/content_hosts_test.rb
34 34
 Environment - Lifecycle environment name
35 35
 Content View - Content view name
36 36
 Host Collections - Comma separated list of host collection names
37
 Name - Is a virtual host, Yes or No
38
 Host - Hypervisor host name for virtual hosts
37
 Virtual - Is a virtual host, Yes or No
38
 Guest of Host - Hypervisor host name for virtual hosts
39 39
 OS - Operating system
40 40
 Arch - Architecture
41 41
 Sockets - Number of sockets
......
76 76
      hostname = "testhost1"
77 77

  
78 78
      file = Tempfile.new('content_hosts_test')
79
      file.write("Name,Count,Organization,Environment,Content View,Host Collections,Virtual,Host,OS,Arch,Sockets,RAM,Cores,SLA,Products,Subscriptions\n")
80
      file.write("#{hostname},1,Test Corporation,Library,Default Organization View,,No,,RHEL 6.4,x86_64,1,4,1,,,\n")
79
      file.write("Name,Count,Organization,Environment,Content View,Virtual,Host,OS,Arch,Sockets,RAM,Cores,SLA,Products,Subscriptions\n")
80
      file.write("#{hostname},1,Test Corporation,Library,Default Organization View,No,,RHEL 6.4,x86_64,1,4,1,,,\n")
81 81
      file.rewind
82 82

  
83 83
      stdout,stderr = capture {
......
113 113
        hammer.run(%W{--reload-cache csv content-hosts --export --organization Test\ Corporation})
114 114
      }
115 115
      assert_equal '', stderr
116
      assert_equal stdout.split("\n")[0], "Name,Organization,Environment,Content View,Host Collections,Virtual,Host,OS,Arch,Sockets,RAM,Cores,SLA,Products,Subscriptions"
116
      assert_equal stdout.split("\n")[0], "Name,Organization,Environment,Content View,Host Collections,Virtual,Guest of Host,OS,Arch,Sockets,RAM,Cores,SLA,Products,Subscriptions"
117 117
      stop_vcr
118 118
    end
119 119

  
......
126 126
      }
127 127
      assert_equal '', stderr
128 128

  
129
      assert_equal stdout.split("\n")[0], "Name,Organization,Environment,Content View,Host Collections,Virtual,Host,OS,Arch,Sockets,RAM,Cores,SLA,Products,Subscription Name,Subscription Type,Subscription Quantity,Subscription SKU,Subscription Contract,Subscription Account,Subscription Start,Subscription End"
129
      assert_equal stdout.split("\n")[0], "Name,Organization,Environment,Content View,Host Collections,Virtual,Guest of Host,OS,Arch,Sockets,RAM,Cores,SLA,Products,Subscription Name,Subscription Type,Subscription Quantity,Subscription SKU,Subscription Contract,Subscription Account,Subscription Start,Subscription End,Subscription Guest"
130 130
      stop_vcr
131 131
    end
132 132

  
test/setup/setup_activation_keys.rb
1
require './test/csv_test_helper'
2
require './lib/hammer_cli_csv'
3

  
4
module Setup
5
  class SetupActivationKeys < MiniTest::Unit::TestCase
6
    def test_setup
7
      start_vcr
8

  
9
      stdout,stderr = capture {
10
        hammer.run(%W{--reload-cache csv activation-keys --verbose --file test/data/setup/activation-keys.csv})
11
      }
12
      assert_equal stderr, ''
13

  
14
      stdout,stderr = capture {
15
        hammer.run(%W{--reload-cache csv activation-keys --verbose --file test/data/setup/activation-keys-itemized.csv})
16
      }
17
      assert_equal stderr, ''
18

  
19
      stop_vcr
20
    end
21
  end
22
end
test/setup/setup_test.rb
8 8
  require './test/setup/setup_lifecycle_environments'
9 9
  require './test/setup/setup_content_views'
10 10
  require './test/setup/setup_content_hosts'
11
  require './test/setup/setup_activation_keys'
11 12
  require './test/setup/setup_domains'
12 13
end

Also available in: Unified diff