Module: Chef::Knife::ProxmoxBase

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(includer) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/chef/knife/proxmox_base.rb', line 8

def self.included(includer)
  includer.class_eval do

    deps do
      require 'rubygems'
      require 'rest_client'
      require 'json'
      require 'chef/json_compat'
      require 'cgi'
      require 'chef/log'
      require 'set'
      require 'net/ssh/multi'
      require 'chef/api_client'
      require 'chef/node'
      require 'readline'
      require 'chef/knife/bootstrap'
      Chef::Knife::Bootstrap.load_deps
    end
    
    # options
    option :pve_cluster_url,
      :short => "-U URL",
      :long  => "--pve_cluster_url URL",
      :description => "Your URL to access Proxmox VE server/cluster",
      :proc  => Proc.new {|url| Chef::Config[:knife][:pve_cluster_url] = url }
      
    option :pve_user_name,
      :short => "-u username",
      :long  => "--username username",
      :description => "Your username in Proxmox VE",
      :proc  => Proc.new {|username| Chef::Config[:knife][:pve_user_name] = username }
      
    option :pve_user_password,
      :short => "-p password",
      :long  => "--password password",
      :description => "Your password in Proxmox VE",
      :proc  => Proc.new {|password| Chef::Config[:knife][:pve_user_password] = password }
      
    option :pve_user_realm,
      :short => "-r realm",
      :long  => "--realm realm",
      :description => "Your realm of Authentication in Proxmox VE",
      :proc  => Proc.new {|realm| Chef::Config[:knife][:pve_user_realm] = realm }
      
    option :pve_node_name,
      :short => "-n node",
      :long  => "--node nodename",
      :description => "Proxmox VE server name where you will actuate",
      :proc  => Proc.new {|node| Chef::Config[:knife][:pve_node_name] = node }
    
  end
end

Instance Method Details

#action_response(action, response) ⇒ Object



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/chef/knife/proxmox_base.rb', line 161

def action_response(action,response)
  result = nil
  taskid = nil
  begin
    if (response.code == 200) then
      result = "OK"
    else
      result = "NOK: error code = " + response.code.to_s
    end
    taskid = JSON.parse(response.body)['data']
    waitfor(taskid)
    Chef::Log.debug("Action: #{action}, Result: #{result}\n")
  rescue Exception => msg
    result = "An exception ocurred.  Use -VV to show it"
    Chef::Log.debug("Action: #{action}, Result: #{msg}\n")
  end
  ui.msg(result)
end

#check_config_parameter(value) ⇒ Object



70
71
72
73
74
75
# File 'lib/chef/knife/proxmox_base.rb', line 70

def check_config_parameter(value)
  if (config[value].nil? or config[value].empty?) then
    ui.error "--#{value} is empty, define a value for it and try again"
    exit 1
  end
end

#check_global_parameter(value) ⇒ Object

Checks that the parameter provided is defined in knife.rb



62
63
64
65
66
67
68
# File 'lib/chef/knife/proxmox_base.rb', line 62

def check_global_parameter(value)
  if (Chef::Config[:knife][value].nil? or Chef::Config[:knife][value].empty?) then
    ui.error "knife[:#{value.to_s}] is empty, define a value for it and try again"
    exit 1
  end
  Chef::Log.debug("knife[:#{value}] = " + Chef::Config[:knife][value])
end

#connectionObject

Establishes the connection with proxmox server



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/chef/knife/proxmox_base.rb', line 78

def connection
  # First, let's check we have all info needed to connect to pve
  [:pve_cluster_url, :pve_node_name, :pve_user_name, :pve_user_password, :pve_user_realm].each do |value|
    check_global_parameter(value)
  end
  
  @connection ||= RestClient::Resource.new(Chef::Config[:knife][:pve_cluster_url])
  @auth_params ||= begin
    token = nil
    csrf_prevention_token = nil
    @connection['access/ticket'].post :username=>Chef::Config[:knife][:pve_user_name],
      :realm=>Chef::Config[:knife][:pve_user_realm],
      :password=>Chef::Config[:knife][:pve_user_password] do |response, request, result, &block| 
      if response.code == 200 then
        data = JSON.parse(response.body)
        ticket = data['data']['ticket']
        csrf_prevention_token = data['data']['CSRFPreventionToken']
        if !ticket.nil? then
          token = 'PVEAuthCookie=' + ticket.gsub!(/:/,'%3A').gsub!(/=/,'%3D')
        end
      end
    end
    {:CSRFPreventionToken => csrf_prevention_token, :cookie => token} 
  end
end

#destroy_item(klass, name, type_name) ⇒ Object

Extracted from Chef::Knife.delete_object, because it has a confirmation step built in… By specifying the ‘–purge’ flag (and also explicitly confirming the server destruction!) the user is already making their intent known. It is not necessary to make them confirm two more times.



252
253
254
255
256
257
258
259
260
# File 'lib/chef/knife/proxmox_base.rb', line 252

def destroy_item(klass, name, type_name)
  begin
    object = klass.load(name)
    object.destroy
    ui.warn("Deleted #{type_name} #{name}")
  rescue Net::HTTPServerException
    ui.warn("Could not find a #{type_name} named #{name} to delete!")
  end
end

#locate_config_value(key) ⇒ Object

locate_config_value: find a value in arguments or default chef config properties



117
118
119
120
# File 'lib/chef/knife/proxmox_base.rb', line 117

def locate_config_value(key)
  key = key.to_sym
  Chef::Config[:knife][key] || config[key]
end

#new_vmidObject

new_vmid: calculates a new vmid from the highest existing vmid



105
106
107
108
109
110
111
112
113
114
# File 'lib/chef/knife/proxmox_base.rb', line 105

def new_vmid
  vmid ||= @connection['cluster/resources?type=vm'].get @auth_params do |response, request, result, &block|
    data = JSON.parse(response.body)['data']
    vmids = Set[]
    data.each {|entry|
      vmids.add entry['vmid']
    }
    (vmids.empty? ? 100 : (vmids.max + 1)).to_s
  end
end

#server_create(vmid, vm_definition) ⇒ Object



216
217
218
219
220
221
# File 'lib/chef/knife/proxmox_base.rb', line 216

def server_create(vmid,vm_definition)
  ui.msg("Creating VM #{vmid}...")
  @connection["nodes/#{Chef::Config[:knife][:pve_node_name]}/openvz"].post "#{vm_definition}", @auth_params do |response, request, result, &block|
    action_response("server create",response)
  end
end

#server_destroy(vmid) ⇒ Object

server_destroy: Destroys the server



239
240
241
242
243
244
245
# File 'lib/chef/knife/proxmox_base.rb', line 239

def server_destroy(vmid)
  node = vmid_to_node(vmid)
  ui.msg("Destroying VM #{vmid} on node #{node}...")
  @connection["nodes/#{node}/openvz/#{vmid}"].delete @auth_params do |response, request, result, &block|
    action_response("server destroy",response)
  end
end

#server_get_data(vmid, field) ⇒ Object

server_get_address: Returns the IP Address of the machine to chef field is a string, and if it doesn’t exist, it will return nil



225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/chef/knife/proxmox_base.rb', line 225

def server_get_data(vmid,field)
  node = vmid_to_node(vmid)
  @connection["nodes/#{node}/openvz/#{vmid}/status/current"].get @auth_params do |response, request, result, &block|
    #action_response("server get data",response)
    # (field.match('all'))?JSON.parse(response.body)['data'].to_json : JSON.parse(response.body)['data'][field]
    if (field == 'all') then
      JSON.parse(response.body)['data']
    else
      JSON.parse(response.body)['data'][field]
    end
  end
end

#server_name_to_vmid(name) ⇒ Object

server_name_to_vmid: Use the name of the server to get the vmid



138
139
140
141
142
143
144
145
# File 'lib/chef/knife/proxmox_base.rb', line 138

def server_name_to_vmid(name)
  @connection['cluster/resources?type=vm'].get @auth_params do |response, request, result, &block|
    data = JSON.parse(response.body)['data']
    data.each {|entry|
      return entry['vmid'] if entry['name'].to_s.match(name)
    }
  end
end

#server_start(vmid) ⇒ Object



193
194
195
196
197
198
199
200
201
# File 'lib/chef/knife/proxmox_base.rb', line 193

def server_start(vmid)
  node = vmid_to_node(vmid)
  ui.msg("Starting VM #{vmid} on node #{node}....")
  @connection["nodes/#{node}/openvz/#{vmid}/status/start"].post "", @auth_params do |response, request, result, &block|
    # take the response and extract the taskid
    action_response("server start",response)
  end
  
end

#server_stop(vmid) ⇒ Object

server_stop: Stops the server



204
205
206
207
208
209
210
211
212
213
214
# File 'lib/chef/knife/proxmox_base.rb', line 204

def server_stop(vmid)
  node = vmid_to_node(vmid)
  ui.msg("Stopping VM #{vmid} on node #{node}...")
  @connection["nodes/#{node}/openvz/#{vmid}/status/stop"].post "", @auth_params do |response, request, result, &block|
    # take the response and extract the taskid
    action_response("server stop",response)
  end
  rescue Exception => e
    ui.warn("The VMID does not match any node")
    exit 1
end

#template_number_to_name(number, storage) ⇒ Object

template_number_to_name: converts the id from the template list to the real name in the storage of the node



124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/chef/knife/proxmox_base.rb', line 124

def template_number_to_name(number,storage)
  template_list = []
  #TODO: esta parte hay que sacarla a un modulo comun de acceso a templates
  @connection["nodes/#{Chef::Config[:knife][:pve_node_name]}/storage/#{storage}/content"].get @auth_params do |response, request, result, &block|
    JSON.parse(response.body)['data'].each { |entry|
      if entry['content'] == 'vztmpl' then
        template_list << entry['volid']
      end
    }
  end
  return CGI.escape(template_list[number.to_i])
end

#vmid_to_node(vmid) ⇒ Object

vmid_to_node: Specify the vmid and get the node in which is. nil otherwise



148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/chef/knife/proxmox_base.rb', line 148

def vmid_to_node(vmid)
  node = nil
  @connection['cluster/resources?type=vm'].get @auth_params do |response, request, result, &block|
    data = JSON.parse(response.body)['data']
    data.each {|entry|
      if entry['vmid'].to_s.match(vmid.to_s) then
        node = entry['node'] 
      end
    }
    return node
  end
end

#waitfor(taskid, timeout = 60) ⇒ Object

waitfor end of the task, need the taskid and the timeout



181
182
183
184
185
186
187
188
189
190
191
# File 'lib/chef/knife/proxmox_base.rb', line 181

def waitfor(taskid, timeout=60)
  taskstatus = nil
  while taskstatus.nil? and timeout>= 0 do
    print "."
    @connection["nodes/#{Chef::Config[:knife][:pve_node_name]}/tasks/#{taskid}/status"].get @auth_params do |response, request, result, &block|
      taskstatus = (JSON.parse(response.body)['data']['status'] == "stopped")?true:nil
    end
    timeout-=1
    sleep(1)
  end
end