hammer_cli_csv 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- N2ExODdkOTg4YzFhYmNhZjIyNDdkNTZjMjFjOGVjYjliOWM0OTA0Mw==
5
- data.tar.gz: !binary |-
6
- Njg1YmE0ZTMzMzE0NjlkM2E2NWMxMDI2N2QwM2Q5NDdmODViYzU2YQ==
2
+ SHA1:
3
+ metadata.gz: e0f784cf2623ea6218aa82c967c03a4df23f5c79
4
+ data.tar.gz: 3ff43bcc04983f69ec6f72da581e8fbceb70feee
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- NzZlYzM4NWMxZGMzYzNlNzljYWZmNWQyZmE3NzQ2OWJjMWY0MjNjMGEzMzdi
10
- NTVlM2NhZmM1ZjE4M2YzZWMxYzQ5M2E1OTZjMWRlNDE2MGUyYmE5YWU0NjYy
11
- ODJmNDMyMWNiYzk4NTEzODk4MjkzMjZlNjE1MDNiOThhNDRkZGM=
12
- data.tar.gz: !binary |-
13
- ZDNhNzIwOWU1N2Y0NWVhYjI5Y2Y1MTgzMzY0NDM2MzZiNjVkNDY3MDk1YzQ1
14
- OWE4MDY2YjkzOGJjYTZlOWNlM2U3ZTE5MzdlMWU2ZTY4YTk2ZWEwNWNjY2Y2
15
- Mzc3MTMzZTEwNWU4NjcxMzBmOGJiMjA5M2VhYWYyNWRmY2RkNjA=
6
+ metadata.gz: c51c13c2d41f6c78f0ce02d68d921e10ae694617354fc3d80049404cdb9ef222fac36a00bd76ad7b06fc2649aa4b0065878bb0abfbf0522482d618539fe57e7a
7
+ data.tar.gz: 49761b76cf989b896339674f0046d676a06d74bf774c8a0d7c50192699d36ad565866137f6d3e603ddb42da5f0a8616ee65cae278c0b5c33ea1ad170e49c7e48
@@ -1,38 +1,23 @@
1
+ # User interface related settings
1
2
  :ui:
2
- :interactive: true
3
- :per_page: 20
4
- :history_file: './log/history'
3
+ :interactive: false
4
+ :history_file: 'log/history'
5
5
 
6
- :watch_plain: true # enable/disable color output of logger in Clamp commands
6
+ :watch_plain: true
7
7
 
8
- :log_dir: './log'
8
+ :reload_cache: false
9
+
10
+ :log_dir: 'log'
9
11
  :log_level: 'error'
10
- :log_api_calls: false
11
- :log_size: 5 # MB
12
12
 
13
- :request_timeout: 120
13
+ # Mark translated strings with X characters (for developers)
14
+ #:mark_translated: false
14
15
 
15
16
  :foreman:
16
17
  :enable_module: true
17
- :host: 'http://katello:3000'
18
- :username: 'admin'
19
- :password: 'changeme'
20
18
 
21
19
  :katello:
22
20
  :enable_module: true
23
21
 
24
22
  :csv:
25
23
  :enable_module: true
26
- :host: 'http://katello:3000'
27
- :username: 'admin'
28
- :password: 'changeme'
29
- :products_sync: false
30
-
31
- :gutterball:
32
- :enable_module: true
33
-
34
- :foreman_remote_execution:
35
- :enable_module: true
36
-
37
- :import:
38
- :enable_module: true
@@ -1,9 +1,17 @@
1
1
  module HammerCLICsv
2
2
  class CsvCommand
3
3
  class ActivationKeysCommand < BaseCommand
4
+ include ::HammerCLICsv::Utils::Subscriptions
5
+
4
6
  command_name 'activation-keys'
5
7
  desc _('import or export activation keys')
6
8
 
9
+ def self.supported?
10
+ true
11
+ end
12
+
13
+ option %w(--itemized-subscriptions), :flag, _('Export one subscription per row, only process update subscriptions on import')
14
+
7
15
  ORGANIZATION = 'Organization'
8
16
  DESCRIPTION = 'Description'
9
17
  LIMIT = 'Limit'
@@ -13,46 +21,71 @@ module HammerCLICsv
13
21
  SERVICELEVEL = "Service Level"
14
22
  RELEASEVER = "Release Version"
15
23
  AUTOATTACH = "Auto-Attach"
16
- SUBSCRIPTIONS = 'Subscriptions'
17
-
18
- def export
19
- CSV.open(option_file || '/dev/stdout', 'wb', {:force_quotes => false}) do |csv|
20
- csv << [NAME, ORGANIZATION, DESCRIPTION, LIMIT, ENVIRONMENT, CONTENTVIEW,
21
- HOSTCOLLECTIONS, AUTOATTACH, SERVICELEVEL, RELEASEVER, SUBSCRIPTIONS]
22
- @api.resource(:organizations).call(:index, {
23
- :per_page => 999999
24
- })['results'].each do |organization|
25
- next if option_organization && organization['name'] != option_organization
26
-
27
- @api.resource(:activation_keys).call(:index, {
28
- 'per_page' => 999999,
29
- 'organization_id' => organization['id']
30
- })['results'].each do |activationkey|
31
- name = namify(activationkey['name'])
32
- count = 1
33
- description = activationkey['description']
34
- limit = activationkey['unlimited_content_hosts'] ? 'Unlimited' : activationkey['max_content_hosts']
35
- environment = activationkey['environment']['label']
36
- contentview = activationkey['content_view']['name']
37
- hostcollections = export_column(activationkey, 'host_collections', 'name')
38
- autoattach = activationkey['auto_attach'] ? 'Yes' : 'No'
39
- servicelevel = activationkey['service_level']
40
- releasever = activationkey['release_version']
41
- subscriptions = CSV.generate do |column|
42
- column << @api.resource(:subscriptions).call(:index, {
43
- 'organization_id' => organization['id'],
44
- 'activation_key_id' => activationkey['id']
45
- })['results'].collect do |subscription|
46
- amount = subscription['amount'] == 0 ? 'Automatic' : subscription['amount']
47
- sku = subscription['product_id'].match(/\A[0-9]/) ? 'Custom' : subscription['product_id']
48
- "#{amount}|#{sku}|#{subscription['product_name']}"
49
- end
50
- end
51
- subscriptions.delete!("\n")
52
- csv << [name, count, organization['name'], description, limit, environment, contentview,
53
- hostcollections, servicelevel, releasever, autoattach, subscriptions]
24
+
25
+ def export(csv)
26
+ if option_itemized_subscriptions?
27
+ export_itemized_subscriptions csv
28
+ else
29
+ export_all csv
30
+ end
31
+ end
32
+
33
+ def export_itemized_subscriptions(csv)
34
+ csv << shared_headers + [Utils::Subscriptions::SUBS_NAME, Utils::Subscriptions::SUBS_TYPE,
35
+ Utils::Subscriptions::SUBS_QUANTITY, Utils::Subscriptions::SUBS_SKU,
36
+ Utils::Subscriptions::SUBS_CONTRACT, Utils::Subscriptions::SUBS_ACCOUNT,
37
+ Utils::Subscriptions::SUBS_START, Utils::Subscriptions::SUBS_END]
38
+ iterate_activationkeys(csv) do |activationkey|
39
+ columns = shared_columns(activationkey)
40
+ @api.resource(:subscriptions).call(:index, {
41
+ 'organization_id' => activationkey['organization']['id'],
42
+ 'activation_key_id' => activationkey['id']
43
+ })['results'].collect do |subscription|
44
+ subscription_type = subscription['product_id'].to_i == 0 ? 'Red Hat' : 'Custom'
45
+ subscription_type += ' Guest' if subscription['type'] == 'STACK_DERIVED'
46
+ subscription_type += ' Temporary' if subscription['type'] == 'UNMAPPED_GUEST'
47
+ amount = (subscription['quantity_attached'].nil? || subscription['quantity_attached'] < 1) ? 'Automatic' : subscription['quantity_attached']
48
+ csv << columns +
49
+ [subscription['product_name'], subscription_type, amount,
50
+ subscription['product_id'], subscription['contract_number'], subscription['account_number'],
51
+ DateTime.parse(subscription['start_date']),
52
+ DateTime.parse(subscription['end_date'])]
53
+ end
54
+ end
55
+ end
56
+
57
+ def export_all(csv)
58
+ csv << shared_headers + [SUBSCRIPTIONS]
59
+ iterate_activationkeys(csv) do |activationkey|
60
+ subscriptions = CSV.generate do |column|
61
+ column << @api.resource(:subscriptions).call(:index, {
62
+ 'organization_id' => activationkey['organization']['id'],
63
+ 'activation_key_id' => activationkey['id']
64
+ })['results'].collect do |subscription|
65
+ amount = (subscription['quantity_attached'].nil? || subscription['quantity_attached'] < 1) ? 'Automatic' : subscription['quantity_attached']
66
+ "#{amount}"\
67
+ "|#{subscription['product_id']}"\
68
+ "|#{subscription['product_name']}"\
69
+ "|#{subscription['contract_number']}|#{subscription['account_number']}"
54
70
  end
55
71
  end
72
+ subscriptions.delete!("\n")
73
+ csv << shared_columns(activationkey) + [subscriptions]
74
+ end
75
+ end
76
+
77
+ def iterate_activationkeys(csv)
78
+ @api.resource(:organizations).call(:index, {
79
+ :per_page => 999999
80
+ })['results'].each do |organization|
81
+ next if option_organization && organization['name'] != option_organization
82
+
83
+ @api.resource(:activation_keys).call(:index, {
84
+ 'per_page' => 999999,
85
+ 'organization_id' => organization['id']
86
+ })['results'].each do |activationkey|
87
+ yield activationkey
88
+ end
56
89
  end
57
90
  end
58
91
 
@@ -60,55 +93,64 @@ module HammerCLICsv
60
93
  @existing = {}
61
94
 
62
95
  thread_import do |line|
63
- create_activationkeys_from_csv(line)
96
+ create_from_csv(line)
64
97
  end
65
98
  end
66
99
 
67
- def create_activationkeys_from_csv(line)
100
+ def create_from_csv(line)
68
101
  return if option_organization && line[ORGANIZATION] != option_organization
69
102
 
70
- if !@existing[line[ORGANIZATION]]
71
- @existing[line[ORGANIZATION]] = {}
72
- @api.resource(:activation_keys).call(:index, {
73
- 'per_page' => 999999,
74
- 'organization_id' => foreman_organization(:name => line[ORGANIZATION])
75
- })['results'].each do |activationkey|
76
- @existing[line[ORGANIZATION]][activationkey['name']] = activationkey['id'] if activationkey
77
- end
78
- end
103
+ update_existing(line)
79
104
 
80
105
  count(line[COUNT]).times do |number|
81
106
  name = namify(line[NAME], number)
82
107
 
83
- params = {
84
- 'organization_id' => foreman_organization(:name => line[ORGANIZATION]),
85
- 'name' => name,
86
- 'environment_id' => lifecycle_environment(line[ORGANIZATION],
87
- :name => line[ENVIRONMENT]),
88
- 'content_view_id' => katello_contentview(line[ORGANIZATION],
89
- :name => line[CONTENTVIEW]),
90
- 'description' => line[DESCRIPTION],
91
- 'unlimited_content_hosts' => (line[LIMIT] == 'Unlimited') ? true : false,
92
- 'max_content_hosts' => (line[LIMIT] == 'Unlimited') ? nil : line[LIMIT].to_i
93
- }
94
- params['auto_attach'] = (line[AUTOATTACH] == 'Yes' ? true : false) if params['auto_attach']
95
- params['service_level'] = line[SERVICELEVEL].nil? || line[SERVICELEVEL].empty? ? nil : line[SERVICELEVEL]
96
- params['release_version'] = line[RELEASEVER].nil? || line[RELEASEVER].empty? ? nil : line[RELEASEVER]
97
- if !@existing[line[ORGANIZATION]].include? name
98
- print _("Creating activation key '%{name}'...") % {:name => name} if option_verbose?
99
- activationkey = @api.resource(:activation_keys).call(:create, params)
100
- @existing[line[ORGANIZATION]][activationkey['name']] = activationkey['id']
108
+ if option_itemized_subscriptions?
109
+ update_itemized_subscriptions(name, line)
101
110
  else
102
- print _("Updating activation key '%{name}'...") % {:name => name} if option_verbose?
103
- params['id'] = @existing[line[ORGANIZATION]][name]
104
- activationkey = @api.resource(:activation_keys).call(:update, params)
111
+ update_or_create(name, line)
105
112
  end
113
+ end
114
+ end
106
115
 
107
- update_subscriptions(activationkey, line)
108
- update_groups(activationkey, line)
116
+ def update_itemized_subscriptions(name, line)
117
+ raise _("Activation key '%{name}' must already exist with --itemized_subscriptions") % {:name => name} unless @existing[line[ORGANIZATION]].include? name
109
118
 
110
- puts _('done') if option_verbose?
119
+ print(_("Updating subscriptions for activation key '%{name}'...") % {:name => name}) if option_verbose?
120
+ activationkey = @api.resource(:activation_keys).call(:show, {:id => @existing[line[ORGANIZATION]][name]})
121
+ update_subscriptions(activationkey, line, false)
122
+ puts _('done') if option_verbose?
123
+ end
124
+
125
+ def update_or_create(name, line)
126
+ params = {
127
+ 'organization_id' => foreman_organization(:name => line[ORGANIZATION]),
128
+ 'name' => name,
129
+ 'environment_id' => lifecycle_environment(line[ORGANIZATION],
130
+ :name => line[ENVIRONMENT]),
131
+ 'content_view_id' => katello_contentview(line[ORGANIZATION],
132
+ :name => line[CONTENTVIEW]),
133
+ 'description' => line[DESCRIPTION],
134
+ 'unlimited_content_hosts' => (line[LIMIT] == 'Unlimited') ? true : false,
135
+ 'max_content_hosts' => (line[LIMIT] == 'Unlimited') ? nil : line[LIMIT].to_i
136
+ }
137
+ params['auto_attach'] = (line[AUTOATTACH] == 'Yes' ? true : false) if params['auto_attach']
138
+ params['service_level'] = line[SERVICELEVEL].nil? || line[SERVICELEVEL].empty? ? nil : line[SERVICELEVEL]
139
+ params['release_version'] = line[RELEASEVER].nil? || line[RELEASEVER].empty? ? nil : line[RELEASEVER]
140
+ if !@existing[line[ORGANIZATION]].include? name
141
+ print _("Creating activation key '%{name}'...") % {:name => name} if option_verbose?
142
+ activationkey = @api.resource(:activation_keys).call(:create, params)
143
+ @existing[line[ORGANIZATION]][activationkey['name']] = activationkey['id']
144
+ else
145
+ print _("Updating activation key '%{name}'...") % {:name => name} if option_verbose?
146
+ params['id'] = @existing[line[ORGANIZATION]][name]
147
+ activationkey = @api.resource(:activation_keys).call(:update, params)
111
148
  end
149
+
150
+ update_subscriptions(activationkey, line, true)
151
+ update_groups(activationkey, line)
152
+
153
+ puts _('done') if option_verbose?
112
154
  end
113
155
 
114
156
  def update_groups(activationkey, line)
@@ -123,38 +165,121 @@ module HammerCLICsv
123
165
  end
124
166
  end
125
167
 
126
- def update_subscriptions(activationkey, line)
127
- if line[SUBSCRIPTIONS] && line[SUBSCRIPTIONS] != ''
128
- subscriptions = CSV.parse_line(line[SUBSCRIPTIONS], {:skip_blanks => true}).collect do |subscription_details|
129
- (amount, sku, name) = subscription_details.split('|')
130
- {
131
- :id => katello_subscription(line[ORGANIZATION], :name => name),
132
- :quantity => (amount.nil? || amount == 'Automatic') ? 0 : amount
133
- }
168
+ def update_subscriptions(activationkey, line, remove_existing)
169
+ existing_subscriptions = @api.resource(:subscriptions).call(:index, {
170
+ 'organization_id' => foreman_organization(:name => line[ORGANIZATION]),
171
+ 'per_page' => 999999,
172
+ 'activation_key_id' => activationkey['id']
173
+ })['results']
174
+ if remove_existing && existing_subscriptions.length > 0
175
+ existing_subscriptions.map! do |existing_subscription|
176
+ {:id => existing_subscription['id'], :quantity => existing_subscription['quantity_consumed']}
134
177
  end
178
+ @api.resource(:activation_keys).call(:remove_subscriptions, {
179
+ 'id' => activationkey['id'],
180
+ 'subscriptions' => existing_subscriptions
181
+ })
182
+ existing_subscriptions = []
183
+ end
135
184
 
136
- existing_subscriptions = @api.resource(:subscriptions).call(:index, {
137
- 'organization_id' => foreman_organization(:name => line[ORGANIZATION]),
138
- 'per_page' => 999999,
139
- 'activation_key_id' => activationkey['id']
140
- })['results']
141
- if existing_subscriptions.length > 0
142
- @api.resource(:activation_keys).call(:remove_subscriptions, {
143
- 'id' => activationkey['id'],
144
- 'subscriptions' => existing_subscriptions
145
- })
185
+ if line[Utils::Subscriptions::SUBS_NAME].nil? && line[Utils::Subscriptions::SUBS_SKU].nil?
186
+ all_in_one_subscription(activationkey, existing_subscriptions, line)
187
+ else
188
+ single_subscription(activationkey, existing_subscriptions, line)
189
+ end
190
+ end
191
+
192
+ def single_subscription(activationkey, existing_subscriptions, line)
193
+ already_attached = false
194
+ if line[Utils::Subscriptions::SUBS_SKU]
195
+ already_attached = existing_subscriptions.detect do |subscription|
196
+ line[Utils::Subscriptions::SUBS_SKU] == subscription['product_id']
197
+ end
198
+ elsif line[Utils::Subscriptions::SUBS_NAME]
199
+ already_attached = existing_subscriptions.detect do |subscription|
200
+ line[Utils::Subscriptions::SUBS_NAME] == subscription['name']
146
201
  end
202
+ end
203
+ if already_attached
204
+ print _(" '%{name}' already attached...") % {:name => already_attached['name']}
205
+ return
206
+ end
147
207
 
148
- @api.resource(:activation_keys).call(:add_subscriptions, {
149
- 'id' => activationkey['id'],
150
- 'subscriptions' => subscriptions
151
- })
208
+ available_subscriptions = @api.resource(:subscriptions).call(:index, {
209
+ 'organization_id' => activationkey['organization']['id'],
210
+ 'activation_key_id' => activationkey['id'],
211
+ 'available_for' => 'activation_key'
212
+ })['results']
213
+
214
+ matches = matches_by_sku_and_name([], line, available_subscriptions)
215
+ matches = matches_by_type(matches, line)
216
+ matches = matches_by_account(matches, line)
217
+ matches = matches_by_contract(matches, line)
218
+ matches = matches_by_quantity(matches, line)
219
+
220
+ raise _("No matching subscriptions") if matches.empty?
221
+
222
+ match = matches[0]
223
+ print _(" attaching '%{name}'...") % {:name => match['name']} if option_verbose?
224
+
225
+ @api.resource(:activation_keys).call(:add_subscriptions, {
226
+ 'id' => activationkey['id'],
227
+ 'subscriptions' => [match]
228
+ })
229
+ end
230
+
231
+ def all_in_one_subscription(activationkey, existing_subscriptions, line)
232
+ return if line[SUBSCRIPTIONS].nil? || line[SUBSCRIPTIONS].empty?
233
+
234
+ subscriptions = CSV.parse_line(line[SUBSCRIPTIONS], {:skip_blanks => true}).collect do |details|
235
+ (amount, sku, name, contract, account) = split_subscription_details(details)
236
+ {
237
+ :id => get_subscription(line[ORGANIZATION], :name => name),
238
+ :quantity => (amount.nil? || amount == 'Automatic') ? 0 : amount.to_i
239
+ }
152
240
  end
241
+
242
+ @api.resource(:activation_keys).call(:add_subscriptions, {
243
+ 'id' => activationkey['id'],
244
+ 'subscriptions' => subscriptions
245
+ })
153
246
  end
154
247
 
155
248
  def usage_limit(limit)
156
249
  Integer(limit) rescue -1
157
250
  end
251
+
252
+ def shared_headers
253
+ [NAME, ORGANIZATION, DESCRIPTION, LIMIT, ENVIRONMENT, CONTENTVIEW,
254
+ HOSTCOLLECTIONS, AUTOATTACH, SERVICELEVEL, RELEASEVER]
255
+ end
256
+
257
+ def shared_columns(activationkey)
258
+ name = namify(activationkey['name'])
259
+ organization = activationkey['organization']['name']
260
+ description = activationkey['description']
261
+ limit = activationkey['unlimited_content_hosts'] ? 'Unlimited' : activationkey['max_content_hosts']
262
+ environment = activationkey['environment'].nil? ? nil : activationkey['environment']['label']
263
+ contentview = activationkey['content_view'].nil? ? nil : activationkey['content_view']['name']
264
+ hostcollections = export_column(activationkey, 'host_collections', 'name')
265
+ autoattach = activationkey['auto_attach'] ? 'Yes' : 'No'
266
+ servicelevel = activationkey['service_level']
267
+ releasever = activationkey['release_version']
268
+ [name, organization, description, limit, environment, contentview, hostcollections,
269
+ autoattach, servicelevel, releasever]
270
+ end
271
+
272
+ def update_existing(line)
273
+ if !@existing[line[ORGANIZATION]]
274
+ @existing[line[ORGANIZATION]] = {}
275
+ @api.resource(:activation_keys).call(:index, {
276
+ 'per_page' => 999999,
277
+ 'organization_id' => foreman_organization(:name => line[ORGANIZATION])
278
+ })['results'].each do |activationkey|
279
+ @existing[line[ORGANIZATION]][activationkey['name']] = activationkey['id'] if activationkey
280
+ end
281
+ end
282
+ end
158
283
  end
159
284
  end
160
285
  end
@@ -6,15 +6,13 @@ module HammerCLICsv
6
6
 
7
7
  OPERATINGSYSTEMS = 'Operating Systems'
8
8
 
9
- def export
10
- CSV.open(option_file || '/dev/stdout', 'wb', {:force_quotes => true}) do |csv|
11
- csv << [NAME, OPERATINGSYSTEMS]
12
- @api.resource(:architectures).call(:index, {:per_page => 999999})['results'].each do |architecture|
13
- architecture = @api.resource(:architectures).call(:show, {:id => architecture['id']})
14
- name = architecture['name']
15
- operatingsystems = export_column(architecture, 'operatingsystems', 'title')
16
- csv << [name, operatingsystems]
17
- end
9
+ def export(csv)
10
+ csv << [NAME, OPERATINGSYSTEMS]
11
+ @api.resource(:architectures).call(:index, {:per_page => 999999})['results'].each do |architecture|
12
+ architecture = @api.resource(:architectures).call(:show, {:id => architecture['id']})
13
+ name = architecture['name']
14
+ operatingsystems = export_column(architecture, 'operatingsystems', 'title')
15
+ csv << [name, operatingsystems]
18
16
  end
19
17
  end
20
18
 
@@ -56,8 +54,6 @@ module HammerCLICsv
56
54
  end
57
55
  print "done\n" if option_verbose?
58
56
  end
59
- rescue RuntimeError => e
60
- raise "#{e}\n #{line}"
61
57
  end
62
58
  end
63
59
  end
@@ -12,9 +12,11 @@ module HammerCLICsv
12
12
  option %w(--threads), 'THREAD_COUNT', 'Number of threads to hammer with',
13
13
  :default => 1, :hidden => true
14
14
  option %w(--export), :flag, 'Export current data instead of importing'
15
- option %w(--file), 'FILE_NAME', 'CSV file (default to /dev/stdout with --csv-export, otherwise required)'
16
- option %w(--prefix), 'PREFIX', 'Prefix for all name columns'
15
+ option %w(--file), 'FILE_NAME', 'CSV file (default to /dev/stdout with --export, otherwise required)'
16
+ option %w(--prefix), 'PREFIX', 'Prefix for all name columns',
17
+ :hidden => true
17
18
  option %w(--organization), 'ORGANIZATION', _('Only process organization matching this name')
19
+ option %w(--continue-on-error), :flag, _('Continue processing even if individual resource error')
18
20
 
19
21
  option %w(--csv-file), 'FILE_NAME', 'Option --csv-file is deprecated. Use --file',
20
22
  :deprecated => "Use --file", :hidden => true,
@@ -23,10 +25,22 @@ module HammerCLICsv
23
25
  :deprecated => "Use --export", :hidden => true,
24
26
  :attribute_name => :option_export
25
27
 
26
-
27
28
  NAME = 'Name'
28
29
  COUNT = 'Count'
29
30
 
31
+ def self.supported?
32
+ false
33
+ end
34
+
35
+ def supported?
36
+ self.class.supported?
37
+ end
38
+
39
+ def help
40
+ print_message _('**** This command is unsupported and is provided as tech preview. ****') unless supported?
41
+ super
42
+ end
43
+
30
44
  def execute
31
45
  @server = (HammerCLI::Settings.settings[:_params] &&
32
46
  HammerCLI::Settings.settings[:_params][:host]) ||
@@ -61,7 +75,19 @@ module HammerCLICsv
61
75
  })
62
76
  end
63
77
 
64
- option_export? ? export : import
78
+ if option_export?
79
+ if option_file
80
+ CSV.open(option_file, 'wb', {:force_quotes => false}) do |csv|
81
+ export csv
82
+ end
83
+ else
84
+ CSV do |csv|
85
+ export csv
86
+ end
87
+ end
88
+ else
89
+ import
90
+ end
65
91
  HammerCLI::EX_OK
66
92
  end
67
93
 
@@ -129,8 +155,12 @@ module HammerCLICsv
129
155
  lines = csv[start_index...finish_index].clone
130
156
  splits << Thread.new do
131
157
  lines.each do |line|
132
- if line[name_column || NAME][0] != '#'
158
+ next if line[name_column || NAME][0] == '#'
159
+ begin
133
160
  yield line
161
+ rescue RuntimeError => e
162
+ message = "#{e}\n#{line}"
163
+ option_continue_on_error? ? $stderr.puts("Error: #{message}") : raise(message)
134
164
  end
135
165
  end
136
166
  end
@@ -484,6 +514,38 @@ module HammerCLICsv
484
514
  result
485
515
  end
486
516
 
517
+ def foreman_medium(options = {})
518
+ @media ||= {}
519
+
520
+ if options[:name]
521
+ return nil if options[:name].nil? || options[:name].empty?
522
+ options[:id] = @media[options[:name]]
523
+ if !options[:id]
524
+ ptable = @api.resource(:media).call(:index, {
525
+ :per_page => 999999,
526
+ 'search' => "name=\"#{options[:name]}\""
527
+ })['results']
528
+ raise "Partition table '#{options[:name]}' not found" if !ptable || ptable.empty?
529
+ options[:id] = ptable[0]['id']
530
+ @media[options[:name]] = options[:id]
531
+ end
532
+ result = options[:id]
533
+ elsif options[:id]
534
+ return nil if options[:id].nil?
535
+ options[:name] = @media.key(options[:id])
536
+ if !options[:name]
537
+ ptable = @api.resource(:media).call(:show, {'id' => options[:id]})
538
+ options[:name] = ptable['name']
539
+ @media[options[:name]] = options[:id]
540
+ end
541
+ result = options[:name]
542
+ elsif !options[:name] && !options[:id]
543
+ result = ''
544
+ end
545
+
546
+ result
547
+ end
548
+
487
549
  def foreman_host(options = {})
488
550
  @query_hosts ||= {}
489
551
 
@@ -737,42 +799,6 @@ module HammerCLICsv
737
799
  result
738
800
  end
739
801
 
740
- def katello_subscription(organization, options = {})
741
- @subscriptions ||= {}
742
- @subscriptions[organization] ||= {}
743
-
744
- if options[:name]
745
- return nil if options[:name].nil? || options[:name].empty?
746
- options[:id] = @subscriptions[organization][options[:name]]
747
- if !options[:id]
748
- results = @api.resource(:subscriptions).call(:index, {
749
- :per_page => 999999,
750
- 'organization_id' => foreman_organization(:name => organization),
751
- 'search' => "name = \"#{options[:name]}\""
752
- })
753
- raise "No subscriptions match '#{options[:name]}'" if results['subtotal'] == 0
754
- raise "Too many subscriptions match '#{options[:name]}'" if results['subtotal'] > 1
755
- subscription = results['results'][0]
756
- @subscriptions[organization][options[:name]] = subscription['id']
757
- options[:id] = @subscriptions[organization][options[:name]]
758
- raise "Subscription '#{options[:name]}' not found" if !options[:id]
759
- end
760
- result = options[:id]
761
- else
762
- return nil if options[:id].nil?
763
- options[:name] = @subscriptions.key(options[:id])
764
- if !options[:name]
765
- subscription = @api.resource(:subscriptions).call(:show, {'id' => options[:id]})
766
- raise "Subscription '#{options[:name]}' not found" if !subscription || subscription.empty?
767
- options[:name] = subscription['name']
768
- @subscriptions[options[:name]] = options[:id]
769
- end
770
- result = options[:name]
771
- end
772
-
773
- result
774
- end
775
-
776
802
  def katello_hostcollection(organization, options = {})
777
803
  @hostcollections ||= {}
778
804
  @hostcollections[organization] ||= {}