opennebula 5.0.2 → 5.1.80.beta1

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: 0b3f5a3432e11308e671bec5f860d0ffe8ff9681
4
- data.tar.gz: 0cbc97bfb8280440743319d12ef1a2db9a05bd49
3
+ metadata.gz: bce5ec2f4c8ec1633421f1998e9a659a3b712fe2
4
+ data.tar.gz: 75718ab515a32acc13a5f931836a7a77a02c6ce4
5
5
  SHA512:
6
- metadata.gz: ccfbb4c8dfd7d67bfe64e3049b50c2885b193cf74ca6693d618dce9af6632e5d27e5b6f30ad11864bcafb68dcfd9a52d525cfb6ac38146f23a5468271df4f0bd
7
- data.tar.gz: d7ef9a12c93613878d1d0f9431ccbeeabdb9d8cf92fe4585b84e1db69c07c08ad1b96e7cd0ff828e77efd62445b6df23096559a25ad2e949562fbca731b92b84
6
+ metadata.gz: c69828852d920460e330c553ce20fd3f71e9450d5702576e4e800bbf12fbde0d273b8d7d03e4dd62c34971ba0c131d6e3fea21afc550de6a71794796f00d97d7
7
+ data.tar.gz: 23be93fa25689ce1d05d920a420cd3f766a47594a7f60d152539b3f013b953444357f8ebd01cdb8c6f0bbaa79c1186ad9beec04cea773c2ca4ee9b00b5c4a495
@@ -17,6 +17,7 @@
17
17
  require 'pp'
18
18
  require 'open3'
19
19
  require 'stringio'
20
+ require 'timeout'
20
21
 
21
22
  # Generic command executor that holds the code shared by all the command
22
23
  # executors.
@@ -42,11 +43,14 @@ require 'stringio'
42
43
 
43
44
 
44
45
  class GenericCommand
46
+ ERROR_OPEN = "ERROR MESSAGE --8<------"
47
+ ERROR_CLOSE = "ERROR MESSAGE ------>8--"
48
+
45
49
  attr_reader :code, :stdout, :stderr, :command
46
50
 
47
51
  # Creates a command and runs it
48
- def self.run(command, logger=nil, stdin=nil)
49
- cmd = self.new(command, logger, stdin)
52
+ def self.run(command, logger=nil, stdin=nil, timeout=nil)
53
+ cmd = self.new(command, logger, stdin, timeout)
50
54
  cmd.run
51
55
  cmd
52
56
  end
@@ -54,39 +58,72 @@ class GenericCommand
54
58
  # Creates the new command:
55
59
  # +command+: string with the command to be executed
56
60
  # +logger+: proc that takes a message parameter and logs it
57
- def initialize(command, logger=nil, stdin=nil)
61
+ def initialize(command, logger=nil, stdin=nil, timeout=nil)
58
62
  @command = command
59
63
  @logger = logger
60
64
  @stdin = stdin
65
+ @timeout = timeout
61
66
  end
62
67
 
63
68
  # Sends a log message to the logger proc
64
- def log(message)
65
- @logger.call(message) if @logger
69
+ def log(message, all=true)
70
+ @logger.call(message, all) if @logger
71
+ end
72
+
73
+ def kill(pid)
74
+ # executed processes now have its own process group to be able
75
+ # to kill all children
76
+ pgid = Process.getpgid(pid)
77
+
78
+ # Kill all processes belonging to process group
79
+ Process.kill("HUP", pgid * -1)
66
80
  end
67
81
 
68
82
  # Runs the command
69
83
  def run
70
- std = execute
84
+ std = nil
85
+ process = Proc.new do
86
+ std = execute
87
+
88
+ # Close standard IO descriptors
89
+ if @stdin
90
+ std[0] << @stdin
91
+ std[0].flush
92
+ end
93
+ std[0].close if !std[0].closed?
71
94
 
72
- # Close standard IO descriptors
73
- if @stdin
74
- std[0] << @stdin
75
- std[0].flush
95
+ @stdout=std[1].read
96
+ std[1].close if !std[1].closed?
97
+
98
+ @stderr=std[2].read
99
+ std[2].close if !std[2].closed?
100
+
101
+ @code=get_exit_code(@stderr)
102
+
103
+ if @code!=0
104
+ log("Command execution fail: #{command}")
105
+ log(@stderr)
106
+ end
76
107
  end
77
- std[0].close if !std[0].closed?
78
108
 
79
- @stdout=std[1].read
80
- std[1].close if !std[1].closed?
109
+ begin
110
+ if @timeout
111
+ Timeout.timeout(@timeout, nil, &process)
112
+ else
113
+ process.call
114
+ end
115
+ rescue Timeout::Error
116
+ error_message = "Timeout executing #{command}"
117
+ log(error_message)
118
+
119
+ @stderr = ERROR_OPEN + "\n" + error_message + "\n" + ERROR_CLOSE
81
120
 
82
- @stderr=std[2].read
83
- std[2].close if !std[2].closed?
121
+ 3.times {|n| std[n].close if !std[n].closed? }
84
122
 
85
- @code=get_exit_code(@stderr)
123
+ pid = std[-1].pid
124
+ self.kill(pid)
86
125
 
87
- if @code!=0
88
- log("Command execution fail: #{command}")
89
- log(@stderr)
126
+ @code = 255
90
127
  end
91
128
 
92
129
  return @code
@@ -94,7 +131,7 @@ class GenericCommand
94
131
 
95
132
  # Parses error message from +stderr+ output
96
133
  def get_error_message
97
- tmp=@stderr.scan(/^ERROR MESSAGE --8<------\n(.*?)ERROR MESSAGE ------>8--$/m)
134
+ tmp=@stderr.scan(/^#{ERROR_OPEN}\n(.*?)#{ERROR_CLOSE}$/m)
98
135
  return "-" if !tmp[0]
99
136
  tmp[0].join(' ').strip
100
137
  end
@@ -124,7 +161,8 @@ class LocalCommand < GenericCommand
124
161
  private
125
162
 
126
163
  def execute
127
- Open3.popen3("#{command} ; echo ExitCode: $? 1>&2")
164
+ Open3.popen3("#{command} ; echo ExitCode: $? 1>&2",
165
+ :pgroup => true)
128
166
  end
129
167
  end
130
168
 
@@ -134,26 +172,28 @@ class SSHCommand < GenericCommand
134
172
  attr_accessor :host
135
173
 
136
174
  # Creates a command and runs it
137
- def self.run(command, host, logger=nil, stdin=nil)
138
- cmd=self.new(command, host, logger, stdin)
175
+ def self.run(command, host, logger=nil, stdin=nil, timeout=nil)
176
+ cmd=self.new(command, host, logger, stdin, timeout)
139
177
  cmd.run
140
178
  cmd
141
179
  end
142
180
 
143
181
  # This one takes another parameter. +host+ is the machine
144
182
  # where the command is going to be executed
145
- def initialize(command, host, logger=nil, stdin=nil)
183
+ def initialize(command, host, logger=nil, stdin=nil, timeout=nil)
146
184
  @host=host
147
- super(command, logger, stdin)
185
+ super(command, logger, stdin, timeout)
148
186
  end
149
187
 
150
188
  private
151
189
 
152
190
  def execute
153
191
  if @stdin
154
- Open3.popen3("ssh #{@host} #{@command} ; echo ExitCode: $? 1>&2")
192
+ Open3.popen3("ssh #{@host} #{@command} ; echo ExitCode: $? 1>&2",
193
+ :pgroup => true)
155
194
  else
156
- Open3.popen3("ssh -n #{@host} #{@command} ; echo ExitCode: $? 1>&2")
195
+ Open3.popen3("ssh -n #{@host} #{@command} ; echo ExitCode: $? 1>&2",
196
+ :pgroup => true)
157
197
  end
158
198
  end
159
199
  end
@@ -161,13 +201,13 @@ end
161
201
  class RemotesCommand < SSHCommand
162
202
 
163
203
  # Creates a command and runs it
164
- def self.run(command, host, remote_dir, logger=nil, stdin=nil, retries=0)
204
+ def self.run(command, host, remote_dir, logger=nil, stdin=nil, retries=0, timeout=nil)
165
205
  cmd_file = command.split(' ')[0]
166
206
 
167
207
  cmd_string = "'if [ -x \"#{cmd_file}\" ]; then #{command}; else\
168
208
  exit #{MAGIC_RC}; fi'"
169
209
 
170
- cmd = self.new(cmd_string, host, logger, stdin)
210
+ cmd = self.new(cmd_string, host, logger, stdin, timeout)
171
211
  cmd.run
172
212
 
173
213
  while cmd.code != 0 and retries != 0
@@ -108,12 +108,11 @@ module DriverExecHelper
108
108
 
109
109
  # Sends a log message to ONE. The +message+ can be multiline, it will
110
110
  # be automatically splitted by lines.
111
- def log(number, message)
111
+ def log(number, message, all=true)
112
112
  in_error_message=false
113
113
  msg=message.strip
114
114
  msg.each_line {|line|
115
- severity='I'
116
-
115
+ severity=all ? 'I' : nil
117
116
  l=line.strip
118
117
 
119
118
  if l=='ERROR MESSAGE --8<------'
@@ -134,21 +133,19 @@ module DriverExecHelper
134
133
  severity='D'
135
134
  when 'INFO'
136
135
  severity='I'
137
- else
138
- severity='I'
139
136
  end
140
137
  end
141
138
  end
142
139
 
143
- send_message("LOG", severity, number, line.strip)
140
+ send_message("LOG", severity, number, line.strip) if severity
144
141
  }
145
142
  end
146
143
 
147
144
  # Generates a proc with that calls log with a hardcoded number. It will
148
145
  # be used to add loging to command actions
149
146
  def log_method(num)
150
- lambda {|message|
151
- log(num, message)
147
+ lambda {|message, all=true|
148
+ log(num, message, all)
152
149
  }
153
150
  end
154
151
 
@@ -53,12 +53,14 @@ class OpenNebulaDriver < ActionManager
53
53
  :concurrency => 10,
54
54
  :threaded => true,
55
55
  :retries => 0,
56
- :local_actions => {}
56
+ :local_actions => {},
57
+ :timeout => nil
57
58
  }.merge!(options)
58
59
 
59
60
  super(@options[:concurrency], @options[:threaded])
60
61
 
61
62
  @retries = @options[:retries]
63
+ @timeout = @options[:timeout]
62
64
 
63
65
  #Set default values
64
66
  initialize_helper(directory, @options)
@@ -94,7 +96,11 @@ class OpenNebulaDriver < ActionManager
94
96
  command = action_command_line(aname, params, options[:script_name])
95
97
 
96
98
  if action_is_local?(aname)
97
- execution = LocalCommand.run(command, log_method(id), Base64::encode64(options[:stdin].to_s.gsub("\n","")))
99
+ stdin = Base64::encode64(options[:stdin].to_s.gsub("\n",""))
100
+ execution = LocalCommand.run(command,
101
+ log_method(id),
102
+ stdin,
103
+ @timeout)
98
104
  elsif options[:ssh_stream]
99
105
  if options[:stdin]
100
106
  cmdin = "cat << EOT | #{command}"
@@ -104,15 +110,19 @@ class OpenNebulaDriver < ActionManager
104
110
  stdin = nil
105
111
  end
106
112
 
107
- execution = options[:ssh_stream].run(cmdin, stdin, command)
113
+ execution = options[:ssh_stream].run(cmdin,
114
+ stdin,
115
+ command,
116
+ @timeout)
108
117
 
109
118
  else
110
119
  execution = RemotesCommand.run(command,
111
- host,
112
- @remote_scripts_base_path,
113
- log_method(id),
114
- options[:stdin],
115
- @retries)
120
+ host,
121
+ @remote_scripts_base_path,
122
+ log_method(id),
123
+ options[:stdin],
124
+ @retries,
125
+ @timeout)
116
126
  end
117
127
 
118
128
  result, info = get_info_from_execution(execution)
@@ -13,6 +13,7 @@
13
13
  # See the License for the specific language governing permissions and #
14
14
  # limitations under the License. #
15
15
  #--------------------------------------------------------------------------- #
16
+ require 'opennebula'
16
17
  require "OpenNebulaDriver"
17
18
  require "CommandManager"
18
19
  require 'base64'
@@ -52,23 +53,8 @@ class VirtualMachineDriver < OpenNebulaDriver
52
53
  :update_sg => "UPDATESG"
53
54
  }
54
55
 
55
- POLL_ATTRIBUTE = {
56
- :memory => "MEMORY",
57
- :cpu => "CPU",
58
- :nettx => "NETTX",
59
- :netrx => "NETRX",
60
- :state => "STATE",
61
- :disk_size => "DISK_SIZE",
62
- :snapshot_size => "SNAPSHOT_SIZE"
63
- }
64
-
65
- VM_STATE = {
66
- :active => 'a',
67
- :paused => 'p',
68
- :error => 'e',
69
- :deleted => 'd',
70
- :unknown => '-'
71
- }
56
+ POLL_ATTRIBUTE = OpenNebula::VirtualMachine::Driver::POLL_ATTRIBUTE
57
+ VM_STATE = OpenNebula::VirtualMachine::Driver::VM_STATE
72
58
 
73
59
  HOST_ARG = 1
74
60
 
@@ -50,7 +50,7 @@ end
50
50
  module CloudClient
51
51
 
52
52
  # OpenNebula version
53
- VERSION = '5.0.2'
53
+ VERSION = '5.1.80'
54
54
 
55
55
  # #########################################################################
56
56
  # Default location for the authentication file
data/lib/opennebula.rb CHANGED
@@ -66,5 +66,5 @@ require 'opennebula/marketplaceapp_pool'
66
66
  module OpenNebula
67
67
 
68
68
  # OpenNebula version
69
- VERSION = '5.0.2'
69
+ VERSION = '5.1.80'
70
70
  end
@@ -54,6 +54,10 @@ module OpenNebula
54
54
  # Driver name for x509 proxy authentication
55
55
  X509_PROXY_AUTH = "x509_proxy"
56
56
 
57
+ # Same as User.cc
58
+ INVALID_NAME_CHARS = [" ", ":", "\t", "\n", "\v", "\f", "\r"]
59
+ INVALID_PASS_CHARS = [" ", "\t", "\n", "\v", "\f", "\r"]
60
+
57
61
  # Creates a User description with just its identifier
58
62
  # this method should be used to create plain User objects.
59
63
  # +id+ the id of the user
@@ -188,15 +192,17 @@ module OpenNebula
188
192
 
189
193
  # Sets the LOGIN_TOKEN for the user
190
194
  #
191
- # @param username [String] of the user
195
+ # @param uname [String] of the user
192
196
  # @param token [String] the login token, if empty OpenNebula will
193
197
  # generate one
194
- # @param expire [String] valid period of the token in secs. If <= 0
198
+ # @param expire [String] valid period of the token in secs. If == 0
195
199
  # the token will be reset
200
+ # @param egid [Integer] Effective GID to use with this token. To use
201
+ # the current GID and user groups set it to -1
196
202
  # @return [String, OpenNebula::Error] token in case of success, Error
197
203
  # otherwise
198
- def login(username, token, expire)
199
- return @client.call(USER_METHODS[:login], username, token, expire)
204
+ def login(uname, token, expire, egid = -1)
205
+ return @client.call(USER_METHODS[:login], uname, token, expire, egid)
200
206
  end
201
207
 
202
208
  #######################################################################
@@ -218,6 +218,27 @@ module OpenNebula
218
218
  'SL_PRIMARYIPADDRESS'
219
219
  ]
220
220
 
221
+ # VirtualMachineDriver constants
222
+ module Driver
223
+ POLL_ATTRIBUTE = {
224
+ :memory => "MEMORY",
225
+ :cpu => "CPU",
226
+ :nettx => "NETTX",
227
+ :netrx => "NETRX",
228
+ :state => "STATE",
229
+ :disk_size => "DISK_SIZE",
230
+ :snapshot_size => "SNAPSHOT_SIZE"
231
+ }
232
+
233
+ VM_STATE = {
234
+ :active => 'a',
235
+ :paused => 'p',
236
+ :error => 'e',
237
+ :deleted => 'd',
238
+ :unknown => '-'
239
+ }
240
+ end
241
+
221
242
  # Creates a VirtualMachine description with just its identifier
222
243
  # this method should be used to create plain VirtualMachine objects.
223
244
  # +id+ the id of the vm
@@ -42,7 +42,6 @@ require 'yaml'
42
42
  require 'opennebula'
43
43
  require 'base64'
44
44
  require 'openssl'
45
- require 'VirtualMachineDriver'
46
45
 
47
46
  ################################################################################
48
47
  # Monkey patch rbvmomi library with some extra functions
@@ -310,18 +309,22 @@ class VIClient
310
309
  def find_vm_template(uuid)
311
310
  version = @vim.serviceContent.about.version
312
311
 
312
+ found_vm = nil
313
+
313
314
  if version.split(".").first.to_i >= 6
314
- @dc.vmFolder.findByUuid(uuid, RbVmomi::VIM::VirtualMachine, @dc)
315
- else
316
- vms = VIClient.get_entities(@dc.vmFolder, 'VirtualMachine')
315
+ found_vm = @dc.vmFolder.findByUuid(uuid, RbVmomi::VIM::VirtualMachine, @dc)
316
+ end
317
317
 
318
- return vms.find do |v|
319
- begin
320
- v.config && v.config.uuid == uuid
321
- rescue RbVmomi::VIM::ManagedObjectNotFound
322
- false
323
- end
324
- end
318
+ return found_vm if found_vm
319
+
320
+ vms = VIClient.get_entities(@dc.vmFolder, 'VirtualMachine')
321
+
322
+ return vms.find do |v|
323
+ begin
324
+ v.config && v.config.uuid == uuid
325
+ rescue RbVmomi::VIM::ManagedObjectNotFound
326
+ false
327
+ end
325
328
  end
326
329
  end
327
330
 
@@ -1357,8 +1360,8 @@ end
1357
1360
  class VCenterVm
1358
1361
  attr_reader :vm
1359
1362
 
1360
- POLL_ATTRIBUTE = VirtualMachineDriver::POLL_ATTRIBUTE
1361
- VM_STATE = VirtualMachineDriver::VM_STATE
1363
+ POLL_ATTRIBUTE = OpenNebula::VirtualMachine::Driver::POLL_ATTRIBUTE
1364
+ VM_STATE = OpenNebula::VirtualMachine::Driver::VM_STATE
1362
1365
 
1363
1366
  ############################################################################
1364
1367
  # Creates a new VIVm using a RbVmomi::VirtualMachine object
@@ -2272,10 +2275,14 @@ private
2272
2275
  vmid = xml.root.elements["/VM/ID"].text
2273
2276
  context = xml.root.elements["/VM/TEMPLATE/CONTEXT"]
2274
2277
 
2275
- # Read existing context if it is not a new VM
2276
- if !newvm
2277
- old_context = vm.config.extraConfig.select{|val|
2278
- val[:key]=="guestinfo.opennebula.context"}
2278
+ token = vm.config.extraConfig.select do |val|
2279
+ val[:key] == "opennebula.token"
2280
+ end
2281
+
2282
+ if token && !token.empty?
2283
+ token = token.first[:value]
2284
+ else
2285
+ token = nil
2279
2286
  end
2280
2287
 
2281
2288
  # Add VMID to VM's extraConfig
@@ -2313,60 +2320,56 @@ private
2313
2320
 
2314
2321
  # OneGate
2315
2322
  onegate_token_flag = xml.root.elements["/VM/TEMPLATE/CONTEXT/TOKEN"]
2316
- if onegate_token_flag and
2317
- onegate_token_flag.text == "YES" and
2318
- !newvm
2319
- # Create the OneGate token string
2320
- vmid_str = xml.root.elements["/VM/ID"].text
2321
- stime_str = xml.root.elements["/VM/STIME"].text
2322
- str_to_encrypt = "#{vmid_str}:#{stime_str}"
2323
-
2324
- user_id = xml.root.elements['//CREATED_BY'].text
2325
-
2326
- if user_id.nil?
2327
- STDERR.puts {"VMID:#{vmid} CREATED_BY not present" \
2328
- " in the VM TEMPLATE"}
2329
- return nil
2330
- end
2331
2323
 
2332
- user = OpenNebula::User.new_with_id(user_id,
2333
- OpenNebula::Client.new)
2334
- rc = user.info
2324
+ if onegate_token_flag and onegate_token_flag.text == "YES"
2325
+ if token
2326
+ onegate_token_64 = token
2327
+ else
2328
+ # Create the OneGate token string
2329
+ vmid_str = xml.root.elements["/VM/ID"].text
2330
+ stime_str = xml.root.elements["/VM/STIME"].text
2331
+ str_to_encrypt = "#{vmid_str}:#{stime_str}"
2335
2332
 
2336
- if OpenNebula.is_error?(rc)
2337
- STDERR.puts {"VMID:#{vmid} user.info" \
2338
- " error: #{rc.message}"}
2339
- return nil
2340
- end
2333
+ user_id = xml.root.elements['//CREATED_BY'].text
2341
2334
 
2342
- token_password = user['TEMPLATE/TOKEN_PASSWORD']
2335
+ if user_id.nil?
2336
+ STDERR.puts {"VMID:#{vmid} CREATED_BY not present" \
2337
+ " in the VM TEMPLATE"}
2338
+ return nil
2339
+ end
2343
2340
 
2344
- if token_password.nil?
2345
- STDERR.puts {"VMID:#{vmid} TOKEN_PASSWORD not present"\
2346
- " in the USER:#{user_id} TEMPLATE"}
2347
- return nil
2348
- end
2341
+ user = OpenNebula::User.new_with_id(user_id,
2342
+ OpenNebula::Client.new)
2343
+ rc = user.info
2349
2344
 
2350
- cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
2351
- cipher.encrypt
2352
- cipher.key = token_password
2353
- onegate_token = cipher.update(str_to_encrypt)
2354
- onegate_token << cipher.final
2345
+ if OpenNebula.is_error?(rc)
2346
+ STDERR.puts {"VMID:#{vmid} user.info" \
2347
+ " error: #{rc.message}"}
2348
+ return nil
2349
+ end
2355
2350
 
2356
- onegate_token_64 = Base64.encode64(onegate_token).chop
2351
+ token_password = user['TEMPLATE/TOKEN_PASSWORD']
2357
2352
 
2358
- context_text += "ONEGATE_TOKEN='#{onegate_token_64}'\n"
2359
- end
2353
+ if token_password.nil?
2354
+ STDERR.puts {"VMID:#{vmid} TOKEN_PASSWORD not present"\
2355
+ " in the USER:#{user_id} TEMPLATE"}
2356
+ return nil
2357
+ end
2360
2358
 
2361
- # If there is an old VM, we need to honor the existing ONEGATE_TOKEN
2362
- if !newvm
2363
- onegate_token =
2364
- Base64.decode64(old_context[0][:value]).split("\n").
2365
- select{|line| line.start_with?("ONEGATE_TOKEN")}[0]
2359
+ cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
2360
+ cipher.encrypt
2361
+ cipher.key = token_password
2362
+ onegate_token = cipher.update(str_to_encrypt)
2363
+ onegate_token << cipher.final
2366
2364
 
2367
- if onegate_token
2368
- context_text += onegate_token
2365
+ onegate_token_64 = Base64.encode64(onegate_token).chop
2366
+ config_array << {
2367
+ :key => 'opennebula.token',
2368
+ :value => onegate_token_64
2369
+ }
2369
2370
  end
2371
+
2372
+ context_text += "ONEGATE_TOKEN='#{onegate_token_64}'\n"
2370
2373
  end
2371
2374
 
2372
2375
  context_text = Base64.encode64(context_text.chop)
@@ -2423,7 +2426,8 @@ private
2423
2426
  vm.config.hardware.device.select { |d|
2424
2427
  if is_disk?(d)
2425
2428
  disks.each{|disk|
2426
- if disk.elements["SOURCE"].text == d.backing.fileName
2429
+ if d.backing.respond_to?(:fileName) &&
2430
+ disk.elements["SOURCE"].text == d.backing.fileName &&
2427
2431
  disks.delete(disk)
2428
2432
  end
2429
2433
  }
@@ -2581,13 +2585,13 @@ private
2581
2585
  end
2582
2586
 
2583
2587
  controller = nil
2584
-
2588
+
2585
2589
  vm.config.hardware.device.each { |device|
2586
2590
  (controller = device ; break) if device.deviceInfo.label == available_controller_label
2587
2591
  }
2588
-
2592
+
2589
2593
  new_unit_number = available_numbers.sort[0]
2590
-
2594
+
2591
2595
  return controller, new_unit_number
2592
2596
  end
2593
2597
 
@@ -2707,6 +2711,7 @@ private
2707
2711
  disks.each{ |disk|
2708
2712
  ds_and_img_name = "[#{disk['DATASTORE']}] #{disk['SOURCE']}"
2709
2713
  vcenter_disk = vm.config.hardware.device.select { |d| is_disk?(d) &&
2714
+ d.backing.respond_to?(:fileName) &&
2710
2715
  d.backing.fileName == ds_and_img_name }[0]
2711
2716
  spec[:deviceChange] << {
2712
2717
  :operation => :remove,
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opennebula
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.2
4
+ version: 5.1.80.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - OpenNebula
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-21 00:00:00.000000000 Z
11
+ date: 2016-09-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -134,9 +134,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
134
134
  version: '0'
135
135
  required_rubygems_version: !ruby/object:Gem::Requirement
136
136
  requirements:
137
- - - ">="
137
+ - - ">"
138
138
  - !ruby/object:Gem::Version
139
- version: '0'
139
+ version: 1.3.1
140
140
  requirements: []
141
141
  rubyforge_project:
142
142
  rubygems_version: 2.5.1