Project

General

Profile

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

hammer-cli-csv / lib / hammer_cli_csv / base.rb @ df47dd3c

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
        finish_index = csv.length if finish_index > csv.length
84
        if start_index <= finish_index
85
          lines = csv[start_index...finish_index].clone
86
          splits << Thread.new do
87
            lines.each do |line|
88
              if line[NAME][0] != '#'
89
                yield line
90
              end
91
            end
92
          end
93
        end
94
      end
95

    
96
      splits.each do |thread|
97
        thread.join
98
      end
99
    end
100

    
101
    def foreman_organization(options = {})
102
      @organizations ||= {}
103

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

    
129
      result
130
    end
131

    
132
    def foreman_location(options = {})
133
      @locations ||= {}
134

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

    
160
      result
161
    end
162

    
163
    def foreman_role(options = {})
164
      @roles ||= {}
165

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

    
191
      result
192
    end
193

    
194
    def foreman_permission(options = {})
195
      @permissions ||= {}
196

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

    
222
      result
223
    end
224

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

    
235
      nil
236
    end
237

    
238
    def foreman_environment(options = {})
239
      @environments ||= {}
240

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

    
266
      result
267
    end
268

    
269
    def foreman_operatingsystem(options = {})
270
      @operatingsystems ||= {}
271

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

    
302
      result
303
    end
304

    
305
    def foreman_architecture(options = {})
306
      @architectures ||= {}
307

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

    
333
      result
334
    end
335

    
336
    def foreman_domain(options = {})
337
      @domains ||= {}
338

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

    
364
      result
365
    end
366

    
367
    def foreman_partitiontable(options = {})
368
      @ptables ||= {}
369

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

    
396
      result
397
    end
398

    
399
    def lifecycle_environment(organization, options = {})
400
      @lifecycle_environments ||= {}
401
      @lifecycle_environments[organization] ||= {
402
      }
403

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

    
430
      result
431
    end
432

    
433
    def katello_contentview(organization, options = {})
434
      @contentviews ||= {}
435
      @contentviews[organization] ||= {}
436

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

    
463
      result
464
    end
465

    
466
    def katello_subscription(organization, options = {})
467
      @subscriptions ||= {}
468
      @subscriptions[organization] ||= {}
469

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

    
499
      result
500
    end
501

    
502
    def katello_hostcollection(organization, options = {})
503
      @hostcollections ||= {}
504
      @hostcollections[organization] ||= {}
505

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

    
534
      result
535
    end
536

    
537
    def build_os_name(name, major, minor)
538
      name += " #{major}" if major && major != ''
539
      name += ".#{minor}" if minor && minor != ''
540
      name
541
    end
542

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

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

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

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

    
583
    def associate_organizations(id, organizations, name)
584
      return if organizations.nil?
585

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

    
605
    def associate_locations(id, locations, name)
606
      return if locations.nil?
607

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

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