hammer_cli_csv 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +5 -13
  2. data/config/cli_config.yml +9 -24
  3. data/lib/hammer_cli_csv/activation_keys.rb +220 -95
  4. data/lib/hammer_cli_csv/architectures.rb +7 -11
  5. data/lib/hammer_cli_csv/base.rb +67 -41
  6. data/lib/hammer_cli_csv/compute_profiles.rb +11 -15
  7. data/lib/hammer_cli_csv/compute_resources.rb +11 -15
  8. data/lib/hammer_cli_csv/containers.rb +10 -12
  9. data/lib/hammer_cli_csv/content_hosts.rb +285 -203
  10. data/lib/hammer_cli_csv/content_view_filters.rb +32 -36
  11. data/lib/hammer_cli_csv/content_views.rb +33 -37
  12. data/lib/hammer_cli_csv/csv.rb +17 -1
  13. data/lib/hammer_cli_csv/domains.rb +15 -19
  14. data/lib/hammer_cli_csv/export.rb +13 -5
  15. data/lib/hammer_cli_csv/host_collections.rb +20 -22
  16. data/lib/hammer_cli_csv/host_groups.rb +73 -65
  17. data/lib/hammer_cli_csv/hosts.rb +30 -34
  18. data/lib/hammer_cli_csv/import.rb +24 -10
  19. data/lib/hammer_cli_csv/installation_media.rb +74 -0
  20. data/lib/hammer_cli_csv/job_templates.rb +37 -41
  21. data/lib/hammer_cli_csv/lifecycle_environments.rb +15 -19
  22. data/lib/hammer_cli_csv/locations.rb +4 -6
  23. data/lib/hammer_cli_csv/operating_systems.rb +17 -21
  24. data/lib/hammer_cli_csv/organizations.rb +11 -13
  25. data/lib/hammer_cli_csv/partition_tables.rb +17 -21
  26. data/lib/hammer_cli_csv/products.rb +200 -92
  27. data/lib/hammer_cli_csv/provisioning_templates.rb +19 -23
  28. data/lib/hammer_cli_csv/puppet_environments.rb +8 -12
  29. data/lib/hammer_cli_csv/puppet_facts.rb +20 -24
  30. data/lib/hammer_cli_csv/puppet_reports.rb +47 -51
  31. data/lib/hammer_cli_csv/reports.rb +6 -10
  32. data/lib/hammer_cli_csv/roles.rb +12 -16
  33. data/lib/hammer_cli_csv/settings.rb +8 -6
  34. data/lib/hammer_cli_csv/smart_proxies.rb +9 -13
  35. data/lib/hammer_cli_csv/splice.rb +1 -3
  36. data/lib/hammer_cli_csv/subnets.rb +24 -28
  37. data/lib/hammer_cli_csv/subscriptions.rb +39 -91
  38. data/lib/hammer_cli_csv/sync_plans.rb +20 -24
  39. data/lib/hammer_cli_csv/users.rb +25 -29
  40. data/lib/hammer_cli_csv/utils/subscriptions.rb +130 -0
  41. data/lib/hammer_cli_csv/version.rb +1 -1
  42. data/lib/hammer_cli_csv.rb +2 -2
  43. data/test/csv_test.rb +32 -0
  44. data/test/csv_test_helper.rb +17 -2
  45. data/test/data/content-hosts.csv +3 -2
  46. data/test/data/hosts.csv +0 -11
  47. data/test/data/settings.csv +1 -0
  48. data/test/data/setup/content-hosts.csv +1 -0
  49. data/test/data/setup/content-views.csv +2 -0
  50. data/test/data/setup/lifecycle-environments.csv +5 -0
  51. data/test/data/setup/locations.csv +6 -0
  52. data/test/data/setup/organizations.csv +2 -0
  53. data/test/data/setup/subscriptions.csv +5 -0
  54. data/test/data/subscriptions.csv +15 -17
  55. data/test/export_test.rb +27 -0
  56. data/test/fixtures/vcr_cassettes/apipie.yml +18528 -0
  57. data/test/fixtures/vcr_cassettes/resources/activation_keys_import/create_and_update.yml +38033 -0
  58. data/test/fixtures/vcr_cassettes/resources/content_hosts_export/export.yml +18846 -0
  59. data/test/fixtures/vcr_cassettes/resources/content_hosts_export/export_subscriptions.yml +323 -0
  60. data/test/fixtures/vcr_cassettes/resources/content_hosts_import/create_and_update.yml +41884 -0
  61. data/test/fixtures/vcr_cassettes/resources/content_hosts_import/import_single_line.yml +38248 -0
  62. data/test/fixtures/vcr_cassettes/resources/settings_import/update_settings.yml +18832 -0
  63. data/test/fixtures/vcr_cassettes/resources/settings_import/update_settings_continue.yml +316 -0
  64. data/test/fixtures/vcr_cassettes/resources/subscriptions_import/manifest_does_not_exist.yml +18785 -0
  65. data/test/fixtures/vcr_cassettes/setup/setup_content_views/setup.yml +19090 -0
  66. data/test/fixtures/vcr_cassettes/setup/setup_lifecycle_environments/setup.yml +924 -0
  67. data/test/fixtures/vcr_cassettes/setup/setup_locations/setup.yml +21102 -0
  68. data/test/fixtures/vcr_cassettes/setup/setup_organizations/setup.yml +19278 -0
  69. data/test/fixtures/vcr_cassettes/setup/setup_subscriptions/setup.yml +419 -0
  70. data/test/import_test.rb +20 -25
  71. data/test/resources/activation_keys_test.rb +72 -0
  72. data/test/resources/content_hosts_test.rb +132 -0
  73. data/test/resources/settings_test.rb +81 -0
  74. data/test/resources/subscriptions_test.rb +56 -0
  75. data/test/setup/setup_content_views.rb +17 -0
  76. data/test/setup/setup_lifecycle_environments.rb +17 -0
  77. data/test/setup/setup_locations.rb +17 -0
  78. data/test/setup/setup_organizations.rb +17 -0
  79. data/test/setup/setup_subscriptions.rb +20 -0
  80. data/test/setup/setup_test.rb +10 -0
  81. data/test/test_runner.rb +175 -0
  82. data/test/vcr_setup.rb +54 -0
  83. metadata +77 -13
  84. data/lib/hammer_cli_csv/exception_handler.rb +0 -42
  85. data/lib/hammer_cli_csv/installation_medias.rb +0 -77
  86. data/test/content_hosts_test.rb +0 -61
  87. data/test/settings_test.rb +0 -30
@@ -1,11 +1,20 @@
1
+ #require 'hammer_cli_csv/utils/subscription_utils'
2
+
1
3
  module HammerCLICsv
2
4
  class CsvCommand
3
5
  class ContentHostsCommand < BaseCommand
4
6
  include ::HammerCLIForemanTasks::Helper
7
+ include ::HammerCLICsv::Utils::Subscriptions
5
8
 
6
9
  command_name 'content-hosts'
7
10
  desc 'import or export content hosts'
8
11
 
12
+ def self.supported?
13
+ true
14
+ end
15
+
16
+ option %w(--itemized-subscriptions), :flag, _('Export one subscription per row, only process update subscriptions on import')
17
+
9
18
  ORGANIZATION = 'Organization'
10
19
  ENVIRONMENT = 'Environment'
11
20
  CONTENTVIEW = 'Content View'
@@ -19,123 +28,68 @@ module HammerCLICsv
19
28
  CORES = 'Cores'
20
29
  SLA = 'SLA'
21
30
  PRODUCTS = 'Products'
22
- SUBSCRIPTIONS = 'Subscriptions'
23
-
24
- def export
25
- CSV.open(option_file || '/dev/stdout', 'wb', {:force_quotes => false}) do |csv|
26
- csv << [NAME, ORGANIZATION, ENVIRONMENT, CONTENTVIEW, HOSTCOLLECTIONS, VIRTUAL, HOST,
27
- OPERATINGSYSTEM, ARCHITECTURE, SOCKETS, RAM, CORES, SLA, PRODUCTS, SUBSCRIPTIONS]
28
- if @server_status['release'] == 'Headpin'
29
- export_sam csv
30
- else
31
- export_foretello csv
32
- end
33
- end
34
- end
35
-
36
- def export_sam(csv)
37
- guests_hypervisor = {}
38
- host_ids = []
39
31
 
40
- @headpin.get(:organizations).each do |organization|
41
- next if option_organization && organization['name'] != option_organization
42
- host_ids = @headpin.get("organizations/#{organization['label']}/systems").collect do |host|
43
- host['guests'].each { |guest| guests_hypervisor[guest['uuid']] = host['name'] }
44
- host['uuid']
45
- end
32
+ def export(csv)
33
+ if option_itemized_subscriptions?
34
+ export_itemized_subscriptions csv
35
+ else
36
+ export_all csv
46
37
  end
38
+ end
47
39
 
48
- host_ids.each do |host_id|
49
- host = @headpin.get("systems/#{host_id}")
50
- host_subscriptions = @headpin.get("systems/#{host_id}/subscriptions")['entitlements']
51
-
52
- name = host['name']
53
- organization_name = host['owner']['displayName']
54
- environment = host['environment']['name']
55
- contentview = host['content_view']['name']
56
- hostcollections = nil
57
- virtual = host['facts']['virt.is_guest'] == 'true' ? 'Yes' : 'No'
58
- hypervisor = guests_hypervisor[host['uuid']]
59
- if host['facts']['distribution.name']
60
- operatingsystem = "#{host['facts']['distribution.name']} "
61
- operatingsystem += host['facts']['distribution.version'] if host['facts']['distribution.version']
62
- operatingsystem.strip!
63
- end
64
- architecture = host['facts']['uname.machine']
65
- sockets = host['facts']['cpu.cpu_socket(s)']
66
- ram = host['facts']['memory.memtotal']
67
- cores = host['facts']['cpu.core(s)_per_socket'] || 1
68
- sla = host['serviceLevel']
69
-
70
- products = CSV.generate do |column|
71
- column << host['installedProducts'].collect do |product|
72
- "#{product['productId']}|#{product['productName']}"
73
- end
74
- end
75
- products.delete!("\n")
76
-
77
- subscriptions = CSV.generate do |column|
78
- column << host_subscriptions.collect do |subscription|
79
- "#{subscription['quantity']}|#{subscription['productId']}|#{subscription['poolName']}"
40
+ def export_itemized_subscriptions(csv)
41
+ csv << shared_headers + [Utils::Subscriptions::SUBS_NAME, Utils::Subscriptions::SUBS_TYPE,
42
+ Utils::Subscriptions::SUBS_QUANTITY, Utils::Subscriptions::SUBS_SKU,
43
+ Utils::Subscriptions::SUBS_CONTRACT, Utils::Subscriptions::SUBS_ACCOUNT,
44
+ Utils::Subscriptions::SUBS_START, Utils::Subscriptions::SUBS_END]
45
+ iterate_hosts(csv) do |host|
46
+ export_line = shared_columns(host)
47
+ if host['subscription_facet_attributes']
48
+ subscriptions = @api.resource(:host_subscriptions).call(:index, {
49
+ 'organization_id' => host['organization_id'],
50
+ 'host_id' => host['id']
51
+ })['results']
52
+ if subscriptions.empty?
53
+ csv << export_line + [nil, nil, nil, nil, nil, nil]
54
+ else
55
+ subscriptions.each do |subscription|
56
+ subscription_type = subscription['product_id'].to_i == 0 ? 'Red Hat' : 'Custom'
57
+ subscription_type += ' Guest' if subscription['type'] == 'STACK_DERIVED'
58
+ subscription_type += ' Temporary' if subscription['type'] == 'UNMAPPED_GUEST'
59
+ csv << export_line + [subscription['product_name'], subscription_type,
60
+ subscription['quantity_consumed'], subscription['product_id'],
61
+ subscription['contract_number'], subscription['account_number'],
62
+ DateTime.parse(subscription['start_date']).strftime('%m/%d/%Y'),
63
+ DateTime.parse(subscription['end_date']).strftime('%m/%d/%Y')]
64
+ end
80
65
  end
66
+ else
67
+ csv << export_line + [nil, nil, nil, nil, nil, nil]
81
68
  end
82
- subscriptions.delete!("\n")
83
-
84
- csv << [name, organization_name, environment, contentview, hostcollections, virtual, hypervisor,
85
- operatingsystem, architecture, sockets, ram, cores, sla, products, subscriptions]
86
69
  end
87
70
  end
88
71
 
89
- def export_foretello(csv)
90
- @api.resource(:organizations).call(:index, {:per_page => 999999})['results'].each do |organization|
91
- next if option_organization && organization['name'] != option_organization
92
-
93
- @api.resource(:systems).call(:index, {
94
- 'per_page' => 999999,
95
- 'organization_id' => foreman_organization(:name => organization['name'])
96
- })['results'].each do |host|
97
- host = @api.resource(:systems).call(:show, {
98
- 'id' => host['uuid'],
99
- 'fields' => 'full'
100
- })
101
-
102
- name = host['name']
103
- organization_name = organization['name']
104
- environment = host['environment']['label']
105
- contentview = host['content_view']['name']
106
- hostcollections = CSV.generate do |column|
107
- column << host['hostCollections'].collect do |hostcollection|
108
- hostcollection['name']
109
- end
110
- end
111
- hostcollections.delete!("\n")
112
- virtual = host['facts']['virt.is_guest'] == 'true' ? 'Yes' : 'No'
113
- hypervisor_host = host['virtual_host'].nil? ? nil : host['virtual_host']['name']
114
- operatingsystem = "#{host['facts']['distribution.name']} " if host['facts']['distribution.name']
115
- operatingsystem += host['facts']['distribution.version'] if host['facts']['distribution.version']
116
- architecture = host['facts']['uname.machine']
117
- sockets = host['facts']['cpu.cpu_socket(s)']
118
- ram = host['facts']['memory.memtotal']
119
- cores = host['facts']['cpu.core(s)_per_socket'] || 1
120
- sla = ''
121
- products = CSV.generate do |column|
122
- column << host['installedProducts'].collect do |product|
123
- "#{product['productId']}|#{product['productName']}"
124
- end
125
- end
126
- products.delete!("\n")
72
+ def export_all(csv)
73
+ csv << shared_headers + [Utils::Subscriptions::SUBSCRIPTIONS]
74
+ iterate_hosts(csv) do |host|
75
+ if host['subscription_facet_attributes']
127
76
  subscriptions = CSV.generate do |column|
128
- column << @api.resource(:subscriptions).call(:index, {
129
- 'organization_id' => organization['id'],
130
- 'system_id' => host['uuid']
77
+ column << @api.resource(:host_subscriptions).call(:index, {
78
+ 'organization_id' => host['organization_id'],
79
+ 'host_id' => host['id']
131
80
  })['results'].collect do |subscription|
132
- "#{subscription['consumed']}|#{subscription['product_id']}|#{subscription['product_name']}"
81
+ "#{subscription['quantity_consumed']}"\
82
+ "|#{subscription['product_id']}"\
83
+ "|#{subscription['product_name']}"\
84
+ "|#{subscription['contract_number']}|#{subscription['account_number']}"
133
85
  end
134
86
  end
135
87
  subscriptions.delete!("\n")
136
- csv << [name, organization_name, environment, contentview, hostcollections, virtual, hypervisor_host,
137
- operatingsystem, architecture, sockets, ram, cores, sla, products, subscriptions]
88
+ else
89
+ subscriptions = nil
138
90
  end
91
+
92
+ csv << shared_columns(host) + [subscriptions]
139
93
  end
140
94
  end
141
95
 
@@ -157,111 +111,99 @@ module HammerCLICsv
157
111
  def import_locally
158
112
  @existing = {}
159
113
  @hypervisor_guests = {}
114
+ @all_subscriptions = {}
160
115
 
161
116
  thread_import do |line|
162
- create_content_hosts_from_csv(line)
117
+ create_from_csv(line)
163
118
  end
164
119
 
165
120
  if !@hypervisor_guests.empty?
166
121
  print(_('Updating hypervisor and guest associations...')) if option_verbose?
167
122
  @hypervisor_guests.each do |host_id, guest_ids|
168
123
  @api.resource(:hosts).call(:update, {
169
- 'id' => host_id,
170
- 'host' => {
171
- 'guest_ids' => guest_ids
124
+ 'id' => host_id,
125
+ 'host' => {
126
+ 'subscription_facet_attributes' => {
127
+ 'autoheal' => false,
128
+ 'hypervisor_guest_uuids' => guest_ids
172
129
  }
130
+ }
173
131
  })
174
132
  end
175
133
  puts _('done') if option_verbose?
176
134
  end
177
135
  end
178
136
 
179
- def create_content_hosts_from_csv(line)
137
+ def create_from_csv(line)
180
138
  return if option_organization && line[ORGANIZATION] != option_organization
181
139
 
182
- if !@existing[line[ORGANIZATION]]
183
- @existing[line[ORGANIZATION]] = true
184
- # Fetching all content hosts is too slow and times out due to the complexity of the data
185
- # rendered in the json.
186
- # http://projects.theforeman.org/issues/6307
187
- total = @api.resource(:hosts).call(:index, {
188
- 'organization_id' => foreman_organization(:name => line[ORGANIZATION]),
189
- 'per_page' => 1
190
- })['total'].to_i
191
- (total / 20 + 2).to_i.times do |page|
192
- @api.resource(:hosts).call(:index, {
193
- 'organization_id' => foreman_organization(:name => line[ORGANIZATION]),
194
- 'page' => page + 1,
195
- 'per_page' => 20
196
- })['results'].each do |host|
197
- @existing[host['name']] = {
198
- :host => host['id'],
199
- :subscription => host['subscription']['id'],
200
- :content => host['content']['id']
201
- }
202
- end
203
- end
204
- end
140
+ update_existing(line)
205
141
 
206
142
  count(line[COUNT]).times do |number|
207
143
  name = namify(line[NAME], number)
208
144
 
209
- if !@existing.include? name
210
- print(_("Creating content host '%{name}'...") % {:name => name}) if option_verbose?
211
- host_id = @api.resource(:host_subscriptions).call(:register, {
212
- 'name' => name,
213
- 'organization_id' => foreman_organization(:name => line[ORGANIZATION]),
214
- 'lifecycle_environment_id' => lifecycle_environment(line[ORGANIZATION], :name => line[ENVIRONMENT]),
215
- 'content_view_id' => katello_contentview(line[ORGANIZATION], :name => line[CONTENTVIEW]),
216
- 'facts' => facts(name, line),
217
- 'installed_products' => products(line),
218
- 'service_level' => line[SLA],
219
- 'type' => 'system'
220
- })['id']
221
- @existing[name] = host_id
145
+ if option_itemized_subscriptions?
146
+ update_itemized_subscriptions(name, line)
222
147
  else
223
- # TODO: remove passing facet IDs to update
224
- # Bug #13849 - updating a host's facet should not require the facet id to be included in facet params
225
- # http://projects.theforeman.org/issues/13849
226
- print(_("Updating content host '%{name}'...") % {:name => name}) if option_verbose?
227
- host_id = @api.resource(:hosts).call(:update, {
228
- 'id' => @existing[name][:host],
229
- 'host' => {
230
- 'name' => name,
231
- 'content_facet_attributes' => {
232
- 'id' => @existing[name][:content],
233
- 'lifecycle_environment_id' => lifecycle_environment(line[ORGANIZATION], :name => line[ENVIRONMENT]),
234
- 'content_view_id' => katello_contentview(line[ORGANIZATION], :name => line[CONTENTVIEW])
235
- },
236
- 'subscription_facet_attributes' => {
237
- 'id' => @existing[name][:subscription],
238
- 'facts' => facts(name, line),
239
- # TODO: PUT /hosts subscription_facet_attributes missing "installed_products"
240
- # http://projects.theforeman.org/issues/13854
241
- #'installed_products' => products(line),
242
- 'service_level' => line[SLA]
243
- }
244
- }
245
- })['host_id']
148
+ update_or_create(name, line)
246
149
  end
150
+ end
151
+ end
247
152
 
248
- if line[VIRTUAL] == 'Yes' && line[HOST]
249
- raise "Content host '#{line[HOST]}' not found" if !@existing[line[HOST]]
250
- @hypervisor_guests[@existing[line[HOST]]] ||= []
251
- @hypervisor_guests[@existing[line[HOST]]] << @existing[name]
252
- end
153
+ private
253
154
 
254
- update_host_facts(host_id, line)
255
- update_host_collections(host_id, line)
256
- update_subscriptions(host_id, line)
155
+ def update_itemized_subscriptions(name, line)
156
+ raise _("Content host '%{name}' must already exist with --itemized-subscriptions") % {:name => name} unless @existing.include? name
257
157
 
258
- puts _('done') if option_verbose?
259
- end
260
- rescue RuntimeError => e
261
- raise "#{e}\n #{line}"
158
+ print(_("Updating subscriptions for content host '%{name}'...") % {:name => name}) if option_verbose?
159
+ host = @api.resource(:hosts).call(:show, {:id => @existing[name]})
160
+ update_subscriptions(host, line, false)
161
+ puts _('done') if option_verbose?
262
162
  end
263
163
 
264
- private
164
+ def update_or_create(name, line)
165
+ if !@existing.include? name
166
+ print(_("Creating content host '%{name}'...") % {:name => name}) if option_verbose?
167
+ params = {
168
+ 'name' => name,
169
+ 'facts' => facts(name, line),
170
+ 'lifecycle_environment_id' => lifecycle_environment(line[ORGANIZATION], :name => line[ENVIRONMENT]),
171
+ 'content_view_id' => katello_contentview(line[ORGANIZATION], :name => line[CONTENTVIEW])
172
+ }
173
+ params['installed_products'] = products(line) if line[PRODUCTS]
174
+ params['service_level'] = line[SLA] if line[SLA]
175
+ host = @api.resource(:host_subscriptions).call(:create, params)
176
+ @existing[name] = host['id']
177
+ else
178
+ print(_("Updating content host '%{name}'...") % {:name => name}) if option_verbose?
179
+ params = {
180
+ 'id' => @existing[name],
181
+ 'host' => {
182
+ 'content_facet_attributes' => {
183
+ 'lifecycle_environment_id' => lifecycle_environment(line[ORGANIZATION], :name => line[ENVIRONMENT]),
184
+ 'content_view_id' => katello_contentview(line[ORGANIZATION], :name => line[CONTENTVIEW])
185
+ },
186
+ 'subscription_facet_attributes' => {
187
+ 'facts' => facts(name, line),
188
+ 'installed_products' => products(line),
189
+ 'service_level' => line[SLA]
190
+ }
191
+ }
192
+ }
193
+ host = @api.resource(:hosts).call(:update, params)
194
+ end
195
+
196
+ if line[VIRTUAL] == 'Yes' && line[HOST]
197
+ raise "Content host '#{line[HOST]}' not found" if !@existing[line[HOST]]
198
+ @hypervisor_guests[@existing[line[HOST]]] ||= []
199
+ @hypervisor_guests[@existing[line[HOST]]] << "#{line[ORGANIZATION]}/#{name}"
200
+ end
201
+
202
+ update_host_collections(host, line)
203
+ update_subscriptions(host, line, true)
204
+
205
+ puts _('done') if option_verbose?
206
+ end
265
207
 
266
208
  def facts(name, line)
267
209
  facts = {}
@@ -278,17 +220,15 @@ module HammerCLICsv
278
220
  facts
279
221
  end
280
222
 
281
- def update_host_facts(host_id, line)
282
- end
283
-
284
- def update_host_collections(host_id, line)
285
- return nil if !line[HOSTCOLLECTIONS]
286
- CSV.parse_line(line[HOSTCOLLECTIONS]).each do |hostcollection_name|
287
- @api.resource(:host_collections).call(:add_hosts, {
288
- 'id' => katello_hostcollection(line[ORGANIZATION], :name => hostcollection_name),
289
- 'hosts_ids' => [host_id]
290
- })
291
- end
223
+ def update_host_collections(host, line)
224
+ # TODO: http://projects.theforeman.org/issues/16234
225
+ # return nil if line[HOSTCOLLECTIONS].nil? || line[HOSTCOLLECTIONS].empty?
226
+ # CSV.parse_line(line[HOSTCOLLECTIONS]).each do |hostcollection_name|
227
+ # @api.resource(:host_collections).call(:add_hosts, {
228
+ # 'id' => katello_hostcollection(line[ORGANIZATION], :name => hostcollection_name),
229
+ # 'host_ids' => [host['id']]
230
+ # })
231
+ # end
292
232
  end
293
233
 
294
234
  def os_name_version(operatingsystem)
@@ -305,44 +245,186 @@ module HammerCLICsv
305
245
 
306
246
  def products(line)
307
247
  return nil if !line[PRODUCTS]
308
- products = CSV.parse_line(line[PRODUCTS]).collect do |product_details|
248
+ CSV.parse_line(line[PRODUCTS]).collect do |product_details|
309
249
  product = {}
310
- # TODO: these get passed straight through to candlepin; probably would be better to process in server
311
- # to allow underscore product_id here
312
- (product['productId'], product['productName']) = product_details.split('|')
250
+ (product['product_id'], product['product_name']) = product_details.split('|')
313
251
  product['arch'] = line[ARCHITECTURE]
314
252
  product['version'] = os_name_version(line[OPERATINGSYSTEM])[1]
315
253
  product
316
254
  end
317
- products
318
255
  end
319
256
 
320
- def update_subscriptions(host_id, line)
257
+ def update_subscriptions(host, line, remove_existing)
321
258
  existing_subscriptions = @api.resource(:host_subscriptions).call(:index, {
322
- 'host_id' => host_id
259
+ 'host_id' => host['id']
323
260
  })['results']
324
- if existing_subscriptions.length != 0
261
+ if remove_existing && existing_subscriptions.length != 0
262
+ existing_subscriptions.map! do |existing_subscription|
263
+ {:id => existing_subscription['id'], :quantity => existing_subscription['quantity_consumed']}
264
+ end
325
265
  @api.resource(:host_subscriptions).call(:remove_subscriptions, {
326
- 'host_id' => host_id,
266
+ 'host_id' => host['id'],
327
267
  'subscriptions' => existing_subscriptions
328
268
  })
269
+ existing_subscriptions = []
270
+ end
271
+
272
+ if line[Utils::Subscriptions::SUBS_NAME].nil? && line[Utils::Subscriptions::SUBS_SKU].nil?
273
+ all_in_one_subscription(host, existing_subscriptions, line)
274
+ else
275
+ single_subscription(host, existing_subscriptions, line)
276
+ end
277
+ end
278
+
279
+ def single_subscription(host, existing_subscriptions, line)
280
+ already_attached = false
281
+ if line[Utils::Subscriptions::SUBS_SKU]
282
+ already_attached = existing_subscriptions.detect do |subscription|
283
+ line[Utils::Subscriptions::SUBS_SKU] == subscription['product_id']
284
+ end
285
+ elsif line[Utils::Subscriptions::SUBS_NAME]
286
+ already_attached = existing_subscriptions.detect do |subscription|
287
+ line[Utils::Subscriptions::SUBS_NAME] == subscription['name']
288
+ end
289
+ end
290
+ if already_attached
291
+ print _(" '%{name}' already attached...") % {:name => already_attached['name']}
292
+ return
329
293
  end
330
294
 
295
+ available_subscriptions = @api.resource(:subscriptions).call(:index, {
296
+ 'organization_id' => host['organization_id'],
297
+ 'host_id' => host['id'],
298
+ 'available_for' => 'host',
299
+ 'match_host' => true
300
+ })['results']
301
+
302
+ matches = matches_by_sku_and_name([], line, available_subscriptions)
303
+ matches = matches_by_type(matches, line)
304
+ matches = matches_by_account(matches, line)
305
+ matches = matches_by_contract(matches, line)
306
+ matches = matches_by_quantity(matches, line)
307
+
308
+ raise _("No matching subscriptions") if matches.empty?
309
+
310
+ match = matches[0]
311
+ print _(" attaching '%{name}'...") % {:name => match['name']} if option_verbose?
312
+
313
+ @api.resource(:host_subscriptions).call(:add_subscriptions, {
314
+ 'host_id' => host['id'],
315
+ 'subscriptions' => existing_subscriptions + [match]
316
+ })
317
+ end
318
+
319
+ def all_in_one_subscription(host, existing_subscriptions, line)
331
320
  return if line[SUBSCRIPTIONS].nil? || line[SUBSCRIPTIONS].empty?
332
321
 
333
322
  subscriptions = CSV.parse_line(line[SUBSCRIPTIONS], {:skip_blanks => true}).collect do |details|
334
- (amount, sku, name) = details.split('|')
323
+ (amount, sku, name, contract, account) = split_subscription_details(details)
335
324
  {
336
- :id => katello_subscription(line[ORGANIZATION], :name => name),
325
+ :id => get_subscription(line[ORGANIZATION], :name => name, :contract => contract,
326
+ :account => account),
337
327
  :quantity => (amount.nil? || amount.empty? || amount == 'Automatic') ? 0 : amount.to_i
338
328
  }
339
329
  end
340
330
 
341
331
  @api.resource(:host_subscriptions).call(:add_subscriptions, {
342
- 'host_id' => host_id,
332
+ 'host_id' => host['id'],
343
333
  'subscriptions' => subscriptions
344
334
  })
345
335
  end
336
+
337
+ def update_existing(line)
338
+ if !@existing[line[ORGANIZATION]]
339
+ @existing[line[ORGANIZATION]] = true
340
+ # Fetching all content hosts can be too slow and times so page
341
+ # http://projects.theforeman.org/issues/6307
342
+ total = @api.resource(:hosts).call(:index, {
343
+ 'organization_id' => foreman_organization(:name => line[ORGANIZATION]),
344
+ 'per_page' => 1
345
+ })['total'].to_i
346
+ (total / 20 + 1).to_i.times do |page|
347
+ @api.resource(:hosts).call(:index, {
348
+ 'organization_id' => foreman_organization(:name => line[ORGANIZATION]),
349
+ 'page' => page + 1,
350
+ 'per_page' => 20
351
+ })['results'].each do |host|
352
+ if host['subscription_facet_attributes']
353
+ @existing[host['name']] = host['id']
354
+ end
355
+ end
356
+ end
357
+ end
358
+ end
359
+
360
+ def iterate_hosts(csv)
361
+ hypervisors = []
362
+ hosts = []
363
+ @api.resource(:organizations).call(:index, {:per_page => 999999})['results'].each do |organization|
364
+ next if option_organization && organization['name'] != option_organization
365
+
366
+ @api.resource(:hosts).call(:index, {
367
+ 'per_page' => 999999,
368
+ 'organization_id' => foreman_organization(:name => organization['name'])
369
+ })['results'].each do |host|
370
+ host = @api.resource(:hosts).call(:show, {
371
+ 'id' => host['id']
372
+ })
373
+ host['facts'] ||= {}
374
+ if host['subscription_facet_attributes']['virtual_guests'].empty?
375
+ hosts.push(host)
376
+ else
377
+ hypervisors.push(host)
378
+ end
379
+ end
380
+ end
381
+ hypervisors.each do |host|
382
+ yield host
383
+ end
384
+ hosts.each do |host|
385
+ yield host
386
+ end
387
+ end
388
+
389
+ def shared_headers
390
+ [NAME, ORGANIZATION, ENVIRONMENT, CONTENTVIEW, HOSTCOLLECTIONS, VIRTUAL, HOST,
391
+ OPERATINGSYSTEM, ARCHITECTURE, SOCKETS, RAM, CORES, SLA, PRODUCTS]
392
+ end
393
+
394
+ def shared_columns(host)
395
+ name = host['name']
396
+ organization_name = host['organization_name']
397
+ if host['content_facet_attributes']
398
+ environment = host['content_facet_attributes']['lifecycle_environment']['name']
399
+ contentview = host['content_facet_attributes']['content_view']['name']
400
+ hostcollections = export_column(host['content_facet_attributes'], 'host_collections', 'name')
401
+ else
402
+ environment = nil
403
+ contentview = nil
404
+ hostcollections = nil
405
+ end
406
+ if host['subscription_facet_attributes']
407
+ hypervisor_host = host['subscription_facet_attributes']['virtual_host'].nil? ? nil : host['subscription_facet_attributes']['virtual_host']['name']
408
+ products = export_column(host['subscription_facet_attributes'], 'installed_products') do |product|
409
+ "#{product['productId']}|#{product['productName']}"
410
+ end
411
+ else
412
+ hypervisor_host = nil
413
+ products = nil
414
+ end
415
+ virtual = host['facts']['virt::is_guest'] == 'true' ? 'Yes' : 'No'
416
+ operatingsystem = host['facts']['distribution::name'] if host['facts']['distribution::name']
417
+ operatingsystem += " #{host['facts']['distribution::version']}" if host['facts']['distribution::version']
418
+ architecture = host['facts']['uname::machine']
419
+ sockets = host['facts']['cpu::cpu_socket(s)']
420
+ ram = host['facts']['memory::memtotal']
421
+ cores = host['facts']['cpu::core(s)_per_socket'] || 1
422
+ sla = ''
423
+
424
+ [name, organization_name, environment, contentview, hostcollections, virtual, hypervisor_host,
425
+ operatingsystem, architecture, sockets, ram, cores, sla, products]
426
+ end
427
+
346
428
  end
347
429
  end
348
430
  end