From 6ee8b4e7a9731f969347e317456e8d9712fe2641 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Wed, 13 Apr 2005 15:24:36 +0000 Subject: reorganizing git-svn-id: https://reductivelabs.com/svn/puppet/library/trunk@96 980ebf18-57e1-0310-9a29-db15c13687c0 --- lib/blink/objects/file.rb | 214 +++++++++++++++++++++++++++++++++++++++++++ lib/blink/objects/package.rb | 197 +++++++++++++++++++++++++++++++++++++++ lib/blink/objects/process.rb | 86 +++++++++++++++++ lib/blink/objects/service.rb | 136 +++++++++++++++++++++++++++ lib/blink/objects/symlink.rb | 107 ++++++++++++++++++++++ 5 files changed, 740 insertions(+) create mode 100755 lib/blink/objects/file.rb create mode 100644 lib/blink/objects/package.rb create mode 100755 lib/blink/objects/process.rb create mode 100755 lib/blink/objects/service.rb create mode 100644 lib/blink/objects/symlink.rb (limited to 'lib/blink/objects') diff --git a/lib/blink/objects/file.rb b/lib/blink/objects/file.rb new file mode 100755 index 000000000..3f2a8dcfc --- /dev/null +++ b/lib/blink/objects/file.rb @@ -0,0 +1,214 @@ +#!/usr/local/bin/ruby -w + +# $Id$ + +require 'digest/md5' +require 'etc' +require 'blink/attribute' + +module Blink + # we first define all of the attribute that our file will use + # because the objects must be defined for us to use them in our + # definition of the file object + class Attribute + class FileUID < Blink::Attribute + require 'etc' + attr_accessor :file + @name = :owner + + def retrieve + stat = nil + + begin + stat = File.stat(self.object[:path]) + rescue + # this isn't correct, but what the hell + raise "File '%s' does not exist: #{$!}" % self.object[:path] + end + + self.value = stat.uid + unless self.should.is_a?(Integer) + begin + user = Etc.getpwnam(self.should) + if user.gid == "" + raise "Could not retrieve uid for %s" % self.object + end + Blink.debug "converting %s to integer %d" % + [self.should,user.uid] + self.should = user.uid + rescue + raise "Could not get any info on user %s" % self.should + end + end + Blink.debug "chown state is %d" % self.value + end + + #def <=>(other) + # if other.is_a?(Integer) + # begin + # other = Etc.getpwnam(other).uid + # rescue + # raise "Could not get uid for #{@params[:uid]}" + # end + # end +# +# self.value <=> other +# end + + def sync + begin + File.chown(value,-1,self.object[:path]) + rescue + raise "failed to sync #{@params[:file]}: #{$!}" + end + + self.object.newevent(:event => :inode_changed) + end + end + + # this attribute should actually somehow turn into many attributes, + # one for each bit in the mode + # I think MetaAttributes are the answer, but I'm not quite sure + class FileMode < Blink::Attribute + require 'etc' + + @name = :mode + + def retrieve + stat = nil + + begin + stat = File.stat(self.object[:path]) + rescue => error + raise "File %s could not be stat'ed: %s" % [self.object[:path],error] + end + + self.value = stat.mode & 007777 + Blink.debug "chmod state is %o" % self.value + end + + def sync + begin + File.chmod(self.should,self.object[:path]) + rescue + raise "failed to chmod #{self.object[:path]}: #{$!}" + end + self.object.newevent(:event => :inode_changed) + end + end + + # not used until I can figure out how to solve the problem with + # metaattributes + class FileSetUID < Blink::Attribute + require 'etc' + + @parent = Blink::Attribute::FileMode + + @name = :setuid + + def <=>(other) + self.value <=> @parent.value[11] + end + + # this just doesn't seem right... + def sync + tmp = 0 + if self.value == true + tmp = 1 + end + @parent.value[11] = tmp + end + end + + class FileGroup < Blink::Attribute + require 'etc' + + @name = :group + + def retrieve + stat = nil + + begin + stat = File.stat(self.object[:path]) + rescue + # this isn't correct, but what the hell + raise "File #{self.object[:path]} does not exist: #{$!}" + end + + self.value = stat.gid + + # we probably shouldn't actually modify the 'should' value + # but i don't see a good way around it right now + # mmmm, should + unless self.should.is_a?(Integer) + begin + group = Etc.getgrnam(self.should) + # yeah, don't ask me + # this is retarded + #p group + if group.gid == "" + raise "Could not retrieve gid for %s" % self.object + end + Blink.debug "converting %s to integer %d" % + [self.should,group.gid] + self.should = group.gid + rescue + raise "Could not get any info on group %s" % self.should + end + end + Blink.debug "chgrp state is %d" % self.value + end + +# def <=>(other) +# # unless we're numeric... +# if other.is_a?(Integer) +# begin +# group = Etc.getgrnam(other) +# # yeah, don't ask me +# # this is retarded +# #p group +# other = group.gid +# if other == "" +# raise "Could not retrieve gid for %s" % other +# end +# rescue +# raise "Could not get any info on group %s" % other +# end +# end +# +# #puts self.should +# self.value <=> other +# end + + def sync + Blink.debug "setting chgrp state to %d" % self.should + begin + # set owner to nil so it's ignored + File.chown(nil,self.should,self.object[:path]) + rescue + raise "failed to chgrp %s to %s: %s" % + [self.object[:path], self.should, $!] + end + self.object.newevent(:event => :inode_changed) + end + end + end + class Objects + class File < Objects + attr_reader :stat, :path, :params + # class instance variable + @params = [ + Blink::Attribute::FileUID, + Blink::Attribute::FileGroup, + Blink::Attribute::FileMode, + Blink::Attribute::FileSetUID, + :path + ] + + @objects = Hash.new + @actions = Hash.new + @namevar = :path + end # Blink::Objects::File + end # Blink::Objects + +end diff --git a/lib/blink/objects/package.rb b/lib/blink/objects/package.rb new file mode 100644 index 000000000..f35368022 --- /dev/null +++ b/lib/blink/objects/package.rb @@ -0,0 +1,197 @@ +#!/usr/local/bin/ruby -w + +# $Id$ + +module Blink + class Objects + # packages are complicated because each package format has completely + # different commands. We need some way to convert specific packages + # into the general package object... + class Package < Objects + attr_reader :version, :format + # class instance variable + @params = [ + :install, + :format, + :version + ] + + @namevar = :name + + # this is already being done in objects.rb + #def Package.inhereted(sub) + # sub.module_eval %q{ + # @objects = Hash.new + # @actions = Hash.new + # } + #end + + def initialize(hash) + end + + def retrieve + end + + def insync? + end + + def sync + end + end # Blink::Objects::Package + + class PackagingType + attr_writer :list, :install, :remove, :check + + @@types = Hash.new(false) + + def PackagingType.[](name) + if @@types.include?(name) + return @@types[name] + else + raise "no such type %s" % name + end + end + + [:list, :install, :remove, :check].each { |method| + self.send(:define_method, method) { + # retrieve the variable + var = eval("@" + method.id2name) + if var.is_a?(Proc) + var.call() + else + raise "only blocks are supported right now" + end + } + } + + def initialize(name) + if block_given? + yield self + end + + @packages = Hash.new(false) + @@types[name] = self + end + + def retrieve + @packages.clear() + + @packages = self.list() + end + end + + PackagingType.new("dpkg") { |type| + type.list = proc { + packages = [] + + # dpkg only prints as many columns as you have available + # which means we don't get all of the info + # stupid stupid + oldcol = ENV["COLUMNS"] + ENV["COLUMNS"] = "500" + + # list out all of the packages + open("| dpkg -l") { |process| + # our regex for matching dpkg output + regex = %r{^(\S+)\s+(\S+)\s+(\S+)\s+(.+)$} + fields = [:status, :name, :version, :description] + hash = {} + + 5.times { process.gets } # throw away the header + + # now turn each returned line into a package object + process.each { |line| + if match = regex.match(line) + hash.clear + + fields.zip(match.captures) { |field,value| + hash[field] = value + } + packages.push Blink::Objects::Package.new(hash) + else + raise "failed to match dpkg line %s" % line + end + } + } + ENV["COLUMNS"] = oldcol + + return packages + } + + # we need package retrieval mechanisms before we can have package + # installation mechanisms... + #type.install = proc { |pkg| + # raise "installation not implemented yet" + #} + + type.remove = proc { |pkg| + cmd = "dpkg -r %s" % pkg.name + output = %x{#{cmd}} + if $? != 0 + raise output + end + } + } + + PackagingType.new("sunpkg") { |type| + type.list = proc { + packages = [] + hash = {} + names = { + "PKGINST" => :name, + "NAME" => nil, + "CATEGORY" => :category, + "ARCH" => :platform, + "VERSION" => :version, + "BASEDIR" => :root, + "HOTLINE" => nil, + "EMAIL" => nil, + "VENDOR" => :vendor, + "DESC" => :description, + "PSTAMP" => nil, + "INSTDATE" => nil, + "STATUS" => nil, + "FILES" => nil + } + + # list out all of the packages + open("| pkginfo -l") { |process| + # we're using the long listing, so each line is a separate piece + # of information + process.each { |line| + case line + when /^$/ then + packages.push Blink::Objects::Package.new(hash) + hash.clear + when /\s*(\w+):\s+(.+)/ + name = $1 + value = $2 + if names.include?(name) + hash[names[name]] = value + else + raise "Could not find %s" % name + end + when /\s+\d+.+/ + # nothing; we're ignoring the FILES info + end + } + } + return packages + } + + # we need package retrieval mechanisms before we can have package + # installation mechanisms... + #type.install = proc { |pkg| + # raise "installation not implemented yet" + #} + + type.remove = proc { |pkg| + cmd = "pkgrm -n %s" % pkg.name + output = %x{#{cmd}} + if $? != 0 + raise output + end + } + } + end # Blink::Objects +end diff --git a/lib/blink/objects/process.rb b/lib/blink/objects/process.rb new file mode 100755 index 000000000..182667d7c --- /dev/null +++ b/lib/blink/objects/process.rb @@ -0,0 +1,86 @@ +#!/usr/local/bin/ruby -w + +require 'blink/operation' +require 'blink/operation/processes' + +# DISABLED +# I'm only working on services, not processes, right now + +module Blink + class Objects + class BProcess < Objects + attr_reader :stat, :path + @params = [:start, :stop, :user, :pattern, :binary, :arguments] # class instance variable + + @objects = Hash.new + @namevar = :pattern + + Blink::Relation.new(self, Blink::Operation::Start, { + :user => :user, + :pattern => :pattern, + :binary => :binary, + :arguments => :arguments + }) + + Blink::Relation.new(self, Blink::Operation::Stop, { + :user => :user, + :pattern => :pattern + }) + + end # Blink::Objects::BProcess + end # Blink::Objects + + class Attribute + class ProcessRunning < Attribute + def retrieve + running = 0 + regex = Regexp.new(@params[:pattern]) + begin + # this ps is only tested on Solaris + # XXX yeah, this definitely needs to be fixed... + %x{ps -ef -U #{@params[:user]}}.split("\n").each { |process| + if regex.match(process) + running += 1 + end + } + rescue + # this isn't correct, but what the hell + Blink::Message.new( + :level => :error, + :source => self.object, + :message => "Failed to run ps" + ) + end + + self.state = running + Blink.debug "there are #{running} #{self.object} processes for start" + end + + def <=>(other) + self.state < 1 + end + + def fix + require 'etc' + # ruby is really cool + uid = 0 + if @params[:user].is_a? Integer + uid = @params[:user] + else + uid = Etc.getpwnam(@params[:user]).uid + end + Kernel.fork { + Process.uid = uid + Process.euid = uid + string = @params[:binary] + (@params[:arguments] || "") + Blink::Message.new( + :level => :notice, + :source => self.object, + :message => "starting" + ) + Kernel.exec(string) + } + end + end + end +end diff --git a/lib/blink/objects/service.rb b/lib/blink/objects/service.rb new file mode 100755 index 000000000..31fd68234 --- /dev/null +++ b/lib/blink/objects/service.rb @@ -0,0 +1,136 @@ +#!/usr/local/bin/ruby -w + +# $Id$ + +# this is our main way of managing processes right now +# +# a service is distinct from a process in that services +# can only be managed through the interface of an init script +# which is why they have a search path for initscripts and such + +module Blink + class Attribute + class ServiceRunning < Attribute + @name = :running + + def retrieve + self.value = self.running() + Blink.debug "Running value for '%s' is '%s'" % + [self.object.name,self.value] + end + + # should i cache this info? + def running + begin + status = self.object.initcmd("status") + Blink.debug "initcmd status for '%s' is '%s'" % + [self.object.name,status] + + if status # the command succeeded + return 1 + else + return 0 + end + rescue SystemCallError + raise "Could not execute %s" % initscript + end + + end + + def sync + if self.running > 0 + status = 1 + else + status = 0 + end + Blink.debug "'%s' status is '%s' and should be '%s'" % + [self,status,should] + if self.should > 0 + if status < 1 + Blink.debug "Starting '%s'" % self + unless self.object.initcmd("start") + raise "Failed to start %s" % self.name + end + else + Blink.debug "'%s' is already running, yo" % self + #Blink.debug "Starting '%s'" % self + #unless self.object.initcmd("start") + # raise "Failed to start %s" % self.name + #end + end + elsif status > 0 + Blink.debug "Stopping '%s'" % self + unless self.object.initcmd("stop") + raise "Failed to stop %s" % self.name + end + else + Blink.debug "Not running '%s' and shouldn't be running" % self + end + end + end + end + class Objects + class Service < Objects + attr_reader :stat + @params = [ + Blink::Attribute::ServiceRunning, + :name, + :pattern + ] + + @objects = Hash.new + @actions = Hash.new + @namevar = :name + + @searchpaths = Array.new + + def Service.addpath(path) + unless @searchpaths.include?(path) + # XXX should we check to see if the path exists? + @searchpaths.push(path) + end + end + + def Service.search(name) + @searchpaths.each { |path| + # must specify that we want the top-level File, not Blink::...::File + fqname = ::File.join(path,name) + begin + stat = ::File.stat(fqname) + rescue + # should probably rescue specific errors... + Blink.debug("Could not find %s in %s" % [name,path]) + next + end + + # if we've gotten this far, we found a valid script + return fqname + } + end + + # it'd be nice if i didn't throw the output away... + def initcmd(cmd) + script = self.initscript + + Blink.debug "Running '%s %s' as initcmd for '%s'" % + [script,cmd,self] + + rvalue = Kernel.system("%s status" % + [self.initscript]) + + rvalue = Kernel.system("%s %s" % + [self.initscript,cmd]) + + rvalue + end + + def initscript + if defined? @initscript + return @initscript + else + @initscript = Service.search(self.name) + end + end + end # Blink::Objects::BProcess + end # Blink::Objects +end diff --git a/lib/blink/objects/symlink.rb b/lib/blink/objects/symlink.rb new file mode 100644 index 000000000..11187e2cf --- /dev/null +++ b/lib/blink/objects/symlink.rb @@ -0,0 +1,107 @@ +#!/usr/local/bin/ruby -w + +# $Id$ + +require 'etc' +require 'blink/attribute' +require 'blink/objects/file' + +module Blink + # okay, how do we deal with parameters that don't have operations + # associated with them? + class Attribute + class SymlinkTarget < Blink::Attribute + require 'etc' + attr_accessor :file + + @name = :target + + def create + begin + Blink.debug("Creating symlink '%s' to '%s'" % + [self.object[:path],self.should]) + unless File.symlink(self.should,self.object[:path]) + raise TypeError.new("Could not create symlink '%s'" % + self.object[:path]) + end + rescue => detail + raise TypeError.new("Cannot create symlink '%s': %s" % + [self.object[:path],detail]) + end + end + + def remove + if FileTest.symlink?(self.object[:path]) + Blink.debug("Removing symlink '%s'" % self.object[:path]) + begin + File.unlink(self.object[:path]) + rescue + raise TypeError.new("Failed to remove symlink '%s'" % + self.object[:path]) + end + elsif FileTest.exists?(self.object[:path]) + raise TypeError.new("Cannot remove normal file '%s'" % + self.object[:path]) + else + Blink.debug("Symlink '%s' does not exist" % + self.object[:path]) + end + end + + def retrieve + stat = nil + + if FileTest.symlink?(self.object[:path]) + self.value = File.readlink(self.object[:path]) + Blink.debug("link value is '%s'" % self.value) + return + else + self.value = nil + return + end + end + + # this is somewhat complicated, because it could exist and be + # a file + def sync + if self.should.nil? + self.remove() + else # it should exist and be a symlink + if FileTest.symlink?(self.object[:path]) + path = File.readlink(self.object[:path]) + if path != self.should + self.remove() + self.create() + end + elsif FileTest.exists?(self.object[:path]) + raise TypeError.new("Cannot replace normal file '%s'" % + self.object[:path]) + else + self.create() + end + end + + self.object.newevent(:event => :inode_changed) + end + end + end + + class Objects + class Symlink < Objects + attr_reader :stat, :path, :params + # class instance variable + @params = [ + Blink::Attribute::FileUID, + Blink::Attribute::FileGroup, + Blink::Attribute::FileMode, + Blink::Attribute::SymlinkTarget, + :path + ] + + @objects = Hash.new + @actions = Hash.new + @namevar = :path + end # Blink::Objects::File + end # Blink::Objects + +end -- cgit