knife-ovh 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,111 @@
1
+ #
2
+ # Author:: Alexis Gruet (<alexis.gruet@kroknet.com>)
3
+ #
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+
7
+ require 'chef/knife'
8
+ require 'rbvmomi'
9
+
10
+ # Base class for vsphere knife commands
11
+ class Chef
12
+ class Knife
13
+ module OvhBase
14
+
15
+ # :nodoc:
16
+ # Would prefer to do this in a rational way, but can't be done b/c of
17
+ # Mixlib::CLI's design :(
18
+ def self.included(includer)
19
+ includer.class_eval do
20
+
21
+ deps do
22
+ require 'socket'
23
+ require 'net/ssh/multi'
24
+ require 'readline'
25
+ require 'chef/json_compat'
26
+ end
27
+
28
+ option :vsphere_user,
29
+ :short => "-u USERNAME",
30
+ :long => "--user USERNAME",
31
+ :description => "The username for the host"
32
+
33
+ option :password,
34
+ :short => "-p PASSWORD",
35
+ :long => "--password PASSWORD",
36
+ :description => "The password for the host"
37
+
38
+ option :datacenter,
39
+ :short => "-d DATACENTER",
40
+ :long => "--datacenter DATACENTER",
41
+ :description => "The Datacenter to create the VM in"
42
+
43
+ option :path,
44
+ :long => "--path SOAP_PATH",
45
+ :description => "The SOAP endpoint path",
46
+ :proc => Proc.new { |p| Chef::Config[:knife][:path] = p },
47
+ :default => "/sdk"
48
+
49
+ option :port,
50
+ :long => "--port PORT",
51
+ :description => "The VI SDK port number to use",
52
+ :proc => Proc.new { |p| Chef::Config[:knife][:port] = p },
53
+ :default => 443
54
+
55
+ option :use_ssl,
56
+ :long => "--ssl USE_SSL",
57
+ :description => "Whether to use SSL connection",
58
+ :default => true
59
+
60
+ option :insecure,
61
+ :short => "-i USE_INSECURE_SSL",
62
+ :long => "--insecure USE_INSECURE_SSL",
63
+ :description => "Determines whether SSL certificate verification is skipped",
64
+ :default => true
65
+ end
66
+ end
67
+
68
+ def get_vim_connection
69
+
70
+ conn_opts = {
71
+ :host => config[:host] || Chef::Config[:knife][:vsphere_host],
72
+ :path => config[:path],
73
+ :port => config[:port],
74
+ :use_ssl => config[:ssl],
75
+ :user => config[:vsphere_user] || Chef::Config[:knife][:vsphere_user],
76
+ :password => config[:password] || Chef::Config[:knife][:vsphere_pass],
77
+ :insecure => config[:insecure]
78
+ }
79
+
80
+ vim = RbVmomi::VIM.connect conn_opts
81
+
82
+ return vim
83
+ end
84
+
85
+ def get_folders(folder)
86
+ folder.childEntity.grep(RbVmomi::VIM::Folder) << folder
87
+ end
88
+
89
+ def find_all_in_folders(folder, type)
90
+ get_folders(folder).
91
+ collect { |f| f.childEntity.grep(type) }.
92
+ flatten
93
+ end
94
+
95
+ def find_in_folders(folder, type, name)
96
+ get_folders(folder).
97
+ collect { |f| f.childEntity.grep(type) }.
98
+ flatten.
99
+ find { |o| o.name == name }
100
+ end
101
+
102
+ def fatal_exit(msg)
103
+ ui.fatal(msg)
104
+ exit 1
105
+ end
106
+
107
+ end
108
+
109
+ end
110
+
111
+ end
@@ -0,0 +1,39 @@
1
+ #
2
+ # Author:: Alexis Gruet (<alexis.gruet@kroknet.com>)
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+
6
+ require 'chef/knife/ovh_base'
7
+
8
+ # Lists all known VM templates in the configured datacenter
9
+ class Chef
10
+ class Knife
11
+ class OvhPccTemplateList < Knife
12
+
13
+ include Knife::OvhBase
14
+
15
+ banner "knife ovh pcc template list"
16
+
17
+ def run
18
+
19
+ $stdout.sync = true
20
+ $stderr.sync = true
21
+
22
+ vim = get_vim_connection
23
+
24
+ dcname = config[:vsphere_dc] || Chef::Config[:knife][:vsphere_dc]
25
+ dc = vim.serviceInstance.find_datacenter(dcname) or abort "datacenter not found"
26
+
27
+ vmFolders = get_folders(dc.vmFolder)
28
+
29
+ vms = find_all_in_folders(dc.vmFolder, RbVmomi::VIM::VirtualMachine).
30
+ select {|v| !v.config.nil? && v.config.template == true }
31
+
32
+ vms.each do |vm|
33
+ puts "#{ui.color("Template Name", :cyan)}: #{vm.name}"
34
+ end
35
+
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,275 @@
1
+ #
2
+ # Author:: Alexis Gruet (<alexis.gruet@kroknet.com>)
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+
6
+ require 'chef/knife/ovh_base'
7
+
8
+ # Clone an existing template into a new VM - bootstrap chef - and optionally give some recipes and/or roles
9
+ #
10
+ # usage:
11
+
12
+ # knife vsphere vm clone web06 00_UBUNTU-10.04 --domain intra.kroknet.com \
13
+ # --hostname web06 --ip 46.105.128.246 --gw 46.105.128.254 --dns 213.186.33.99 --netmask 255.255.255.224 \
14
+ # -d pcc-178-33-102-96_datacenter144 --ssh-user agruet --ssh-password test --distro ubuntu10.04-apt \
15
+ # -r 'recipe[kroknet-apt-repository_dev]'
16
+
17
+ class Chef
18
+ class Knife
19
+ class OvhPccVmClone < Knife
20
+
21
+ include Knife::OvhBase
22
+
23
+ deps do
24
+ require 'readline'
25
+ require 'netaddr'
26
+ require 'chef/json_compat'
27
+ require 'chef/knife/bootstrap'
28
+ Chef::Knife::Bootstrap.load_deps
29
+ end
30
+
31
+ banner "knife ovh pcc vm clone VMNAME TEMPLATE (options)"
32
+
33
+ attr_accessor :initial_sleep_delay
34
+
35
+ option :customization_ip,
36
+ :long => "--ip IP",
37
+ :description => "ip address for customization"
38
+
39
+ option :customization_netmask,
40
+ :long => "--netmask NETMASK",
41
+ :description => "netmask for customization"
42
+
43
+ option :customization_gw,
44
+ :long => "--gw GATEWAY",
45
+ :description => "gateway for customization"
46
+
47
+ option :customization_dns,
48
+ :long => "--dns DNS IP",
49
+ :description => "dns ip for customization"
50
+
51
+ option :customization_domain,
52
+ :long => "--domain CUST_DOMAIN",
53
+ :description => "domain name for customization"
54
+
55
+ option :customization_hostname,
56
+ :long => "--hostname HOSTNAME",
57
+ :description => "Unqualified hostname for customization"
58
+
59
+ option :customization_tz,
60
+ :long => "--tz CUST_TIMEZONE",
61
+ :description => "Timezone 'Area/Location' format"
62
+
63
+ option :distro,
64
+ :short => "-d DISTRO",
65
+ :long => "--distro DISTRO",
66
+ :description => "Bootstrap a distro using a template",
67
+ :proc => Proc.new { |d| Chef::Config[:knife][:distro] = d },
68
+ :default => "ubuntu10.04-apt"
69
+
70
+ option :bootstrap_version,
71
+ :long => "--bootstrap-version VERSION",
72
+ :description => "The version of Chef to install",
73
+ :proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_version] = v }
74
+
75
+ option :run_list,
76
+ :short => "-r RUN_LIST",
77
+ :long => "--run-list RUN_LIST",
78
+ :description => "Comma separated list of roles/recipes to apply",
79
+ :proc => lambda { |o| o.split(/[\s,]+/) },
80
+ :default => []
81
+
82
+ option :ssh_user,
83
+ :short => "-x USERNAME",
84
+ :long => "--ssh-user USERNAME",
85
+ :description => "The ssh username",
86
+ :default => "root"
87
+
88
+ option :ssh_password,
89
+ :short => "-P PASSWORD",
90
+ :long => "--ssh-password PASSWORD",
91
+ :description => "The ssh password"
92
+
93
+ option :power,
94
+ :long => "--start STARTVM",
95
+ :description => "Indicates whether to start the VM after a successful clone",
96
+ :default => true
97
+
98
+ def locate_config_value(key)
99
+ key = key.to_sym
100
+ Chef::Config[:knife][key] || config[key]
101
+ end
102
+
103
+ def tcp_test_ssh(hostname)
104
+ tcp_socket = TCPSocket.new(hostname, 22)
105
+ readable = IO.select([tcp_socket], nil, nil, 5)
106
+ if readable
107
+ Chef::Log.debug("sshd accepting connections on #{hostname}, banner is #{tcp_socket.gets}")
108
+ yield
109
+ true
110
+ else
111
+ false
112
+ end
113
+ rescue SocketError
114
+ sleep 2
115
+ false
116
+ rescue Errno::ETIMEDOUT
117
+ false
118
+ rescue Errno::EPERM
119
+ false
120
+ rescue Errno::ECONNREFUSED
121
+ sleep 2
122
+ false
123
+ # This happens on OVH quite often
124
+ rescue Errno::EHOSTUNREACH
125
+ sleep 2
126
+ false
127
+ ensure
128
+ tcp_socket && tcp_socket.close
129
+ end
130
+
131
+ # Run !
132
+ def run
133
+
134
+ $stdout.sync = true
135
+
136
+ vmname = @name_args[0]
137
+
138
+ if vmname.nil?
139
+ show_usage
140
+ fatal_exit("You must specify a virtual machine name")
141
+ end
142
+
143
+ template = @name_args[1]
144
+ if template.nil?
145
+ show_usage
146
+ fatal_exit("You must specify a template name")
147
+ end
148
+
149
+ vim = get_vim_connection
150
+
151
+ dcname = config[:vsphere_dc] || Chef::Config[:knife][:vsphere_dc]
152
+ dc = vim.serviceInstance.find_datacenter(dcname) or abort "datacenter not found"
153
+
154
+ hosts = find_all_in_folders(dc.hostFolder, RbVmomi::VIM::ComputeResource)
155
+ rp = hosts.first.resourcePool
156
+
157
+ src_vm = find_in_folders(dc.vmFolder, RbVmomi::VIM::VirtualMachine, template) or
158
+ abort "VM/Template not found"
159
+
160
+ # kroknet - <alexis.gruet@kroknet.com>
161
+ # Fix to handle the vm creation with many parameters
162
+
163
+ fixed_name = RbVmomi::VIM.CustomizationFixedName
164
+ fixed_name.name = config[:customization_hostname]
165
+
166
+ # Global settings
167
+ vm_dns = RbVmomi::VIM.CustomizationGlobalIPSettings
168
+
169
+ my_dns = config[:customization_dns].split(',');
170
+ my_suffix = config[:customization_domain].split(',');
171
+
172
+ vm_dns.dnsServerList = my_dns
173
+ vm_dns.dnsSuffixList = my_suffix
174
+
175
+ # Who am i ?
176
+ identity_settings = RbVmomi::VIM.CustomizationLinuxPrep
177
+
178
+ identity_settings.hostName = fixed_name
179
+ identity_settings.domain = config[:customization_domain]
180
+ identity_settings.hwClockUTC = false
181
+ identity_settings.timeZone = 'Europe/Paris'
182
+
183
+ cidr_ip = NetAddr::CIDR.create(config[:customization_ip])
184
+
185
+ # IP
186
+ vm_ip = RbVmomi::VIM::CustomizationFixedIp(:ipAddress => cidr_ip.ip)
187
+
188
+ # IPV4 Settings eth0
189
+ vm_ip_settings = RbVmomi::VIM.CustomizationIPSettings
190
+
191
+ my_gw = config[:customization_gw].split(',');
192
+
193
+ vm_ip_settings.ip = vm_ip
194
+ vm_ip_settings.subnetMask = config[:customization_netmask]
195
+ vm_ip_settings.dnsServerList = my_dns
196
+ vm_ip_settings.gateway = my_gw
197
+ vm_ip_settings.dnsDomain = config[:customization_domain]
198
+
199
+ #adapter mapping
200
+ adapter_mapping = RbVmomi::VIM.CustomizationAdapterMapping
201
+ adapter_mapping.adapter = vm_ip_settings
202
+
203
+ customization_spec = RbVmomi::VIM.CustomizationSpec
204
+
205
+ multi_nic = [ adapter_mapping ]
206
+
207
+ customization_spec.globalIPSettings = vm_dns
208
+ customization_spec.identity = identity_settings
209
+ customization_spec.nicSettingMap = multi_nic
210
+
211
+ rspec = RbVmomi::VIM.VirtualMachineRelocateSpec(:pool => rp)
212
+
213
+ clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(:customization => customization_spec,
214
+ :location => rspec,
215
+ :powerOn => false,
216
+ :template => false)
217
+
218
+ task = src_vm.CloneVM_Task(:folder => src_vm.parent, :name => vmname, :spec => clone_spec)
219
+ puts "Cloning template #{template} to new VM #{vmname}"
220
+ task.wait_for_completion
221
+ puts "Finished creating virtual machine #{vmname}"
222
+
223
+ if config[:power]
224
+ vm = find_in_folders(dc.vmFolder, RbVmomi::VIM::VirtualMachine, vmname) or
225
+ fatal_exit("VM #{vmname} not found")
226
+ vm.PowerOnVM_Task.wait_for_completion
227
+ puts "Powered on virtual machine #{vmname}"
228
+
229
+ # TODO:
230
+ # Fix this crappy stuff - while loop is used
231
+ # to ensure the hostname is well done updated by the the tools
232
+ # should be nice to ask francois from ovh to figured out if one method exist to deal with this corner case
233
+ #
234
+ print "\n#{ui.color("Waiting for server", :magenta)}"
235
+
236
+ while vm.guest.hostName != vmname
237
+ print(".")
238
+ sleep 2
239
+ end
240
+
241
+ puts("\n")
242
+ print "\n#{ui.color("VM #{vmname} - Ready - Starting chef bootstrap", :magenta)}"
243
+ puts("\n")
244
+ print "\n#{ui.color("As to bootstrap chef we need a ssh conn - lets check for it", :magenta)}"
245
+
246
+ #or fatal_exit( "\n#{ui.color("No way to connect the remote server via SSH - IP : #{cidr_ip.ip} - If use used a private IP, ensure a vpn connection exist", :magenta)}" )
247
+
248
+ print "." until tcp_test_ssh(config[:customization_ip]) {
249
+ bootstrap = Chef::Knife::Bootstrap.new
250
+ bootstrap.name_args = [cidr_ip.ip]
251
+ bootstrap.config[:run_list] = config[:run_list]
252
+ bootstrap.config[:ssh_user] = config[:ssh_user]
253
+ bootstrap.config[:chef_node_name] = config[:chef_node_name]
254
+ bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
255
+ bootstrap.config[:distro] = locate_config_value(:distro)
256
+ bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
257
+ bootstrap.config[:environment] = config[:environment]
258
+
259
+ bootstrap.run
260
+
261
+ puts("\n")
262
+ puts("\n")
263
+ print "\n#{ui.color("server is up and bootstraped with a chef-client", :green)}"
264
+ puts("\n")
265
+
266
+ }
267
+ end
268
+
269
+ end # end run
270
+
271
+ end
272
+
273
+ end
274
+
275
+ end
@@ -0,0 +1,42 @@
1
+ #
2
+ # Author:: Alexis Gruet (<alexis.gruet@kroknet.com>)
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+
6
+ require 'chef/knife/ovh_base'
7
+
8
+ # Delete a virtual machine from vCenter
9
+ class Chef
10
+ class Knife
11
+ class OvhPccVmDelete < Knife
12
+
13
+ include Knife::OvhBase
14
+
15
+ banner "knife ovh pcc vm delete VMNAME"
16
+
17
+ def run
18
+ $stdout.sync = true
19
+
20
+ vmname = @name_args[0]
21
+
22
+ if vmname.nil?
23
+ show_usage
24
+ fatal_exit("You must specify a virtual machine name")
25
+ end
26
+
27
+ vim = get_vim_connection
28
+
29
+ dcname = config[:vsphere_dc] || Chef::Config[:knife][:vsphere_dc]
30
+ dc = vim.serviceInstance.find_datacenter(dcname) or
31
+ fatal_exit("datacenter not found")
32
+
33
+ vm = find_in_folders(dc.vmFolder, RbVmomi::VIM::VirtualMachine,vmname) or fatal_exit("VM #{vmname} not found")
34
+
35
+ vm.PowerOffVM_Task.wait_for_completion unless vm.runtime.powerState == "poweredOff"
36
+ vm.Destroy_Task
37
+ puts "Deleted virtual machine #{vmname}"
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,47 @@
1
+ #
2
+ # Author:: Alexis Gruet (<alexis.gruet@kroknet.com>)
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+
6
+ require 'chef/knife/ovh_base'
7
+
8
+ # Lists all known virtual machines in the configured datacenter
9
+ class Chef
10
+ class Knife
11
+ class OvhPccVmList < Knife
12
+
13
+ include Knife::OvhBase
14
+
15
+ banner "knife ovh pcc vm list"
16
+
17
+ option :folder,
18
+ :short => "-f SHOWFOLDER",
19
+ :long => "--folder",
20
+ :description => "The folder to list VMs in"
21
+
22
+ def run
23
+
24
+ $stdout.sync = true
25
+
26
+ vim = get_vim_connection
27
+
28
+ dcname = config[:vsphere_dc] || Chef::Config[:knife][:vsphere_dc]
29
+ dc = vim.serviceInstance.find_datacenter(dcname) or abort "datacenter not found"
30
+
31
+ baseFolder = dc.vmFolder;
32
+
33
+ if config[:folder]
34
+ baseFolder = get_folders(dc.vmFolder).find { |f| f.name == config[:folder]} or
35
+ abort "no such folder #{config[:folder]}"
36
+ end
37
+
38
+ vms = find_all_in_folders(baseFolder, RbVmomi::VIM::VirtualMachine)
39
+ vms.each do |vm|
40
+ puts "#{ui.color("VM Name", :cyan)}: #{vm.name}"
41
+ end
42
+ end
43
+
44
+ end
45
+ end
46
+ end
47
+
@@ -0,0 +1,6 @@
1
+ module Knife
2
+ module Ovh
3
+ VERSION = "0.0.2"
4
+ MAJOR, MINOR, TINY = VERSION.split('.')
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: knife-ovh
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 2
9
+ version: 0.0.2
10
+ platform: ruby
11
+ authors:
12
+ - Alexis Gruet
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2012-01-06 00:00:00 +01:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: netaddr
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 5
30
+ - 0
31
+ version: 1.5.0
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: chef
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ~>
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 0
43
+ - 10
44
+ - 0
45
+ version: 0.10.0
46
+ type: :runtime
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: rbvmomi
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ~>
54
+ - !ruby/object:Gem::Version
55
+ segments:
56
+ - 1
57
+ - 5
58
+ - 0
59
+ version: 1.5.0
60
+ type: :runtime
61
+ version_requirements: *id003
62
+ description: Ovh cloud interface vSphere Support for Chef's Knife Command
63
+ email: alexis.gruet@kroknet.com
64
+ executables: []
65
+
66
+ extensions: []
67
+
68
+ extra_rdoc_files: []
69
+
70
+ files:
71
+ - lib/chef/knife/ovh_base.rb
72
+ - lib/chef/knife/ovh_pcc_template_list.rb
73
+ - lib/chef/knife/ovh_pcc_vm_clone.rb
74
+ - lib/chef/knife/ovh_pcc_vm_delete.rb
75
+ - lib/chef/knife/ovh_pcc_vm_list.rb
76
+ - lib/knife-ovh/version.rb
77
+ has_rdoc: true
78
+ homepage:
79
+ licenses: []
80
+
81
+ post_install_message:
82
+ rdoc_options: []
83
+
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ segments:
91
+ - 0
92
+ version: "0"
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ segments:
98
+ - 0
99
+ version: "0"
100
+ requirements: []
101
+
102
+ rubyforge_project:
103
+ rubygems_version: 1.3.6
104
+ signing_key:
105
+ specification_version: 3
106
+ summary: Ovh cloud interface vSphere Support for Knife
107
+ test_files: []
108
+