openstack_taster 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 847cb5a20b108d188a612e254b26c30d553efd90
4
- data.tar.gz: 36527d054338e59a9a6410541a3e532b1b096caa
3
+ metadata.gz: 2f23a80c511e0cdf522d55d4e398b7f8ba95d9d5
4
+ data.tar.gz: 4337f06484caba1510f9e0fa6b46ce1708c7e107
5
5
  SHA512:
6
- metadata.gz: 94623399b8a7d779b9db8e96410dcaffb4c144c2c41d0b2ba6ecbbdd6d5da1e100ac55b67be4efe9fb959a4b1f6ca7926928715e8bd56c1f60fbedb83b68f975
7
- data.tar.gz: ab0ae7a389592b96ae43e5a712c2ed5af1a438cd277dae8ee6126d3bd6492aaa5ade5713d1b7db3ace62aab69cb590e5ff7b22961a3366ce6f0d9c7caa361d67
6
+ metadata.gz: 970215a90be5debc307211a6733e02a016949ea7fd2165bcef7ae4c9b3d5a6a6ce9879abd9ff4100d15a2042682ac8995b08924306e73b5fb01040cd3474437d
7
+ data.tar.gz: da521ffbf76233edca3d11d94ff634ea99b1a4306aa9942b3a2288d11bd15e168e4df432ad15de6636ec8e151d7d9332175ff521ed5890ce4898d0ea0fee3a1a
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'optparse'
5
+ require 'English'
5
6
 
6
7
  suites = {
7
8
  'security' => 'Runs the security test suite',
@@ -12,11 +13,21 @@ settings = {}
12
13
  ARGV << '-h' if ARGV.empty?
13
14
 
14
15
  parser = OptionParser.new do |opts|
15
- opts.banner = "Usage: openstack_taster <image_name> {suite_name} [--create-snapshot]\n" \
16
- " or: openstack_taster <image_name> [--create-snapshot]\n" \
17
- ' or: openstack_taster <option>'
16
+ opts.banner = "Usage: openstack_taster -i <image_name> -u <ssh_user> [-s suite_name] [--create-snapshot]\n"
18
17
  opts.separator('')
19
18
  opts.separator('Available Arguments:')
19
+ opts.on('-i', '--image=<image_name>', 'Image name to use for the instance.') do |i|
20
+ settings[:image_name] = i
21
+ end
22
+ opts.on('-u', '--user=<ssh_user>', 'SSH username for logging into the instance.') do |u|
23
+ settings[:ssh_user] = u
24
+ end
25
+ opts.on('-s', '--suite=<suite_name>', 'Test suite(s) to use.') do |s|
26
+ settings[:suite] = s
27
+ end
28
+ opts.on('-f', '--flavor=<flavor>', 'Set flavor for the instance.') do |f|
29
+ settings[:flavor] = f
30
+ end
20
31
  opts.on('-c', '--create-snapshot', 'Create snapshot upon test failure.') do
21
32
  settings[:create_snapshot] = true
22
33
  end
@@ -32,7 +43,16 @@ parser = OptionParser.new do |opts|
32
43
  end
33
44
 
34
45
  begin
35
- params = parser.parse!
46
+ parser.parse!
47
+ mandatory = [:image_name, :ssh_user]
48
+ missing = mandatory.select { |p| settings[p].nil? }
49
+ raise OptionParser::MissingArgument, missing.join(', ') unless missing.empty? || settings[:exit] == true
50
+ rescue OptionParser::MissingArgument
51
+ puts
52
+ puts 'Error: ' + $ERROR_INFO.to_s
53
+ puts
54
+ puts parser unless settings[:exit] == true
55
+ exit 1
36
56
  rescue OptionParser::InvalidOption => io
37
57
  puts io.message
38
58
  puts
@@ -48,16 +68,11 @@ end
48
68
  exit if settings[:exit] # exit inside OptionParser causes problems.
49
69
 
50
70
  begin
51
- case params.length
52
- when 1
53
- image_name = params[0]
71
+ if settings[:suite].nil?
54
72
  suites.each_key { |suite| settings[suite.to_sym] = true }
55
- when 2
56
- raise "#{params[1]} is not a test suite!" unless suites.include? params[1]
57
- image_name = params[0]
58
- settings[params[1].to_sym] = true
59
73
  else
60
- raise 'Incorrect format!'
74
+ raise "#{settings[:suite]} is not a test suite!" unless suites.include? settings[:suite]
75
+ settings[settings[:suite].to_sym] = true
61
76
  end
62
77
  rescue StandardError => e
63
78
  puts e.message
@@ -66,6 +81,8 @@ rescue StandardError => e
66
81
  exit(1)
67
82
  end
68
83
 
84
+ settings[:flavor] = 'm1.tiny' if settings[:flavor].nil?
85
+
69
86
  require 'fog/openstack'
70
87
  require 'openstack_taster'
71
88
 
@@ -95,6 +112,6 @@ image = Fog::Image::OpenStack.new(OPENSTACK_CREDS)
95
112
  network = Fog::Network::OpenStack.new(OPENSTACK_CREDS)
96
113
 
97
114
  exit OpenStackTaster.new(
98
- compute, volume, image, network,
115
+ compute, volume, image, network, ENV['OS_NETWORK_REF'], settings[:flavor],
99
116
  SSH_KEYS, LOG_DIR
100
- ).taste(image_name, settings)
117
+ ).taste(settings[:image_name], settings)
@@ -9,13 +9,15 @@ require 'inspec'
9
9
 
10
10
  # @author Andrew Tolvstad, Samarendra Hedaoo, Cody Holliday
11
11
  class OpenStackTaster
12
- INSTANCE_FLAVOR_NAME = 'm1.tiny'
13
- INSTANCE_NETWORK_NAME = 'public'
14
12
  INSTANCE_NAME_PREFIX = 'taster'
15
13
  INSTANCE_VOLUME_MOUNT_POINT = '/mnt/taster_volume'
16
14
 
15
+ VOLUME_NAME_PREFIX = 'test_volume'
16
+ VOLUME_DESCRIPTION = 'Test volume for OpenStack Taster.'
17
+ VOLUME_SIZE = 1
18
+ VOLUME_FILESYSTEM = 'ext4'
17
19
  VOLUME_TEST_FILE_NAME = 'info'
18
- VOLUME_TEST_FILE_CONTENTS = nil # Contents would be something like 'test-vol-1 on openpower8.osuosl.bak'
20
+ VOLUME_TEST_FILE_CONTENTS = 'test-volume'
19
21
  TIMEOUT_INSTANCE_CREATE = 20
20
22
  TIMEOUT_VOLUME_ATTACH = 10
21
23
  TIMEOUT_VOLUME_PERSIST = 20
@@ -32,6 +34,8 @@ class OpenStackTaster
32
34
  volume_service,
33
35
  image_service,
34
36
  network_service,
37
+ network_name,
38
+ instance_flavor,
35
39
  ssh_keys,
36
40
  log_dir
37
41
  )
@@ -40,6 +44,7 @@ class OpenStackTaster
40
44
  @image_service = image_service
41
45
  @network_service = network_service
42
46
 
47
+ @network_name = network_name || 'public'
43
48
  @volumes = @volume_service.volumes
44
49
 
45
50
  @ssh_keypair = ssh_keys[:keypair]
@@ -50,9 +55,9 @@ class OpenStackTaster
50
55
  @log_dir = log_dir + "/#{@session_id}"
51
56
 
52
57
  @instance_flavor = @compute_service.flavors
53
- .select { |flavor| flavor.name == INSTANCE_FLAVOR_NAME }.first
58
+ .select { |flavor| flavor.name == instance_flavor }.first
54
59
  @instance_network = @network_service.networks
55
- .select { |network| network.name == INSTANCE_NETWORK_NAME }.first
60
+ .select { |network| network.name == @network_name }.first
56
61
  end
57
62
 
58
63
  # Taste a specified image
@@ -74,14 +79,12 @@ class OpenStackTaster
74
79
 
75
80
  abort("#{image_name} is not an available image.") if image.nil?
76
81
 
77
- distro_user_name = image.name.downcase.gsub(/[^a-z].*$/, '') # truncate downcased name at first non-alpha char
78
- distro_arch = image.name.downcase.slice(-2, 2)
82
+ distro = image.name.downcase[/^[a-z]*/]
79
83
  instance_name = format(
80
- '%s-%s-%s-%s',
84
+ '%s-%s-%s',
81
85
  INSTANCE_NAME_PREFIX,
82
86
  Time.new.strftime(TIME_SLUG_FORMAT),
83
- distro_user_name,
84
- distro_arch
87
+ distro
85
88
  )
86
89
 
87
90
  FileUtils.mkdir_p(@log_dir) unless Dir.exist?(@log_dir)
@@ -91,7 +94,8 @@ class OpenStackTaster
91
94
  error_log(
92
95
  instance_logger,
93
96
  'info',
94
- "Tasting #{image.name} as '#{instance_name}' with username '#{distro_user_name}'.\nBuilding...",
97
+ "Tasting #{image.name} as '#{instance_name}' with username '#{settings[:ssh_user]}' and " \
98
+ "flavor '#{settings[:flavor]}'.\nBuilding...",
95
99
  true
96
100
  )
97
101
 
@@ -121,8 +125,8 @@ class OpenStackTaster
121
125
 
122
126
  # Run tests
123
127
  return_values = []
124
- return_values.push taste_security(instance, distro_user_name) if settings[:security]
125
- return_values.push taste_volumes(instance, distro_user_name) if settings[:volumes]
128
+ return_values.push taste_security(instance, settings[:ssh_user]) if settings[:security]
129
+ return_values.push taste_volumes(instance, settings[:ssh_user]) if settings[:volumes]
126
130
 
127
131
  if settings[:create_snapshot] && !return_values.all?
128
132
  error_log(instance.logger, 'info', "Tests failed for instance '#{instance.id}'. Creating image...", true)
@@ -152,7 +156,7 @@ class OpenStackTaster
152
156
  def taste_security(instance, username)
153
157
  opts = {
154
158
  'backend' => 'ssh',
155
- 'host' => instance.addresses['public'].first['addr'],
159
+ 'host' => instance.addresses[@network_name].first['addr'],
156
160
  'port' => 22,
157
161
  'user' => username,
158
162
  'sudo' => true,
@@ -251,37 +255,68 @@ class OpenStackTaster
251
255
  # @param username [String] the username to use when logging into the instance
252
256
  # @return [Boolean] Whether or not the tests succeeded
253
257
  def taste_volumes(instance, username)
254
- mount_failures = @volumes.reject do |volume|
255
- if volume.attachments.any?
256
- error_log(instance.logger, 'info', "Volume '#{volume.name}' is already in an attached state; skipping.", true)
257
- next
258
- end
259
-
260
- unless volume_attach?(instance, volume)
261
- error_log(instance.logger, 'error', "Volume '#{volume.name}' failed to attach.", true)
262
- next
263
- end
258
+ volume_name = format(
259
+ '%s-%s-%s',
260
+ VOLUME_NAME_PREFIX,
261
+ Time.new.strftime(TIME_SLUG_FORMAT),
262
+ instance.id
263
+ )
264
+ begin
265
+ volume = @compute_service.volumes.create name: volume_name, size: VOLUME_SIZE, description: VOLUME_DESCRIPTION
266
+ rescue Excon::Error => e
267
+ puts 'Failed to create volume. check log for details.'
268
+ error_log(instance.logger, 'error', e.message)
269
+ false
270
+ end
264
271
 
265
- volume_mount_unmount?(instance, username, volume)
272
+ loop do
273
+ volume.reload
274
+ sleep 2
275
+ break if volume.ready?
276
+ error_log(instance.logger, 'info', "volume #{volume.name} not ready, waiting...", true)
266
277
  end
267
278
 
268
- detach_failures = @volumes.reject do |volume|
269
- volume_detach?(instance, volume)
279
+ if volume_attach?(instance, volume)
280
+ vdev = @volume_service.volumes.find_by_id(volume.id).attachments.first['device']
281
+ mkfs_command = [
282
+ ["sudo parted --script #{vdev} mklabel gpt mkpart primary 1 100%", ''],
283
+ ["sudo mkfs.#{VOLUME_FILESYSTEM} -Fq #{vdev}1", '']
284
+ ]
285
+ with_ssh(instance, username) do |ssh|
286
+ mkfs_command.each do |command, expected|
287
+ result = ssh.exec!(command)
288
+ next unless result != expected
289
+ error_log(
290
+ instance.logger,
291
+ 'error',
292
+ "Failure while running '#{command}':\n\texpected '#{expected}'\n\tgot '#{result}'",
293
+ true
294
+ )
295
+ return false
296
+ end
297
+ end
298
+ mount = volume_mount_unmount?(instance, username, volume)
299
+ detach = volume_detach?(instance, volume)
300
+ else
301
+ error_log(instance.logger, 'error', "Volume '#{volume.id}' failed to attach.", true)
302
+ return false
270
303
  end
271
304
 
272
- if mount_failures.empty? && detach_failures.empty?
273
- error_log(instance.logger, 'info', "\nEncountered 0 failures.", true)
305
+ if mount && detach
306
+ error_log(instance.logger, 'info', "\nVolume testing passed!.", true)
274
307
  true
275
308
  else
276
309
  error_log(
277
310
  instance.logger,
278
311
  'error',
279
- "\nEncountered #{mount_failures.count} mount failures and #{detach_failures.count} detach failures.",
312
+ "\nVolume mounted: #{mount} detached: #{detach}.",
280
313
  true
281
314
  )
282
315
  error_log(instance.logger, 'error', "\nEncountered failures.", true)
283
316
  false
284
317
  end
318
+ error_log(instance.logger, 'info', "Deleting volume #{volume.id}.", true)
319
+ volume.destroy if volume.ready?
285
320
  end
286
321
 
287
322
  # A helper method to execute a series of commands remotely on an instance. This helper
@@ -294,7 +329,7 @@ class OpenStackTaster
294
329
  instance.logger.progname = 'SSH'
295
330
  begin
296
331
  Net::SSH.start(
297
- instance.addresses['public'].first['addr'],
332
+ instance.addresses[@network_name].first['addr'],
298
333
  username,
299
334
  verbose: :info,
300
335
  paranoid: false,
@@ -372,6 +407,7 @@ class OpenStackTaster
372
407
  ['sudo partprobe -s', nil],
373
408
  ["[ -d '#{mount}' ] || sudo mkdir #{mount}", ''],
374
409
  ["sudo mount #{vdev} #{mount}", ''],
410
+ ["sudo sh -c 'echo -n #{file_contents} > #{mount}/#{file_name}'", ''],
375
411
  ["sudo cat #{mount}/#{file_name}", file_contents],
376
412
  ["sudo umount #{mount}", '']
377
413
  ]
@@ -405,7 +441,8 @@ class OpenStackTaster
405
441
  puts 'Logging partition list and dmesg...'
406
442
 
407
443
  record_info_commands = [
408
- 'cat /proc/partitions',
444
+ 'lsblk -l',
445
+ 'lsblk -fl',
409
446
  'dmesg | tail -n 20'
410
447
  ]
411
448
 
@@ -60,6 +60,15 @@ control 'security-1.0' do
60
60
  its('stdout') { should cmp(/\(ALL\) ((NO)*PASSWD)*: ALL/) }
61
61
  end
62
62
  end
63
+ end
64
+
65
+ control 'ports-1.0' do
66
+ impact 1.0
67
+ title 'Openstack Image Ports Test'
68
+ desc 'Tests the open ports of images used for Openstack.'
69
+
70
+ # Skip these tests if we detect openstack is installed
71
+ only_if { !file('/etc/keystone').exist? }
63
72
 
64
73
  # ssh should be the only thing listening
65
74
  describe port.where { protocol =~ /tcp/ && port != 22 } do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openstack_taster
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - OSU Open Source Lab
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-12-19 00:00:00.000000000 Z
11
+ date: 2018-06-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: inspec