vagrant-xenserver-jc 0.0.13

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. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/CHANGELOG.md +40 -0
  4. data/Gemfile +14 -0
  5. data/LICENSE +8 -0
  6. data/README.md +167 -0
  7. data/Rakefile +14 -0
  8. data/example_box/install_wheezy.sh +128 -0
  9. data/example_box/metadata.json +3 -0
  10. data/example_box/mkbox.sh +17 -0
  11. data/lib/vagrant-xenserver/action/clone_disk.rb +30 -0
  12. data/lib/vagrant-xenserver/action/clone_vm.rb +30 -0
  13. data/lib/vagrant-xenserver/action/configure_network.rb +61 -0
  14. data/lib/vagrant-xenserver/action/connect_xs.rb +47 -0
  15. data/lib/vagrant-xenserver/action/create_template.rb +87 -0
  16. data/lib/vagrant-xenserver/action/create_vifs.rb +86 -0
  17. data/lib/vagrant-xenserver/action/create_vm.rb +95 -0
  18. data/lib/vagrant-xenserver/action/destroy_vm.rb +37 -0
  19. data/lib/vagrant-xenserver/action/download_xva.rb +101 -0
  20. data/lib/vagrant-xenserver/action/dummy.rb +16 -0
  21. data/lib/vagrant-xenserver/action/halt_vm.rb +23 -0
  22. data/lib/vagrant-xenserver/action/is_created.rb +20 -0
  23. data/lib/vagrant-xenserver/action/is_running.rb +20 -0
  24. data/lib/vagrant-xenserver/action/is_suspended.rb +20 -0
  25. data/lib/vagrant-xenserver/action/prepare_nfs_settings.rb +85 -0
  26. data/lib/vagrant-xenserver/action/prepare_nfs_valid_ids.rb +17 -0
  27. data/lib/vagrant-xenserver/action/read_ssh_info.rb +97 -0
  28. data/lib/vagrant-xenserver/action/read_state.rb +35 -0
  29. data/lib/vagrant-xenserver/action/resume_vm.rb +30 -0
  30. data/lib/vagrant-xenserver/action/set_vm_params.rb +28 -0
  31. data/lib/vagrant-xenserver/action/start_vm.rb +31 -0
  32. data/lib/vagrant-xenserver/action/suspend_vm.rb +30 -0
  33. data/lib/vagrant-xenserver/action/upload_vhd.rb +164 -0
  34. data/lib/vagrant-xenserver/action/upload_xva.rb +100 -0
  35. data/lib/vagrant-xenserver/action/validate_network.rb +112 -0
  36. data/lib/vagrant-xenserver/action/wait_himn.rb +58 -0
  37. data/lib/vagrant-xenserver/action.rb +272 -0
  38. data/lib/vagrant-xenserver/config.rb +102 -0
  39. data/lib/vagrant-xenserver/errors.rb +68 -0
  40. data/lib/vagrant-xenserver/plugin.rb +70 -0
  41. data/lib/vagrant-xenserver/provider.rb +36 -0
  42. data/lib/vagrant-xenserver/util/exnhandler.rb +49 -0
  43. data/lib/vagrant-xenserver/util/uploader.rb +215 -0
  44. data/lib/vagrant-xenserver/version.rb +6 -0
  45. data/lib/vagrant-xenserver.rb +17 -0
  46. data/locales/en.yml +38 -0
  47. data/vagrant-xenserver.gemspec +27 -0
  48. metadata +173 -0
@@ -0,0 +1,28 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module XenServer
5
+ module Action
6
+ class SetVMParams
7
+ def initialize(app, env)
8
+ @app = app
9
+ @logger = Log4r::Logger.new("vagrant::xenserver::actions::set_vm_params")
10
+ end
11
+
12
+ def call(env)
13
+ myvm = env[:machine].id
14
+
15
+ if env[:machine].provider_config.pv
16
+ env[:xc].VM.set_HVM_boot_policy(myvm,"")
17
+ env[:xc].VM.set_PV_bootloader(myvm,"pygrub")
18
+ end
19
+
20
+ mem = ((env[:machine].provider_config.memory) * (1024*1024)).to_s
21
+ env[:xc].VM.set_memory_limits(myvm,mem,mem,mem,mem)
22
+
23
+ @app.call env
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,31 @@
1
+ require "log4r"
2
+ require "xmlrpc/client"
3
+ require "vagrant-xenserver/util/uploader"
4
+ require "rexml/document"
5
+ require "json"
6
+ require "xenapi"
7
+
8
+ module VagrantPlugins
9
+ module XenServer
10
+ module Action
11
+ class StartVM
12
+ def initialize(app, env)
13
+ @app = app
14
+ @logger = Log4r::Logger.new("vagrant::xenserver::actions::start_vm")
15
+ end
16
+
17
+ def call(env)
18
+ myvm = env[:machine].id
19
+
20
+ begin
21
+ env[:xc].VM.start(myvm,false,false)
22
+ rescue XenApi::Errors::NoHostsAvailable
23
+ raise Errors::NoHostsAvailable
24
+ end
25
+
26
+ @app.call env
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,30 @@
1
+ require "log4r"
2
+ require "xmlrpc/client"
3
+
4
+ module VagrantPlugins
5
+ module XenServer
6
+ module Action
7
+ class SuspendVM
8
+ def initialize(app, env)
9
+ @app = app
10
+ @logger = Log4r::Logger.new("vagrant::xenserver::actions::suspend_vm")
11
+ end
12
+
13
+ def call(env)
14
+ myvm = env[:machine].id
15
+
16
+ suspend_task = env[:xc].Async.VM.suspend(myvm)
17
+ while env[:xc].task.get_status(suspend_task) == 'pending' do
18
+ sleep 1
19
+ end
20
+ suspend_result = env[:xc].task.get_status(suspend_task)
21
+ if suspend_result != "success"
22
+ raise Errors::APIError
23
+ end
24
+
25
+ @app.call env
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,164 @@
1
+ require "log4r"
2
+ require "xenapi"
3
+ require "vagrant-xenserver/util/uploader"
4
+ require "vagrant-xenserver/util/exnhandler"
5
+ require "rexml/document"
6
+ require "json"
7
+
8
+ module VagrantPlugins
9
+ module XenServer
10
+ module Action
11
+ class UploadVHD
12
+
13
+ @@lock = Mutex.new
14
+
15
+ def initialize(app, env)
16
+ @app = app
17
+ @logger = Log4r::Logger.new("vagrant::xenserver::actions::upload_vhd")
18
+ end
19
+
20
+ def get_vhd_size(box_vhd_file)
21
+ # Find out virtual size of the VHD
22
+ disk_info={}
23
+ begin
24
+ begin
25
+ disk_info=JSON.parse(IO.popen(["qemu-img", "info",box_vhd_file,"--output=json"]).read)
26
+ rescue JSON::ParserError
27
+ size=`qemu-img info #{box_vhd_file} | grep "virtual size" | cut "-d(" -f2 | cut "-d " -f1`
28
+ disk_info['virtual-size']=size.strip
29
+ end
30
+ rescue
31
+ @logger.error("Error getting virtual size of VHD: #{box_vhd_file}")
32
+ raise Errors::QemuImgError
33
+ end
34
+ return disk_info['virtual-size']
35
+ end
36
+
37
+ def call(env)
38
+ if env[:machine].provider_config.xva_url.nil?
39
+ box_vhd_file = env[:machine].box.directory.join('box.vhd').to_s
40
+
41
+ if File.exist?(box_vhd_file)
42
+ hostname = env[:machine].provider_config.xs_host
43
+ session = env[:xc].xenapi_session
44
+
45
+ @logger.info("box name=" + env[:machine].box.name.to_s)
46
+ @logger.info("box version=" + env[:machine].box.version.to_s)
47
+
48
+ md5=`dd if=#{box_vhd_file} bs=1M count=1 | md5sum | cut '-d ' -f1`.strip
49
+
50
+ @logger.info("md5=#{md5}")
51
+
52
+ # Find out if it has already been uploaded
53
+ @@lock.synchronize do
54
+
55
+ vdis = env[:xc].VDI.get_all_records
56
+
57
+ vdi_tag = "vagrant:" + env[:machine].box.name.to_s + "/" + md5
58
+
59
+ vdi_ref_rec = vdis.find { |reference,record|
60
+ @logger.info(record['tags'].to_s)
61
+ record['tags'].include?(vdi_tag)
62
+ }
63
+
64
+ if not vdi_ref_rec
65
+ virtual_size = get_vhd_size(box_vhd_file)
66
+ @logger.info("virtual_size=#{virtual_size}")
67
+ pool=env[:xc].pool.get_all
68
+ default_sr=env[:xc].pool.get_default_SR(pool[0])
69
+ @logger.info("default_SR="+default_sr)
70
+
71
+ # Verify the default SR is valid:
72
+ begin
73
+ env[:xc].SR.get_uuid(default_sr)
74
+ rescue
75
+ raise Errors::NoDefaultSR
76
+ end
77
+
78
+ vdi_record = {
79
+ 'name_label' => 'Vagrant disk',
80
+ 'name_description' => 'Base disk uploaded for the vagrant box '+env[:machine].box.name.to_s+' v'+env[:machine].box.version.to_s,
81
+ 'SR' => default_sr,
82
+ 'virtual_size' => "#{virtual_size}",
83
+ 'type' => 'user',
84
+ 'sharable' => false,
85
+ 'read_only' => false,
86
+ 'other_config' => {},
87
+ 'xenstore_data' => {},
88
+ 'sm_config' => {},
89
+ 'tags' => [] }
90
+
91
+ begin
92
+ vdi_result=env[:xc].VDI.create(vdi_record)
93
+ rescue XenApi::Errors::GenericError => e
94
+ MyUtil::Exnhandler.handle_xenapiexn("VDI.create",e,@logger)
95
+ end
96
+
97
+ @logger.info("created VDI: " + vdi_result.to_s)
98
+ vdi_uuid = env[:xc].VDI.get_uuid(vdi_result)
99
+ @logger.info("uuid: "+vdi_uuid)
100
+
101
+ # Create a task to so we can get the result of the upload
102
+ task = env[:xc].task.create("vagrant-vhd-upload",
103
+ "Task to track progress of the XVA upload from vagrant")
104
+
105
+ url = "https://#{hostname}/import_raw_vdi?session_id=#{session}&task_id=#{task}&vdi=#{vdi_result}&format=vhd"
106
+
107
+ uploader_options = {}
108
+ uploader_options[:ui] = env[:ui]
109
+ uploader_options[:insecure] = true
110
+
111
+ uploader = MyUtil::Uploader.new(box_vhd_file, url, uploader_options)
112
+
113
+ begin
114
+ uploader.upload!
115
+ rescue
116
+ env[:xc].task.cancel(task)
117
+ end
118
+
119
+ task_status = ""
120
+
121
+ begin
122
+ sleep(0.2)
123
+ task_status = env[:xc].task.get_status(task)
124
+ end while task_status == "pending"
125
+
126
+ @logger.info("task_status="+task_status)
127
+
128
+ if task_status != "success"
129
+ # Task failed - let's find out why:
130
+ error_list = env[:xc].task.get_error_info(task)
131
+ MyUtil::Exnhandler.handle("import HTTP handler", error_list)
132
+ end
133
+
134
+ task_result = env[:xc].task.get_result(task)
135
+
136
+ doc = REXML::Document.new(task_result)
137
+
138
+ doc.elements.each('value/array/data/value') do |ele|
139
+ vdi = ele.text
140
+ end
141
+
142
+ @logger.info("task_result=" + task_result)
143
+
144
+ tag_result=env[:xc].VDI.add_tags(vdi_result,vdi_tag)
145
+
146
+ @logger.info("Added tags")
147
+
148
+ env[:box_vdi] = vdi_result
149
+ else
150
+ (reference,record) = vdi_ref_rec
151
+ env[:box_vdi] = reference
152
+ @logger.info("box_vdi="+reference)
153
+
154
+ end
155
+ end
156
+ end
157
+ end
158
+
159
+ @app.call(env)
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,100 @@
1
+ require "log4r"
2
+ require "xmlrpc/client"
3
+ require "vagrant-xenserver/util/uploader"
4
+ require "rexml/document"
5
+
6
+ module VagrantPlugins
7
+ module XenServer
8
+ module Action
9
+ class UploadXVA
10
+ def initialize(app, env)
11
+ @app = app
12
+ @logger = Log4r::Logger.new("vagrant::xenserver::actions::upload_xva")
13
+ end
14
+
15
+ def call(env)
16
+ box_name = env[:machine].box.name.to_s
17
+ box_version = env[:machine].box.version.to_s
18
+
19
+ Action.getlock.synchronize do
20
+ templates = env[:xc].VM.get_all_records_where("field \"is_a_template\"=\"true\"")
21
+ template = templates.detect { |vm,vmr|
22
+ vmr["other_config"]["box_name"] == box_name &&
23
+ vmr["other_config"]["box_version"] == box_version
24
+ }
25
+
26
+ box_xva_file = env[:machine].box.directory.join('box.xva').to_s
27
+
28
+ if File.exist?(box_xva_file) && template.nil?
29
+ #box_image_file = env[:machine].box.directory.join('export.xva').to_s
30
+ hostname = env[:machine].provider_config.xs_host
31
+ session = env[:session]
32
+
33
+ @logger.info("box name=" + env[:machine].box.name.to_s)
34
+ @logger.info("box version=" + env[:machine].box.version.to_s)
35
+
36
+ # Create a task to so we can get the result of the upload
37
+ task = env[:xc].task.create("vagrant-xva-upload",
38
+ "Task to track progress of the XVA upload from vagrant")
39
+
40
+ url = "https://#{hostname}/import?session_id=#{env[:xc].xenapi_session}&task_id=#{task}"
41
+
42
+ uploader_options = {}
43
+ uploader_options[:ui] = env[:ui]
44
+ uploader_options[:insecure] = true
45
+
46
+ uploader = MyUtil::Uploader.new(box_xva_file, url, uploader_options)
47
+
48
+ begin
49
+ uploader.upload!
50
+ rescue
51
+ env[:xc].task.cancel(task)
52
+ end
53
+
54
+ task_status = ""
55
+
56
+ begin
57
+ sleep(0.2)
58
+ task_status = env[:xc].task.get_status(task)
59
+ end while task_status == "pending"
60
+
61
+ if task_status != "success"
62
+ # Task failed - let's find out why:
63
+ error_list = env[:xc].task.get_error_info(task)
64
+ MyUtil::Exnhandler.handle("VM.import", error_list)
65
+ end
66
+
67
+ task_result = env[:xc].task.get_result(task)
68
+
69
+ doc = REXML::Document.new(task_result)
70
+
71
+ @logger.debug("task_result=\"#{task_result}\"")
72
+ template_ref = doc.elements['value/array/data/value'].text
73
+
74
+ @logger.info("template_ref=" + template_ref)
75
+
76
+ # Make sure it's really a template, and add the xva_url to other_config:
77
+ env[:xc].VM.set_is_a_template(template_ref,true)
78
+ env[:xc].VM.add_to_other_config(template_ref,"box_name",box_name)
79
+ env[:xc].VM.add_to_other_config(template_ref,"box_version",box_version)
80
+
81
+ # Hackity hack: HVM booting guests don't need to set the bootable flag
82
+ # on their VBDs, but PV do. Let's set bootable=true on VBD device=0
83
+ # just in case.
84
+
85
+ vbds = env[:xc].VM.get_VBDs(template_ref)
86
+ vbds.each { |vbd|
87
+ if env[:xc].VBD.get_userdevice(vbd) == "0"
88
+ env[:xc].VBD.set_bootable(vbd, true)
89
+ end
90
+ }
91
+ env[:template] = template_ref
92
+ end
93
+ end
94
+
95
+ @app.call(env)
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,112 @@
1
+ require "log4r"
2
+ require "xmlrpc/client"
3
+
4
+ module VagrantPlugins
5
+ module XenServer
6
+ module Action
7
+ class ValidateNetwork
8
+ def initialize(app, env)
9
+ @app = app
10
+ @logger = Log4r::Logger.new("vagrant::xenserver::actions::validate_network")
11
+ end
12
+
13
+ def call(env)
14
+ myvm = env[:machine].id
15
+
16
+ # only find network without :fowarded port, then sort by name label
17
+ vifs = env[:machine].config.vm.networks.reject {
18
+ |k,v| k == :forwarded_port}.sort_by {
19
+ |k,v| v[:network] || ""}
20
+
21
+ # this will hold any vifs with no device (ethX)
22
+ vifs_unknown = []
23
+
24
+ # create a placeholder hash for all vifs
25
+ # note that eth0 is already used by HIMN, so start with 1
26
+ eth = Hash.new
27
+ vifs.count.times do |x|
28
+ x += 1
29
+ eth["eth#{x}".to_sym] = {}
30
+ end
31
+
32
+ # Get All networks without HIMN
33
+ allnetworks = env[:xc].network.get_all_records.reject {
34
+ |ref,net| net['other_config']['is_host_internal_management_network'] }
35
+
36
+ # find the network type (public/external or private/single-server)
37
+ allnets = {}
38
+ allnetworks.each do |ref, params|
39
+ allnets[ref] = params
40
+ allnets[ref]["network_type"] = params["PIFs"].empty? ? "private_network" : "external_network"
41
+ end
42
+ # convert allnets to string for error message
43
+ allnets_str = allnets.map { |k,v| "#{v['name_label']} (#{v['network_type']})"}.join(", ")
44
+
45
+ # foreach vifs which has device defined, assign it to `eth` Hash
46
+ vifs.each do |k,v|
47
+ # Check if network name label in configuration matches
48
+ # the network on Xenserver Host
49
+ netrefrec = allnets.find { |ref,net| net['name_label'].upcase == v[:network].upcase }
50
+ (net_ref, net_rec) = netrefrec
51
+ if net_ref.nil?
52
+ raise Errors::InvalidNetwork, network: v[:network], allnetwork: allnets_str, vm: env[:machine].name
53
+ end
54
+
55
+ # Assign network UUID/ref
56
+ v[:net_ref] = net_ref
57
+
58
+ # Assign network type
59
+ v[:network_type] = net_rec["network_type"]
60
+
61
+ # no match, assign a device number (ethX) later
62
+ if v[:device].nil?
63
+ # vifs_unknown will contains vifs without :device defined
64
+ # SORTED BY NETWORK NAME LABEL
65
+ vifs_unknown.push(v)
66
+ else
67
+ if v[:device].start_with?("eth") and eth.include?(v[:device].to_sym)
68
+ eth[v[:device].to_sym] = v
69
+ else
70
+ raise "Configration Error in netowrk `#{v[:network]}' for device name `#{v[:device]}'"
71
+ end
72
+ end
73
+ end
74
+
75
+ # Populate `eth' hash from the rest of unconfigured vifs
76
+ vifs_unknown.each do |vif|
77
+ unconfigured_eth = eth.find {|k,v| v.empty?}[0]
78
+ vif[:device] = unconfigured_eth.to_s
79
+ eth[unconfigured_eth.to_sym] = vif
80
+ end
81
+
82
+ # Validate network settings:
83
+ # - if `ip' and dhcp is set, raise error
84
+ # - ip and netmask must be defined it dhcp is not set
85
+ # - external_network
86
+ # - can have gateway
87
+ # - private_network (internal xenserver host-only network)
88
+ # - CANNOT have gateway
89
+ # - if `ip' defined, raise error if dhcp is set
90
+ eth.each do |e, opt|
91
+ raise Errors::InvalidInterface, eth: e, opt: opt[:proto], net: opt[:network],
92
+ message: "No IP address is defined" if opt[:proto] == 'static' and opt[:ip].nil?
93
+
94
+ raise Errors::InvalidInterface, eth: e, opt: opt[:proto], net: opt[:network],
95
+ message: "No IP netmask is defined" if opt[:proto] == 'static' and opt[:netmask].nil?
96
+
97
+ raise Errors::InvalidInterface, eth: e, opt: opt[:proto], net: opt[:network],
98
+ message: "Cannot assign IP #{opt[:ip]} here" if opt[:proto] == 'dhcp' and !opt[:ip].nil?
99
+
100
+ raise Errors::InvalidInterface, eth: e, opt: opt[:network_type], net: opt[:network],
101
+ message: "Cannot assign gateway #{opt[:gateway]} here" if opt[:network_type] == "private_network" and !opt[:gateway].nil?
102
+ end
103
+
104
+ # Put eth on global env
105
+ env[:vifs] = eth
106
+
107
+ @app.call env
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,58 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module XenServer
5
+ module Action
6
+ # This action wait for SSH Communicator via HIMN interface is ready
7
+ class WaitForHIMNCommunicator
8
+ def initialize(app, env)
9
+ @app = app
10
+ @logger = Log4r::Logger.new("vagrant_xenserver::action::wait_himn")
11
+ end
12
+
13
+ def call(env)
14
+ if env[:machine].provider_config.use_himn
15
+ env[:machine_ssh_info] = wait_ssh_via_himn(env)
16
+ end
17
+
18
+ @app.call(env)
19
+ end
20
+
21
+ def wait_ssh_via_himn(env)
22
+ machine = env[:machine]
23
+ return nil if machine.id.nil?
24
+
25
+ # Find the machine
26
+ networks = env[:xc].network.get_all_records
27
+
28
+ begin
29
+ vifs = env[:xc].VM.get_VIFs(machine.id)
30
+ rescue
31
+ @logger.info("Machine couldn't be found, assuming it got destroyed.")
32
+ machine.id = nil
33
+ return nil
34
+ end
35
+
36
+ himn = networks.find { |ref,net| net['other_config']['is_host_internal_management_network'] }
37
+ (himn_ref,himn_rec) = himn
38
+
39
+ assigned_ips = himn_rec['assigned_ips']
40
+ (vif,ip) = assigned_ips.find { |vif,ip| vifs.include? vif }
41
+ command = "ssh '#{machine.provider_config.xs_host}' -l '#{machine.provider_config.xs_username}' \"true &>/dev/null </dev/tcp/#{ip.to_s}/22 && echo open || echo closed\""
42
+ max_retry = 20
43
+ begin
44
+ retries ||= 0
45
+ ssh_ready = `#{command}`.strip
46
+ sleep 1
47
+ raise if ssh_ready == "closed"
48
+ rescue
49
+ if retries == max_retry
50
+ raise Errors::HIMNCommunicatorError
51
+ end
52
+ retry if (retries += 1) <= max_retry
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end