require 'puppet'

Puppet::Type.type(:ve).provide(:openvz
) do
	desc "Provides OpenVZ backend for the Virtual Environment type. Very
	simplistic so far."

	commands :vzlist => "/usr/sbin/vzlist"
	commands :vzctl  => "/usr/sbin/vzctl"
	commands :mkfs   => "/sbin/mkfs"

	mk_resource_methods

	def self.prefetch(resources)
		instances.each do |provider|
			if ve = resources[provider.id]
				ve.provider = provider
			end
		end
	end

	def self.instances
		providers = []
		# TODO: do not mix stderr with stdout (loads rubbish in case of warnings)
		cmd = "#{command(:vzlist)} --all --no-header -o veid,status,name,hostname"
		execpipe(cmd) do |process|
			process.each do |line|
				vals = line.split(' ')
				ns = begin
					get_setting(vals[0], 'NAMESERVER')
				rescue Exception => e
					Puppet.notice e
					:unknown
				end
				ip = begin
					get_setting(vals[0], 'IP_ADDRESS')
				rescue Exception => e
					Puppet.notice e
					:unknown
				end
				p = new(:id => vals[0], :status => vals[1], :name => vals[2], :hostname => vals[3], :ensure => :present, :nameserver => ns, :ipaddr => ip)
				providers << p
			end
		end
		providers
	end

	def exists?
		@property_hash[:ensure] == :present
	end

	def create
		#dev = "/dev/#{@resource[:vgname]}/#{@resource[:lvname]}"
		#scratch dev if @resource[:scratchdevice]
		#mkfs '-t', @resource[:fstype], "/dev/#{@resource[:vgname]}/#{@resource[:lvname]}"
		args = [ 'create', @resource[:id], '--ostemplate', @resource[:ostemplate] ]
		if priv = @resource[:private]
			args << '--private' << priv
		end
		if hn = @resource.should(:hostname)
			args << '--hostname' << hn
		end
		if nm = @resource.should(:name)
			args << '--name' << nm
		end
		vzctl args

		args = [ 'set', @resource[:id], '--save' ]
		if nss = @resource.should(:nameserver)
			[nss].flatten.each do |ns|
				args << '--nameserver' << ns
			end
		end
		if ips = @resource.should(:ipaddr)
			[ips].flatten.each do |ip|
				args << '--ipadd' << ip
			end
		end
		if sds = @resource[:searchdomain]
			[sds].flatten.each do |sd|
				args << '--searchdomain' << sd
			end
		end
		vzctl args

		if @resource.should(:status) == :running
			vzctl 'start', @resource[:id]
		end
	end

	def destroy
		#dev = "/dev/#{@resource[:vgname]}/#{@resource[:lvname]}"
		#lvremove '--force', "#{@resource[:vgname]}/#{@resource[:name]}"
		#scratch dev if @resource[:scratchdevice]
		vzctl 'stop',    @resource[:id] if status == 'running'
		vzctl 'destroy', @resource[:id]
		File.unlink("/etc/vz/conf/#{@resource[:id]}.conf.destroyed")
	end

	def status=(required_status)
		args = case symbolize(status)
			when :stopped
				case symbolize(required_status)
				when :running: ['start']
				when :mounted: ['mount']
				end
			when :running
				case symbolize(required_status)
				when :mounted: %w{stop mount}
				when :stopped: ['stop']
				end
			when :mounted
				case symbolize(required_status)
				when :running: ['start']
				when :stopped: ['umount']
				end
			end
		raise Puppet::Error, "unknown transition \"#{status}\" -> \"#{required_status}\"" if args == nil
		for arg in args
			vzctl arg, @resource[:id]
		end
	end

	def nameserver=(required_ns)
		vzctl 'set', @resource[:id], '--save', '--nameserver', required_ns
	end

	def hostname=(required_hostname)
		vzctl 'set', @resource[:id], '--save', '--hostname', required_hostname
	end

	def name=(required_name)
		vzctl 'set', @resource[:id], '--save', '--name', required_name
	end

	def ipaddr=(required_ip)
		new_ips = required_ip - ipaddr
		Puppet.notice "new ips: [#{new_ips}]"
		vzctl 'set', @resource[:id], '--save', required_ip.map { |ip| [ '--ipadd', ip ] }.flatten
	end


	def self.get_setting(id, setting)
		result = nil
		file = "/etc/vz/conf/#{id}.conf"
		File.new(file).each do |l|
			next unless l =~ /^(\w+)=(.*?) *$/
			name, val = $1, $2
			next unless name == setting
			result = if val[0] == ?" and val[-1] == ?"
				val[1..-2]
			else
				if val[0] == ?" or val[-1] == ?"
					raise Exception, "Invalid quoting of #{setting} value in #{file}"
				end
				val
			end
			break
		end
		raise Exception, "Value of #{setting} not found in #{file}" unless result
		result.split(/\s+/)
	end

end

