corl 0.4.1 → 0.4.2

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.
Files changed (48) hide show
  1. data/.gitmodules +1 -1
  2. data/Gemfile +1 -1
  3. data/Gemfile.lock +15 -8
  4. data/VERSION +1 -1
  5. data/bootstrap/os/ubuntu/00_base.sh +10 -1
  6. data/bootstrap/os/ubuntu/05_ruby.sh +6 -0
  7. data/bootstrap/os/ubuntu/06_puppet.sh +6 -6
  8. data/bootstrap/os/ubuntu/09_nucleon.sh +14 -0
  9. data/bootstrap/os/ubuntu/10_corl.sh +7 -2
  10. data/corl.gemspec +16 -9
  11. data/lib/CORL/action/authorize.rb +57 -0
  12. data/lib/CORL/action/bootstrap.rb +5 -0
  13. data/lib/CORL/action/destroy.rb +64 -0
  14. data/lib/CORL/action/exec.rb +9 -0
  15. data/lib/CORL/action/image.rb +39 -7
  16. data/lib/CORL/action/images.rb +4 -3
  17. data/lib/CORL/action/lookup.rb +2 -2
  18. data/lib/CORL/action/regions.rb +51 -0
  19. data/lib/CORL/action/seed.rb +1 -1
  20. data/lib/CORL/action/spawn.rb +8 -9
  21. data/lib/CORL/action/ssh.rb +74 -0
  22. data/lib/CORL/action/start.rb +37 -5
  23. data/lib/CORL/action/stop.rb +37 -5
  24. data/lib/CORL/configuration/file.rb +34 -7
  25. data/lib/CORL/event/puppet.rb +1 -1
  26. data/lib/CORL/machine/aws.rb +153 -0
  27. data/lib/CORL/machine/physical.rb +14 -5
  28. data/lib/CORL/machine/rackspace.rb +58 -0
  29. data/lib/CORL/network/default.rb +1 -1
  30. data/lib/CORL/node/aws.rb +40 -16
  31. data/lib/CORL/node/local.rb +4 -3
  32. data/lib/CORL/node/rackspace.rb +25 -7
  33. data/lib/CORL/provisioner/puppetnode.rb +11 -9
  34. data/lib/core/errors.rb +6 -0
  35. data/lib/core/mod/fog_aws_server.rb +38 -0
  36. data/lib/core/plugin/action.rb +3 -11
  37. data/lib/core/plugin/configuration.rb +20 -2
  38. data/lib/{CORL/machine/fog.rb → core/plugin/fog_machine.rb} +92 -92
  39. data/lib/core/plugin/{fog.rb → fog_node.rb} +20 -7
  40. data/lib/core/plugin/machine.rb +58 -37
  41. data/lib/core/plugin/network.rb +76 -111
  42. data/lib/core/plugin/node.rb +271 -87
  43. data/lib/core/plugin/provisioner.rb +1 -1
  44. data/lib/corl.rb +6 -14
  45. data/locales/en.yml +18 -1
  46. metadata +39 -32
  47. data/lib/CORL/node/google.rb +0 -111
  48. data/lib/core/util/ssh.rb +0 -286
@@ -0,0 +1,74 @@
1
+
2
+ module CORL
3
+ module Action
4
+ class Ssh < Plugin::CloudAction
5
+
6
+ #-----------------------------------------------------------------------------
7
+ # Settings
8
+
9
+ def configure
10
+ super do
11
+ codes :network_failure
12
+
13
+ register :ssh_nodes, :array, nil do |values|
14
+ if values.nil?
15
+ warn('corl.actions.bootstrap.errors.ssh_nodes_empty')
16
+ next false
17
+ end
18
+
19
+ node_plugins = CORL.loaded_plugins(:node)
20
+ success = true
21
+
22
+ values.each do |value|
23
+ if info = CORL.plugin_class(:node).translate_reference(value)
24
+ if ! node_plugins.keys.include?(info[:provider].to_sym) || info[:name].empty?
25
+ warn('corl.actions.bootstrap.errors.ssh_nodes', { :value => value, :node_provider => info[:provider], :name => info[:name] })
26
+ success = false
27
+ end
28
+ end
29
+ end
30
+ success
31
+ end
32
+
33
+ config[:node_provider].default = :rackspace
34
+ end
35
+ end
36
+
37
+ #---
38
+
39
+ def ignore
40
+ node_ignore - [ :net_provider, :node_provider ]
41
+ end
42
+
43
+ def arguments
44
+ [ :ssh_nodes ]
45
+ end
46
+
47
+ #-----------------------------------------------------------------------------
48
+ # Operations
49
+
50
+ def execute
51
+ super do |local_node, network|
52
+ if network
53
+ batch_success = network.batch(settings[:ssh_nodes], settings[:node_provider], false) do |node|
54
+ render_options = { :id => node.id, :hostname => node.hostname }
55
+
56
+ info('corl.actions.ssh.start', render_options)
57
+ success = node.terminal(extended_config(:ssh, {}))
58
+ if success
59
+ info('corl.actions.ssh.success', render_options)
60
+ else
61
+ render_options[:status] = node.status
62
+ error('corl.actions.ssh.failure', render_options)
63
+ end
64
+ success
65
+ end
66
+ myself.status = code.batch_error unless batch_success
67
+ else
68
+ myself.status = code.network_failure
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -9,18 +9,50 @@ class Start < Plugin::CloudAction
9
9
  def configure
10
10
  super do
11
11
  codes :network_failure
12
+
13
+ register :start_nodes, :array, nil do |values|
14
+ if values.nil?
15
+ warn('corl.actions.start.errors.start_nodes_empty')
16
+ next false
17
+ end
18
+
19
+ node_plugins = CORL.loaded_plugins(:node)
20
+ success = true
21
+
22
+ values.each do |value|
23
+ if info = CORL.plugin_class(:node).translate_reference(value)
24
+ if ! node_plugins.keys.include?(info[:provider].to_sym) || info[:name].empty?
25
+ warn('corl.actions.start.errors.start_nodes', { :value => value, :node_provider => info[:provider], :name => info[:name] })
26
+ success = false
27
+ end
28
+ end
29
+ end
30
+ success
31
+ end
12
32
  end
13
33
  end
14
34
 
35
+ #---
36
+
37
+ def ignore
38
+ [ :nodes ]
39
+ end
40
+
41
+ def arguments
42
+ [ :start_nodes ]
43
+ end
44
+
15
45
  #-----------------------------------------------------------------------------
16
46
  # Operations
17
47
 
18
48
  def execute
19
- super do |node, network|
20
- info('corl.actions.start.start')
21
-
22
- if network && node
23
-
49
+ super do |local_node, network|
50
+ if network
51
+ batch_success = network.batch(settings[:start_nodes], settings[:node_provider], settings[:parallel]) do |node|
52
+ info('corl.actions.start.start', { :provider => node.plugin_provider, :name => node.plugin_name })
53
+ node.start
54
+ end
55
+ myself.status = code.batch_error unless batch_success
24
56
  else
25
57
  myself.status = code.network_failure
26
58
  end
@@ -9,18 +9,50 @@ class Stop < Plugin::CloudAction
9
9
  def configure
10
10
  super do
11
11
  codes :network_failure
12
+
13
+ register :stop_nodes, :array, nil do |values|
14
+ if values.nil?
15
+ warn('corl.actions.stop.errors.stop_nodes_empty')
16
+ next false
17
+ end
18
+
19
+ node_plugins = CORL.loaded_plugins(:node)
20
+ success = true
21
+
22
+ values.each do |value|
23
+ if info = CORL.plugin_class(:node).translate_reference(value)
24
+ if ! node_plugins.keys.include?(info[:provider].to_sym) || info[:name].empty?
25
+ warn('corl.actions.stop.errors.stop_nodes', { :value => value, :node_provider => info[:provider], :name => info[:name] })
26
+ success = false
27
+ end
28
+ end
29
+ end
30
+ success
31
+ end
12
32
  end
13
33
  end
14
34
 
35
+ #---
36
+
37
+ def ignore
38
+ [ :nodes ]
39
+ end
40
+
41
+ def arguments
42
+ [ :stop_nodes ]
43
+ end
44
+
15
45
  #-----------------------------------------------------------------------------
16
46
  # Operations
17
47
 
18
48
  def execute
19
- super do |node, network|
20
- info('corl.actions.stop.start')
21
-
22
- if network && node
23
-
49
+ super do |local_node, network|
50
+ if network
51
+ batch_success = network.batch(settings[:stop_nodes], settings[:node_provider], settings[:parallel]) do |node|
52
+ info('corl.actions.stop.start', { :provider => node.plugin_provider, :name => node.plugin_name })
53
+ node.stop
54
+ end
55
+ myself.status = code.batch_error unless batch_success
24
56
  else
25
57
  myself.status = code.network_failure
26
58
  end
@@ -6,7 +6,7 @@ class File < CORL.plugin_class(:configuration)
6
6
  #-----------------------------------------------------------------------------
7
7
  # Configuration plugin interface
8
8
 
9
- def normalize
9
+ def normalize(reload)
10
10
  super
11
11
 
12
12
  logger.info("Setting source configuration project")
@@ -151,6 +151,7 @@ class File < CORL.plugin_class(:configuration)
151
151
  else
152
152
  # Never encountered before
153
153
  config_name = nil
154
+
154
155
  config_name = select_largest(router.get(parents)) unless parents.empty?
155
156
  split_config.call(value, config_name, keys)
156
157
  end
@@ -165,12 +166,12 @@ class File < CORL.plugin_class(:configuration)
165
166
  file_data.set([ config_name, keys ].flatten, value)
166
167
  else
167
168
  # Router is non existent
168
- if config_name = select_largest(router)
169
+ if config_name = select_largest(router.export)
169
170
  # Pick largest router from top level
170
171
  file_data.set([ config_name, keys ].flatten, value)
171
172
  else
172
173
  # Resort to sane defaults
173
- default_provider = Manager.connection.type_default(:translator)
174
+ default_provider = CORL.type_default(:translator)
174
175
  config_name = "corl.#{default_provider}"
175
176
  file_data.set([ config_name, keys ].flatten, value)
176
177
  end
@@ -180,7 +181,6 @@ class File < CORL.plugin_class(:configuration)
180
181
  end
181
182
 
182
183
  # Whew! Glad that's over...
183
-
184
184
  split_config.call(config.export, router.export)
185
185
  file_data
186
186
  end
@@ -254,8 +254,8 @@ class File < CORL.plugin_class(:configuration)
254
254
  if success
255
255
  case method_config.get(:type, :source)
256
256
  when :source
257
- new_file = project.local_path(Util::Disk.filename([ attach_path, name ]))
258
-
257
+ new_file = project.local_path(Util::Disk.filename([ attach_path, name ]))
258
+
259
259
  logger.debug("Attaching source data (length: #{data.length}) to configuration at #{attach_path}")
260
260
  success = Util::Disk.write(new_file, data)
261
261
 
@@ -276,7 +276,7 @@ class File < CORL.plugin_class(:configuration)
276
276
  end
277
277
  end
278
278
  end
279
- if success
279
+ if success && autosave
280
280
  logger.debug("Attaching data to project as #{new_file}")
281
281
  success = update_project(new_file, method_config)
282
282
  end
@@ -284,6 +284,31 @@ class File < CORL.plugin_class(:configuration)
284
284
  end
285
285
  end
286
286
 
287
+ #---
288
+
289
+ def delete_attachments(ids, options = {})
290
+ super do |method_config|
291
+ success = true
292
+ files = []
293
+
294
+ array(ids).each do |id|
295
+ file = ::File.join(project.directory, id.to_s)
296
+
297
+ if Util::Disk.delete(file)
298
+ files << file
299
+ else
300
+ success = false
301
+ end
302
+ end
303
+
304
+ if success && autosave
305
+ logger.debug("Removing attached data from project as #{files.join(', ')}")
306
+ success = update_project(files, method_config)
307
+ end
308
+ success ? files : nil
309
+ end
310
+ end
311
+
287
312
  #-----------------------------------------------------------------------------
288
313
  # Utilities
289
314
 
@@ -354,6 +379,8 @@ class File < CORL.plugin_class(:configuration)
354
379
  #---
355
380
 
356
381
  def select_largest(router)
382
+ return router unless router.is_a?(Hash)
383
+
357
384
  config_map = {}
358
385
 
359
386
  count_config_names = lambda do |data|
@@ -6,7 +6,7 @@ class Puppet < CORL.plugin_class(:event)
6
6
  #-----------------------------------------------------------------------------
7
7
  # Puppet event interface
8
8
 
9
- def normalize
9
+ def normalize(reload)
10
10
  super
11
11
 
12
12
  if get(:string)
@@ -0,0 +1,153 @@
1
+
2
+ module CORL
3
+ module Machine
4
+ class Aws < Fog
5
+
6
+ #-----------------------------------------------------------------------------
7
+ # Checks
8
+
9
+ #-----------------------------------------------------------------------------
10
+ # Property accessors / modifiers
11
+
12
+ def set_connection
13
+ require 'unf'
14
+ super
15
+ Kernel.load File.join(File.dirname(__FILE__), '..', '..', 'core', 'mod', 'fog_aws_server.rb')
16
+ end
17
+
18
+ #-----------------------------------------------------------------------------
19
+ # Management
20
+
21
+ def init_server
22
+ super do
23
+ myself.plugin_name = @server.id
24
+
25
+ node[:id] = plugin_name
26
+ node[:public_ip] = @server.public_ip_address
27
+ node[:private_ip] = @server.private_ip_address
28
+ node[:machine_type] = @server.flavor_id
29
+ node[:image] = @server.image_id
30
+ node.user = @server.username unless node.user
31
+
32
+ @server.private_key_path = node.private_key if node.private_key
33
+ @server.public_key_path = node.public_key if node.public_key
34
+ end
35
+ end
36
+
37
+ #---
38
+
39
+ def init_ssh(ssh_port)
40
+ # Security group initialization
41
+ if compute && ssh_port != 22
42
+ ensure_security_group("CORL_SSH_#{ssh_port}", ssh_port)
43
+ end
44
+ end
45
+
46
+ #---
47
+
48
+ def create(options = {})
49
+ super do |config|
50
+ # Keypair initialization
51
+ if key_pair = compute.key_pairs.get(keypair_name)
52
+ key_pair.destroy
53
+ end
54
+ compute.key_pairs.create(
55
+ :name => keypair_name,
56
+ :public_key => Util::Disk.read(node.public_key)
57
+ )
58
+ config[:key_name] = keypair_name
59
+ end
60
+ end
61
+
62
+ #---
63
+
64
+ def reload(options = {})
65
+ super do |config|
66
+ success = server.reboot
67
+
68
+ server.wait_for { ready? } if success
69
+ success
70
+ end
71
+ end
72
+
73
+ #---
74
+
75
+ def create_image(options = {})
76
+ super do |image_name, config, success|
77
+ image_name = image_name.gsub(/[^A-Za-z0-9\(\)\.\-\_\/]+/, '_')
78
+ image_description = config.get(:description, "CORL backup image")
79
+
80
+ data = compute.create_image(server.identity, image_name, image_description)
81
+ image_id = data.body['imageId']
82
+
83
+ ::Fog.wait_for do
84
+ compute.describe_images('ImageId' => image_id).body['imagesSet'].first['imageState'] == 'available'
85
+ end
86
+
87
+ if image_id
88
+ node[:image] = image_id
89
+ success = true
90
+ end
91
+ success
92
+ end
93
+ end
94
+
95
+ #---
96
+
97
+ def destroy(options = {})
98
+ super do |config|
99
+ unless config.get(:stop, false)
100
+ # Keypair destruction
101
+ if key_pair = compute.key_pairs.get(keypair_name)
102
+ key_pair.destroy
103
+ end
104
+ end
105
+ true
106
+ end
107
+ end
108
+
109
+ #-----------------------------------------------------------------------------
110
+ # Utilities
111
+
112
+ def keypair_name
113
+ "CORL_#{node.plugin_name}"
114
+ end
115
+
116
+ #---
117
+
118
+ def ensure_security_group(group_name, from_port, to_port = nil, options = {})
119
+ config = Config.ensure(options)
120
+ security_group = compute.security_groups.get(group_name)
121
+ cidrip = config.get(:cidrip, '0.0.0.0/0')
122
+ protocol = config.get(:protocol, 'tcp')
123
+ to_port = from_port if to_port.nil?
124
+
125
+ if security_group.nil?
126
+ security_group = compute.security_groups.create(
127
+ :name => group_name,
128
+ :description => config.get(:description, "Opening port range: #{from_port} to #{to_port}")
129
+ )
130
+ raise unless security_group # TODO: Better error class
131
+ end
132
+
133
+ authorized = false
134
+ if security_group.ip_permissions
135
+ authorized = security_group.ip_permissions.detect do |ip_permission|
136
+ ip_permission['ipRanges'].first && ip_permission['ipRanges'].first['cidrIp'] == cidrip &&
137
+ ip_permission['fromPort'] == from_port &&
138
+ ip_permission['ipProtocol'] == protocol &&
139
+ ip_permission['toPort'] == to_port
140
+ end
141
+ end
142
+ unless authorized
143
+ security_group.authorize_port_range(Range.new(from_port, to_port))
144
+ end
145
+
146
+ if server
147
+ server.groups = [ group_name ] | server.groups
148
+ server.save
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
@@ -6,7 +6,7 @@ class Physical < CORL.plugin_class(:machine)
6
6
  #-----------------------------------------------------------------------------
7
7
  # Machine plugin interface
8
8
 
9
- def normalize
9
+ def normalize(reload)
10
10
  super
11
11
  myself.plugin_name = hostname
12
12
  end
@@ -40,7 +40,7 @@ class Physical < CORL.plugin_class(:machine)
40
40
  #---
41
41
 
42
42
  def public_ip
43
- fact(:ipaddress)
43
+ CORL.ip_address
44
44
  end
45
45
 
46
46
  #---
@@ -99,13 +99,13 @@ class Physical < CORL.plugin_class(:machine)
99
99
 
100
100
  #---
101
101
 
102
- def exec(commands, options = {})
102
+ def exec(commands, options = {}, &code)
103
103
  super do |config, results|
104
- logger.debug("Executing shell commands ( #{commands.inspect} ) on machine #{name}")
104
+ logger.debug("Executing shell commands ( #{commands.inspect} ) on machine #{plugin_name}")
105
105
 
106
106
  commands.each do |command|
107
107
  result = CORL.cli_run(command, config) do |op, command_str, data|
108
- block_given? ? yield(op, command_str, data) : true
108
+ code ? code.call(op, command_str, data) : true
109
109
  end
110
110
  results << result
111
111
  end
@@ -115,6 +115,15 @@ class Physical < CORL.plugin_class(:machine)
115
115
 
116
116
  #---
117
117
 
118
+ def terminal(user, options = {})
119
+ super do |config|
120
+ logger.debug("Launching terminals on the local machine is not currently supported")
121
+ 1
122
+ end
123
+ end
124
+
125
+ #---
126
+
118
127
  def start(options = {})
119
128
  super do
120
129
  logger.warn("This machine is already running so can not be started")
@@ -0,0 +1,58 @@
1
+
2
+ module CORL
3
+ module Machine
4
+ class Rackspace < Fog
5
+
6
+ #-----------------------------------------------------------------------------
7
+ # Checks
8
+
9
+ #-----------------------------------------------------------------------------
10
+ # Property accessors / modifiers
11
+
12
+ #-----------------------------------------------------------------------------
13
+ # Management
14
+
15
+ def init_server
16
+ super do
17
+ myself.plugin_name = @server.id
18
+
19
+ node[:id] = plugin_name
20
+ node[:public_ip] = @server.public_ip_address
21
+ node[:private_ip] = @server.private_ip_address
22
+ node[:machine_type] = @server.flavor.id
23
+ node[:image] = @server.image.id
24
+ node.user = @server.username unless node.user
25
+
26
+ @server.private_key_path = node.private_key if node.private_key
27
+ @server.public_key_path = node.public_key if node.public_key
28
+ end
29
+ end
30
+
31
+ #---
32
+
33
+ def reload(options = {})
34
+ super do |config|
35
+ success = server.reboot(config.get(:type, 'SOFT'))
36
+
37
+ server.wait_for { ready? } if success
38
+ success
39
+ end
40
+ end
41
+
42
+ #---
43
+
44
+ def create_image(options = {})
45
+ super do |image_name, config, success|
46
+ image = server.create_image(image_name)
47
+ image.wait_for { ready? }
48
+
49
+ if image
50
+ node[:image] = image.id
51
+ success = true
52
+ end
53
+ success
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -6,7 +6,7 @@ class Default < CORL.plugin_class(:network)
6
6
  #-----------------------------------------------------------------------------
7
7
  # Cloud plugin interface
8
8
 
9
- def normalize
9
+ def normalize(reload)
10
10
  super
11
11
  end
12
12
 
data/lib/CORL/node/aws.rb CHANGED
@@ -1,11 +1,17 @@
1
1
 
2
2
  module CORL
3
3
  module Node
4
- class Aws < Node::Fog
4
+ class Aws < Fog
5
5
 
6
6
  #-----------------------------------------------------------------------------
7
7
  # Node plugin interface
8
-
8
+
9
+ def normalize(reload)
10
+ super do
11
+ :aws
12
+ end
13
+ end
14
+
9
15
  #-----------------------------------------------------------------------------
10
16
  # Checks
11
17
 
@@ -18,9 +24,14 @@ class Aws < Node::Fog
18
24
 
19
25
  def regions
20
26
  [
21
- 'us-west-2a',
22
- 'us-west-2b',
23
- 'us-west-2c'
27
+ 'us-east-1',
28
+ 'us-west-1',
29
+ 'us-west-2',
30
+ 'eu-west-1',
31
+ 'ap-northeast-1',
32
+ 'ap-southeast-1',
33
+ 'ap-southeast-2',
34
+ 'sa-east-1'
24
35
  ]
25
36
  end
26
37
 
@@ -30,7 +41,8 @@ class Aws < Node::Fog
30
41
  def machine_config
31
42
  super do |config|
32
43
  config.import({
33
- :provider => 'AWS'
44
+ :provider => 'AWS',
45
+ :region => region
34
46
  })
35
47
 
36
48
  config[:aws_access_key_id] = api_user if api_user
@@ -38,24 +50,33 @@ class Aws < Node::Fog
38
50
  end
39
51
  end
40
52
 
53
+ #---
54
+
55
+ def create_config
56
+ { :flavor_id => machine_type, :image_id => image, :username => user }
57
+ end
58
+
41
59
  #-----------------------------------------------------------------------------
42
60
  # Node operations
43
61
 
44
62
  def create(options = {})
45
63
  super do |op, config|
46
64
  if op == :config
47
- config[:private_key] = private_key if private_key
48
- config[:public_key] = public_key if public_key
49
-
50
- config.defaults({
51
- :name => hostname,
52
- :flavor_id => machine_type,
53
- :image_id => image
54
- })
65
+ config.defaults(create_config)
55
66
  end
56
67
  end
57
68
  end
58
69
 
70
+ #---
71
+
72
+ def start(options = {})
73
+ super do |op, config|
74
+ if op == :config
75
+ config.defaults(create_config)
76
+ end
77
+ end
78
+ end
79
+
59
80
  #-----------------------------------------------------------------------------
60
81
  # Utilities
61
82
 
@@ -73,13 +94,16 @@ class Aws < Node::Fog
73
94
  #---
74
95
 
75
96
  def render_image(image)
76
- sprintf("[ %20s ][ %10s ] %10s - %s", image_id(image), image.state, image.architecture, image.name)
97
+ location = image.location.split('/').first
98
+ sprintf("[ %20s ][ %10s ] %10s - %s (%s)", image_id(image), image.state, image.architecture, image.name, location)
77
99
  end
78
100
 
79
101
  #---
80
102
 
81
103
  def image_search_text(image)
82
- sprintf("%s %s %s %s %s", image_id(image), image.name, image.description, image.state, image.architecture)
104
+ location = image.location.split('/').first
105
+ location = location.match(/^\d+$/) ? '' : location
106
+ sprintf("%s %s %s %s %s %s %s", image_id(image), image.name, image.description, image.state, image.architecture, image.owner_id, location)
83
107
  end
84
108
  end
85
109
  end
@@ -6,9 +6,10 @@ class Local < CORL.plugin_class(:node)
6
6
  #-----------------------------------------------------------------------------
7
7
  # Node plugin interface
8
8
 
9
- def normalize
10
- super
11
- myself.machine = create_machine(:machine, :physical, machine_config)
9
+ def normalize(reload)
10
+ super do
11
+ myself.machine = create_machine(:machine, :physical, machine_config)
12
+ end
12
13
  end
13
14
 
14
15
  #-----------------------------------------------------------------------------