vagrant-xapi 0.0.1 → 1.0.0

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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +83 -0
  3. data/LICENSE +21 -0
  4. data/README.md +460 -4
  5. data/docs/architecture.md +1420 -0
  6. data/docs/operations.md +531 -0
  7. data/lib/vagrant-xapi/action/base.rb +103 -0
  8. data/lib/vagrant-xapi/action/cloud_init.rb +130 -0
  9. data/lib/vagrant-xapi/action/create.rb +127 -0
  10. data/lib/vagrant-xapi/action/destroy.rb +68 -0
  11. data/lib/vagrant-xapi/action/network.rb +229 -0
  12. data/lib/vagrant-xapi/action/package.rb +103 -0
  13. data/lib/vagrant-xapi/action/power.rb +185 -0
  14. data/lib/vagrant-xapi/action/snapshots.rb +43 -0
  15. data/lib/vagrant-xapi/action.rb +317 -0
  16. data/lib/vagrant-xapi/api/client.rb +393 -0
  17. data/lib/vagrant-xapi/api/errors.rb +98 -0
  18. data/lib/vagrant-xapi/api/http_transfer.rb +332 -0
  19. data/lib/vagrant-xapi/api/redactor.rb +77 -0
  20. data/lib/vagrant-xapi/api/session_registry.rb +59 -0
  21. data/lib/vagrant-xapi/api/task.rb +156 -0
  22. data/lib/vagrant-xapi/api/transport.rb +249 -0
  23. data/lib/vagrant-xapi/cap.rb +90 -0
  24. data/lib/vagrant-xapi/command/adopt.rb +92 -0
  25. data/lib/vagrant-xapi/command/base.rb +79 -0
  26. data/lib/vagrant-xapi/command/console.rb +87 -0
  27. data/lib/vagrant-xapi/command/diff.rb +267 -0
  28. data/lib/vagrant-xapi/command/hosts.rb +63 -0
  29. data/lib/vagrant-xapi/command/info.rb +94 -0
  30. data/lib/vagrant-xapi/command/marked.rb +85 -0
  31. data/lib/vagrant-xapi/command/orphans.rb +96 -0
  32. data/lib/vagrant-xapi/command/root.rb +55 -0
  33. data/lib/vagrant-xapi/command/templates.rb +91 -0
  34. data/lib/vagrant-xapi/config.rb +333 -0
  35. data/lib/vagrant-xapi/errors.rb +113 -0
  36. data/lib/vagrant-xapi/extensions.rb +80 -0
  37. data/lib/vagrant-xapi/glue.rb +330 -0
  38. data/lib/vagrant-xapi/legacy/plugin.rb +61 -0
  39. data/lib/vagrant-xapi/legacy/xenapi.rb +129 -0
  40. data/lib/vagrant-xapi/plugin.rb +58 -0
  41. data/lib/vagrant-xapi/provider.rb +135 -0
  42. data/lib/vagrant-xapi/services/adoption.rb +73 -0
  43. data/lib/vagrant-xapi/services/base.rb +242 -0
  44. data/lib/vagrant-xapi/services/box_source.rb +218 -0
  45. data/lib/vagrant-xapi/services/console.rb +115 -0
  46. data/lib/vagrant-xapi/services/content_diff.rb +96 -0
  47. data/lib/vagrant-xapi/services/destroyer.rb +104 -0
  48. data/lib/vagrant-xapi/services/diff_disks.rb +346 -0
  49. data/lib/vagrant-xapi/services/differ.rb +382 -0
  50. data/lib/vagrant-xapi/services/disk_image.rb +122 -0
  51. data/lib/vagrant-xapi/services/disks.rb +281 -0
  52. data/lib/vagrant-xapi/services/exporter.rb +128 -0
  53. data/lib/vagrant-xapi/services/file_diff.rb +249 -0
  54. data/lib/vagrant-xapi/services/guest_files.rb +730 -0
  55. data/lib/vagrant-xapi/services/hosts.rb +55 -0
  56. data/lib/vagrant-xapi/services/image_upload.rb +172 -0
  57. data/lib/vagrant-xapi/services/inventory.rb +225 -0
  58. data/lib/vagrant-xapi/services/ip_detector.rb +107 -0
  59. data/lib/vagrant-xapi/services/lock.rb +293 -0
  60. data/lib/vagrant-xapi/services/lookup.rb +181 -0
  61. data/lib/vagrant-xapi/services/networks.rb +248 -0
  62. data/lib/vagrant-xapi/services/power.rb +108 -0
  63. data/lib/vagrant-xapi/services/rfb.rb +129 -0
  64. data/lib/vagrant-xapi/services/snapshots.rb +81 -0
  65. data/lib/vagrant-xapi/services/template_cache.rb +263 -0
  66. data/lib/vagrant-xapi/services/tunnels.rb +217 -0
  67. data/lib/vagrant-xapi/services/vm_builder.rb +215 -0
  68. data/lib/vagrant-xapi/version.rb +7 -0
  69. data/lib/vagrant-xapi/xenserver/config.rb +69 -0
  70. data/lib/vagrant-xapi/xenserver/migrate.rb +103 -0
  71. data/lib/vagrant-xapi/xenserver/plugin.rb +96 -0
  72. data/lib/vagrant-xapi/xenserver/provider.rb +22 -0
  73. data/lib/vagrant-xapi/xenserver/takeover.rb +79 -0
  74. data/lib/vagrant-xapi.rb +21 -2
  75. data/lib/xenapi.rb +22 -0
  76. data/locales/en.yml +517 -0
  77. data/locales/legacy.en.yml +6 -0
  78. metadata +86 -5
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VagrantPlugins
4
+ module Xapi
5
+ VERSION = "1.0.0"
6
+ end
7
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../config"
4
+
5
+ module VagrantPlugins
6
+ module Xapi
7
+ module Xenserver
8
+ # config.vm.provider :xenserver do |xs| ... end: the options of
9
+ # vagrant-xenserver, on top of every option of :xapi.
10
+ #
11
+ # After finalize! the xs_* readers return the values in effect, for
12
+ # code in Vagrantfiles that reads them (e.g. triggers that connect with
13
+ # XenApi::Client): xs_use_ssl is always true and xs_port the HTTPS port.
14
+ class Config < Xapi::Config
15
+ LEGACY_OPTIONS = %i[xs_host xs_port xs_use_ssl xs_username xs_password name pv use_himn].freeze
16
+
17
+ attr_accessor(*LEGACY_OPTIONS)
18
+
19
+ def initialize
20
+ super
21
+ LEGACY_OPTIONS.each { |opt| instance_variable_set(:"@#{opt}", UNSET_VALUE) }
22
+ end
23
+
24
+ def finalize!
25
+ @host = @xs_host if @host == UNSET_VALUE && set?(@xs_host)
26
+ @username = @xs_username if @username == UNSET_VALUE && set?(@xs_username)
27
+ @password = @xs_password if @password == UNSET_VALUE && set?(@xs_password)
28
+ @name_label = @name if @name_label == UNSET_VALUE && set?(@name)
29
+ @port = https_port if @port == UNSET_VALUE && set?(@xs_port)
30
+ if @use_himn == true
31
+ @ip_detection = [:himn] if @ip_detection == UNSET_VALUE
32
+ @ssh_via_host = true if @ssh_via_host == UNSET_VALUE
33
+ end
34
+ super
35
+ @pv = @pv == UNSET_VALUE ? nil : @pv
36
+ @use_himn = @use_himn == true
37
+ @xs_host = @host
38
+ @xs_port = @port
39
+ @xs_use_ssl = true
40
+ @xs_username = @username
41
+ @xs_password = @password
42
+ @name = @name_label
43
+ end
44
+
45
+ def validate(machine)
46
+ machine.ui.warn(I18n.t("vagrant_xapi.xenserver.pv_ignored")) if @pv
47
+ super
48
+ end
49
+
50
+ def instance_variables_hash
51
+ super.tap { |h| h["xs_password"] = "[REDACTED]" if h["xs_password"] }
52
+ end
53
+
54
+ private
55
+
56
+ def set?(value)
57
+ value != UNSET_VALUE && !value.nil? && !value.to_s.empty?
58
+ end
59
+
60
+ # vagrant-xenserver defaulted to port 80, where its xenapi gem spoke
61
+ # plain HTTP whatever xs_use_ssl said (TLS only on port 443).
62
+ # vagrant-xapi always uses HTTPS, so that port becomes 443.
63
+ def https_port
64
+ @xs_port.to_i == 80 ? 443 : @xs_port.to_i
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+
5
+ require_relative "../command/base"
6
+ require_relative "../services/adoption"
7
+ require_relative "../services/lookup"
8
+
9
+ module VagrantPlugins
10
+ module Xapi
11
+ module Command
12
+ # vagrant xapi migrate: moves this project's machines from the provider
13
+ # :xenserver to :xapi, after `config.vm.provider :xenserver` in the
14
+ # Vagrantfile became `:xapi`. Vagrant keeps a machine's id under
15
+ # .vagrant/machines/<name>/<provider>; without the move it would create
16
+ # new VMs. The VM's exclusive disks are marked as its own, so that
17
+ # `vagrant destroy` under :xapi still removes disks that triggers
18
+ # attached, as it did under :xenserver. Machines whose id is still an
19
+ # OpaqueRef of vagrant-xenserver are left alone: any vagrant command
20
+ # with the :xenserver Vagrantfile (e.g. `vagrant status`) takes them
21
+ # over first.
22
+ class Migrate < Base
23
+
24
+ def execute
25
+ opts = OptionParser.new do |o|
26
+ o.banner = "Usage: vagrant xapi migrate"
27
+ o.separator ""
28
+ o.separator "Moves this project's :xenserver machines to the :xapi provider."
29
+ end
30
+ argv = parse_options(opts)
31
+ return 0 if argv.nil?
32
+ raise Vagrant::Errors::CLIInvalidUsage, help: opts.help.chomp unless argv.empty?
33
+
34
+ moved = machine_dirs.count { |dir| migrate(dir) }
35
+ @env.ui.info(t("migrate_done", count: moved))
36
+ 0
37
+ end
38
+
39
+ private
40
+
41
+ def machine_dirs
42
+ root = @env.local_data_path && Pathname.new(@env.local_data_path.to_s).join("machines")
43
+ return [] unless root&.directory?
44
+
45
+ root.glob("*/xenserver").select(&:directory?).sort
46
+ end
47
+
48
+ def migrate(dir)
49
+ name = dir.parent.basename.to_s
50
+ id = dir.join("id").file? ? dir.join("id").read.strip : ""
51
+ return false if id.empty?
52
+
53
+ unless Services::Lookup.uuid?(id)
54
+ @env.ui.warn(t("migrate_needs_takeover", machine: name))
55
+ return false
56
+ end
57
+ target = dir.parent.join("xapi")
58
+ if target.exist?
59
+ @env.ui.warn(t("migrate_target_exists", machine: name))
60
+ return false
61
+ end
62
+
63
+ # Lock the index entry first: if another Vagrant process holds the
64
+ # machine, nothing has moved yet.
65
+ entry = index_entry(dir)
66
+ begin
67
+ File.rename(dir.to_s, target.to_s)
68
+ if entry
69
+ entry.provider = "xapi"
70
+ @env.machine_index.set(entry)
71
+ end
72
+ ensure
73
+ @env.machine_index.release(entry) if entry
74
+ end
75
+ mark_disks(name, id)
76
+ @env.ui.info(t("migrated", machine: name, uuid: id))
77
+ true
78
+ rescue Vagrant::Errors::MachineLocked
79
+ @env.ui.warn(t("migrate_locked", machine: name))
80
+ false
81
+ end
82
+
83
+ def mark_disks(name, uuid)
84
+ machine = @env.machine(name.to_sym, :xapi)
85
+ ctx = Glue.context(machine)
86
+ vm = Errors.wrap { Services::Lookup.new(ctx).vm_by_uuid(uuid) }
87
+ Errors.wrap { Services::Adoption.new(ctx).mark_disks(vm) } if vm
88
+ rescue Vagrant::Errors::VagrantError => e
89
+ @env.ui.warn(t("migrate_disks_not_marked", machine: name, error: e.message))
90
+ end
91
+
92
+ # The machine's entry in Vagrant's machine index (`vagrant
93
+ # global-status`), locked; nil when it has none.
94
+ def index_entry(dir)
95
+ index_uuid = dir.join("index_uuid")
96
+ return nil unless index_uuid.file?
97
+
98
+ @env.machine_index.get(index_uuid.read.strip)
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Compatibility with vagrant-xenserver: Vagrantfiles written for it keep
4
+ # working, with everything that plugin did.
5
+ #
6
+ # - the provider :xenserver with the xs_* options (config.rb, provider.rb),
7
+ # - machines that vagrant-xenserver created (takeover.rb),
8
+ # - its `vagrant destroy`, which removed every disk of the VM,
9
+ # - `vagrant xapi migrate` to move machines to :xapi (migrate.rb).
10
+ #
11
+ # It hooks into the core through VagrantPlugins::Xapi::Extensions; see
12
+ # docs/architecture.md, section 17. The XenApi::Client stand-in for triggers
13
+ # is not part of this: it is temporary and lives in its own directory.
14
+
15
+ require_relative "../extensions"
16
+
17
+ module VagrantPlugins
18
+ module Xapi
19
+ module Xenserver
20
+ PROVIDER = :xenserver
21
+
22
+ # Both plugins would register :xenserver; the old one keeps it.
23
+ def self.old_plugin_installed?
24
+ Vagrant::Plugin::Manager.instance.installed_plugins.key?("vagrant-xenserver")
25
+ rescue StandardError
26
+ false
27
+ end
28
+
29
+ def self.machine?(machine)
30
+ machine.provider_name.to_s == PROVIDER.to_s
31
+ end
32
+
33
+ # Once per process, the first time a :xenserver machine is loaded.
34
+ def self.notice(machine)
35
+ return if @noticed
36
+
37
+ @noticed = true
38
+ machine.env.ui.info(I18n.t("vagrant_xapi.xenserver.notice"), prefix: false)
39
+ end
40
+ end
41
+
42
+ if Xenserver.old_plugin_installed?
43
+ # Not Kernel#warn: it prints nothing when Ruby runs without warnings,
44
+ # as Vagrant's installer does.
45
+ $stderr.puts "vagrant-xapi: vagrant-xenserver is installed too, so vagrant-xapi leaves the :xenserver " \
46
+ "provider to it. Remove it with `vagrant plugin uninstall vagrant-xenserver`."
47
+ else
48
+ class XenserverPlugin < Vagrant.plugin("2")
49
+ name "xapi (vagrant-xenserver compatibility)"
50
+ description "Runs Vagrantfiles of vagrant-xenserver (provider :xenserver) with vagrant-xapi."
51
+
52
+ # Not defaultable, like :xapi: Vagrant still picks it when a
53
+ # Vagrantfile configures :xenserver.
54
+ provider(Xenserver::PROVIDER, parallel: true, defaultable: false, priority: 5,
55
+ box_format: %w[xenserver xapi libvirt]) do
56
+ require_relative "provider"
57
+ Xenserver::Provider
58
+ end
59
+
60
+ config(Xenserver::PROVIDER, :provider) do
61
+ require_relative "config"
62
+ Xenserver::Config
63
+ end
64
+
65
+ Plugin::CAPABILITIES.each do |cap|
66
+ provider_capability(Xenserver::PROVIDER, cap) do
67
+ require_relative "../cap"
68
+ Cap
69
+ end
70
+ end
71
+ end
72
+
73
+ Extensions.add_provider_name(Xenserver::PROVIDER)
74
+
75
+ Extensions.on_foreign_id do |machine, ctx|
76
+ if Xenserver.machine?(machine)
77
+ require_relative "takeover"
78
+ Xenserver::Takeover.new(machine, ctx).run
79
+ end
80
+ end
81
+
82
+ # vagrant-xenserver removed every disk of the VM on destroy, also disks
83
+ # that triggers attached; mark the VM's exclusive disks as its own so
84
+ # they go as well. Sharable disks and disks of other VMs stay.
85
+ Extensions.on_before_destroy do |machine, ctx, vm|
86
+ if Xenserver.machine?(machine)
87
+ require_relative "../services/adoption"
88
+ Services::Adoption.new(ctx).mark_disks(vm)
89
+ end
90
+ end
91
+
92
+ Extensions.add_subcommand(:migrate, "../xenserver/migrate", "Migrate",
93
+ "move machines of vagrant-xenserver Vagrantfiles to :xapi")
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../provider"
4
+
5
+ module VagrantPlugins
6
+ module Xapi
7
+ module Xenserver
8
+ # The :xapi provider under the name :xenserver, saying once per run
9
+ # that the compatibility with vagrant-xenserver is in use.
10
+ class Provider < Xapi::Provider
11
+ def initialize(machine)
12
+ super
13
+ Xenserver.notice(machine)
14
+ end
15
+
16
+ def to_s
17
+ "XAPI as :xenserver (#{@machine.id || "new VM"})"
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../errors"
4
+ require_relative "../glue"
5
+ require_relative "../services/adoption"
6
+ require_relative "../services/lookup"
7
+
8
+ module VagrantPlugins
9
+ module Xapi
10
+ module Xenserver
11
+ # Takes over a VM that vagrant-xenserver created. Its machine id is the
12
+ # VM's OpaqueRef. The VM and its exclusive disks get this machine's
13
+ # marking, and the id becomes the VM UUID. A reference that no longer
14
+ # exists means the VM is gone: not created, as vagrant-xenserver did.
15
+ # It is never looked up by name: another VM with the same name (say a
16
+ # colleague's, from the same Vagrantfile) would be marked as this
17
+ # machine's and later destroyed.
18
+ class Takeover
19
+ def initialize(machine, ctx)
20
+ @machine = machine
21
+ @ctx = ctx
22
+ @client = ctx.client
23
+ end
24
+
25
+ def run
26
+ return unless @machine.id.to_s.start_with?("OpaqueRef:")
27
+
28
+ vm = by_ref(@machine.id.to_s)
29
+ return @machine.id = nil unless vm
30
+
31
+ rec = @client.call("VM.get_record", vm)
32
+ return @machine.id = nil if rec["is_a_template"] || rec["is_a_snapshot"] || rec["is_control_domain"]
33
+
34
+ return @machine.id = nil unless own_or_unmarked?(rec)
35
+
36
+ adoption = Services::Adoption.new(@ctx)
37
+ adoption.mark_vm(vm, marking)
38
+ adoption.mark_disks(vm)
39
+ @machine.ui.info(I18n.t("vagrant_xapi.xenserver.taken_over", name: rec["name_label"], uuid: rec["uuid"]))
40
+ @machine.id = rec["uuid"]
41
+ end
42
+
43
+ private
44
+
45
+ # nil when XAPI no longer knows the reference; other errors go up.
46
+ def by_ref(ref)
47
+ @client.call("VM.get_uuid", ref)
48
+ ref
49
+ rescue Api::XapiError => e
50
+ raise unless e.code == "HANDLE_INVALID"
51
+
52
+ nil
53
+ end
54
+
55
+ # A VM that vagrant-xapi already marked for another machine is not
56
+ # taken: this machine no longer refers to it (not created), and the
57
+ # VM stays as it is. A warning names its owner, for `vagrant xapi
58
+ # adopt --force` if it is this machine's after all. This machine's own
59
+ # marking (an earlier takeover) is fine.
60
+ def own_or_unmarked?(rec)
61
+ oc = rec["other_config"] || {}
62
+ return true unless Services::Marking.marked?(oc)
63
+ return true if oc[Services::Marking::MACHINE] == @machine.name.to_s &&
64
+ oc[Services::Marking::PROJECT] == Glue.project_path(@machine)
65
+
66
+ @machine.ui.warn(I18n.t("vagrant_xapi.xenserver.other_owner", uuid: rec["uuid"],
67
+ machine: oc[Services::Marking::MACHINE].to_s,
68
+ project: oc[Services::Marking::PROJECT].to_s))
69
+ false
70
+ end
71
+
72
+ def marking
73
+ Services::Marking.vm_marking(machine_name: @machine.name, project: Glue.project_path(@machine),
74
+ client_id: Glue.client_id(@machine.env))
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
data/lib/vagrant-xapi.rb CHANGED
@@ -1,2 +1,21 @@
1
- warn "vagrant-xapi 0.0.1 from rubygems.org is a placeholder without a plugin. " \
2
- "Remove it with `vagrant plugin uninstall vagrant-xapi`."
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+
5
+ module VagrantPlugins
6
+ module Xapi
7
+ lib_path = Pathname.new(File.expand_path("vagrant-xapi", __dir__))
8
+ autoload :Action, lib_path.join("action").to_s
9
+ autoload :Errors, lib_path.join("errors").to_s
10
+
11
+ def self.source_root
12
+ @source_root ||= Pathname.new(File.expand_path("..", __dir__))
13
+ end
14
+ end
15
+ end
16
+
17
+ require_relative "vagrant-xapi/version"
18
+ require_relative "vagrant-xapi/plugin"
19
+
20
+ I18n.load_path << VagrantPlugins::Xapi.source_root.join("locales", "en.yml").to_s
21
+ I18n.reload!
data/lib/xenapi.rb ADDED
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ # `require "xenapi"` in Vagrantfiles written for vagrant-xenserver loads
4
+ # vagrant-xapi's stand-in. Part of the temporary compatibility in
5
+ # lib/vagrant-xapi/legacy; delete this file with it.
6
+ #
7
+ # When vagrant-xenserver is installed too, vagrant-xapi leaves it alone: its
8
+ # own `require "xenapi"`, and the triggers, get the real gem it brought
9
+ # along, also when this file comes first on the load path.
10
+ module XenApi
11
+ def self.real_gem_file
12
+ return nil unless defined?(VagrantPlugins::Xapi::Xenserver) && VagrantPlugins::Xapi::Xenserver.old_plugin_installed?
13
+
14
+ Gem.find_files("xenapi.rb").find { |file| File.expand_path(file) != File.expand_path(__FILE__) }
15
+ end
16
+ end
17
+
18
+ if (real = XenApi.real_gem_file)
19
+ require real
20
+ else
21
+ require_relative "vagrant-xapi/legacy/xenapi" unless defined?(XenApi::Client)
22
+ end