Project

General

Profile

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

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

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
      end
70

    
71
      private
72

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

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

    
101
        return product
102
      end
103

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

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

    
131
        sync_repository(line, product['name'], repository)
132
      end
133

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

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

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

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

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

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

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

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

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

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

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

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

    
265
      end
266

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

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

    
289
        content_set
290
      end
291
    end
292
  end
293
end