deltacloud-core 0.4.0 → 0.4.1

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.
data/bin/deltacloudd CHANGED
@@ -73,6 +73,9 @@ BANNER
73
73
  opts.on( '-t', '--timeout TIMEOUT', 'Timeout for single request (default: 60)') do |timeout|
74
74
  ENV["API_TIMEOUT"] = timeout
75
75
  end
76
+ opts.on( '-V', '--verbose', 'Set verbose logging on') do |verbose|
77
+ ENV["API_VERBOSE"] ||= 'true'
78
+ end
76
79
  opts.on( '-h', '--help', '') { options[:help] = true }
77
80
 
78
81
  opts.separator <<EOS
@@ -166,10 +169,6 @@ have_rerun = library_present?('rerun')
166
169
  unless have_thin
167
170
  require 'rack'
168
171
 
169
- # We can't chdir with webrick so add our root directory
170
- # onto the load path
171
- $: << dirname
172
-
173
172
  # Read in config.ru and convert it to an instance of Rack::Builder
174
173
  cfgfile = File.read(File.join(dirname, 'config.ru'))
175
174
  inner_app = eval("Rack::Builder.new {(" + cfgfile + "\n )}.to_app",
@@ -196,8 +195,7 @@ else
196
195
  argv_opts << ['start'] unless Thin::Runner.commands.include?(options[0])
197
196
  argv_opts << ['--address', ENV["API_HOST"] ]
198
197
  argv_opts << ['--port', ENV["API_PORT"] ]
199
- argv_opts << ['--rackup', 'config.ru' ]
200
- argv_opts << ['--chdir', dirname ]
198
+ argv_opts << ['--rackup', File.join(dirname, 'config.ru') ]
201
199
  argv_opts << ['-e', options[:env] ]
202
200
  argv_opts << ['--timeout', ENV["API_TIMEOUT"] || '60']
203
201
  argv_opts << ['--threaded', '-D' ]
@@ -208,7 +206,7 @@ else
208
206
  if options[:daemon]
209
207
  options[:env] = "production"
210
208
  argv_opts << [ "--daemonize", "--user", options[:user] || 'nobody', "--tag", "deltacloud-#{ENV['API_DRIVER']}"]
211
- argv << [ "--pid", options[:pid]] if options[:pid]
209
+ argv_opts << [ "--pid", options[:pid]] if options[:pid]
212
210
  end
213
211
  argv_opts.flatten!
214
212
 
@@ -220,6 +218,7 @@ else
220
218
  rerun.start
221
219
  rerun.join
222
220
  else
221
+ $:.unshift File.join(dirname)
223
222
  thin = Thin::Runner.new(argv_opts)
224
223
  thin.run!
225
224
  end
data/config.ru CHANGED
@@ -16,8 +16,10 @@
16
16
 
17
17
  require 'rubygems'
18
18
 
19
- $:.unshift File.join(File.dirname(__FILE__), '.')
19
+ top_srcdir = File.dirname(__FILE__)
20
20
 
21
- require 'server.rb'
21
+ $:.unshift File.join(top_srcdir, 'lib')
22
+
23
+ load File.join(top_srcdir, 'server.rb')
22
24
 
23
25
  run Sinatra::Application
@@ -29,7 +29,7 @@ Gem::Specification.new do |s|
29
29
  which implements the REST interface.
30
30
  EOF
31
31
 
32
- s.version = '0.4.0'
32
+ s.version = '0.4.1'
33
33
  s.date = Time.now
34
34
  s.summary = %q{Deltacloud REST API}
35
35
  s.files = FileList[
@@ -73,11 +73,26 @@ Gem::Specification.new do |s|
73
73
  s.add_dependency('json', '>= 1.1.9')
74
74
  s.add_dependency('net-ssh', '>= 2.0.0')
75
75
  s.add_dependency('thin', '>= 1.2.5')
76
- s.add_dependency('nokogiri', ">= 1.4.3")
77
- s.add_development_dependency('compass', '>= 0.8.17')
78
- s.add_development_dependency('nokogiri', '>= 1.4.1')
79
- s.add_development_dependency('rack-test', '>= 0.5.3')
80
- s.add_development_dependency('cucumber', '>= 0.6.3')
81
- s.add_development_dependency('rcov', '>= 0.9.8')
76
+ s.add_dependency('nokogiri', '>= 1.4.3')
77
+
78
+ # dependencies for various cloud providers:
79
+ # Amazon EC2 S3
80
+ s.add_dependency('aws', '>=2.5.4')
81
+ # Microsoft Azure
82
+ s.add_dependency('waz-storage', '>=1.1.0')
83
+
84
+ # Rackspace Cloudservers Cloudfiles
85
+ s.add_dependency('cloudservers')
86
+ s.add_dependency('cloudfiles')
87
+
88
+ # Terremark Vcloud Express
89
+ s.add_dependency('fog')
90
+ s.add_dependency('excon')
91
+
92
+ # Rhevm and Condor Cloud
93
+ s.add_dependency('rest-client')
94
+
95
+ # Condor Cloud
96
+ s.add_dependency('uuidtools', '>= 2.1.1')
82
97
 
83
98
  end
data/lib/deltacloud.rb ADDED
@@ -0,0 +1,27 @@
1
+ #
2
+ # Licensed to the Apache Software Foundation (ASF) under one or more
3
+ # contributor license agreements. See the NOTICE file distributed with
4
+ # this work for additional information regarding copyright ownership. The
5
+ # ASF licenses this file to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance with the
7
+ # License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ # License for the specific language governing permissions and limitations
15
+ # under the License.
16
+
17
+ require 'deltacloud/drivers'
18
+
19
+ require 'deltacloud/core_ext'
20
+
21
+ require 'deltacloud/base_driver'
22
+ require 'deltacloud/hardware_profile'
23
+ require 'deltacloud/state_machine'
24
+ require 'deltacloud/helpers'
25
+ require 'deltacloud/models'
26
+ require 'deltacloud/validation'
27
+ require 'deltacloud/runner'
@@ -16,3 +16,4 @@
16
16
 
17
17
  require 'deltacloud/core_ext/string'
18
18
  require 'deltacloud/core_ext/integer'
19
+ require 'deltacloud/core_ext/hash'
@@ -0,0 +1,29 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one or more
2
+ # contributor license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright ownership. The
4
+ # ASF licenses this file to you under the Apache License, Version 2.0 (the
5
+ # "License"); you may not use this file except in compliance with the
6
+ # License. You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations
14
+ # under the License.
15
+
16
+ class Hash
17
+ def gsub_keys(rgx_pattern, replacement)
18
+ remove = []
19
+ self.each_key do |key|
20
+ if key.to_s.match(rgx_pattern)
21
+ new_key = key.to_s.gsub(rgx_pattern, replacement).downcase
22
+ self[new_key] = self[key]
23
+ remove << key
24
+ end
25
+ end
26
+ #remove the original keys
27
+ self.delete_if{|k,v| remove.include?(k)}
28
+ end
29
+ end
@@ -34,7 +34,8 @@ module Deltacloud
34
34
  def driver_config
35
35
  if Thread::current[:drivers].nil?
36
36
  Thread::current[:drivers] = {}
37
- Dir[File.join(File::dirname(__FILE__), '..', 'config', 'drivers', '*.yaml')].each do |driver_file|
37
+ top_srcdir = File.join(File.dirname(__FILE__), '..', '..')
38
+ Dir[File.join(top_srcdir, 'config', 'drivers', '*.yaml')].each do |driver_file|
38
39
  Thread::current[:drivers].merge!(YAML::load(File::read(driver_file)))
39
40
  end
40
41
  end
@@ -36,7 +36,7 @@ module Deltacloud
36
36
  module Condor
37
37
 
38
38
  require 'base64'
39
- require 'uuid'
39
+ require 'uuidtools'
40
40
  require 'fileutils'
41
41
 
42
42
  class CondorDriver < Deltacloud::BaseDriver
@@ -139,7 +139,7 @@ module Deltacloud
139
139
  config_server_address = nil
140
140
  end
141
141
  end
142
- vm_uuid ||= UUID::new.generate
142
+ vm_uuid ||= UUIDTools::UUID.random_create.to_s
143
143
  vm_otp ||= vm_uuid[0..7]
144
144
  new_client(credentials) do |condor|
145
145
  config_server_address ||= condor.ip_agent.address
@@ -212,21 +212,23 @@ module Deltacloud
212
212
  def create_instance(credentials, image_id, opts={})
213
213
  ec2 = new_client(credentials)
214
214
  instance_options = {}
215
- instance_options.merge!(:user_data => opts[:user_data]) if opts[:user_data]
216
- instance_options.merge!(:key_name => opts[:keyname]) if opts[:keyname]
217
- instance_options.merge!(:availability_zone => opts[:realm_id]) if opts[:realm_id]
218
- instance_options.merge!(:instance_type => opts[:hwp_id]) if opts[:hwp_id] && opts[:hwp_id].length > 0
215
+ if opts[:user_data]
216
+ instance_options[:user_data] = Base64::decode64(opts[:user_data])
217
+ end
218
+ instance_options[:key_name] = opts[:keyname] if opts[:keyname]
219
+ instance_options[:availability_zone] = opts[:realm_id] if opts[:realm_id]
220
+ instance_options[:instance_type] = opts[:hwp_id] if opts[:hwp_id] && opts[:hwp_id].length > 0
219
221
  firewalls = opts.inject([]){|res, (k,v)| res << v if k =~ /firewalls\d+$/; res}
220
- instance_options.merge!(:group_ids => firewalls ) unless firewalls.empty?
221
- instance_options.merge!(
222
- :min_count => opts[:instance_count],
223
- :max_count => opts[:instance_count]
224
- ) if opts[:instance_count] and opts[:instance_count].length!=0
222
+ instance_options[:group_ids] = firewalls unless firewalls.empty?
223
+ if opts[:instance_count] and opts[:instance_count].length != 0
224
+ instance_options[:min_count] = opts[:instance_count]
225
+ instance_options[:max_count] = opts[:instance_count]
226
+ end
225
227
  if opts[:snapshot_id] and opts[:device_name]
226
- instance_options.merge!(:block_device_mappings => [{
228
+ instance_options[:block_device_mappings] = [{
227
229
  :snapshot_id => opts[:snapshot_id],
228
230
  :device_name => opts[:device_name]
229
- }])
231
+ }]
230
232
  end
231
233
  safely do
232
234
  new_instance = convert_instance(ec2.launch_instances(image_id, instance_options).first)
@@ -18,6 +18,7 @@
18
18
  require 'deltacloud/base_driver'
19
19
  require 'yaml'
20
20
  require 'deltacloud/drivers/mock/mock_client'
21
+ require 'base64'
21
22
 
22
23
  module Deltacloud::Drivers::Mock
23
24
 
@@ -200,7 +201,7 @@ module Deltacloud::Drivers::Mock
200
201
  :realm_id=>realm_id,
201
202
  :create_image=>true,
202
203
  :actions=>instance_actions_for( 'RUNNING' ),
203
- :user_data => opts[:user_data]
204
+ :user_data => opts[:user_data] ? Base64::decode64(opts[:user_data]) : nil
204
205
  }
205
206
  @client.store(:instances, instance)
206
207
  Instance.new( instance )
@@ -23,10 +23,26 @@ require 'json'
23
23
 
24
24
  module RHEVM
25
25
 
26
+ # NOTE: Injected file will be available in floppy drive inside
27
+ # the instance. (Be sure you 'modprobe floppy' on Linux)
28
+ FILEINJECT_PATH = "deltacloud-user-data.txt"
29
+
26
30
  def self.client(url)
27
31
  RestClient::Resource.new(url)
28
32
  end
29
33
 
34
+ class BackendVersionUnsupportedException < StandardError; end
35
+ class RHEVMBackendException < StandardError
36
+ def initialize(message)
37
+ @message = message
38
+ super
39
+ end
40
+
41
+ def message
42
+ @message
43
+ end
44
+ end
45
+
30
46
  class Client
31
47
 
32
48
  attr_reader :credentials, :api_entrypoint
@@ -61,11 +77,31 @@ module RHEVM
61
77
  RHEVM::client(@api_entrypoint)["/vms/%s" % id].delete(headers)
62
78
  else
63
79
  xml_response = Client::parse_response(RHEVM::client(@api_entrypoint)["/vms/%s/%s" % [id, action]].post('<action/>', headers))
64
- return false if (xml_response/'action/status').first.text!="COMPLETE"
80
+ return false if (xml_response/'action/status').first.text.strip.upcase!="COMPLETE"
65
81
  end
66
82
  return true
67
83
  end
68
84
 
85
+ def api_version?(major)
86
+ headers = {
87
+ :content_type => 'application/xml',
88
+ :accept => 'application/xml'
89
+ }
90
+ headers.merge!(auth_header)
91
+ result_xml = Nokogiri::XML(RHEVM::client(@api_entrypoint)["/"].get(headers))
92
+ (result_xml/'/api/system_version').first[:major].strip == major
93
+ end
94
+
95
+ def cluster_version?(cluster_id, major)
96
+ headers = {
97
+ :content_type => 'application/xml',
98
+ :accept => 'application/xml'
99
+ }
100
+ headers.merge!(auth_header)
101
+ result_xml = Nokogiri::XML(RHEVM::client(@api_entrypoint)["/clusters/%s" % cluster_id].get(headers))
102
+ (result_xml/'/cluster/version').first[:major].strip == major
103
+ end
104
+
69
105
  def create_vm(template_id, opts={})
70
106
  opts ||= {}
71
107
  builder = Nokogiri::XML::Builder.new do
@@ -78,6 +114,23 @@ module RHEVM
78
114
  cpu {
79
115
  topology( :cores => (opts[:hwp_cpu] || '1'), :sockets => '1' )
80
116
  }
117
+ if opts[:user_data] and not opts[:user_data].empty?
118
+ if api_version?('3') and cluster_version?((opts[:realm_id] || clusters.first.id), '3')
119
+ custom_properties {
120
+ #
121
+ # FIXME: 'regexp' parameter is just a temporary workaround. This
122
+ # is a reported and verified bug and should be fixed in next
123
+ # RHEV-M release.
124
+ #
125
+ custom_property({
126
+ :name => "floppyinject",
127
+ :value => "#{RHEVM::FILEINJECT_PATH}:#{Base64.decode64(opts[:user_data])}",
128
+ :regexp => "^([^:]+):(.*)$"})
129
+ }
130
+ else
131
+ raise BackendVersionUnsupportedException.new
132
+ end
133
+ end
81
134
  }
82
135
  end
83
136
  headers = opts[:headers] || {}
@@ -86,7 +139,16 @@ module RHEVM
86
139
  :accept => 'application/xml',
87
140
  })
88
141
  headers.merge!(auth_header)
89
- vm = RHEVM::client(@api_entrypoint)["/vms"].post(Nokogiri::XML(builder.to_xml).root.to_s, headers)
142
+ begin
143
+ vm = RHEVM::client(@api_entrypoint)["/vms"].post(Nokogiri::XML(builder.to_xml).root.to_s, headers)
144
+ rescue
145
+ if $!.respond_to?(:http_body)
146
+ fault = (Nokogiri::XML($!.http_body)/'/fault/detail').first
147
+ fault = fault.text.gsub(/\[|\]/, '') if fault
148
+ end
149
+ fault ||= $!.message
150
+ raise RHEVMBackendException::new(fault)
151
+ end
90
152
  RHEVM::VM::new(self, Nokogiri::XML(vm).root)
91
153
  end
92
154
 
@@ -37,6 +37,8 @@ class RHEVMDriver < Deltacloud::BaseDriver
37
37
  constraint :max_length, 50
38
38
  end
39
39
 
40
+ feature :instances, :user_data
41
+
40
42
  USER_NAME_MAX = feature(:instances, :user_name).constraints[:max_length]
41
43
 
42
44
  # FIXME: These values are just for ilustration
@@ -192,6 +194,9 @@ class RHEVMDriver < Deltacloud::BaseDriver
192
194
  params[:hwp_id] = opts[:hwp_id] if opts[:hwp_id]
193
195
  params[:hwp_memory] = opts[:hwp_memory] if opts[:hwp_memory]
194
196
  params[:hwp_cpu] = opts[:hwp_cpu] if opts[:hwp_cpu]
197
+ if opts[:user_data]
198
+ params[:user_data] = opts[:user_data].gsub(/\n/,'')
199
+ end
195
200
  convert_instance(client, client.create_vm(image_id, params))
196
201
  end
197
202
  end
@@ -305,8 +310,8 @@ class RHEVMDriver < Deltacloud::BaseDriver
305
310
  def convert_realm(r, dc)
306
311
  Realm.new(
307
312
  :id => r.id,
308
- :name => dc.name,
309
- :state => dc.status == 'UP' ? 'AVAILABLE' : 'DOWN',
313
+ :name => r.name,
314
+ :state => dc.status.strip.upcase == 'UP' ? 'AVAILABLE' : 'DOWN',
310
315
  :limit => :unlimited
311
316
  )
312
317
  end
@@ -41,14 +41,14 @@ module Deltacloud::Drivers::VSphere
41
41
  vsphere = new_client(credentials)
42
42
  safely do
43
43
  service = vsphere.serviceInstance.content
44
- max_memory, max_cpu_cores = 0, 0
44
+ max_memory, max_cpu_cores = [], []
45
45
  service.rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).each do |dc|
46
- max_memory += dc.hostFolder.childEntity.first.summary.effectiveMemory
47
- max_cpu_cores += dc.hostFolder.childEntity.first.summary.numCpuCores
46
+ max_memory << dc.hostFolder.childEntity.first.summary.effectiveMemory
47
+ max_cpu_cores << dc.hostFolder.childEntity.first.summary.numCpuCores
48
48
  end
49
49
  [Deltacloud::HardwareProfile::new('default') do
50
- cpu (1..max_cpu_cores)
51
- memory (128..max_memory)
50
+ cpu (1..max_cpu_cores.min)
51
+ memory (128..max_memory.min)
52
52
  architecture ['x86_64', 'i386']
53
53
  end]
54
54
  end
@@ -207,6 +207,7 @@ module Deltacloud::Drivers::VSphere
207
207
  safely do
208
208
  rootFolder = vsphere.serviceInstance.content.rootFolder
209
209
  vm = find_vm(credentials, opts[:image_id])
210
+ raise "ERROR: Could not find the image in given datacenter" unless vm[:instance]
210
211
  # New instance need valid resource pool and datastore to be placed.
211
212
  # For this reason, realm_id **needs** to be set.
212
213
  if opts and opts[:realm_id]
@@ -219,6 +220,7 @@ module Deltacloud::Drivers::VSphere
219
220
  relocate = { :pool => resourcePool, :datastore => datastore }
220
221
  relocateSpec = RbVmomi::VIM.VirtualMachineRelocateSpec(relocate)
221
222
  # Set extra configuration for VM, like template_id
223
+ raise "ERROR: Memory must be multiple of 4" unless valid_memory_value?(opts[:hwp_memory])
222
224
  machine_config = {
223
225
  :memoryMB => opts[:hwp_memory],
224
226
  :numCPUs => opts[:hwp_cpu],
@@ -240,6 +242,7 @@ module Deltacloud::Drivers::VSphere
240
242
  machine_config[:extraConfig] << {
241
243
  :key => 'user_data_file', :value => "#{opts[:name]}.iso"
242
244
  }
245
+ device.connectable.startConnected = true
243
246
  device.backing = RbVmomi::VIM.VirtualCdromIsoBackingInfo(:fileName => "[#{opts[:realm_id] || vm[:datastore]}] "+
244
247
  "/#{VSphere::FileManager::DIRECTORY_PATH}/#{opts[:name]}.iso")
245
248
  machine_config.merge!({
@@ -259,6 +262,7 @@ module Deltacloud::Drivers::VSphere
259
262
  machine_config[:extraConfig] << {
260
263
  :key => 'user_iso_file', :value => "#{opts[:name]}.iso"
261
264
  }
265
+ device.connectable.startConnected = true
262
266
  device.backing = RbVmomi::VIM.VirtualCdromIsoBackingInfo(:fileName => "[#{opts[:realm_id] || vm[:datastore]}] "+
263
267
  "/#{VSphere::FileManager::DIRECTORY_PATH}/#{opts[:name]}.iso")
264
268
  machine_config.merge!({
@@ -376,7 +380,7 @@ module Deltacloud::Drivers::VSphere
376
380
  def convert_realm(datastore)
377
381
  Realm::new(
378
382
  :id => datastore.name,
379
- :name => datastore.name,
383
+ :name => datastore.name,
380
384
  :limit => datastore.summary.freeSpace,
381
385
  :state => datastore.summary.accessible ? 'AVAILABLE' : 'UNAVAILABLE'
382
386
  )
@@ -400,6 +404,10 @@ module Deltacloud::Drivers::VSphere
400
404
  new_state
401
405
  end
402
406
 
407
+ def valid_memory_value?(val)
408
+ true if (val.to_i%4) == 0
409
+ end
410
+
403
411
  end
404
412
 
405
413
  end