Project

General

Profile

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

hammer-cli-csv / lib / hammer_cli_csv / products.rb @ c06f1783

1
module HammerCLICsv
2
  class CsvCommand
3
    class ProductsCommand < BaseCommand
4
      command_name 'products'
5
      desc         _('import or export products')
6

    
7
      option %w(--[no-]sync), :flag, _('Sync product repositories (default true)'), :default => true
8
      LABEL = 'Label'
9
      ORGANIZATION = 'Organization'
10
      REPOSITORY = 'Repository'
11
      REPOSITORY_TYPE = 'Repository Type'
12
      CONTENT_SET = 'Content Set'
13
      RELEASE = 'Release'
14
      REPOSITORY_URL = 'Repository Url'
15
      DESCRIPTION = 'Description'
16

    
17
      def export(csv)
18
        csv << [NAME, LABEL, ORGANIZATION, DESCRIPTION, REPOSITORY, REPOSITORY_TYPE,
19
                CONTENT_SET, RELEASE, REPOSITORY_URL]
20
        # TODO: DOWNLOAD_POLICY
21
        @api.resource(:organizations).call(:index, {
22
            :per_page => 999999
23
        })['results'].each do |organization|
24
          next if option_organization && organization['name'] != option_organization
25
          @api.resource(:products).call(:index, {
26
              'per_page' => 999999,
27
              'enabled' => true,
28
              'organization_id' => organization['id']
29
          })['results'].each do |product|
30
            @api.resource(:repositories).call(:index, {
31
                'product_id' => product['id'],
32
                'organization_id' => organization['id']
33
            })['results'].each do |repository|
34
              repository = @api.resource(:repositories).call(:show, {:id => repository['id']})
35
              if repository['product_type'] == 'custom'
36
                repository_type = "Custom #{repository['content_type'].capitalize}"
37
                content_set = nil
38
              else
39
                repository_type = "Red Hat #{repository['content_type'].capitalize}"
40
                content_set = get_content_set(organization, product, repository)
41
              end
42
              release = repository['minor'] #=~ /Server/ ? repository['minor'] : "#{repository['major']}.#{repository['minor']}"
43
              csv << [product['name'], product['label'], organization['name'],
44
                      product['description'], repository['name'], repository_type,
45
                      content_set, release, repository['url']]
46
            end
47
          end
48
        end
49
      end
50

    
51
      def import
52
        @existing_products = {}
53
        @existing_repositories = {}
54

    
55
        thread_import do |line|
56
          create_products_from_csv(line)
57
        end
58
      end
59

    
60
      def create_products_from_csv(line)
61
        return if option_organization && line[ORGANIZATION] != option_organization
62

    
63
        count(line[COUNT]).times do |number|
64
          product = create_or_update_product(line, number)
65
          create_or_update_repository(line, number, product)
66
          puts _('done') if option_verbose?
67
        end
68

    
69
      rescue RuntimeError => e
70
        raise "#{e}\n       #{line}"
71
      end
72

    
73
      private
74

    
75
      def create_or_update_product(line, number)
76
        if !@existing_products[line[ORGANIZATION]]
77
          get_existing_products(line)
78
        end
79

    
80
        product_name = namify(line[NAME], number)
81
        if line[REPOSITORY_TYPE] =~ /Red Hat/
82
          product = enable_red_hat_product(line, product_name)
83
        else
84
          # TODO: product label? other?
85
          params = {
86
            :name => product_name,
87
            'organization_id' => foreman_organization(:name => line[ORGANIZATION])
88
          }
89
          params[:description] = line[DESCRIPTION] if !line[DESCRIPTION].nil? &&
90
                                                      !line[DESCRIPTION].empty?
91
          product = @existing_products[line[ORGANIZATION]][product_name]
92
          if product.nil?
93
            print _("Creating product '%{name}'...") % {:name => product_name} if option_verbose?
94
            product = @api.resource(:products).call(:create, params)
95
            @existing_products[line[ORGANIZATION]][product_name] = product
96
          else
97
            print _("Updating product '%{name}'...") % {:name => product_name} if option_verbose?
98
            params[:id] = product['id']
99
            @api.resource(:products).call(:update, params)
100
          end
101
        end
102

    
103
        return product
104
      end
105

    
106
      def create_or_update_repository(line, number, product)
107
        repository_name = namify(line[REPOSITORY], number)
108
        repository = get_repository(line, product['name'], product['id'], repository_name)
109
        if !repository
110
          if line[REPOSITORY_TYPE] =~ /Red Hat/
111
            raise _("Red Hat product '%{product_name}' does not have repository '%{repository_name}'") %
112
              {:product_name => product['name'], :repository_name => repository_name}
113
          end
114

    
115
          if option_verbose?
116
            print _("Creating repository '%{repository_name}' in product '%{product_name}'...") %
117
              {:repository_name => repository_name, :product_name => product['name']}
118
          end
119
          # TODO: add repo label column (optional)
120
          repository = @api.resource(:repositories).call(:create, {
121
              'organization_id' => foreman_organization(:name => line[ORGANIZATION]),
122
              'name' => repository_name,
123
              'label' => labelize(repository_name),
124
              'product_id' => product['id'],
125
              'url' => line[REPOSITORY_URL],
126
              'content_type' => content_type(line[REPOSITORY_TYPE])
127
          })
128
          @existing_repositories[line[ORGANIZATION] + product['name']][line[LABEL]] = repository
129
        else
130
          # TODO: update url
131
        end
132

    
133
        sync_repository(line, product['name'], repository)
134
      end
135

    
136
      def get_existing_products(line)
137
        @existing_products[line[ORGANIZATION]] = {}
138
        @api.resource(:products).call(:index, {
139
            'per_page' => 999999,
140
            'organization_id' => foreman_organization(:name => line[ORGANIZATION]),
141
            'enabled' => true
142
        })['results'].each do |product|
143
          @existing_products[line[ORGANIZATION]][product['name']] = product
144

    
145
          @api.resource(:repositories).call(:index, {
146
              'page_size' => 999999, 'paged' => true,
147
              'organization_id' => foreman_organization(:name => line[ORGANIZATION]),
148
              'product_id' => product['id'],
149
              'enabled' => true,
150
              'library' => true
151
          })['results'].each do |repository|
152
            @existing_repositories[line[ORGANIZATION] + product['name']] ||= {}
153
            @existing_repositories[line[ORGANIZATION] + product['name']][repository['label']] = repository['id']
154
          end
155
        end
156
      end
157

    
158
      def get_repository(line, product_name, product_id, repository_name)
159
        @existing_repositories[line[ORGANIZATION] + product_name] ||= {}
160
        if !@existing_repositories[line[ORGANIZATION] + product_name][repository_name]
161
          @api.resource(:repositories).call(:index, {
162
              'organization_id' => foreman_organization(:name => line[ORGANIZATION]),
163
              'library' => true,
164
              'all' => false,
165
              'product_id' => product_id
166
          })['results'].each do |repository|
167
            @existing_repositories[line[ORGANIZATION] + product_name][repository['name']] = repository
168
          end
169
        end
170
        @existing_repositories[line[ORGANIZATION] + product_name][repository_name]
171
      end
172

    
173
      def enable_red_hat_product(line, product_name)
174
        product = @existing_products[line[ORGANIZATION]][product_name]
175
        unless product
176
          product = @api.resource(:products).call(:index, {
177
              'per_page' => 999999,
178
              'organization_id' => foreman_organization(:name => line[ORGANIZATION]),
179
              'name' => product_name
180
          })['results'][0]
181
          raise _("Red Hat product '%{product_name}' does not exist") %
182
              {:product_name => product_name} if product.nil?
183
          @existing_repositories[line[ORGANIZATION] + product['name']] = {}
184
        end
185
        product = @api.resource(:products).call(:show, {:id => product['id']})
186

    
187
        results = @api.resource(:repository_sets).call(:index, {
188
            'per_page' => 999999,
189
            'organization_id' => foreman_organization(:name => line[ORGANIZATION]),
190
            'product_id' => product['id'],
191
            'name' => line[CONTENT_SET]
192
        })['results']
193
        raise "No match for content set '#{line[CONTENT_SET]}'" if results.length == 0
194
        raise "Multiple matches for content set '#{line[CONTENT_SET]}'" if results.length != 1
195
        repository_set = results[0]
196

    
197
        repository = repository_set['repositories'].find do |repo|
198
          repo['name'] == line[REPOSITORY]
199
        end
200

    
201
        if repository.nil?
202
          print _('Enabling repository %{name}...') % {:name => line[REPOSITORY]} if option_verbose?
203
          product_content = product['product_content'].find do |content|
204
            content['content']['name'] == line[CONTENT_SET]
205
          end
206
          raise "No match for content set '#{line[CONTENT_SET]}'" if !product_content
207

    
208
          basearch,releasever = parse_basearch_releasever(line[REPOSITORY])
209
          params = {
210
              'id' => product_content['content']['id'],
211
              'product_id' => product['id'],
212
              'basearch' => basearch,
213
              'releasever' => releasever
214
          }
215
          @api.resource(:repository_sets).call(:enable, params)
216
          puts _('done') if option_verbose?
217
        else
218
          puts _('Repository %{name} already enabled') % {:name => repository['name']} if option_verbose?
219
        end
220
        product
221
      end
222

    
223
      # basearch and releasever are required for repo set enable. The repository ends with, for example,
224
      # "x86_64 6.1" or "ia64 6 Server"
225
      def parse_basearch_releasever(content_set)
226
        pieces = content_set.split
227
        if pieces[-1] == 'Server'
228
          return pieces[-3], "#{pieces[-2]}#{pieces[-1]}"
229
        else
230
          return pieces[-2], pieces[-1]
231
        end
232
      end
233

    
234
      def content_type(repository_type)
235
        case repository_type
236
        when /yum/i
237
          'yum'
238
        when /puppet/i
239
          'puppet'
240
        else
241
          raise "Unrecognized repository type '#{repository_type}'"
242
        end
243
      end
244

    
245
      def sync_repository(line, name, repository)
246
        if (HammerCLI::Settings.get(:csv, :products_sync) == true || HammerCLI::Settings.get(:csv, :products_sync).nil?) &&
247
            option_sync?
248
          if option_verbose?
249
            print _("syncing repository '%{repository_name}' in product '%{name}'...") %
250
              {:repository_name => repository['name'], :name => name}
251
          end
252
          if repository['last_sync']
253
            print _("previously synced, skipping...") if option_verbose?
254
          else
255
            exec_sync_repository(line, repository)
256
          end
257
        end
258
      end
259

    
260
      def exec_sync_repository(line, repository)
261
        args = %W{ --server #{ @server } --username #{ @username } --password #{ @password }
262
                   repository synchronize
263
                   --id #{ repository['id'] }
264
                   --organization-id #{ foreman_organization(:name => line[ORGANIZATION]) } }
265
        hammer.run(args)
266

    
267
      rescue RuntimeError => e
268
        raise "#{e}\n       #{line}"
269
      end
270

    
271
      def get_content_set(organization, product, repository)
272
        organization_id = organization['id']
273
        product_id = product['id']
274
        @content_sets ||={}
275
        @content_sets[organization_id] ||= {}
276
        if @content_sets[organization_id][product_id].nil?
277
          @content_sets[organization_id][product_id] = {}
278
          @api.resource(:repository_sets).call(:index, {
279
              'per_page' => 999999,
280
              'organization_id' => organization_id,
281
              'product_id' => product_id
282
          })['results'].each do |repository_set|
283
            content_set = repository_set['name']
284
            repository_set['repositories'].each do |repo|
285
              @content_sets[organization_id][product_id][repo['id']] = content_set
286
            end
287
          end
288
        end
289
        content_set = @content_sets[organization_id][product_id][repository['id']]
290

    
291
        raise "Content set for repository '#{repository['name']}' not found in product '#{product['name']}" unless content_set
292

    
293
        content_set
294
      end
295
    end
296
  end
297
end