diff options
| author | Luke Kanies <luke@madstop.com> | 2005-06-27 21:44:46 +0000 |
|---|---|---|
| committer | Luke Kanies <luke@madstop.com> | 2005-06-27 21:44:46 +0000 |
| commit | 8f95084cd854aef4e3493854e58cefd352cdc68d (patch) | |
| tree | f31288d1cbbd60c0fdc7c04bbd6960516a6893be /lib/blink | |
| parent | 6f074138779e558fd7017880f606dcf3527233f9 (diff) | |
| download | puppet-8f95084cd854aef4e3493854e58cefd352cdc68d.tar.gz puppet-8f95084cd854aef4e3493854e58cefd352cdc68d.tar.xz puppet-8f95084cd854aef4e3493854e58cefd352cdc68d.zip | |
renaming blink to puppet
git-svn-id: https://reductivelabs.com/svn/puppet/library/trunk@302 980ebf18-57e1-0310-9a29-db15c13687c0
Diffstat (limited to 'lib/blink')
| -rw-r--r-- | lib/blink/client.rb | 120 | ||||
| -rw-r--r-- | lib/blink/element.rb | 41 | ||||
| -rw-r--r-- | lib/blink/event.rb | 201 | ||||
| -rw-r--r-- | lib/blink/fact.rb | 65 | ||||
| -rw-r--r-- | lib/blink/function.rb | 72 | ||||
| -rw-r--r-- | lib/blink/message.rb | 64 | ||||
| -rw-r--r-- | lib/blink/selector.rb | 82 | ||||
| -rw-r--r-- | lib/blink/statechange.rb | 111 | ||||
| -rw-r--r-- | lib/blink/storage.rb | 48 | ||||
| -rw-r--r-- | lib/blink/transaction.rb | 162 | ||||
| -rw-r--r-- | lib/blink/transportable.rb | 200 | ||||
| -rw-r--r-- | lib/blink/type.rb | 851 | ||||
| -rw-r--r-- | lib/blink/type/component.rb | 74 | ||||
| -rw-r--r-- | lib/blink/type/file.rb | 450 | ||||
| -rw-r--r-- | lib/blink/type/package.rb | 366 | ||||
| -rw-r--r-- | lib/blink/type/process.rb | 83 | ||||
| -rw-r--r-- | lib/blink/type/service.rb | 186 | ||||
| -rw-r--r-- | lib/blink/type/state.rb | 135 | ||||
| -rw-r--r-- | lib/blink/type/symlink.rb | 109 | ||||
| -rw-r--r-- | lib/blink/type/typegen.rb | 146 | ||||
| -rw-r--r-- | lib/blink/type/typegen/filerecord.rb | 243 | ||||
| -rw-r--r-- | lib/blink/type/typegen/filetype.rb | 316 |
22 files changed, 0 insertions, 4125 deletions
diff --git a/lib/blink/client.rb b/lib/blink/client.rb deleted file mode 100644 index f266d2dfb..000000000 --- a/lib/blink/client.rb +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/local/bin/ruby -w - -# $Id$ - -# the available clients - -require 'blink' -require 'blink/function' -require 'blink/type' -require 'blink/fact' -require 'blink/transaction' -require 'blink/transportable' -require 'http-access2' -require 'soap/rpc/driver' -require 'soap/rpc/httpserver' -#require 'webrick/https' -require 'logger' - -module Blink - class ClientError < RuntimeError; end - #--------------------------------------------------------------- - class Client < SOAP::RPC::HTTPServer - def initialize(hash) - # to whom do we connect? - @server = nil - @nil = nil - @url = hash[:Server] - if hash.include?(:Listen) and hash[:Listen] == false - Blink.notice "We're a local client" - @localonly = true - @driver = @url - else - Blink.notice "We're a networked client" - @localonly = false - @driver = SOAP::RPC::Driver.new(@url, 'urn:blink-server') - @driver.add_method("getconfig", "name") - end - unless @localonly - hash.delete(:Server) - - Blink.notice "Server is %s" % @url - - hash[:BindAddress] ||= "0.0.0.0" - hash[:Port] ||= 17444 - hash[:Debug] ||= true - hash[:AccessLog] ||= [] - - super(hash) - end - end - - def getconfig - Blink.debug "server is %s" % @url - #client.loadproperty('files/sslclient.properties') - Blink.notice("getting config") - objects = nil - if @localonly - objects = @driver.getconfig(self) - else - objects = @driver.getconfig(Blink::Fact["hostname"]) - end - self.config(objects) - end - - # this method is how the client receives the tree of Transportable - # objects - # for now, just descend into the tree and perform and necessary - # manipulations - def config(tree) - Blink.notice("Calling config") - container = Marshal::load(tree).to_type - - # this is a gross hack... but i don't see a good way around it - # set all of the variables to empty - Blink::Transaction.init - # for now we just evaluate the top-level container, but eventually - # there will be schedules and such associated with each object, - # and probably with the container itself - transaction = container.evaluate - #transaction = Blink::Transaction.new(objects) - transaction.toplevel = true - transaction.evaluate - self.shutdown - end - - def callfunc(name,args) - Blink.notice("Calling callfunc on %s" % name) - if function = Blink::Function[name] - #Blink.debug("calling function %s" % function) - value = function.call(args) - #Blink.debug("from %s got %s" % [name,value]) - return value - else - raise "Function '%s' not found" % name - end - end - - private - - def on_init - @default_namespace = 'urn:blink-client' - add_method(self, 'config', 'config') - add_method(self, 'callfunc', 'name', 'arguments') - end - - def cert(filename) - OpenSSL::X509::Certificate.new(File.open(File.join(@dir, filename)) { |f| - f.read - }) - end - - def key(filename) - OpenSSL::PKey::RSA.new(File.open(File.join(@dir, filename)) { |f| - f.read - }) - end - - end - #--------------------------------------------------------------- -end diff --git a/lib/blink/element.rb b/lib/blink/element.rb deleted file mode 100644 index dd187977a..000000000 --- a/lib/blink/element.rb +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/local/bin/ruby -w - -# $Id$ - -# included so we can test object types -require 'blink' - - -#--------------------------------------------------------------- -# the base class for both types and states -# very little functionality; basically just defines the interface -# and provides a few simple across-the-board functions like 'noop' -class Blink::Element - attr_writer :noop - - #--------------------------------------------------------------- - # all of our subclasses must respond to each of these methods... - @@interface_methods = [ - :retrieve, :insync?, :sync, :fqpath, :evaluate - ] - - # so raise an error if a method that isn't overridden gets called - @@interface_methods.each { |method| - self.send(:define_method,method) { - raise "%s has not overridden %s" % [self.class,method] - } - } - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # for testing whether we should actually do anything - def noop - unless defined? @noop - @noop = false - end - return @noop || Blink[:noop] || false - end - #--------------------------------------------------------------- - -end -#--------------------------------------------------------------- diff --git a/lib/blink/event.rb b/lib/blink/event.rb deleted file mode 100644 index 2cdda9976..000000000 --- a/lib/blink/event.rb +++ /dev/null @@ -1,201 +0,0 @@ -#!/usr/local/bin/ruby -w - -# $Id$ - -# included so we can test object types -require 'blink' -require 'blink/type' - -module Blink - # events are transient packets of information; they result in one or more (or none) - # subscriptions getting triggered, and then they get cleared - # eventually, these will be passed on to some central event system - class Event - # subscriptions are permanent associations determining how different - # objects react to an event - class Subscription - attr_accessor :source, :event, :target, :method - - def initialize(hash) - @triggered = false - - hash.each { |method,value| - # assign each value appropriately - # this is probably wicked-slow - self.send(method.to_s + "=",value) - } - Blink.warning "New Subscription: '%s' => '%s'" % - [@source,@event] - end - - # the transaction is passed in so that we can notify it if - # something fails - def trigger(transaction) - # this is potentially incomplete, because refreshing an object - # could theoretically kick off an event, which would not get run - # or, because we're executing the first subscription rather than - # the last, a later-refreshed object could somehow be connected - # to the "old" object rather than "new" - # but we're pretty far from that being a problem - if transaction.triggercount(self) > 0 - Blink.verbose "%s has already run" % self - else - Blink.verbose "'%s' matched '%s'; triggering '%s' on '%s'" % - [@source,@event,@method,@target] - begin - if @target.respond_to?(@method) - @target.send(@method) - else - Blink.verbose "'%s' of type '%s' does not respond to '%s'" % - [@target,@target.class,@method.inspect] - end - rescue => detail - # um, what the heck do i do when an object fails to refresh? - # shouldn't that result in the transaction rolling back? - # XXX yeah, it should - Blink.error "'%s' failed to %s: '%s'" % - [@target,@method,detail] - raise - #raise "We need to roll '%s' transaction back" % - #transaction - end - transaction.triggered(self) - end - end - end - - attr_accessor :event, :object, :transaction - - @@events = [] - - @@subscriptions = [] - - def Event.process - Blink.warning "Processing events" - @@events.each { |event| - @@subscriptions.find_all { |sub| - #Blink.warning "Sub source: '%s'; event object: '%s'" % - # [sub.source.inspect,event.object.inspect] - sub.source == event.object and - (sub.event == event.event or - sub.event == :ALL_EVENTS) - }.each { |sub| - Blink.notice "Found sub" - sub.trigger(event.transaction) - } - } - - @@events.clear - end - - def Event.subscribe(hash) - if hash[:event] == '*' - hash[:event] = :ALL_EVENTS - end - sub = Subscription.new(hash) - - # add to the correct area - @@subscriptions.push sub - end - - def initialize(args) - unless args.include?(:event) and args.include?(:object) - raise "Event.new called incorrectly" - end - - @event = args[:event] - @object = args[:object] - @transaction = args[:transaction] - - Blink.warning "New Event: '%s' => '%s'" % - [@object,@event] - - # initially, just stuff all instances into a central bucket - # to be handled as a batch - @@events.push self - end - end -end - - -#--------------------------------------------------------------- -# here i'm separating out the methods dealing with handling events -# currently not in use, so... - -class Blink::NotUsed - #--------------------------------------------------------------- - # return action array - # these are actions to use for responding to events - # no, this probably isn't the best way, because we're providing - # access to the actual hash, which is silly - def action - if not defined? @actions - puts "defining action hash" - @actions = Hash.new - end - @actions - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # call an event - # this is called on subscribers by the trigger method from the obj - # which sent the event - # event handling should probably be taking place in a central process, - # but.... - def event(event,obj) - Blink.debug "#{self} got event #{event} from #{obj}" - if @actions.key?(event) - Blink.debug "calling it" - @actions[event].call(self,obj,event) - else - p @actions - end - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # subscribe to an event or all events - # this entire event system is a hack job and needs to - # be replaced with a central event handler - def subscribe(args,&block) - obj = args[:object] - event = args[:event] || '*'.intern - if obj.nil? or event.nil? - raise "subscribe was called wrongly; #{obj} #{event}" - end - obj.action[event] = block - #events.each { |event| - unless @notify.key?(event) - @notify[event] = Array.new - end - unless @notify[event].include?(obj) - Blink.debug "pushing event '%s' for object '%s'" % [event,obj] - @notify[event].push(obj) - end - # } - #else - # @notify['*'.intern].push(obj) - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # initiate a response to an event - def trigger(event) - subscribers = Array.new - if @notify.include?('*') and @notify['*'].length > 0 - @notify['*'].each { |obj| subscribers.push(obj) } - end - if (@notify.include?(event) and (! @notify[event].empty?) ) - @notify[event].each { |obj| subscribers.push(obj) } - end - Blink.debug "triggering #{event}" - subscribers.each { |obj| - Blink.debug "calling #{event} on #{obj}" - obj.event(event,self) - } - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- -end # Blink::Type diff --git a/lib/blink/fact.rb b/lib/blink/fact.rb deleted file mode 100644 index 2b46aabd8..000000000 --- a/lib/blink/fact.rb +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/local/bin/ruby -w - -# $Id$ - -# an interface for registering and retrieving facts -# this is an abstract interface, and should just be used to interact -# with another library - -# currently a very thin veneer on 'facter' - -require 'facter' -require 'blink' -require 'blink/type' - -module Blink - class Fact - def Fact.[](name) - fact = Facter[name] - if fact.value.nil? - raise "Could not retrieve fact %s" % name - end - Blink.debug("fact: got %s from %s for %s" % [fact.value,fact,name]) - return fact.value - end - - # just pass the block to 'add' - # the block has to do things like set the interpreter, - # the code (which can be a ruby block), and maybe the - # os and osrelease - def Fact.add(name,&block) - Facter[name].add(&block) - end - - def Fact.name - return :fact - end - - def Fact.namevar - return :name - end - - #Blink::Type.newtype(self) - - # we're adding a new resolution mechanism here; this is just how - # types work - # we don't have any real interest in the returned object - def initialize(hash) - name = hash[:name] - hash.delete(:name) - Fact.add(name) { |fact| - method = nil - hash.each { |key,value| - if key.is_a?(String) - method = key + "=" - elsif key.is_a?(Symbol) - method = key.id2name + "=" - else - raise "Key must be either string or symbol" - end - fact.send(method,value) - } - } - end - end -end diff --git a/lib/blink/function.rb b/lib/blink/function.rb deleted file mode 100644 index d5cc11107..000000000 --- a/lib/blink/function.rb +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/local/bin/ruby -w - -# $Id$ - -require 'blink' -require 'blink/fact' - -module Blink - class Function - @@functions = Hash.new(nil) - - #--------------------------------------------------------------- - def Function.[](name) - return @@functions[name] - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def call(args) - @code.call(args) - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # we want a 'proc' item instead of a block, so that we can return - # from it - def initialize(name,code) - @name = name - @code = code - - @@functions[name] = self - end - #--------------------------------------------------------------- - end - - Function.new("fact", proc { |fact| - require 'blink/fact' - - value = Fact[fact] - Blink.debug("retrieved %s as %s" % [fact,value]) - value - }) - - Function.new("addfact", proc { |args| - require 'blink/fact' - #Blink.debug("running addfact") - - hash = nil - if args.is_a?(Array) - hash = Hash[*args] - end - name = nil - if hash.has_key?("name") - name = hash["name"] - hash.delete("name") - elsif hash.has_key?(:name) - name = hash[:name] - hash.delete(:name) - else - raise "Functions must have names" - end - #Blink.debug("adding fact %s" % name) - newfact = Fact.add(name) { |fact| - hash.each { |key,value| - method = key + "=" - fact.send(method,value) - } - } - - #Blink.debug("got fact %s" % newfact) - }) -end diff --git a/lib/blink/message.rb b/lib/blink/message.rb deleted file mode 100644 index 31d5fa503..000000000 --- a/lib/blink/message.rb +++ /dev/null @@ -1,64 +0,0 @@ -# $Id$ - -module Blink - #------------------------------------------------------------ - # provide feedback of various types to the user - # modeled after syslog messages - # each level of message prints in a different color - class Message - @@messages = Array.new - @@levels = [ :debug, :verbose, :notice, :warning, :error ] - @@colors = { - :debug => SLATE, - :verbose => ORANGE, - :notice => PINK, - :warning => GREEN, - :error => YELLOW - } - - attr_accessor :level, :message, :source - - def Message.loglevels - return @@levels - end - - def initialize(args) - unless args.include?(:level) && args.include?(:message) && - args.include?(:source) - raise "Blink::Message called incorrectly" - end - - if args[:level].class == String - @level = args[:level].intern - elsif args[:level].class == Symbol - @level = args[:level] - else - raise "Level is not a string or symbol: #{args[:level].class}" - end - @message = args[:message] - @source = args[:source] - @time = Time.now - # this should include the host name, and probly lots of other - # stuff, at some point - unless @@levels.include?(level) - raise "Invalid message level #{level}" - end - - @@messages.push(self) - Blink.newmessage(self) - end - - def to_s - # this probably won't stay, but until this leaves the console, - # i'm going to use coloring... - #return "#{@time} #{@source} (#{@level}): #{@message}" - #return @@colors[@level] + "%s %s (%s): %s" % [ - # @time, @source, @level, @message - #] + RESET - return @@colors[@level] + "%s (%s): %s" % [ - @source, @level, @message - ] + RESET - end - end - #------------------------------------------------------------ -end diff --git a/lib/blink/selector.rb b/lib/blink/selector.rb deleted file mode 100644 index 51e82b09d..000000000 --- a/lib/blink/selector.rb +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/local/bin/ruby -w - -# $Id$ - -require 'blink' -require 'blink/fact' - -module Blink - #--------------------------------------------------------------- - # this class will provide something like a 'select' statement, but it will - # return a value - # it will be used something like this: - # value = Selector.new( - # proc { test() } => value, - # proc { test2() } => value2, - # ) - - # each test gets evaluated in turn; the first one to return true has its - # value returned as the value of the statement - # this will be used to provide abstraction in objects, but it's currently - # unused - - class Selector < Array - attr_accessor :default - - def add(value,&block) - option = Option.new(value,&block) - @ohash[value] = option - @oarray.push(option) - end - - def evaluate - @oarray.each { |option| - if option.true? - return option.value - end - } - return nil - end - - # we have to support providing different values based on - # different criteria, e.g., default is X, SunOS gets Y, and - # host Yayness gets Z. - # thus, no invariant - def initialize - @oarray = [] - @ohash = {} - - if block_given? - yield self - end - end - - def to_s - return self.evaluate() - end - - class Option - attr_accessor :value, :test, :invariant - - def initialize(value,&block) - @value = value - @test = block - end - - def to_s - if self.evaluate - return value - end - end - - def true? - unless @test.is_a?(Proc) - raise "Cannot yet evaluate non-code tests" - end - - return @test.call() - end - end - #--------------------------------------------------------------- - end -end diff --git a/lib/blink/statechange.rb b/lib/blink/statechange.rb deleted file mode 100644 index f88108d8a..000000000 --- a/lib/blink/statechange.rb +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/local/bin/ruby -w - -# $Id$ - -# the class responsible for actually doing any work - -# enables no-op and logging/rollback - -module Blink - class StateChange - attr_accessor :is, :should, :type, :path, :state, :transaction, :run - - #--------------------------------------------------------------- - def initialize(state) - @state = state - #@state.parent.newchange - @path = state.fqpath - @is = state.is - @should = state.should - - @run = false - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def go - if @state.noop - #Blink.notice "%s is noop" % @state - return nil - end - - begin - event = @state.sync - @run = true - - # default to a simple event type - if event.nil? - event = @state.parent.class.name.id2name + "_changed" - elsif ! event.is_a?(Symbol) - Blink.notice "State '%s' returned invalid event '%s'; resetting to default" % - [@state.class,event] - - event = @state.parent.class.name.id2name + "_changed" - end - - # i should maybe include object type, but the event type - # should basically point to that, right? - return Blink::Event.new( - :event => event, - :object => @state.parent, - :transaction => @transaction, - :message => self.to_s - ) - rescue => detail - Blink.error "%s failed: %s" % [self.to_s,detail] - raise - # there should be a way to ask the state what type of event - # it would have generated, but... - pname = @state.parent.class.name.id2name - #if pname.is_a?(Symbol) - # pname = pname.id2name - #end - return Blink::Event.new( - :event => pname + "_failed", - :object => @state.parent, - :transaction => @transaction, - :message => "Failed: " + self.to_s - ) - end - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def forward - #Blink.notice "moving change forward" - - unless defined? @transaction - raise "StateChange '%s' tried to be executed outside of transaction" % - self - end - - return self.go - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def backward - @state.should = @is - @state.retrieve - - Blink.notice "Rolling %s backward" % self - return self.go - - #raise "Moving statechanges backward is currently unsupported" - #@type.change(@path,@should,@is) - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def noop - return @state.noop - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def to_s - return "%s: %s => %s" % [@state,@is,@should] - end - #--------------------------------------------------------------- - end -end diff --git a/lib/blink/storage.rb b/lib/blink/storage.rb deleted file mode 100644 index 9fc21d38b..000000000 --- a/lib/blink/storage.rb +++ /dev/null @@ -1,48 +0,0 @@ -# $Id$ - -module Blink - # a class for storing state - class Storage - include Singleton - @@state = Hash.new { |hash,key| - hash[key] = Hash.new(nil) - } - @@splitchar = "\t" - - def initialize - self.class.load - end - - def Storage.load - # XXX I should probably use a better default state dir - Blink[:statefile] ||= "/var/tmp/blinkstate" - return unless File.exists?(Blink[:statefile]) - File.open(Blink[:statefile]) { |file| - file.gets { |line| - myclass, key, value = line.split(@@splitchar) - - @@state[myclass][key] = Marshal::load(value) - } - } - end - - def Storage.state(myclass) - unless myclass.is_a? Class - myclass = myclass.class - end - result = @@state[myclass] - return result - end - - def Storage.store - File.open(Blink[:statefile], File::CREAT|File::WRONLY, 0600) { |file| - @@state.each { |klass, thash| - thash.each { |key,value| - mvalue = Marshal::dump(value) - file.puts([klass,key,mvalue].join(@@splitchar)) - } - } - } - end - end -end diff --git a/lib/blink/transaction.rb b/lib/blink/transaction.rb deleted file mode 100644 index 87b2f950c..000000000 --- a/lib/blink/transaction.rb +++ /dev/null @@ -1,162 +0,0 @@ -#!/usr/local/bin/ruby -w - -# $Id$ - -# the class that actually walks our object/state tree, collects the changes, -# and performs them - -# there are two directions of walking: -# - first we recurse down the tree and collect changes -# - then we walk back up the tree through 'refresh' after the changes - -require 'blink' -require 'blink/statechange' - -#--------------------------------------------------------------- -module Blink -class Transaction - attr_accessor :toplevel, :component - - #--------------------------------------------------------------- - # a bit of a gross hack; a global list of objects that have failed to sync, - # so that we can verify during later syncs that our dependencies haven't - # failed - def Transaction.init - @@failures = Hash.new(0) - @@changed = [] - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # for now, just store the changes for executing linearly - # later, we might execute them as we receive them - def change(change) - @changes.push change - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # i don't need to worry about ordering, because it's not possible to specify - # an object as a dependency unless it's already been mentioned within the language - # thus, an object gets defined, then mentioned as a dependency, and the objects - # are synced in that order automatically - def evaluate - Blink.notice "executing %s changes or transactions" % @changes.length - - return @changes.collect { |change| - if change.is_a?(Blink::StateChange) - change.transaction = self - events = nil - begin - events = [change.forward].flatten - #@@changed.push change.state.parent - rescue => detail - Blink.error("%s failed: %s" % [change,detail]) - raise - # at this point, we would normally do error handling - # but i haven't decided what to do for that yet - # so just record that a sync failed for a given object - #@@failures[change.state.parent] += 1 - # this still could get hairy; what if file contents changed, - # but a chmod failed? how would i handle that error? dern - end - - if events.nil? - Blink.verbose "No events returned?" - else - # first handle the subscriptions on individual objects - events.each { |event| - change.state.parent.subscribers?(event).each { |sub| - sub.trigger(self) - } - } - end - events - elsif change.is_a?(Blink::Transaction) - change.evaluate - else - raise "Transactions cannot handle objects of type %s" % child.class - end - }.flatten.reject { |event| - event.nil? - }.each { |event| - # this handles subscriptions on the components, rather than - # on idividual objects - self.component.subscribers?(event).each { |sub| - sub.trigger(self) - } - } - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # this should only be called by a Blink::Container object now - # and it should only receive an array - def initialize(tree) - @tree = tree - @toplevel = false - - @triggered = Hash.new(0) - - # of course, this won't work on the second run - unless defined? @@failures - @toplevel = true - self.class.init - end - # change collection is in-band, and message generation is out-of-band - # of course, exception raising is also out-of-band - @changes = @tree.collect { |child| - # these children are all Blink::Type instances - # not all of the children will return a change, and Containers - # return transactions - child.evaluate - }.flatten.reject { |child| - child.nil? # remove empties - } - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def rollback - @changes.each { |change| - if change.is_a?(Blink::StateChange) - next unless change.run - #change.transaction = self - begin - change.backward - #@@changed.push change.state.parent - rescue => detail - Blink.error("%s rollback failed: %s" % [change,detail]) - # at this point, we would normally do error handling - # but i haven't decided what to do for that yet - # so just record that a sync failed for a given object - #@@failures[change.state.parent] += 1 - # this still could get hairy; what if file contents changed, - # but a chmod failed? how would i handle that error? dern - end - elsif change.is_a?(Blink::Transaction) - # yay, recursion - change.rollback - else - raise "Transactions cannot handle objects of type %s" % child.class - end - } - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def triggercount(sub) - Blink.notice "Triggercount on %s is %s" % [sub,@triggered[sub]] - return @triggered[sub] - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def triggered(sub) - @triggered[sub] += 1 - Blink.notice "%s was triggered; count is %s" % [sub,@triggered[sub]] - end - #--------------------------------------------------------------- -end -end -#--------------------------------------------------------------- diff --git a/lib/blink/transportable.rb b/lib/blink/transportable.rb deleted file mode 100644 index 522a34d7c..000000000 --- a/lib/blink/transportable.rb +++ /dev/null @@ -1,200 +0,0 @@ -#!/usr/local/bin/ruby -w - -# $Id$ - -require 'blink' - -module Blink - #------------------------------------------------------------ - class TransObject < Hash - attr_accessor :type - - @@ohash = {} - @@oarray = [] - - def TransObject.add(object) - @@oarray.push object - - # this is just so we can check, at parse time, whether a required - # object has already been mentioned when it is listed as required - # because we're ordered, as long as an object gets made before its - # dependent objects will get synced later - @@ohash[object.longname] = object - end - - def TransObject.clear - @@oarray.clear - end - - def TransObject.list - return @@oarray - end - - def initialize(name,type) - self[:name] = name - @type = type - self.class.add(self) - end - - def longname - return [self.type,self[:name]].join('--') - end - - def name - return self[:name] - end - - def to_s - return "%s(%s) => %s" % [@type,self[:name],super] - end - - def to_type - retobj = nil - if type = Blink::Type.type(self.type) - #begin - # this will fail if the type already exists - # which may or may not be a good thing... - retobj = type.new(self) - #rescue => detail - # Blink.error "Failed to create %s: %s" % [type.name,detail] - # puts self.class - # puts self.inspect - # exit - #end - else - raise "Could not find object type %s" % self.type - end - - return retobj - end - end - #------------------------------------------------------------ - - #------------------------------------------------------------ - class TransSetting - attr_accessor :type, :name, :args, :evalcount - - def initialize - @evalcount = 0 - end - - def evaluate - @evalcount += 0 - if type = Blink::Type.type(self.type) - # call the settings - name = self.name - unless name.is_a?(Symbol) - name = name.intern - end - if type.allowedmethod(name) - type.send(self.name,self.args) - else - Blink.error("%s does not respond to %s" % [self.type,self.name]) - end - else - raise "Could not find object type %s" % setting.type - end - end - end - #------------------------------------------------------------ - - #------------------------------------------------------------ - # just a linear container for objects - class TransBucket < Array - attr_accessor :name, :type - - def push(*args) - args.each { |arg| - case arg - when Blink::TransBucket, Blink::TransObject, Blink::TransSetting - # nada - else - raise "TransBuckets cannot handle objects of type %s" % - arg.class - end - } - super - end - - def to_type - # this container will contain the equivalent of all objects at - # this level - #container = Blink::Component.new(:name => @name, :type => @type) - unless defined? @name - raise "TransBuckets must have names" - end - unless defined? @type - Blink.verbose "TransBucket '%s' has no type" % @name - end - hash = { - :name => @name, - :type => @type - } - if defined? @parameters - @parameters.each { |param,value| - Blink.warning "Defining %s on %s of type %s" % - [param,@name,@type] - hash[param] = value - } - else - Blink.warning "%s has no parameters" % @name - end - container = Blink::Component.new(hash) - nametable = {} - - self.each { |child| - # the fact that we descend here means that we are - # always going to execute depth-first - # which is _probably_ a good thing, but one never knows... - if child.is_a?(Blink::TransBucket) - # just perform the same operation on any children - container.push(child.to_type) - elsif child.is_a?(Blink::TransSetting) - # XXX this is wrong, but for now just evaluate the settings - child.evaluate - elsif child.is_a?(Blink::TransObject) - # do a simple little naming hack to see if the object already - # exists in our scope - # this assumes that type/name combinations are globally - # unique - name = [child[:name],child.type].join("--") - - if nametable.include?(name) - object = nametable[name] - child.each { |var,value| - # don't rename; this shouldn't be possible anyway - next if var == :name - - Blink.notice "Adding %s to %s" % [var,name] - # override any existing values - object[var] = value - } - else # the object does not exist yet in our scope - # now we have the object instantiated, in our scope - object = child.to_type - nametable[name] = object - - # this sets the order of the object - container.push object - end - else - raise "TransBucket#to_type cannot handle objects of type %s" % - child.class - end - } - - # at this point, no objects at are level are still Transportable - # objects - return container - end - - def param(param,value) - unless defined? @parameters - @parameters = {} - end - @parameters[param] = value - end - - end - #------------------------------------------------------------ -end diff --git a/lib/blink/type.rb b/lib/blink/type.rb deleted file mode 100644 index 51cd904e7..000000000 --- a/lib/blink/type.rb +++ /dev/null @@ -1,851 +0,0 @@ -#!/usr/local/bin/ruby -w - -# $Id$ - -# included so we can test object types -require 'blink' -require 'blink/element' -require 'blink/event' -require 'blink/type/state' - - -# XXX see the bottom of the file for the rest of the inclusions - -#--------------------------------------------------------------- -# This class is the abstract base class for the mechanism for organizing -# work. No work is actually done by this class or its subclasses; rather, -# the subclasses include states which do the actual work. -# See state.rb for how work is actually done. - -# our duck type interface -- if your object doesn't match this interface, -# it won't work - -# all of our first-class objects (objects, states, and components) will -# respond to these methods -# although states don't inherit from Blink::Type -# although maybe Blink::State should... - -# the default behaviour that this class provides is to just call a given -# method on each contained object, e.g., in calling 'sync', we just run: -# object.each { |subobj| subobj.sync() } - -# to use this interface, just define an 'each' method and 'include Blink::Type' - -module Blink -class Type < Blink::Element - attr_accessor :children, :parameters, :parent - include Enumerable - - @@allobjects = Array.new # an array for all objects - @abstract = true - - @name = :blink # a little fakery, since Blink itself isn't a type - @namevar = :notused - - @states = [] - @parameters = [:notused] - - @allowedmethods = [:noop,:debug,:statefile] - - @@metaparams = [ - :onerror, - :schedule, - :check, - :require - ] - - #--------------------------------------------------------------- - #--------------------------------------------------------------- - # class methods dealing with Type management - #--------------------------------------------------------------- - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # these objects are used for mapping type names (e.g., 'file') - # to actual object classes; because Type.inherited is - # called before the <subclass>.name method is defined, we need - # to store each class in an array, and then later actually iterate - # across that array and make a map - @@typeary = [self] # so that the allowedmethods stuff works - @@typehash = Hash.new { |hash,key| - if key.is_a?(String) - key = key.intern - end - if hash.include?(key) - hash[key] - else - raise "Object type %s not found" % key - end - } - - #--------------------------------------------------------------- - # a test for whether this type is allowed to have instances - # on clients - # subclasses can just set '@abstract = true' to mark themselves - # as abstract - def Type.abstract - if defined? @abstract - return @abstract - else - return false - end - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def Type.allowedmethod(method) - if defined? @allowedmethods and @allowedmethods.include?(method) - return true - else - return false - end - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def Type.statefile(file) - Blink[:statefile] = file - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # ill thought-out - # this needs to return @noop - #def noop(ary) - # Blink[:noop] = ary.shift - #end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - #def debug(ary) - # Blink[:debug] = ary.shift - #end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # this is meant to be run multiple times, e.g., when a new - # type is defined at run-time - def Type.buildtypehash - @@typeary.each { |otype| - if @@typehash.include?(otype.name) - if @@typehash[otype.name] != otype - Blink.warning("Object type %s is already defined (%s vs %s)" % - [otype.name,@@typehash[otype.name],otype]) - end - else - @@typehash[otype.name] = otype - end - } - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def Type.eachtype - @@typeary.each { |type| yield type } - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # this should make it so our subclasses don't have to worry about - # defining these class instance variables - def Type.inherited(sub) - sub.initvars - - #Blink.notice("subtype %s(%s) just created" % [sub,sub.superclass]) - # add it to the master list - # unfortunately we can't yet call sub.name, because the #inherited - # method gets called before any commands in the class definition - # get executed, which, um, sucks - @@typeary.push(sub) - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # this is so we don't have to eval this code - # init all of our class instance variables - def Type.initvars - @objects = Hash.new - @actions = Hash.new - #Blink.verbose "initing validstates for %s" % self - @validstates = {} - @validparameters = {} - - unless defined? @states - @states = {} - end - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def Type.metaclass - if defined? @metaclass - return @metaclass - else - return false - end - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # this is used for mapping object types (e.g., Blink::Type::File) - # to names (e.g., "file") - def Type.name - return @name - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def Type.newtype(type) - raise "Type.newtype called, but I don't know why" - @@typeary.push(type) - if @@typehash.has_key?(type.name) - Blink.notice("Redefining object type %s" % type.name) - end - @@typehash[type.name] = type - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def Type.type(type) - unless @@typeary.length == @@typehash.length - Type.buildtypehash - end - @@typehash[type] - end - #--------------------------------------------------------------- - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - #--------------------------------------------------------------- - # class methods dealing with type instance management - #--------------------------------------------------------------- - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # retrieve a named object - def Type.[](name) - if @objects.has_key?(name) - return @objects[name] - else - return nil - end - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def Type.[]=(name,object) - newobj = nil - if object.is_a?(Blink::Type) - newobj = object - else - raise "must pass a Blink::Type object" - end - - if @objects.has_key?(newobj.name) - raise "Object '%s' of type '%s' already exists with id '%s' vs. '%s'" % - [newobj.name,newobj.class.name, - @objects[newobj.name].object_id,newobj.object_id] - else - #Blink.debug("adding %s of type %s to class list" % - # [object.name,object.class]) - @objects[newobj.name] = newobj - end - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # remove all type instances - def Type.allclear - @@typeary.each { |subtype| - Blink.notice "Clearing %s of objects" % subtype - subtype.clear - } - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # per-type clearance - def Type.clear - if defined? @objects - @objects.clear - end - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # all objects total - def Type.push(object) - @@allobjects.push object - #Blink.debug("adding %s of type %s to master list" % - # [object.name,object.class]) - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # some simple stuff to make it easier to get a name from everyone - def Type.namevar - unless defined? @namevar and ! @namevar.nil? - raise "Class %s has no namevar defined" % self - end - return @namevar - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def Type.has_key?(name) - return @objects.has_key?(name) - end - #--------------------------------------------------------------- - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - #--------------------------------------------------------------- - # class and instance methods dealing with parameters and states - #--------------------------------------------------------------- - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def Type.buildstatehash - unless defined? @validstates - @validstates = Hash.new(false) - end - @states.each { |stateklass| - name = stateklass.name - if @validstates.include?(name) - if @validstates[name] != stateklass - raise "Redefining state %s(%s) in %s" % [name,stateklass,self] - else - # it's already there, so don't bother - end - else - @validstates[name] = stateklass - end - } - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # Is the parameter in question a meta-parameter? - def Type.metaparam(param) - @@metaparams.include?(param) - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # this is probably only used by FileRecord objects - def Type.parameters=(params) - Blink.notice "setting parameters to [%s]" % params.join(" ") - @parameters = params.collect { |param| - if param.class == Symbol - param - else - param.intern - end - } - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def Type.states - return @states - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def Type.validstates - return @validstates - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def Type.validstate(name) - unless @validstates.length == @states.length - self.buildstatehash - end - if @validstates.include?(name) - return @validstates[name] - else - return false - end - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def Type.validparameter(name) - unless defined? @parameters - raise "Class %s has not defined parameters" % self - end - return @parameters.include?(name) - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def Type.validparam(name) - self.validstate(name) or self.validparameter(name) or false - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # this abstracts accessing parameters and states, and normalizes - # access to always be symbols, not strings - def [](name) - mname = name - if name.is_a?(String) - mname = name.intern - end - unless self.class.validparam(name) - raise "Invalid parameter %s" % [mname] - end - if @states.include?(mname) - # if they're using [], they don't know if we're a state or a string - # thus, return a string - # if they want the actual state object, they should use state() - return @states[mname].is - elsif @parameters.include?(mname) - return @parameters[mname] - else - return nil - end - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # this abstracts setting parameters and states, and normalizes - # access to always be symbols, not strings - def []=(name,value) - mname = name - if name.is_a?(String) - mname = name.intern - end - - if Blink::Type.metaparam(mname) - # call the metaparam method - self.send(("meta" + mname.id2name),value) - elsif stateklass = self.class.validstate(mname) - if value.is_a?(Blink::State) - Blink.debug "'%s' got handed a state for '%s'" % [self,mname] - @states[mname] = value - else - if @states.include?(mname) - @states[mname].should = value - else - @states[mname] = stateklass.new( - :parent => self, - :should => value - ) - #Blink.notice "Adding parent to %s" % mname - #@states[mname].parent = self - end - end - elsif self.class.validparameter(mname) - @parameters[mname] = value - else - raise "Invalid parameter %s" % [mname] - end - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # removing states - def delete(attr) - if @states.has_key?(attr) - @states.delete(attr) - else - raise "Undefined state '#{attr}' in #{self}" - end - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def state(name) - return @states[name] - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def parameter(name) - return @parameters[name] - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - #--------------------------------------------------------------- - # instance methods related to instance intrinsics - # e.g., initialize() and name() - #--------------------------------------------------------------- - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def initialize(hash) - @children = [] - @evalcount = 0 - - @subscriptions = [] - - # states and parameters are treated equivalently from the outside: - # as name-value pairs (using [] and []=) - # internally, however, parameters are merely a hash, while states - # point to State objects - # further, the lists of valid states and parameters are defined - # at the class level - @states = Hash.new(false) - @parameters = Hash.new(false) - - @noop = false - - # which objects to notify when we change - @notify = [] - - # keeping stats for the total number of changes, and how many were - # completely sync'ed - # this isn't really sufficient either, because it adds lots of special cases - # such as failed changes - # it also doesn't distinguish between changes from the current transaction - # vs. changes over the process lifetime - @totalchanges = 0 - @syncedchanges = 0 - @failedchanges = 0 - - hash.each { |var,value| - unless var.is_a? Symbol - hash[var.intern] = value - hash.delete(var) - end - } - - if hash.include?(:noop) - @noop = hash[:noop] - hash.delete(:noop) - end - - # we have to set the name of our object before anything else, - # because it might be used in creating the other states - namevar = self.class.namevar - - # if they're not using :name for the namevar but we got :name (probably - # from the parser) - if namevar != :name and hash.include?(:name) and ! hash[:name].nil? - self[namevar] = hash[:name] - hash.delete(:name) - # else if we got the namevar - elsif hash.has_key?(namevar) and ! hash[namevar].nil? - self[namevar] = hash[namevar] - hash.delete(namevar) - # else something's screwy - else - p hash - p namevar - raise TypeError.new("A name must be provided to %s at initialization time" % - self.class) - end - - hash.each { |param,value| - #Blink.debug("adding param '%s' with value '%s'" % - # [param,value]) - self[param] = value - } - - # add this object to the specific class's list of objects - #Blink.notice("Adding [%s] to %s" % [self.name,self.class]) - self.class[self.name] = self - - # and then add it to the master list - Blink::Type.push(self) - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # return the full path to us, for logging and rollback - # some classes (e.g., FileTypeRecords) will have to override this - def fqpath - return self.class, self.name - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # this might result from a state or from a parameter - def name - return self[self.class.namevar] - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def retrieve - # it's important to use the method here, so we always get - # them back in the right order - self.states.collect { |state| - state.retrieve - } - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def sync - self.collect { |child| - child.sync - }.reject { |event| - ! (event.is_a?(Symbol) or event.is_a?(String)) - }.flatten - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def to_s - self.name - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - #--------------------------------------------------------------- - # instance methods dealing with contained states - #--------------------------------------------------------------- - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def states - Blink.debug "%s has %s states" % [self,@states.length] - tmpstates = [] - self.class.states.each { |state| - if @states.include?(state.name) - tmpstates.push(@states[state.name]) - end - } - unless tmpstates.length == @states.length - raise "Something went very wrong with tmpstates creation" - end - return tmpstates - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def eachstate - self.states.each { |state| - yield state - } - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # iterate across all children, and then iterate across states - # we do children first so we're sure that all dependent objects - # are checked first - # we ignore parameters here, because they only modify how work gets - # done, they don't ever actually result in work specifically - def each - # we want to return the states in the order that each type - # specifies it, because it may (as in the case of File#create) - # be important - @children.each { |child| - yield child - } - self.eachstate { |state| - yield state - } - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def push(*child) - @children.push(*child) - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - #--------------------------------------------------------------- - # instance methods dealing with actually doing work - #--------------------------------------------------------------- - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def newchange - @totalchanges += 1 - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # this method is responsible for collecting state changes - # we always descend into the children before we evaluate our current - # states - # this returns any changes resulting from testing, thus 'collect' - # rather than 'each' - def evaluate - unless defined? @evalcount - Blink.error "No evalcount defined on '%s' of type '%s'" % - [self.name,self.class] - end - # if we're a metaclass and we've already evaluated once... - if self.metaclass and @evalcount > 0 - return - end - @evalcount += 1 - - changes = @children.collect { |child| - child.evaluate - } - - # this only operates on states, not states + children - self.retrieve - unless self.insync? - changes << self.states.find_all { |state| - ! state.insync? - }.collect { |state| - Blink::StateChange.new(state) - } - end - # collect changes and return them - # these changes could be from child objects or from contained states - #self.collect { |child| - # child.evaluate - #} - return changes - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # if all contained objects are in sync, then we're in sync - def insync? - insync = true - - self.states.each { |state| - unless state.insync? - Blink.debug("%s is not in sync" % state) - insync = false - end - } - - Blink.debug("%s sync status is %s" % [self,insync]) - return insync - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # do we actually do work, or do we modify the system instead? - # instances of a metaclass only get executed once per client process, - # while instances of normal classes get run every time - def metaclass - return self.class.metaclass - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - #--------------------------------------------------------------- - # Meta-parameter methods: These methods deal with the results - # of specifying metaparameters - #--------------------------------------------------------------- - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # this just marks states that we definitely want to retrieve values - # on - def metacheck(args) - unless args.is_a?(Array) - args = [args] - end - - # these are states that we might not have values for but we want to retrieve - # values for anyway - args.each { |state| - unless state.is_a?(Symbol) - state = state.intern - end - next if @states.include?(state) - - stateklass = nil - unless stateklass = self.class.validstate(state) - raise "%s is not a valid state for %s" % [state,self.class] - end - - # XXX it's probably a bad idea to have code this important in - # two places - @states[state] = stateklass.new( - :parent => self - ) - #@states[state] = stateklass.new() - #@states[state].parent = self - } - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def subscribe(hash) - if hash[:event] == '*' - hash[:event] = :ALL_EVENTS - end - - hash[:source] = self - sub = Blink::Event::Subscription.new(hash) - - # add to the correct area - @subscriptions.push sub - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # return all of the subscriptions to a given event - def subscribers?(event) - @subscriptions.find_all { |sub| - sub.event == event.event or - sub.event == :ALL_EVENTS - } - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # for each object we require, subscribe to all events that it - # generates - # we might reduce the level of subscription eventually, but for now... - def metarequire(requires) - unless requires.is_a?(Array) - requires = [requires] - end - requires.each { |rname| - # we just have a name and a type, and we need to convert it - # to an object... - type = nil - object = nil - tname = rname[0] - unless type = Blink::Type.type(tname) - raise "Could not find type %s" % tname - end - name = rname[1] - unless object = type[name] - raise "Could not retrieve object '%s' of type '%s'" % - [name,type] - end - Blink.debug("%s requires %s" % [self.name,object]) - - # for now, we only support this one method, 'refresh' - object.subscribe( - :event => '*', - :target => self, - :method => :refresh - ) - #object.addnotify(self) - } - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def metaonerror(response) - Blink.debug("Would have called metaonerror") - @onerror = response - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def metaschedule(schedule) - @schedule = schedule - end - #--------------------------------------------------------------- -end # Blink::Type -end - -require 'blink/type/service' -require 'blink/type/file' -require 'blink/type/symlink' -require 'blink/type/package' -require 'blink/type/component' -require 'blink/statechange' -#require 'blink/type/typegen' -#require 'blink/type/typegen/filetype' -#require 'blink/type/typegen/filerecord' diff --git a/lib/blink/type/component.rb b/lib/blink/type/component.rb deleted file mode 100644 index 38bf3326d..000000000 --- a/lib/blink/type/component.rb +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/local/bin/ruby -w - -# $Id$ - -# the object allowing us to build complex structures -# this thing contains everything else, including itself - -require 'blink' -require 'blink/type' -require 'blink/transaction' - -module Blink - class Component < Blink::Type - include Enumerable - - @name = :component - @namevar = :name - - @states = [] - @parameters = [:name,:type] - - def each - @children.each { |child| yield child } - end - - def initialize(args) - @children = [] - super(args) - Blink.verbose "Made component with name %s" % self.name - end - - # now we decide whether a transaction is dumb, and just accepts - # changes from the container, or whether it collects them itself - # for now, because i've already got this implemented, let transactions - # collect the changes themselves - def evaluate - transaction = Blink::Transaction.new(@children) - transaction.component = self - return transaction - end - - def push(*ary) - ary.each { |child| - unless child.is_a?(Blink::Element) - Blink.notice "Got object of type %s" % child.class - raise "Containers can only contain Blink::Elements" - end - @children.push child - } - end - - def name - return "%s[%s]" % [@parameters[:type],@parameters[:name]] - end - - def refresh - @children.collect { |child| - if child.respond_to?(:refresh) - child.refresh - end - } - end - - def retrieve - self.collect { |child| - child.retrieve - } - end - - def to_s - return "component(%s)" % self.name - end - end -end diff --git a/lib/blink/type/file.rb b/lib/blink/type/file.rb deleted file mode 100644 index 9d0f98c61..000000000 --- a/lib/blink/type/file.rb +++ /dev/null @@ -1,450 +0,0 @@ -#!/usr/local/bin/ruby -w - -# $Id$ - -require 'digest/md5' -require 'etc' -require 'blink/type/state' - -module Blink - # we first define all of the state that our file will use - # because the objects must be defined for us to use them in our - # definition of the file object - class State - class FileCreate < Blink::State - require 'etc' - attr_accessor :file - @name = :create - @event = :file_created - - def should=(value) - # default to just about anything meaning 'true' - if value == false or value.nil? - @should = false - else - @should = true - end - end - - def retrieve - stat = nil - - self.is = FileTest.exist?(self.parent[:path]) - Blink.debug "'exists' state is %s" % self.is - end - - - def sync - begin - File.open(self.parent[:path],"w") { # just create an empty file - } - rescue => detail - raise detail - end - return :file_created - end - end - - class FileChecksum < Blink::State - @name = :checksum - @event = :file_modified - - def should=(value) - @checktype = value - state = Blink::Storage.state(self) - if hash = state[self.parent[:path]] - if hash.include?(@checktype) - @should = hash[@checktype] - else - Blink.verbose "Found checksum for %s but not of type %s" % - [self.parent[:path],@checktype] - @should = nil - end - else - Blink.debug "No checksum for %s" % self.parent[:path] - end - end - - def retrieve - unless defined? @checktype - @checktype = "md5" - end - - sum = "" - case @checktype - when "md5": - File.open(self.parent[:path]) { |file| - sum = Digest::MD5.hexdigest(file.read) - } - when "md5lite": - File.open(self.parent[:path]) { |file| - sum = Digest::MD5.hexdigest(file.read(512)) - } - when "timestamp","mtime": - sum = File.stat(self.parent[:path]).mtime - when "time": - sum = File.stat(self.parent[:path]).ctime - end - - self.is = sum - - Blink.debug "checksum state is %s" % self.is - end - - - # at this point, we don't actually modify the system, we just kick - # off an event if we detect a change - def sync - if self.updatesum - return :file_modified - else - return nil - end - end - - def updatesum - state = Blink::Storage.state(self) - unless state.include?(self.parent[:path]) - state[self.parent[:path]] = Hash.new - end - # if we're replacing, vs. updating - if state[self.parent[:path]].include?(@checktype) - Blink.debug "Replacing checksum %s with %s" % - [state[self.parent[:path]][@checktype],@is] - result = true - else - Blink.verbose "Creating checksum %s for %s of type %s" % - [@is,self.parent[:path],@checktype] - result = false - end - state[self.parent[:path]][@checktype] = @is - return result - end - end - - class FileUID < Blink::State - require 'etc' - attr_accessor :file - @name = :owner - @event = :inode_changed - - def retrieve - stat = self.parent.stat(true) - - self.is = stat.uid - if defined? @should - unless @should.is_a?(Integer) - begin - user = Etc.getpwnam(@should) - if user.gid == "" - raise "Could not retrieve uid for '%s'" % self.parent - end - Blink.debug "converting %s to integer '%d'" % - [@should,user.uid] - @should = user.uid - rescue - raise "Could not get any info on user '%s'" % @should - end - end - end - - Blink.debug "chown state is %d" % self.is - end - - def sync - if @is == -1 - self.parent.stat(true) - self.retrieve - Blink.notice "%s: after refresh, is '%s'" % [self.class.name,@is] - end - - unless self.parent.stat - Blink.error "File '%s' does not exist; cannot chown" % - self.parent[:path] - end - - begin - File.chown(self.should,-1,self.parent[:path]) - rescue => detail - raise "failed to chown '%s' to '%s': %s" % - [self.parent[:path],self.should,detail] - end - - return :inode_changed - end - end - - # this state should actually somehow turn into many states, - # one for each bit in the mode - # I think MetaStates are the answer, but I'm not quite sure - class FileMode < Blink::State - require 'etc' - - @name = :mode - @event = :inode_changed - - def should=(should) - # this is pretty hackish, but i need to make sure the number is in - # octal, yet the number can only be specified as a string right now - unless should.is_a?(Integer) # i've already converted it correctly - unless should =~ /^0/ - should = "0" + should - end - should = Integer(should) - end - @should = should - end - - def retrieve - stat = self.parent.stat(true) - self.is = stat.mode & 007777 - - Blink.debug "chmod state is %o" % self.is - end - - def sync - if @is == -1 - self.parent.stat(true) - self.retrieve - Blink.notice "%s: after refresh, is '%s'" % [self.class.name,@is] - end - - unless self.parent.stat - Blink.error "File '%s' does not exist; cannot chmod" % - self.parent[:path] - return - end - - begin - File.chmod(self.should,self.parent[:path]) - rescue - raise "failed to chmod #{self.parent[:path]}: #{$!}" - end - return :inode_changed - end - end - - # not used until I can figure out how to solve the problem with - # metastates - class FileSetUID < Blink::State - require 'etc' - - @parent = Blink::State::FileMode - - @name = :setuid - @event = :inode_changed - - def <=>(other) - self.is <=> @parent.value[11] - end - - # this just doesn't seem right... - def sync - unless defined? @is or @is == -1 - self.parent.stat(true) - self.retrieve - Blink.notice "%s: should is '%s'" % [self.class.name,self.should] - end - tmp = 0 - if self.is == true - tmp = 1 - end - @parent.value[11] = tmp - return :inode_changed - end - end - - class FileGroup < Blink::State - require 'etc' - - @name = :group - @event = :inode_changed - - def retrieve - stat = self.parent.stat(true) - - self.is = 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 - if defined? @should - unless self.should.is_a?(Integer) - begin - require 'blink/fact' - group = Etc.getgrnam(self.should) - # apparently os x is six shades of weird - os = Blink::Fact["Operatingsystem"] - - gid = "" - case os - when "Darwin": - gid = group.passwd - else - gid = group.gid - end - if gid == "" - raise "Could not retrieve gid for %s" % self.parent - end - Blink.debug "converting %s to integer %d" % - [self.should,gid] - self.should = gid - rescue - #raise "Could not get any info on group %s" % self.should - raise - end - end - end - Blink.debug "chgrp state is %d" % self.is - end - - def sync - Blink.debug "setting chgrp state to %s" % self.should - if @is == -1 - self.parent.stat(true) - self.retrieve - Blink.notice "%s: after refresh, is '%s'" % [self.class.name,@is] - end - - unless self.parent.stat - Blink.error "File '%s' does not exist; cannot chgrp" % - self.parent[:path] - return - end - - begin - # set owner to nil so it's ignored - File.chown(nil,self.should,self.parent[:path]) - rescue - raise "failed to chgrp %s to %s: %s" % - [self.parent[:path], self.should, $!] - end - return :inode_changed - end - end - end - class Type - class File < Type - attr_reader :params - # class instance variable - @states = [ - Blink::State::FileCreate, - Blink::State::FileUID, - Blink::State::FileGroup, - Blink::State::FileMode, - Blink::State::FileChecksum, - Blink::State::FileSetUID - ] - - @parameters = [ - :path, - :recurse - ] - - @name = :file - @namevar = :path - - # a wrapper method to make sure the file exists before doing anything - def retrieve - unless stat = self.stat(true) - Blink.verbose "File %s does not exist" % self[:path] - @states.each { |name,state| - state.is = -1 - } - return - end - super - end - - def stat(refresh = false) - if @stat.nil? or refresh == true - begin - @stat = ::File.stat(self[:path]) - rescue => error - Blink.debug "Failed to stat %s: %s" % - [self[:path],error] - @stat = nil - end - end - - return @stat - end - - def initialize(hash) - arghash = hash.dup - super - @stat = nil - - # if recursion is enabled and we're a directory... - if @parameters[:recurse] and self.stat.directory? - recurse = self[:recurse] - # we might have a string, rather than a number - if recurse.is_a?(String) - if recurse =~ /^[0-9]+$/ - recurse = Integer(recurse) - elsif recurse =~ /^inf/ # infinite recursion - recurse = true - end - end - - # unless we're at the end of the recursion - if recurse != 0 - arghash.delete("recurse") - if recurse.is_a?(Integer) - recurse -= 1 # reduce the level of recursion - end - - arghash[:recurse] = recurse - - # now make each contained file/dir a child - unless defined? @children - @children = [] - end - - Dir.foreach(self[:path]) { |file| - next if file =~ /^\.\.?/ # skip . and .. - - arghash[:path] = ::File.join(self[:path],file) - - child = nil - # if the file already exists... - if child = self.class[arghash[:path]] - arghash.each { |var,value| - next if var == :path - child[var] = value - } - else # create it anew - child = self.class.new(arghash) - end - @children.push child - } - end - end - end - end # Blink::Type::File - end # Blink::Type - - class FileSource - attr_accessor :name - - @sources = Hash.new(nil) - - def FileSource.[]=(name,sub) - @sources[name] = sub - end - - def FileSource.[](name) - return @sources[name] - end - - def initialize(name) - @name = name - - if block_given? - yield self - end - - FileSource[name] = self - end - end -end diff --git a/lib/blink/type/package.rb b/lib/blink/type/package.rb deleted file mode 100644 index 77bcd76b2..000000000 --- a/lib/blink/type/package.rb +++ /dev/null @@ -1,366 +0,0 @@ -#!/usr/local/bin/ruby -w - -# $Id$ - -require 'blink/type/state' -require 'blink/fact' - -module Blink - class State - class PackageInstalled < Blink::State - @name = :install - - def retrieve - #self.is = Blink::PackageTyping[@object.format][@object.name] - unless @parent.class.listed - @parent.class.getpkglist - end - Blink.debug "package install state is %s" % self.is - end - - def sync - #begin - raise "cannot sync package states yet" - #rescue - # raise "failed to sync #{@params[:file]}: #{$!}" - #end - - #return :package_installed - end - end - end - - class Type - # 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 < Type - attr_reader :version, :format - @states = [ - Blink::State::PackageInstalled - ] - @parameters = [ - :format, - :name, - :status, - :version, - :category, - :platform, - :root, - :vendor, - :description - ] - - @name = :package - @namevar = :name - @listed = false - - @allowedmethods = [:types] - @@types = nil - - def Package.clear - @listed = false - super - end - - def Package.listed - return @listed - end - - def Package.types(array) - unless array.is_a?(Array) - array = [array] - end - @@types = array - Blink.warning "Types are %s" % array.join(" ") - end - - def Package.getpkglist - if @@types.nil? - case Blink::Fact["operatingsystem"] - when "SunOS": @@types = ["sunpkg"] - when "Linux": - case Blink::Fact["distro"] - when "Debian": @@types = ["dpkg"] - else - raise "No default type for " + Blink::Fact["distro"] - end - else - raise "No default type for " + Blink::Fact["operatingsystem"] - end - end - - list = @@types.collect { |type| - if typeobj = Blink::PackagingType[type] - # pull all of the objects - typeobj.list - else - raise "Could not find package type '%s'" % type - end - }.flatten - @listed = true - return list - end - - def Package.installedpkg(hash) - # this is from code, so we don't have to do as much checking - name = hash[:name] - - # if it already exists, modify the existing one - if object = Package[name] - states = {} - object.states.each { |state| - Blink.warning "Adding %s" % state.name.inspect - states[state.name] = state - } - hash.each { |var,value| - if states.include?(var) - Blink.verbose "%s is a set state" % var.inspect - states[var].is = value - else - Blink.verbose "%s is not a set state" % var.inspect - if object[var] and object[var] != value - Blink.warning "Overriding %s => %s on %s with %s" % - [var,object[var],name,value] - end - - object[var] = value - - # swap the values if we're a state - if states.include?(var) - Blink.verbose "Swapping %s because it's a state" % var - states[var].is = value - states[var].should = nil - else - Blink.verbose "%s is not a state" % var.inspect - Blink.verbose "States are %s" % states.keys.collect { |st| - st.inspect - }.join(" ") - end - end - } - return object - else # just create it - return self.new(hash) - end - end - - # okay, there are two ways that a package could be created... - # either through the language, in which case the hash's values should - # be set in 'should', or through comparing against the system, in which - # case the hash's values should be set in 'is' - def initialize(hash) - super - end - - end # Blink::Type::Package - end - - class PackagingType - attr_writer :list, :install, :remove, :check - - @@types = Hash.new(false) - - def PackagingType.[](name) - if @@types.include?(name) - return @@types[name] - else - Blink.warning name.inspect - Blink.warning @@types.keys.collect { |key| - key.inspect - }.join(" ") - return nil - end - end - - # whether a package is installed or not - def [](name) - return @packages[name] - 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, :install, :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::Type::Package.installedpkg(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" => :install, - "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::Type::Package.installedpkg(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 - } - } - - # this is how we retrieve packages - class PackageSource - attr_accessor :uri - attr_writer :retrieve - - @@sources = Hash.new(false) - - def PackageSource.get(file) - type = file.sub(%r{:.+},'') - source = nil - if source = @@sources[type] - return source.retrieve(file) - else - raise "Unknown package source: %s" % type - end - end - - def initialize(name) - if block_given? - yield self - end - - @@sources[name] = self - end - - def retrieve(path) - @retrieve.call(path) - end - - end - - PackageSource.new("file") { |obj| - obj.retrieve = proc { |path| - # this might not work for windows... - file = path.sub(%r{^file://},'') - - if FileTest.exists?(file) - return file - else - raise "File %s does not exist" % file - end - } - } -end diff --git a/lib/blink/type/process.rb b/lib/blink/type/process.rb deleted file mode 100644 index b42f75f26..000000000 --- a/lib/blink/type/process.rb +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/local/bin/ruby -w - -# DISABLED -# I'm only working on services, not processes, right now - -module Blink - class State - class ProcessRunning < State - 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.parent, - :message => "Failed to run ps" - ) - end - - self.state = running - Blink.debug "there are #{running} #{self.parent} 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.parent, - :message => "starting" - ) - Kernel.exec(string) - } - end - end - end - class Type - class BProcess < Type - attr_reader :stat, :path - @parameters = [:start, :stop, :user, :pattern, :binary, :arguments] - @name = :process - - @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::Type::BProcess - end # Blink::Type - -end diff --git a/lib/blink/type/service.rb b/lib/blink/type/service.rb deleted file mode 100644 index f17f17e3f..000000000 --- a/lib/blink/type/service.rb +++ /dev/null @@ -1,186 +0,0 @@ -#!/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 State - class ServiceRunning < State - @name = :running - #@event = :file_created - - # this whole thing is annoying - # i should probably just be using booleans, but for now, i'm not... - def should=(should) - case should - when false,0,"0": - should = 0 - when true,1,"1": - should = 1 - else - Blink.warning "%s: interpreting '%s' as false" % - [self.class,should] - should = 0 - end - @should = should - end - - def retrieve - self.is = self.running() - Blink.debug "Running value for '%s' is '%s'" % - [self.parent.name,self.is] - end - - # should i cache this info? - def running - begin - status = self.parent.initcmd("status") - Blink.debug "initcmd status for '%s' is '%s'" % - [self.parent.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] - event = nil - if self.should > 0 - if status < 1 - Blink.debug "Starting '%s'" % self - if self.parent.initcmd("start") - event = :service_started - else - raise "Failed to start '%s'" % self.parent.name - end - else - Blink.debug "'%s' is already running, yo" % self - #Blink.debug "Starting '%s'" % self - #unless self.parent.initcmd("start") - # raise "Failed to start %s" % self.name - #end - end - elsif status > 0 - Blink.debug "Stopping '%s'" % self - if self.parent.initcmd("stop") - event = :service_stopped - else - raise "Failed to stop %s" % self.name - end - else - Blink.debug "Not running '%s' and shouldn't be running" % self - end - - return event - end - end - end - class Type - class Service < Type - attr_reader :stat - @states = [ - Blink::State::ServiceRunning - ] - @parameters = [ - :name, - :pattern - ] - - @functions = [ - :setpath - ] - - @name = :service - @namevar = :name - - @searchpaths = Array.new - @allowedmethods = [:setpath] - - 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 - } - raise "Could not find init script for '%s'" % name - end - - def Service.setpath(ary) - # verify each of the paths exists - #ary.flatten! - @searchpaths = ary.find_all { |dir| - retvalue = false - begin - retvalue = ::File.stat(dir).directory? - rescue => detail - Blink.verbose("Directory %s does not exist: %s" % [dir,detail]) - # just ignore it - end - # disallow relative paths - #if dir !~ /^\// - # retvalue = false - #end - retvalue - } - end - - # it'd be nice if i didn't throw the output away... - # this command returns true if the exit code is 0, and returns - # false otherwise - def initcmd(cmd) - script = self.initscript - - Blink.debug "Executing '%s %s' as initcmd for '%s'" % - [script,cmd,self] - - rvalue = Kernel.system("%s %s" % - [script,cmd]) - - Blink.debug "'%s' ran with exit status '%s'" % - [cmd,rvalue] - - - rvalue - end - - def initscript - if defined? @initscript - return @initscript - else - @initscript = Service.search(self.name) - end - end - - def refresh - self.initcmd("restart") - end - end # Blink::Type::Service - end # Blink::Type -end diff --git a/lib/blink/type/state.rb b/lib/blink/type/state.rb deleted file mode 100644 index 4706b04b7..000000000 --- a/lib/blink/type/state.rb +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/local/bin/ruby -w - -# $Id$ - -require 'blink' -require 'blink/element' -require 'blink/statechange' - -#--------------------------------------------------------------- -# this is a virtual base class for states -# states are self-contained building blocks for objects - -# States can currently only be used for comparing a virtual "should" value -# against the real state of the system. For instance, you could verify that -# a file's owner is what you want, but you could not create two file objects -# and use these methods to verify that they have the same owner -module Blink -class State < Blink::Element - attr_accessor :is, :should, :parent - - @virtual = true - - #--------------------------------------------------------------- - # every state class must tell us what its name will be (as a symbol) - # this determines how we will refer to the state during usage - # e.g., the Owner state for Files might say its name is :owner; - # this means that we can say "file[:owner] = 'yayness'" - def State.name - return @name - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # which event gets generated if this state change happens - def State.generates - return @event - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # if we're not in sync, return a statechange capable of putting us - # in sync - def evaluate - Blink.verbose "evaluating %s" % self - self.retrieve - if self.insync? - Blink.verbose "%s is in sync" % self - return nil - else - return Blink::StateChange.new(self) - end - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # return the full path to us, for logging and rollback - def fqpath - return @parent.fqpath, self.name - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # we aren't actually comparing the states themselves, we're only - # comparing the "should" value with the "is" value - def insync? - Blink.debug "%s value is '%s', should be '%s'" % - [self,self.is.inspect,self.should.inspect] - self.is == self.should - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def initialize(hash) - @is = nil - - unless hash.include?(:parent) - raise "State %s was not passed a parent" % self - end - @parent = hash[:parent] - - if hash.include?(:should) - self.should = hash[:should] - else # we got passed no argument - # leave @should undefined - end - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # for testing whether we should actually do anything - def noop - unless defined? @noop - @noop = false - end - tmp = @noop || self.parent.noop || Blink[:noop] || false - Blink.notice "noop is %s" % tmp - return tmp - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - #def refresh(transaction) - # self.retrieve - - # we definitely need some way to batch these refreshes, so a - # given object doesn't get refreshed multiple times in a single - # run - # @parent.refresh - #end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # each state class must define the name() method, and state instances - # do not change that name - # this implicitly means that a given object can only have one state - # instance of a given state class - def name - return self.class.name - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # retrieve the current state from the running system - def retrieve - raise "'retrieve' method was not overridden by %s" % self.class - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def to_s - return "%s(%s)" % [@parent.name,self.name] - end - #--------------------------------------------------------------- -end -end diff --git a/lib/blink/type/symlink.rb b/lib/blink/type/symlink.rb deleted file mode 100644 index 772b5a831..000000000 --- a/lib/blink/type/symlink.rb +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/local/bin/ruby -w - -# $Id$ - -require 'etc' -require 'blink/type/state' -require 'blink/type/file' - -module Blink - # okay, how do we deal with parameters that don't have operations - # associated with them? - class State - class SymlinkTarget < Blink::State - require 'etc' - attr_accessor :file - - @name = :target - - def create - begin - Blink.debug("Creating symlink '%s' to '%s'" % - [self.parent[:path],self.should]) - unless File.symlink(self.should,self.parent[:path]) - raise TypeError.new("Could not create symlink '%s'" % - self.parent[:path]) - end - rescue => detail - raise TypeError.new("Cannot create symlink '%s': %s" % - [self.parent[:path],detail]) - end - end - - def remove - if FileTest.symlink?(self.parent[:path]) - Blink.debug("Removing symlink '%s'" % self.parent[:path]) - begin - File.unlink(self.parent[:path]) - rescue - raise TypeError.new("Failed to remove symlink '%s'" % - self.parent[:path]) - end - elsif FileTest.exists?(self.parent[:path]) - raise TypeError.new("Cannot remove normal file '%s'" % - self.parent[:path]) - else - Blink.debug("Symlink '%s' does not exist" % - self.parent[:path]) - end - end - - def retrieve - stat = nil - - if FileTest.symlink?(self.parent[:path]) - self.is = File.readlink(self.parent[:path]) - Blink.debug("link value is '%s'" % self.is) - return - else - self.is = 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.parent[:path]) - path = File.readlink(self.parent[:path]) - if path != self.should - self.remove() - self.create() - end - elsif FileTest.exists?(self.parent[:path]) - raise TypeError.new("Cannot replace normal file '%s'" % - self.parent[:path]) - else - self.create() - end - end - - #self.parent.newevent(:event => :inode_changed) - end - end - end - - class Type - class Symlink < Type - attr_reader :stat, :path, :params - # class instance variable - @states = [ - Blink::State::FileUID, - Blink::State::FileGroup, - Blink::State::FileMode, - Blink::State::SymlinkTarget - ] - - @parameters = [ - :path - ] - - @name = :symlink - @namevar = :path - end # Blink::Type::Symlink - end # Blink::Type - -end diff --git a/lib/blink/type/typegen.rb b/lib/blink/type/typegen.rb deleted file mode 100644 index 85f04912c..000000000 --- a/lib/blink/type/typegen.rb +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/local/bin/ruby -w - -# $Id$ - -# parse and write configuration files using objects with minimal parsing abilities - -require 'etc' -require 'blink/type' - -module Blink - class Type -class TypeGenerator < Blink::Type - include Enumerable - - @namevar = :name - @name = :typegen - @abstract = true - - @parameters = [:name] - @states = [] - - #--------------------------------------------------------------- - def TypeGenerator.[](name) - return @subclasses[name] - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def TypeGenerator.inherited(subclass) - #subclass.initvars - super(subclass) - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # we don't need to 'super' here because type.rb already runs initvars - # in Type#inherited - def TypeGenerator.initvars - @subclasses = Hash.new(nil) - super - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def TypeGenerator.name - return @name - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def TypeGenerator.name=(name) - @name = name - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def TypeGenerator.namevar - return @namevar || :name - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def TypeGenerator.namevar=(namevar) - Blink.debug "Setting namevar for %s to %s" % [self,namevar] - unless namevar.is_a? Symbol - namevar = namevar.intern - end - @namevar = namevar - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def TypeGenerator.newtype(arghash) - unless defined? @parameters - raise "Type %s is set up incorrectly" % self - end - - arghash.each { |key,value| - if key.class != Symbol - # convert to a symbol - arghash[key.intern] = value - arghash.delete key - key = key.intern - end - unless @parameters.include?(key) - raise "Invalid argument %s on class %s" % - [key,self] - end - - } - - # turn off automatically checking all arguments - #@parameters.each { |option| - # unless arghash.include?(option) - # p arghash - # raise "Must pass %s to class %s" % - # [option,self] - # end - #} - - if @subclasses.include?(arghash[:name]) - raise "File type %s already exists" % arghash[:name] - end - - klassname = arghash[:name].capitalize - - # create the file type - Blink::Type.module_eval " - class %s < %s - end" % [klassname,self] - klass = eval(klassname) - klass.name = arghash[:name] - - @subclasses[arghash[:name]] = klass - - arghash.each { |option,value| - method = option.id2name + "=" - if klass.respond_to?(method) - #Blink.debug "Setting %s on %s to '%s'" % [option,klass,arghash[option]] - klass.send(method,arghash[option]) - else - Blink.debug "%s does not respond to %s" % [klass,method] - end - } - - # i couldn't get the method definition stuff to work - # oh well - # probably wouldn't want it in the end anyway - #@parameters.each { |option| - # writer = option.id2name + "=" - # readproc = proc { eval("@" + option.id2name) } - # klass.send(:define_method,option,readproc) - # writeproc = proc { |value| module_eval("@" + option.id2name) = value } - # klass.send(:define_method,writer,writeproc) - # klass.send(writer,hash[option]) - #} - - #Blink::Type.inherited(klass) - Blink::Type.buildtypehash - return klass - end - #--------------------------------------------------------------- -end -#--------------------------------------------------------------- -end -end diff --git a/lib/blink/type/typegen/filerecord.rb b/lib/blink/type/typegen/filerecord.rb deleted file mode 100644 index fb9030c12..000000000 --- a/lib/blink/type/typegen/filerecord.rb +++ /dev/null @@ -1,243 +0,0 @@ -#!/usr/local/bin/ruby -w - -# $Id$ - -# parse and write configuration files using objects with minimal parsing abilities - -require 'etc' -require 'blink/type' -require 'blink/type/typegen' - -#--------------------------------------------------------------- -class Blink::Type::FileRecord < Blink::Type::TypeGenerator - attr_accessor :fields, :namevar, :splitchar, :object - - @parameters = [:name, :splitchar, :fields, :namevar, :filetype, :regex, :joinchar] - @abstract = true - @metaclass = true - - @namevar = :name - @name = :filerecord - - #--------------------------------------------------------------- - def FileRecord.newtype(hash) - #shortname = hash[:name] - #hash[:name] = hash[:filetype].name.capitalize + hash[:name].capitalize - klass = super(hash) - #klass.name = shortname - klass.parameters = hash[:fields] - #klass.namevar = hash[:namevar] - klass.filetype = hash[:filetype] - hash.delete(:fields) - hash.delete(:namevar) - return klass - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def FileRecord.fields=(ary) - @fields = ary - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def FileRecord.fields - return @fields - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def FileRecord.filetype - @filetype - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def FileRecord.filetype=(filetype) - if filetype.is_a?(String) - @filetype = Blink::Type::FileType[filetype] - elsif filetype.is_a?(Blink::Type::FileType) - @filetype = filetype - else - raise "Cannot use objects of type %s as filetypes" % filetype - end - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def FileRecord.joinchar=(char) - @joinchar = char - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def FileRecord.joinchar - unless defined? @joinchar - @joinchar = nil - end - @joinchar - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def FileRecord.match(object,line) - matchobj = nil - begin - matchobj = self.regex.match(line) - rescue RegexpError => detail - raise - end - - if matchobj.nil? - return nil - else - child = self.new(object) - child.match = matchobj - return child - end - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def FileRecord.regex=(regex) - @regex = regex - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def FileRecord.regex - # the only time @regex is allowed to be nil is if @splitchar is defined - if @regex.nil? - if @splitchar.nil? - raise "%s defined incorrectly -- splitchar or regex must be specified" % - self - else - ary = [] - text = @fields.collect { |field| - "([^%s]*)" % @splitchar - }.join(@splitchar) - begin - @regex = Regexp.new(text) - rescue RegexpError => detail - raise "Could not create splitregex from %s" % @splitchar - end - Blink.debug("Created regexp %s" % @regex) - end - elsif @regex.is_a?(String) - begin - @regex = Regexp.new(@regex) - rescue RegexpError => detail - raise "Could not create splitregex from %s" % @regex - end - end - return @regex - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def FileRecord.splitchar=(char) - @splitchar = char - #@regex = %r{#{char}} - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def FileRecord.splitchar - return @splitchar - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - #def [](field) - # @parameters[field] - #end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - #def []=(field,value) - # @parameters[field] = value - #end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def ==(other) - unless self.class == other.class - return false - end - - unless self.name == other.name - return false - end - @parameters.keys { |field| - unless self[field] == other[field] - Blink.debug("%s -> %s has changed" % [self.name, field]) - return false - end - } - return true - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def initialize(hash) - if self.class == Blink::Type::FileRecord - self.class.newtype(hash) - return - end - @parameters = {} - #if block_given? - # yield self - #end - super(hash) - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def match=(matchobj) - @match = matchobj - #puts "captures are [%s]" % [matchobj.captures] - self.class.fields.zip(matchobj.captures) { |field,value| - @parameters[field] = value - #puts "%s => %s" % [field,@parameters[field]] - } - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def record=(record) - begin - ary = record.split(self.class.regex) - rescue RegexpError=> detail - raise RegexpError.new(detail) - end - self.class.fields.each { |field| - @parameters[field] = ary.shift - #puts "%s => %s" % [field,@parameters[field]] - } - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def name - if @parameters.include?(self.class.namevar) - return @parameters[self.class.namevar] - else - raise "No namevar '%s' for objects of type %s" % - [self.class.namevar,self.class.to_s] - end - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def to_s - ary = self.class.fields.collect { |field| - if ! @parameters.include?(field) - raise "Object %s is missing field %s" % [self.name,field] - else - @parameters[field] - end - }.join(self.class.joinchar || self.class.splitchar) - end - #--------------------------------------------------------------- -end -#--------------------------------------------------------------- diff --git a/lib/blink/type/typegen/filetype.rb b/lib/blink/type/typegen/filetype.rb deleted file mode 100644 index 4b3b89db8..000000000 --- a/lib/blink/type/typegen/filetype.rb +++ /dev/null @@ -1,316 +0,0 @@ -#!/usr/local/bin/ruby -w - -# $Id$ - -# parse and write configuration files using objects with minimal parsing abilities - -require 'blink/type' -require 'blink/type/typegen' - -class Blink::Type::FileType < Blink::Type::TypeGenerator - attr_accessor :childtype - - @parameters = [:name, :linesplit, :escapednewlines] - #@abstract = true - @metaclass = true - - @namevar = :name - @name = :filetype - - @modsystem = true - - #--------------------------------------------------------------- - def FileType.newtype(hash) - unless hash.include?(:linesplit) - hash[:linesplit] = "\n" - end - - # i don't think there's any reason to 'super' this - #klass = Blink::Type::TypeGenerator.newtype(hash) - klass = super(hash) - - klass.escapednewlines = true - klass.namevar = :name - klass.parameters = [:name, :path, :complete] - - #klass.childtype = Blink::Type::FileRecord.newtype( - # :name => hash[:name] + "_record", - # :splitchar => hash[:recordsplit], - # :fields => hash[:fields], - # :namevar => hash[:namevar] - #) - #klass.addrecord( - # :name => hash[:name] + "_record", - # :splitchar => hash[:recordsplit], - # :fields => hash[:fields], - # :namevar => hash[:namevar] - #) - - return klass - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # currently not used - def FileType.addrecord(hash) - unless defined? @records - @records = {} - end - hash[:filetype] = self - - # default to the naming field being the first field provided - unless hash.include?(:namevar) - hash[:namevar] = hash[:fields][0] - end - - recordtype = Blink::Type::FileRecord.newtype(hash) - @records[recordtype.name] = recordtype - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def FileType.records - return @records - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def FileType.escapednewlines=(value) - @escnlines = value - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def FileType.escapednewlines - if ! defined? @escnlines or @escnlines.nil? - return false - else - return @escnlines - end - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def FileType.childtype - unless defined? @childtype - @childtype = nil - end - return @childtype - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def FileType.childtype=(childtype) - @childtype = childtype - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def FileType.regex - return @regex - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def FileType.linesplit=(linesplit) - @regex = %r{#{linesplit}} - @linesplit = linesplit - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def FileType.linesplit - return @linesplit - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - #def [](name) - # return @childhash[name] - #end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - #def []=(name,value) - #end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # we don't really have a 'less-than/greater-than' sense here - # so i'm sticking with 'equals' until those make sense - def ==(other) - unless self.children.length == other.children.length - Blink.debug("file has %s records instead of %s" % - [self.children.length, other.children.length]) - return self.children.length == other.children.length - end - equal = true - self.zip(other.children) { |schild,ochild| - unless schild == ochild - Blink.debug("%s has changed in %s" % - [schild.name,self.name]) - equal = false - break - end - } - - return equal - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # create a new record with a block - def add(type,&block) - obj = self.class.records[type].new(self,&block) - Blink.debug("adding %s" % obj.name) - @childary.push(obj) - @childhash[obj.name] = obj - - return obj - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def children - return @childary - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # remove a record - def delete(name) - if @childhash.has_key?(name) - child = @childhash[name] - - @childhash.delete(child) - @childary.delete(child) - else - raise "No such entry %s" % name - end - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def each - @childary.each { |child| - yield child - } - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # create a new file - def initialize(hash) - # if we are the FileType object itself, we create a new type - # otherwise, we create an instance of an existing type - # yes, this should be more straightforward - if self.class == Blink::Type::FileType - self.class.newtype(hash) - return - end - Blink.debug "Creating new '%s' file with path '%s' and name '%s'" % - [self.class.name,hash["path"],hash[:name]] - Blink.debug hash.inspect - @file = hash["path"] - - @childary = [] - @childhash = {} - super - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # this is where we're pretty different from other objects - # we can choose to either reparse the existing file and compare - # the objects, or we can write our file out and do an - # text comparison - def insync? - tmp = self.class.new(@file) - tmp.retrieve - - return self == tmp - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - #def name - # return @file - #end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - # read the whole file in and turn it into each of the appropriate - # objects - def retrieve - str = "" - ::File.open(@file) { |fname| - fname.each { |line| - str += line - } - } - - if self.class.escapednewlines - endreg = %r{\\\n\s*} - str.gsub!(endreg,'') - end - @childary = str.split(self.class.regex).collect { |line| - childobj = nil - self.class.records.each { |name,recordtype| - if childobj = recordtype.match(self,line) - break - end - } - if childobj.nil? - Blink.warning("%s: could not match %s" % [self.name,line]) - #Blink.warning("could not match %s" % line) - next - end - - begin - Blink.debug("got child: %s(%s)" % [childobj.class,childobj.to_s]) - rescue NoMethodError - Blink.warning "Failed: %s" % childobj - end - childobj - }.reject { |child| - child.nil? - } - - @childary.each { |child| - begin - @childhash[child.name] = child - rescue NoMethodError => detail - p child - p child.class - puts detail - exit - end - } - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def sync - #unless self.insync? - self.write - #end - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def to_s - return @childary.collect { |child| - child.to_s - }.join(self.class.linesplit) + self.class.linesplit - end - #--------------------------------------------------------------- - - #--------------------------------------------------------------- - def write - ::File.open(@file, "w") { |file| - file.write(self.to_s) - } - end - #--------------------------------------------------------------- -end -#--------------------------------------------------------------- |
