Project

General

Profile

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

hammer-cli-csv / lib / hammer_cli_csv / base.rb @ 8c7ba4af

1
# Copyright 2013-2014 Red Hat, Inc.
2
#
3
# This software is licensed to you under the GNU General Public
4
# License as published by the Free Software Foundation; either version
5
# 2 of the License (GPLv2) or (at your option) any later version.
6
# There is NO WARRANTY for this software, express or implied,
7
# including the implied warranties of MERCHANTABILITY,
8
# NON-INFRINGEMENT, or FITNESS FOR A PARTICULAR PURPOSE. You should
9
# have received a copy of GPLv2 along with this software; if not, see
10
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
11

    
12
require 'apipie-bindings'
13
require 'hammer_cli'
14
require 'json'
15
require 'csv'
16
require 'hammer_cli_csv/csv'
17

    
18
module HammerCLICsv
19
  class BaseCommand < HammerCLI::Apipie::Command
20
    option %w(-v --verbose), :flag, 'be verbose'
21
    option %w(--threads), 'THREAD_COUNT', 'Number of threads to hammer with', :default => 1
22
    option %w(--csv-export), :flag, 'Export current data instead of importing'
23
    option %w(--csv-file), 'FILE_NAME', 'CSV file (default to /dev/stdout with --csv-export, otherwise required)'
24
    option %w(--prefix), 'PREFIX', 'Prefix for all name columns'
25
    option %w(--server), 'SERVER', 'Server URL'
26
    option %w(-u --username), 'USERNAME', 'Username to access server'
27
    option %w(-p --password), 'PASSWORD', 'Password to access server'
28

    
29
    NAME = 'Name'
30
    COUNT = 'Count'
31

    
32
    def execute
33
      if !option_csv_file
34
        if option_csv_export?
35
          # rubocop:disable UselessAssignment
36
          option_csv_file = '/dev/stdout'
37
        else
38
          # rubocop:disable UselessAssignment
39
          option_csv_file = '/dev/stdin'
40
        end
41
      end
42

    
43
      @api = ApipieBindings::API.new({
44
                                       :uri => option_server || HammerCLI::Settings.get(:csv, :host),
45
                                       :username => option_username || HammerCLI::Settings.get(:csv, :username),
46
                                       :password => option_password || HammerCLI::Settings.get(:csv, :password),
47
                                       :api_version => 2
48
                                     })
49

    
50
      option_csv_export? ? export : import
51
      HammerCLI::EX_OK
52
    end
53

    
54
    def namify(name_format, number = 0)
55
      if name_format.index('%')
56
        name = name_format % number
57
      else
58
        name = name_format
59
      end
60
      name = "#{option_prefix}#{name}" if option_prefix
61
      name
62
    end
63

    
64
    def labelize(name)
65
      name.gsub(/[^a-z0-9\-_]/i, '_')
66
    end
67

    
68
    def thread_import(return_headers = false)
69
      csv = []
70
      CSV.foreach(option_csv_file || '/dev/stdin', {
71
                                                     :skip_blanks => true,
72
                                                     :headers => :first_row,
73
                                                     :return_headers => return_headers
74
                                                   }) do |line|
75
        csv << line
76
      end
77
      lines_per_thread = csv.length / option_threads.to_i + 1
78
      splits = []
79

    
80
      option_threads.to_i.times do |current_thread|
81
        start_index = ((current_thread) * lines_per_thread).to_i
82
        finish_index = ((current_thread + 1) * lines_per_thread).to_i
83
        lines = csv[start_index...finish_index].clone
84
        splits << Thread.new do
85
          lines.each do |line|
86
            if line[NAME][0] != '#'
87
              yield line
88
            end
89
          end
90
        end
91
      end
92

    
93
      splits.each do |thread|
94
        thread.join
95
      end
96
    end
97

    
98
    def foreman_organization(options = {})
99
      @organizations ||= {}
100

    
101
      if options[:name]
102
        return nil if options[:name].nil? || options[:name].empty?
103
        options[:id] = @organizations[options[:name]]
104
        if !options[:id]
105
          organization = @api.resource(:organizations).call(:index, {
106
                                                              :per_page => 999999,
107
                                                              'search' => "name=\"#{options[:name]}\""
108
                                                            })['results']
109
          raise "Organization '#{options[:name]}' not found" if !organization || organization.empty?
110
          options[:id] = organization[0]['id']
111
          @organizations[options[:name]] = options[:id]
112
        end
113
        result = options[:id]
114
      else
115
        return nil if options[:id].nil?
116
        options[:name] = @organizations.key(options[:id])
117
        if !options[:name]
118
          organization = @api.resource(:organizations).call(:show, {'id' => options[:id]})
119
          raise "Organization 'id=#{options[:id]}' not found" if !organization || organization.empty?
120
          options[:name] = organization['name']
121
          @organizations[options[:name]] = options[:id]
122
        end
123
        result = options[:name]
124
      end
125

    
126
      result
127
    end
128

    
129
    def foreman_location(options = {})
130
      @locations ||= {}
131

    
132
      if options[:name]
133
        return nil if options[:name].nil? || options[:name].empty?
134
        options[:id] = @locations[options[:name]]
135
        if !options[:id]
136
          location = @api.resource(:locations).call(:index, {
137
                                                      :per_page => 999999,
138
                                                      'search' => "name=\"#{options[:name]}\""
139
                                                    })['results']
140
          raise "Location '#{options[:name]}' not found" if !location || location.empty?
141
          options[:id] = location[0]['id']
142
          @locations[options[:name]] = options[:id]
143
        end
144
        result = options[:id]
145
      else
146
        return nil if options[:id].nil?
147
        options[:name] = @locations.key(options[:id])
148
        if !options[:name]
149
          location = @api.resource(:locations).call(:show, {'id' => options[:id]})
150
          raise "Location 'id=#{options[:id]}' not found" if !location || location.empty?
151
          options[:name] = location['name']
152
          @locations[options[:name]] = options[:id]
153
        end
154
        result = options[:name]
155
      end
156

    
157
      result
158
    end
159

    
160
    def foreman_role(options = {})
161
      @roles ||= {}
162

    
163
      if options[:name]
164
        return nil if options[:name].nil? || options[:name].empty?
165
        options[:id] = @roles[options[:name]]
166
        if !options[:id]
167
          role = @api.resource(:roles).call(:index, {
168
                                              :per_page => 999999,
169
                                              'search' => "name=\"#{options[:name]}\""
170
                                            })['results']
171
          raise "Role '#{options[:name]}' not found" if !role || role.empty?
172
          options[:id] = role[0]['id']
173
          @roles[options[:name]] = options[:id]
174
        end
175
        result = options[:id]
176
      else
177
        return nil if options[:id].nil?
178
        options[:name] = @roles.key(options[:id])
179
        if !options[:name]
180
          role = @api.resource(:roles).call(:show, {'id' => options[:id]})
181
          raise "Role 'id=#{options[:id]}' not found" if !role || role.empty?
182
          options[:name] = role['name']
183
          @roles[options[:name]] = options[:id]
184
        end
185
        result = options[:name]
186
      end
187

    
188
      result
189
    end
190

    
191
    def foreman_permission(options = {})
192
      @permissions ||= {}
193

    
194
      if options[:name]
195
        return nil if options[:name].nil? || options[:name].empty?
196
        options[:id] = @permissions[options[:name]]
197
        if !options[:id]
198
          permission = @api.resource(:permissions).call(:index, {
199
                                                          :per_page => 999999,
200
                                                          'name' => options[:name]
201
                                                        })['results']
202
          raise "Permission '#{options[:name]}' not found" if !permission || permission.empty?
203
          options[:id] = permission[0]['id']
204
          @permissions[options[:name]] = options[:id]
205
        end
206
        result = options[:id]
207
      else
208
        return nil if options[:id].nil?
209
        options[:name] = @permissions.key(options[:id])
210
        if !options[:name]
211
          permission = @api.resource(:permissions).call(:show, {'id' => options[:id]})
212
          raise "Permission 'id=#{options[:id]}' not found" if !permission || permission.empty?
213
          options[:name] = permission['name']
214
          @permissions[options[:name]] = options[:id]
215
        end
216
        result = options[:name]
217
      end
218

    
219
      result
220
    end
221

    
222
    def foreman_filter(role, resource, search)
223
      search = nil if search && search.empty?
224
      filters = @api.resource(:filters).call(:index, {
225
                                               :per_page => 999999,
226
                                               'search' => "role=\"#{role}\""
227
                                             })['results']
228
      filters.each do |filter|
229
        return filter['id'] if filter['resource_type'] == resource && filter['search'] == search
230
      end
231

    
232
      nil
233
    end
234

    
235
    def foreman_environment(options = {})
236
      @environments ||= {}
237

    
238
      if options[:name]
239
        return nil if options[:name].nil? || options[:name].empty?
240
        options[:id] = @environments[options[:name]]
241
        if !options[:id]
242
          environment = @api.resource(:environments).call(:index, {
243
                                                            :per_page => 999999,
244
                                                            'search' => "name=\"#{ options[:name] }\""
245
                                                          })['results']
246
          raise "Puppet environment '#{options[:name]}' not found" if !environment || environment.empty?
247
          options[:id] = environment[0]['id']
248
          @environments[options[:name]] = options[:id]
249
        end
250
        result = options[:id]
251
      else
252
        return nil if options[:id].nil?
253
        options[:name] = @environments.key(options[:id])
254
        if !options[:name]
255
          environment = @api.resource(:environments).call(:show, {'id' => options[:id]})
256
          raise "Puppet environment '#{options[:name]}' not found" if !environment || environment.empty?
257
          options[:name] = environment['name']
258
          @environments[options[:name]] = options[:id]
259
        end
260
        result = options[:name]
261
      end
262

    
263
      result
264
    end
265

    
266
    def foreman_operatingsystem(options = {})
267
      @operatingsystems ||= {}
268

    
269
      if options[:name]
270
        return nil if options[:name].nil? || options[:name].empty?
271
        options[:id] = @operatingsystems[options[:name]]
272
        if !options[:id]
273
          (osname, major, minor) = split_os_name(options[:name])
274
          search = "name=\"#{osname}\" and major=\"#{major}\" and minor=\"#{minor}\""
275
          operatingsystems = @api.resource(:operatingsystems).call(:index, {
276
                                                                     :per_page => 999999,
277
                                                                     'search' => search
278
                                                                   })['results']
279
          operatingsystem = operatingsystems[0]
280
          raise "Operating system '#{options[:name]}' not found" if !operatingsystem || operatingsystem.empty?
281
          options[:id] = operatingsystem['id']
282
          @operatingsystems[options[:name]] = options[:id]
283
        end
284
        result = options[:id]
285
      else
286
        return nil if options[:id].nil?
287
        options[:name] = @operatingsystems.key(options[:id])
288
        if !options[:name]
289
          operatingsystem = @api.resource(:operatingsystems).call(:show, {'id' => options[:id]})
290
          raise "Operating system 'id=#{options[:id]}' not found" if !operatingsystem || operatingsystem.empty?
291
          options[:name] = build_os_name(operatingsystem['name'],
292
                                         operatingsystem['major'],
293
                                         operatingsystem['minor'])
294
          @operatingsystems[options[:name]] = options[:id]
295
        end
296
        result = options[:name]
297
      end
298

    
299
      result
300
    end
301

    
302
    def foreman_architecture(options = {})
303
      @architectures ||= {}
304

    
305
      if options[:name]
306
        return nil if options[:name].nil? || options[:name].empty?
307
        options[:id] = @architectures[options[:name]]
308
        if !options[:id]
309
          architecture = @api.resource(:architectures).call(:index, {
310
                                                              :per_page => 999999,
311
                                                              'search' => "name=\"#{options[:name]}\""
312
                                                            })['results']
313
          raise "Architecture '#{options[:name]}' not found" if !architecture || architecture.empty?
314
          options[:id] = architecture[0]['id']
315
          @architectures[options[:name]] = options[:id]
316
        end
317
        result = options[:id]
318
      else
319
        return nil if options[:id].nil?
320
        options[:name] = @architectures.key(options[:id])
321
        if !options[:name]
322
          architecture = @api.resource(:architectures).call(:show, {'id' => options[:id]})
323
          raise "Architecture 'id=#{options[:id]}' not found" if !architecture || architecture.empty?
324
          options[:name] = architecture['name']
325
          @architectures[options[:name]] = options[:id]
326
        end
327
        result = options[:name]
328
      end
329

    
330
      result
331
    end
332

    
333
    def foreman_domain(options = {})
334
      @domains ||= {}
335

    
336
      if options[:name]
337
        return nil if options[:name].nil? || options[:name].empty?
338
        options[:id] = @domains[options[:name]]
339
        if !options[:id]
340
          domain = @api.resource(:domains).call(:index, {
341
                                                  :per_page => 999999,
342
                                                  'search' => "name=\"#{options[:name]}\""
343
                                                })['results']
344
          raise "Domain '#{options[:name]}' not found" if !domain || domain.empty?
345
          options[:id] = domain[0]['id']
346
          @domains[options[:name]] = options[:id]
347
        end
348
        result = options[:id]
349
      else
350
        return nil if options[:id].nil?
351
        options[:name] = @domains.key(options[:id])
352
        if !options[:name]
353
          domain = @api.resource(:domains).call(:show, {'id' => options[:id]})
354
          raise "Domain 'id=#{options[:id]}' not found" if !domain || domain.empty?
355
          options[:name] = domain['name']
356
          @domains[options[:name]] = options[:id]
357
        end
358
        result = options[:name]
359
      end
360

    
361
      result
362
    end
363

    
364
    def foreman_partitiontable(options = {})
365
      @ptables ||= {}
366

    
367
      if options[:name]
368
        return nil if options[:name].nil? || options[:name].empty?
369
        options[:id] = @ptables[options[:name]]
370
        if !options[:id]
371
          ptable = @api.resource(:ptables).call(:index, {
372
                                                  :per_page => 999999,
373
                                                  'search' => "name=\"#{options[:name]}\""
374
                                                })['results']
375
          raise "Partition table '#{options[:name]}' not found" if !ptable || ptable.empty?
376
          options[:id] = ptable[0]['id']
377
          @ptables[options[:name]] = options[:id]
378
        end
379
        result = options[:id]
380
      elsif options[:id]
381
        return nil if options[:id].nil?
382
        options[:name] = @ptables.key(options[:id])
383
        if !options[:name]
384
          ptable = @api.resource(:ptables).call(:show, {'id' => options[:id]})
385
          options[:name] = ptable['name']
386
          @ptables[options[:name]] = options[:id]
387
        end
388
        result = options[:name]
389
      elsif !options[:name] && !options[:id]
390
        result = ''
391
      end
392

    
393
      result
394
    end
395

    
396
    def lifecycle_environment(organization, options = {})
397
      @lifecycle_environments ||= {}
398
      @lifecycle_environments[organization] ||= {
399
      }
400

    
401
      if options[:name]
402
        return nil if options[:name].nil? || options[:name].empty?
403
        options[:id] = @lifecycle_environments[organization][options[:name]]
404
        if !options[:id]
405
          @api.resource(:lifecycle_environments).call(:index, {
406
                                                        :per_page => 999999,
407
                                                        'organization_id' => foreman_organization(:name => organization),
408
                                                      })['results'].each do |environment|
409
            @lifecycle_environments[organization][environment['name']] = environment['id']
410
          end
411
          options[:id] = @lifecycle_environments[organization][options[:name]]
412
          raise "Lifecycle environment '#{options[:name]}' not found" if !options[:id]
413
        end
414
        result = options[:id]
415
      else
416
        return nil if options[:id].nil?
417
        options[:name] = @lifecycle_environments.key(options[:id])
418
        if !options[:name]
419
          environment = @api.resource(:lifecycle_environments).call(:show, {'id' => options[:id]})
420
          raise "Lifecycle environment '#{options[:name]}' not found" if !environment || environment.empty?
421
          options[:name] = environment['name']
422
          @lifecycle_environments[options[:name]] = options[:id]
423
        end
424
        result = options[:name]
425
      end
426

    
427
      result
428
    end
429

    
430
    def katello_contentview(organization, options = {})
431
      @contentviews ||= {}
432
      @contentviews[organization] ||= {}
433

    
434
      if options[:name]
435
        return nil if options[:name].nil? || options[:name].empty?
436
        options[:id] = @contentviews[organization][options[:name]]
437
        if !options[:id]
438
          @api.resource(:content_views).call(:index, {
439
                                               :per_page => 999999,
440
                                               'organization_id' => foreman_organization(:name => organization)
441
                                             })['results'].each do |contentview|
442
            @contentviews[organization][contentview['name']] = contentview['id']
443
          end
444
          options[:id] = @contentviews[organization][options[:name]]
445
          raise "Content view '#{options[:name]}' not found" if !options[:id]
446
        end
447
        result = options[:id]
448
      else
449
        return nil if options[:id].nil?
450
        options[:name] = @contentviews.key(options[:id])
451
        if !options[:name]
452
          contentview = @api.resource(:content_views).call(:show, {'id' => options[:id]})
453
          raise "Puppet contentview '#{options[:name]}' not found" if !contentview || contentview.empty?
454
          options[:name] = contentview['name']
455
          @contentviews[options[:name]] = options[:id]
456
        end
457
        result = options[:name]
458
      end
459

    
460
      result
461
    end
462

    
463
    def katello_subscription(organization, options = {})
464
      @subscriptions ||= {}
465
      @subscriptions[organization] ||= {}
466

    
467
      if options[:name]
468
        return nil if options[:name].nil? || options[:name].empty?
469
        options[:id] = @subscriptions[organization][options[:name]]
470
        if !options[:id]
471
          results = @api.resource(:subscriptions).call(:index, {
472
                                                         :per_page => 999999,
473
                                                         'organization_id' => foreman_organization(:name => organization),
474
                                                         'search' => "name:\"#{options[:name]}\""
475
                                                       })
476
          raise "No subscriptions match '#{options[:name]}'" if results['subtotal'] == 0
477
          raise "Too many subscriptions match '#{options[:name]}'" if results['subtotal'] > 1
478
          subscription = results['results'][0]
479
          @subscriptions[organization][options[:name]] = subscription['id']
480
          options[:id] = @subscriptions[organization][options[:name]]
481
          raise "Subscription '#{options[:name]}' not found" if !options[:id]
482
        end
483
        result = options[:id]
484
      else
485
        return nil if options[:id].nil?
486
        options[:name] = @subscriptions.key(options[:id])
487
        if !options[:name]
488
          subscription = @api.resource(:subscriptions).call(:show, {'id' => options[:id]})
489
          raise "Subscription '#{options[:name]}' not found" if !subscription || subscription.empty?
490
          options[:name] = subscription['name']
491
          @subscriptions[options[:name]] = options[:id]
492
        end
493
        result = options[:name]
494
      end
495

    
496
      result
497
    end
498

    
499
    def katello_hostcollection(organization, options = {})
500
      @hostcollections ||= {}
501
      @hostcollections[organization] ||= {}
502

    
503
      if options[:name]
504
        return nil if options[:name].nil? || options[:name].empty?
505
        options[:id] = @hostcollections[organization][options[:name]]
506
        if !options[:id]
507
          @api.resource(:host_collections).call(:index,
508
                  {
509
                    :per_page => 999999,
510
                    'organization_id' => foreman_organization(:name => organization),
511
                    'search' => "name:\"#{options[:name]}\""
512
                  })['results'].each do |hostcollection|
513
            @hostcollections[organization][hostcollection['name']] = hostcollection['id'] if hostcollection
514
          end
515
          options[:id] = @hostcollections[organization][options[:name]]
516
          raise "System group '#{options[:name]}' not found" if !options[:id]
517
        end
518
        result = options[:id]
519
      else
520
        return nil if options[:id].nil?
521
        options[:name] = @hostcollections.key(options[:id])
522
        if !options[:name]
523
          hostcollection = @api.resource(:host_collections).call(:show, {'id' => options[:id]})
524
          raise "System group '#{options[:name]}' not found" if !hostcollection || hostcollection.empty?
525
          options[:name] = hostcollection['name']
526
          @hostcollections[options[:name]] = options[:id]
527
        end
528
        result = options[:name]
529
      end
530

    
531
      result
532
    end
533

    
534
    def build_os_name(name, major, minor)
535
      name += " #{major}" if major && major != ''
536
      name += ".#{minor}" if minor && minor != ''
537
      name
538
    end
539

    
540
    # "Red Hat 6.4" => "Red Hat", "6", "4"
541
    # "Red Hat 6"   => "Red Hat", "6", ''
542
    def split_os_name(name)
543
      tokens = name.split(' ')
544
      is_number = Float(tokens[-1]) rescue false
545
      if is_number
546
        (major, minor) = tokens[-1].split('.').flatten
547
        name = tokens[0...-1].join(' ')
548
      else
549
        name = tokens.join(' ')
550
      end
551
      [name, major || '', minor || '']
552
    end
553

    
554
    def export_column(object, name, field)
555
      return '' unless object[name]
556
      values = CSV.generate do |column|
557
        column << object[name].collect do |fields|
558
          fields[field]
559
        end
560
      end
561
      values.delete!("\n")
562
    end
563

    
564
    def collect_column(column)
565
      return [] if column.nil? || column.empty?
566
      CSV.parse_line(column, {:skip_blanks => true}).collect do |value|
567
        yield value
568
      end
569
    end
570

    
571
    def pluralize(name)
572
      case name
573
      when /smart_proxy/
574
        'smart_proxies'
575
      else
576
        "#{name}s"
577
      end
578
    end
579

    
580
    def associate_organizations(id, organizations, name)
581
      return if organizations.nil?
582

    
583
      associations ||= {}
584
      CSV.parse_line(organizations).each do |organization|
585
        organization_id = foreman_organization(:name => organization)
586
        if associations[organization].nil?
587
          associations[organization] = @api.resource(:organizations).call(:show, {'id' => organization_id})[pluralize(name)].collect do |reference_object|
588
            reference_object['id']
589
          end
590
        end
591
        associations[organization] += [id] if !associations[organization].include? id
592
        @api.resource(:organizations)
593
          .call(:update, {
594
                  'id' => organization_id,
595
                  'organization' => {
596
                    "#{name}_ids" => associations[organization]
597
                  }
598
                })
599
      end if organizations && !organizations.empty?
600
    end
601

    
602
    def associate_locations(id, locations, name)
603
      return if locations.nil?
604

    
605
      associations ||= {}
606
      CSV.parse_line(locations).each do |location|
607
        location_id = foreman_location(:name => location)
608
        if associations[location].nil?
609
          associations[location] = @api.resource(:locations).call(:show, {'id' => location_id})[pluralize(name)].collect do |reference_object|
610
            reference_object['id']
611
          end
612
        end
613
        associations[location] += [id] if !associations[location].include? id
614

    
615
        @api.resource(:locations)
616
          .call(:update, {
617
                  'id' => location_id,
618
                  'location' => {
619
                    "#{name}_ids" => associations[location]
620
                  }
621
                })
622
      end if locations && !locations.empty?
623
    end
624
  end
625
end