opennebula 5.0.2 → 5.1.80.beta1

Sign up to get free protection for your applications and to get access to all the features.
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