Project

General

Profile

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

hammer-cli-csv / lib / hammer_cli_csv / activation_keys.rb @ 611959a3

1
module HammerCLICsv
2
  class CsvCommand
3
    class ActivationKeysCommand < BaseCommand
4
      include ::HammerCLICsv::Utils::Subscriptions
5

    
6
      command_name 'activation-keys'
7
      desc         _('import or export activation keys')
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

    
15
      ORGANIZATION = 'Organization'
16
      DESCRIPTION = 'Description'
17
      LIMIT = 'Limit'
18
      ENVIRONMENT = 'Environment'
19
      CONTENTVIEW = 'Content View'
20
      HOSTCOLLECTIONS = 'Host Collections'
21
      SERVICELEVEL = "Service Level"
22
      RELEASEVER = "Release Version"
23
      AUTOATTACH = "Auto-Attach"
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']}"
70
            end
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
89
        end
90
      end
91

    
92
      def import
93
        @existing = {}
94

    
95
        thread_import do |line|
96
          create_from_csv(line)
97
        end
98
      end
99

    
100
      def create_from_csv(line)
101
        return if option_organization && line[ORGANIZATION] != option_organization
102

    
103
        update_existing(line)
104

    
105
        count(line[COUNT]).times do |number|
106
          name = namify(line[NAME], number)
107

    
108
          if option_itemized_subscriptions?
109
            update_itemized_subscriptions(name, line)
110
          else
111
            update_or_create(name, line)
112
          end
113
        end
114
      end
115

    
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
118

    
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)
148
        end
149

    
150
        update_subscriptions(activationkey, line, true)
151
        update_groups(activationkey, line)
152

    
153
        puts _('done') if option_verbose?
154
      end
155

    
156
      def update_groups(activationkey, line)
157
        if line[HOSTCOLLECTIONS] && line[HOSTCOLLECTIONS] != ''
158
          # TODO: note that existing system groups are not removed
159
          CSV.parse_line(line[HOSTCOLLECTIONS], {:skip_blanks => true}).each do |name|
160
            @api.resource(:activation_keys).call(:add_host_collections, {
161
                'id' => activationkey['id'],
162
                'host_collection_ids' => [katello_hostcollection(line[ORGANIZATION], :name => name)]
163
            })
164
          end
165
        end
166
      end
167

    
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']}
177
          end
178
          @api.resource(:activation_keys).call(:remove_subscriptions, {
179
            'id' => activationkey['id'],
180
            'subscriptions' => existing_subscriptions
181
          })
182
          existing_subscriptions = []
183
        end
184

    
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']
201
          end
202
        end
203
        if already_attached
204
          print _(" '%{name}' already attached...") % {:name => already_attached['name']}
205
          return
206
        end
207

    
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
          }
240
        end
241

    
242
        @api.resource(:activation_keys).call(:add_subscriptions, {
243
                                               'id' => activationkey['id'],
244
                                               'subscriptions' => subscriptions
245
                                             })
246
      end
247

    
248
      def usage_limit(limit)
249
        Integer(limit) rescue -1
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
283
    end
284
  end
285
end