Class: ForemanVM

Inherits:
Object
  • Object
show all
Defined in:
lib/foreman_vm.rb,
lib/foreman_vm/domain.rb,
lib/foreman_vm/getopt.rb,
lib/foreman_vm/volume.rb,
lib/foreman_vm/storage_pool.rb

Overview

– Legacy code below here

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeForemanVM

Returns a new instance of ForemanVM.



322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/foreman_vm.rb', line 322

def initialize
  @log = Logger.new(STDERR)
  @action = nil
  @config = ForemanAP::Config.new
  @cluster = ForemanAP::Cluster.new(
		  @config.hypervisors, 
		  @config.libvirt_user, 
		  @config.libvirt_password)
#FIXME: reenable this: @workqueue = ForemanAP::Workqueue.new('foreman-vm')
  @console = ForemanAP::ConsoleViewer.new(@cluster)


  # TODO: transition to using @config.foreman_user everywhere
  #          instead of @user/@password
  #          
  if @config.foreman_user
    @user = @config.foreman_user
    @password = @config.foreman_password
  else
    @user = ENV['USER']
    @password = nil
  end
  @foreman_api = ForemanAP::ForemanAPI.new(@config.foreman_uri, @user, @password)

  # Build specifications
  @buildspec = {
    'cpus' => '1',            # Number of vCPUs
    'memory' => '536870912',  # Memory, in bytes (default: 512MB)
    'disk_capacity' => '20G',
    'disk_format' => 'raw',
    'storage_pool' => 'vm-corp-004',
    'domain' => `dnsdomainname`.chomp,
    'network_interface' => 'vnet0.201',
    'provision_method' => 'build',
    'owner' => 'nil',
  #  'image_id' => '/srv/images/centos6-generic-template.qcow2',
    'console' => false,

    #Hidden for testing purposes
    '_clone' => false,
    '_copy' => false,
    '_libgfapi' => true,
    '_disk_backing_file' => '/var/lib/libvirt/images/centos6-dude-template.qcow2',
  }
end

Instance Attribute Details

#actionObject (readonly)

Returns the value of attribute action.



26
27
28
# File 'lib/foreman_vm.rb', line 26

def action
  @action
end

#clusterObject

Returns the value of attribute cluster.



28
29
30
# File 'lib/foreman_vm.rb', line 28

def cluster
  @cluster
end

#configObject (readonly)

Returns the value of attribute config.



26
27
28
# File 'lib/foreman_vm.rb', line 26

def config
  @config
end

#consoleObject

Returns the value of attribute console.



28
29
30
# File 'lib/foreman_vm.rb', line 28

def console
  @console
end

#logObject

Returns the value of attribute log.



28
29
30
# File 'lib/foreman_vm.rb', line 28

def log
  @log
end

#workqueueObject

Returns the value of attribute workqueue.



28
29
30
# File 'lib/foreman_vm.rb', line 28

def workqueue
  @workqueue
end

Instance Method Details

#ask_passwordObject

Ask the user for their Foreman password



73
74
75
76
77
78
# File 'lib/foreman_vm.rb', line 73

def ask_password
  printf 'Enter your Foreman password: '
  system "stty -echo"
  @password = STDIN.gets.chomp
  system "stty echo"
end

#buildspec=(spec) ⇒ Object

Update the build specification



67
68
69
# File 'lib/foreman_vm.rb', line 67

def buildspec=(spec)
  @buildspec.merge! spec
end

#clone_volumeObject

Clone an existing disk volume to reduce the build time



71
72
73
74
75
76
77
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
103
# File 'lib/foreman_vm/storage_pool.rb', line 71

def clone_volume
  delete_volume

  # BUG: We would like to do this, but it creates the file owned by root:root
  #virsh "vol-create-as --pool #{@buildspec['storage_pool']} --name #{fqdn()}-disk1 --capacity 30G --format qcow2 --backing-vol #{@buildspec['_disk_backing_file']} --backing-vol-format qcow2"
  #
  # WORKAROUND: use an XML volume definition to set the owner/group
  #  
  xml = "<volume>
  <name>#{fqdn}-disk1</name>
  <key>/gvol/images/#{fqdn}-disk1</key>
  <source>
  </source>
  <capacity unit='bytes'>32212254720</capacity>
  <allocation unit='bytes'>197120</allocation>
  <target>
    <path>/gvol/images/#{fqdn}-disk1</path>
    <format type='qcow2'/>
    <permissions>
      <mode>0660</mode>
      <owner>107</owner>
      <group>107</group>
    </permissions>
  </target>
  <backingStore>
    <path>#{@buildspec['_disk_backing_file']}</path>
    <format type='qcow2'/>
  </backingStore>
</volume>
"
  @log.debug "creating volume: #{xml}"
  virsh("vol-create --pool gvol --file /dev/stdin >/dev/null", xml)
end

#compute_resource=(txt) ⇒ Object



35
36
37
38
# File 'lib/foreman_vm.rb', line 35

def compute_resource=(txt)
  # KLUDGE: use shortnames because Foreman does
  @buildspec['compute_resource'] = txt.gsub(/\..*/, '')
end

#copy_volumeObject

Copy an existing disk volume to reduce the build time



63
64
65
66
67
# File 'lib/foreman_vm/storage_pool.rb', line 63

def copy_volume
  delete_volume

  virsh "vol-clone --pool #{@buildspec['storage_pool']} --vol #{@buildspec['_disk_backing_file']} --newname #{fqdn()}-disk1"
end

#createObject

Build a new virtual machine



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/foreman_vm.rb', line 206

def create
  spec = @buildspec

  # If no compute resource is given, select the one with the most
  # available memory.
  spec['compute_resource'] ||= @cluster.best_fit(spec['name'], normalize_memory(spec['memory'])).gsub(/\..*/, '')

  if spec['_clone'] == true
   #FIXME: does not belong here
   spec['disk_format'] = 'qcow2'
   spec['storage_pool'] = 'gvol'
  end

  refresh_storage_pool(spec)

  rec = {
      'domain_id' => @foreman_api.get_id('domains', spec['domain']),
      'subnet_id' => @foreman_api.get_id('subnets', spec['subnet']),
      'name' => spec['name'],
      'build' => "true",
      'enabled' => "true",

      # XXX-FIXME: hardcoded, should not use this..
#'compute_profile_id' => '5',

      'compute_resource_id' => @foreman_api.get_id('compute_resources', spec['compute_resource']) ,
      'environment_id' => @foreman_api.get_id('environments', spec['environment']),
      'managed' => true, 
      'hostgroup_id' => @foreman_api.get_id('hostgroups', spec['hostgroup'], 'title'),
      'provision_method' => spec['provision_method'],
      'compute_attributes' => {
        'memory' => normalize_memory(spec['memory']), 
        'image_id' => spec['image_id'],
        'nics_attributes' => {
        '0' => {
        'bridge' => spec['network_interface'],
        'model' => 'virtio', 
        'type' => 'bridge',
      }
      }, 
      'interfaces_attributes' => {
        '0' => {
          'bridge' => spec['network_interface'],
          'model' => 'virtio',
          'type' => 'bridge'
        },
      },
      'cpus' => spec['cpus'], 
      'start' => '0', 
      'volumes_attributes' => {
        '0' => {
          'capacity' => spec['disk_capacity'], 
          'pool_name' => spec['storage_pool'],
          'format_type' => spec['disk_format'],
        }
      }
      } 
  }
  if spec['organization']
    rec['organization_id'] = @foreman_api.get_id('organizations', spec['organization'], 'title')
  end
  if spec['owner']
    rec['owner_id'] = @foreman_api.get_id('users', spec['owner'], 'login')
  end
  if spec['provision_method'] == 'image'
    rec['image_id'] = 3
    rec['image_name'] = 'centos6-generic'
    rec['compute_attributes']['image_id'] = spec['image_id']
  end
  if spec['_clone'] or spec['_copy']
    rec['build'] = false
  end

  # configure the volumes
  # TODO: use a BuildSpec object for everything.
  spec2 = ForemanAP::BuildSpec.new
  spec2.disk_capacity = spec['disk_capacity']
  spec2.storage_pool = spec['storage_pool']
  spec2.disk_format = spec['disk_format']
  ### XXX-TESTING:
    rec['compute_attributes']['volumes_attributes'] = {}
    ###rec['compute_attributes']['volumes_attributes'] = spec2.to_foreman_api['compute_attributes']['volumes_attributes']
#pp rec
  #raise 'FIXME'

  @foreman_api.request(:post, "/hosts", rec)

  raise 'FIXME - not implemented' if spec['_clone'] == true or ['spec_copy'] == true

  # Create volumes and attach them to the VM
  create_storage

  #DEADWOOD:
  #####if spec['_clone'] == true
  #####  clone_volume
  #####elsif spec['_copy'] == true
  #####  copy_volume
  #####else
  #####  # crude way to fix the permissions
  #####  wipe_volume
  #####end
  #####enable_libgfapi if spec['_libgfapi']

  #FIXME: implement this
  #raise 'Duplicate IP address' if ip_address_in_use? $GET_THE_ADDRESS_HERE

  @cluster.guest(fqdn).start

  # Attach to the console
  if spec['console']
    guest = spec['name'] + '.' + spec['domain']
    host = spec['compute_resource'] + '.' + spec['domain']
    console_attach(host, guest)
  end
end

#create_storageObject

Create storage and attach it to the virtual machine



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/foreman_vm.rb', line 185

def create_storage
  pool = @config.storage_pool
  host = @cluster.member(@cluster.find(fqdn))
  disk_number = 0
  @buildspec['disk_capacity'].split(',').each do |disk_size|
    disk_number += 1
    capacity = normalize_memory(disk_size)
    puts "create #{fqdn} - #{capacity}"
    basedir = '/gvol/images' #XXX-HARDCODED
    path = basedir + '/' + fqdn + '-disk' + disk_number.to_s
    @cluster.guest(fqdn).add_libgfapi_volume(
      "gvol/images/#{fqdn}-disk#{disk_number.to_s}",
      @config.glusterfs_server,
      disk_number 
    )
    host.storage_pool(pool).create_volume(path, capacity)
  end
end

#defer(action) ⇒ Object

Submit a deferred job



141
142
143
144
145
146
147
148
# File 'lib/foreman_vm.rb', line 141

def defer(action)
  @workqueue.enqueue({
    'user' => @user,
    'action' => action,
    'buildspec' => @buildspec,
    'api_version' => 1,
  })
end

#deleteObject

Destroy a virtual machine



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/foreman_vm.rb', line 110

def delete
  # Check if it uses libgfapi. If so, we need to disable it
#  if self.dumpxml =~ /protocol='gluster'/
#    self.stop
#    self.disable_libgfapi
#  end

  # Call 'virsh destroy' to kill the VM
  begin
    @foreman_api.request(:delete, "/hosts/#{self.fqdn}", {'id' => self.fqdn})
  rescue
    # Try again, to workaround a bug where the first deletion fails..
    @foreman_api.request(:delete, "/hosts/#{self.fqdn}", {'id' => self.fqdn})

    # When the bug hits, the volume is left behind. force it's deletion 
    #
    # Horrible kludge: hardcoded something that will allow the delete to work
    gvol = @cluster.member(@cluster.members[0]).storage_pool('gvol')
    gvol.refresh
    gvol.volume("#{self.fqdn}-disk1").delete
  end
end

#delete_volumeObject

Delete the disk volume associated with the VM



34
35
36
# File 'lib/foreman_vm/volume.rb', line 34

def delete_volume
  virsh "vol-delete #{self.fqdn}-disk1 --pool #{@buildspec['storage_pool']}"
end

#disable_libgfapiObject

Modify the VM definition to stop using libgfapi



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/foreman_vm/domain.rb', line 135

def disable_libgfapi

  self.stop

  require 'rexml/document'
  doc = REXML::Document.new(virsh("dumpxml --security-info #{self.fqdn}"))
  
  # Convert the file-backed disk into a libgfapi disk
  doc.elements.each('domain/devices/disk') do |ele|
   ele.attributes['type'] = 'file'
  end
  doc.elements.each('domain/devices/disk') do |ele|
   ele.delete_element('source')
   ele.add_element('source', {'file'=>"/gvol/images/#{self.fqdn}-disk1", 'protocol' => 'gluster'})
  end
  
  virsh("undefine #{self.fqdn}")
  virsh("define /dev/stdin >/dev/null 2>&1", doc.to_s)
end

#domstateObject

Get the state of the domain



116
117
118
# File 'lib/foreman_vm/domain.rb', line 116

def domstate
  virsh("domstate #{self.fqdn}").chomp.chomp
end

#dumpxmlObject



129
130
131
# File 'lib/foreman_vm/domain.rb', line 129

def dumpxml
  puts virsh("dumpxml --security-info #{self.fqdn}")
end

#enable_libgfapi(glusterfs_server) ⇒ Object

Modify the VM definition to use libgfapi glusterfs_server the FQDN of the GlusterFS server



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/foreman_vm/domain.rb', line 157

def enable_libgfapi(glusterfs_server)
  require 'rexml/document'
  doc = REXML::Document.new(virsh("dumpxml --security-info #{self.fqdn}"))
  
  raise 'cannot enable libgfapi while the VM is running' if domstate == 'running'
  
  # When cloning or copying, no need to boot from the network
  if @buildspec['_clone'] or @buildspec['_copy']
    doc.delete_element "/domain/os/boot[@dev='network']"
  end

  # Set cache=none just in case
  # Set the disk type, just in case
  doc.elements.each('domain/devices/disk/driver') do |ele|
   ele.attributes['cache'] = 'none'
   ele.attributes['type'] = @buildspec['disk_format']
  end
  
  # Convert the file-backed disk into a libgfapi disk
  doc.elements.each('domain/devices/disk') do |ele|
   ele.attributes['type'] = 'network'
  end
  diskcount = 1  # XXX-KLUDGE: we should actually look at the disk filename
  doc.elements.each('domain/devices/disk') do |ele|
   ele.delete_element('source')
   ele.add_element('source', {'name'=>"gvol/images/#{self.fqdn}-disk#{diskcount}", 'protocol' => 'gluster'})
   diskcount += 1
  end
  doc.elements.each('domain/devices/disk/source') do |ele|
   ele.add_element('host', {'name'=>glusterfs_server, 'transport'=>'tcp', 'port'=>'0'})
  end
  
  virsh("undefine #{self.fqdn}")
  virsh("define /dev/stdin >/dev/null 2>&1", doc.to_s)
end

#fqdnObject

Get the FQDN of the VM



42
43
44
# File 'lib/foreman_vm.rb', line 42

def fqdn
  @buildspec['name'] + '.' + @buildspec['domain']
end

#hypervisorObject

Get the hypervisor that hosts the the VM



48
49
50
# File 'lib/foreman_vm.rb', line 48

def hypervisor
  @buildspec['compute_resource'] ||= @cluster.find(fqdn)
end

#job_statusObject

View the list of deferred jobs (TODO: stop leaking Beanstalkd details)



135
136
137
# File 'lib/foreman_vm.rb', line 135

def job_status
  'job status: ' + @workqueue.jobs
end

#name=(arg) ⇒ Object

Set the VM hostname



54
55
56
57
58
59
60
61
62
63
# File 'lib/foreman_vm.rb', line 54

def name=(arg)
  if arg =~ /(.*?)\.(.*)/
    @hostname = $1
    @buildspec['name'] = $1
    @buildspec['domain'] = $2
  else
    @hostname = arg
    @buildspec['name'] = arg
  end
end

#parse_optionsObject

Parse command line options



6
7
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/foreman_vm/getopt.rb', line 6

def parse_options
  optparse = OptionParser.new do |opts|
    
    opts.on( '--user USER', 'The username to login to Foreman with') do |arg|
      @user = arg
    end
    
    opts.on( '--password PASSWORD', 'The password to login to Foreman with') do |arg|
      @password = arg
    end
    
    opts.on( '--password-file FILE', 'The file containing your Foreman password') do |arg|
      raise 'File does not exist' unless File.exists? arg
      @password = `cat #{arg}`.chomp
    end
    
    opts.on( '--foreman-uri URI', 'The URI of the Foreman password') do |arg|
      @foreman_uri = arg
      #options['foreman_uri'] = arg
    end
    
    
    opts.separator ""
    opts.separator "Specific options for building hosts:"
    
    opts.on( '--console', 'Attach to the virtual machine console') do |arg|
      @buildspec['console'] = true
      @action = 'console' if @action.nil? #XXX-KLUDGE for testing
    end
    
    opts.on( '--cpus CPUS', 'The number of CPUs to assign to the VM') do |arg|
      @buildspec['cpus'] = arg
    end
    
    opts.on( '--memory BYTES', 'The amount of memory, in bytes, to assign to the VM') do |arg|
      @buildspec['memory'] = arg
    end
    
    opts.on( '--domain DOMAIN', 'The DNS domain name to use') do |arg|
      @buildspec['domain'] = arg
    end
    
    opts.on( '--organization ORG', 'The organization name in Foreman') do |arg|
      @buildspec['organization'] = arg
    end

    opts.on( '--owner OWNER', 'The owner name in Foreman') do |arg|
      @buildspec['owner'] = arg
    end
    
    opts.on( '--hostgroup HOSTGROUP', 'The Foreman hostgroup') do |arg|
      @buildspec['hostgroup'] = arg
    end
    
    opts.on( '--compute-resource RESOURCE', 'The Foreman compute resource') do |arg|
      @buildspec['compute_resource'] = arg
    end
    
    opts.on( '--provision-method METHOD', 'The provisioning method (image or network)') do |arg|
      @buildspec['provision_method'] = arg
    end
    
    opts.on( '--environment ENVIRONMENT', 'The Puppet environment') do |arg|
      @buildspec['environment'] = arg
    end
    
    opts.on( '--network-interface INTERFACE', 'The network interface') do |arg|
      @buildspec['network_interface'] = arg
    end
    
    opts.on( '--disk-capacity SIZE', 'The size of the first disk, using M or G suffixes') do |arg|
      @buildspec['disk_capacity'] = arg
    end
    
    opts.on( '--disk-format FORMAT', 'The format of the disk. Can be raw or qcow2') do |arg|
      @buildspec['disk_format'] = arg
    end
    
    opts.on( '--storage-pool POOL', 'The storage pool') do |arg|
      @buildspec['storage_pool'] = arg
    end
    
    opts.on( '--subnet SUBNET', 'The subnet') do |arg|
      @buildspec['subnet'] = arg
    end
    
    # TODO: some nice header thing
    
    opts.separator ""
    opts.separator "Actions:"
    
    opts.on( '--rebuild', 'Rebuild the host') do |arg|
      @action = 'rebuild'
    end
    
    opts.on( '--create', 'Create the host') do |arg|
      @action = 'create'
    end

    opts.on( '--create-storage', 'Create storage and attach it to the host') do |arg|
      @action = 'create-storage'
    end
    
    
    opts.on( '--delete', 'Delete the host') do |arg|
      @action = 'delete'
    end

    opts.on( '--start', 'Power on the VM') do |arg|
      @action = 'start'
    end

    opts.on( '--stop', 'Power off the VM, without doing a graceful shutdown') do |arg|
      @action = 'stop'
    end

    opts.on( '--dumpxml', 'Show the libvirt XML virtual machine definition') do |arg|
      @action = 'dumpxml'
    end

    opts.on( '--monitor-boot', 'Watch the console of a VM as it boots') do |arg|
      @action = 'monitor-boot'
    end

    opts.on( '--enable-libgfapi', 'Enable GlusterFS libgfapi') do |arg|
      @action = 'enable-libgfapi'
    end

    opts.on( '--disable-libgfapi', 'Disable GlusterFS libgfapi') do |arg|
      @action = 'disable-libgfapi'
    end
    
    ## Verbosity (DEBUG, WARN, INFO, etc.)
    ##log_level="DEBUG"
    
    opts.on( '-h', '--help', 'Display this screen' ) do
      puts opts
      exit
    end
  end
  
  optparse.parse!

  ask_password if @password.nil?
end

#rebuildObject

Rebuild a virtual machine



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/foreman_vm.rb', line 152

def rebuild
    # Determine the hypervisor 
    @buildspec['compute_resource'] = @cluster.find(fqdn)

    refresh_storage_pool(@buildspec)

    # Destroy the puppet certificate
    #  XXX-KLUDGE
    system "curl -X DELETE http://util-stage-001.brontolabs.local:8443/puppet/ca/#{fqdn}"

# TODO: if spec['_copy']...
  if @buildspec['_clone']
    virsh "destroy #{self.fqdn}" if domstate == 'running'
    @buildspec['disk_format'] = 'qcow2'
    enable_libgfapi @config.glusterfs_server
    clone_volume
    start()
  else
    # Build via Kickstart:
    #
    
    # Destroy the puppet certificate, and enable build at the next boot
    @foreman_api.request(:put, "/hosts/#{fqdn}", { 'host' => {'build' => '1' }})

    # Call 'virsh destroy' to kill the VM, then power it back on
    stop
    sleep 3
    start
  end
end

#snapshot_createObject

Create a snapshot of the virtual machine



84
85
86
87
88
89
# File 'lib/foreman_vm/domain.rb', line 84

def snapshot_create
  raise 'the VM must be powered down before taking a snapshot' \
     if domstate != 'running'
  raise 'a snapshot already exists' if snapshot_list =~ /shutoff/
  virsh("snapshot-create #{fqdn}")
end

#snapshot_deleteObject

Delete a virtual machine snapshot



101
102
103
# File 'lib/foreman_vm/domain.rb', line 101

def snapshot_delete
  virsh("snapshot-delete #{fqdn} --current")
end

#snapshot_listObject



78
79
80
# File 'lib/foreman_vm/domain.rb', line 78

def snapshot_list
  virsh("snapshot-list #{self.fqdn}")
end

#snapshot_revertObject

Revert a virtual machine back to a snapshot



93
94
95
96
97
# File 'lib/foreman_vm/domain.rb', line 93

def snapshot_revert
  raise 'a snapshot does not exist' if snapshot_list =~ /shutoff/
  stop
  virsh("snapshot-revert #{fqdn} --current")
end

#startObject

Power on the virtual machine



122
123
124
125
126
# File 'lib/foreman_vm/domain.rb', line 122

def start
  if domstate != 'running'
    virsh("start #{self.fqdn}")
  end
end

#stopObject

Power off the virtual machine



107
108
109
110
111
# File 'lib/foreman_vm/domain.rb', line 107

def stop
  if domstate != 'shut off'
    virsh("destroy #{self.fqdn}")
  end
end

#virsh(command, xml = nil) ⇒ Object

Run a virsh command



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/foreman_vm.rb', line 82

def virsh(command,xml=nil)
  
  # KLUDGE: virsh requires the FQDN of the hypervisor
  #         while foreman uses shortname
  hypervisor_fqdn = hypervisor
  unless hypervisor_fqdn =~ /\./
    hypervisor_fqdn += '.' + `dnsdomainname`.chomp
  end

  ENV['LIBVIRT_AUTH_FILE'] = File.dirname(__FILE__) + '/../conf/auth.conf'
  buf = "virsh -c qemu+tcp://#{hypervisor_fqdn}/system " + command
  @log.info "running virsh #{command} on #{hypervisor_fqdn}"
  if xml.nil?
    res = `#{buf}`
    raise "virsh command returned #{$?}: #{buf}" if $? != 0;
  else
    f = IO.popen(buf, 'w')
    f.puts xml
    f.close
    #XXX-FIXME error check
    res = '(FIXME -- NEED TO CAPTURE STDOUT)'
  end
  return res
end

#vm_exists?(hostname) ⇒ Boolean

Check if a VM with a given hostname exists.

Returns:

  • (Boolean)


31
32
33
# File 'lib/foreman_vm.rb', line 31

def vm_exists?(hostname)
  @cluster.find(hostname).nil? ? false : true
end

#wipe_volumeObject

Wipe an existing disk volume to fix the permissions This is sadly needed to get the uid:gid to be qemu:qemu



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/foreman_vm/volume.rb', line 41

def wipe_volume
  delete_volume

  # BUG: We would like to do this, but it creates the file owned by root:root
  #virsh "vol-create-as --pool #{@buildspec['storage_pool']} --name #{fqdn()}-disk1 --capacity 30G --format qcow2 --backing-vol #{@buildspec['_disk_backing_file']} --backing-vol-format qcow2"
  #
  # WORKAROUND: use an XML volume definition to set the owner/group
  #  
  xml = "<volume>
  <name>#{fqdn}-disk1</name>
  <key>/gvol/images/#{fqdn}-disk1</key>
  <source>
  </source>
  <capacity unit='bytes'>32212254720</capacity>
  <allocation unit='bytes'>197120</allocation>
  <target>
    <path>/gvol/images/#{fqdn}-disk1</path>
    <format type='raw'/>
    <permissions>
      <mode>0660</mode>
      <owner>107</owner>
      <group>107</group>
    </permissions>
  </target>
</volume>
"
  @log.debug "creating volume: #{xml}"
  virsh("vol-create --pool gvol --file /dev/stdin >/dev/null", xml)
end