Edit

thodg/cl-unix-cybernetics/resource.lisp

Branch :

  • Show log

    Commit

  • Author : Thomas de Grivel
    Date : 2014-10-22 09:22:43
    Hash : 6df4dcf4
    Message : OS detection based on output of `uname`. Implement probes as generic functions returning properties lists. Each probe can be specialized on resource type and OS class.

  • resource.lisp
  • ;;
    ;;  adams  -  Remote system administration tools
    ;;
    ;;  Copyright 2013 Thomas de Grivel <billitch@gmail.com>
    ;;
    ;;  Permission to use, copy, modify, and distribute this software for any
    ;;  purpose with or without fee is hereby granted, provided that the above
    ;;  copyright notice and this permission notice appear in all copies.
    ;;
    ;;  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    ;;  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    ;;  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    ;;  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    ;;  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    ;;  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    ;;  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    ;;
    
    (in-package :adams)
    
    (unless (boundp '+undefined+)
      (defconstant +undefined+ '#:undefined))
    
    ;;  Probe
    
    (defclass probe ()
      ((name :initarg :name
    	 :initform (error "Probe without a name.")
    	 :reader probe-name
    	 :type symbol)
       (properties :initarg :properties
    	       :initform (error "Probe without properties.")
    	       :reader probe-properties)))
    
    (defgeneric probe-generic-function (probe))
    
    (defmethod probe-generic-function ((probe probe))
      (symbol-function (probe-name probe)))
    
    (defmethod print-object ((probe probe) stream)
      (print-unreadable-object (probe stream :type t :identity (not *print-pretty*))
        (format stream "~S (~{~A~^ ~})"
    	    (probe-name probe)
    	    (probe-properties probe))))
    
    ;;  Resource meta class
    
    (defvar *the-resource-class*)
    
    (defclass resource-class (standard-class)
      ((direct-probes :initarg :direct-probes
    		  :initform ()
    		  :reader resource-class-direct-probes
    		  :type list)
       (probes :initarg :direct-probes
    	   :initform ()
    	   :reader resource-class-probes
    	   :type list))
      (:default-initargs :direct-superclasses (list *the-resource-class*)))
    
    (defmethod closer-mop:validate-superclass ((class resource-class)
    					   (super standard-class))
      t)
    
    (defgeneric resource-class-probe-class (resource-class))
    
    (defmethod resource-class-probe-class ((resource-class resource-class))
      'probe)
    
    (defgeneric compute-probes (resource-class))
    
    (defmethod compute-probes ((resource-class resource-class))
      (iter (for class in (closer-mop:class-precedence-list resource-class))
            (for direct-probes = (when (typep class 'resource-class)
    			       (resource-class-direct-probes class)))
    	(dolist (probe-definition direct-probes)
    	  (collect (apply #'make-instance
    			  (resource-class-probe-class resource-class)
    			  :name probe-definition)))))
    
    (defmethod closer-mop:finalize-inheritance :after ((resource-class resource-class))
      (setf (slot-value resource-class 'probes)
    	(compute-probes resource-class)))
    
    (defmacro define-resource-class (name direct-superclasses
    				 direct-slots direct-probes
    				 &optional options)
      `(defclass ,name ,(or direct-superclasses
    			'(resource))
         ,direct-slots
         (:metaclass resource-class)
         (:direct-probes ,@direct-probes)
         ,@options))
    
    ;;  Resources
    
    (defclass resource (standard-object)
      ((id :type atom
           :initarg :id
           :initform (error "Missing ID for resource.")
           :reader resource-id)
       (specified-properties :type list
    			 :initarg :specified-properties
    			 :initform nil
    			 :reader specified-properties)
       (probed-properties :type list
    		      :initarg :probed-properties
    		      :initform nil
    		      :reader probed-properties))
      (:metaclass resource-class))
    
    (setq *the-resource-class* (find-class 'resource))
    
    (defmethod print-object ((res resource) stream)
      (print-unreadable-object (res stream :type t :identity *print-readably*)
        (format stream "~S ~D ~D" (resource-id res)
    	    (/ (length (specified-properties res)) 2)
    	    (/ (length (probed-properties res)) 2))))
    
    ;;  Probes
    
    (defun os-class (os)
      (etypecase os
        (null t)
        ((eql t) t)
        (symbol (find-class os))
        (os (class-of os))
        (standard-class os)))
    
    (defgeneric find-probe (resource property os))
    
    (defmethod find-probe ((resource resource)
    		       (property symbol)
    		       os)
        (some (lambda (probe)
    	    (when (find property (probe-properties probe) :test #'eq)
    	      (let ((f (probe-generic-function probe)))
    		(when (compute-applicable-methods f (list resource os))
    		  f))))
    	  (resource-class-probes (class-of resource))))
    
    (defgeneric probe (resource property))
    
    (defmethod probe ((resource resource) (property symbol))
      (with-slots (probed-properties) resource
        (let* ((os (os))
    	   (probe (or (find-probe resource property os)
    		      (error "No probe found for ~S property ~S on ~A"
    			     resource property (class-name (class-of os)))))
    	   (result (funcall probe resource os)))
          (when (eq +undefined+ (getf result property +undefined+))
    	(error "Probe did not return expected property.~%~
                    resource: ~S~%~
                    property: ~S~%~
                    probe: ~S~%~
                    result: ~S"
    	       resource property probe result))
          (setf probed-properties
    	    (append result probed-properties))
          result)))
    
    (defgeneric get-probed (resource property))
    
    (defmethod get-probed ((resource resource) (property symbol))
      (let ((value (getf (probed-properties resource) property +undefined+)))
        (when (eq +undefined+ value)
          (setq value (getf (probe resource property) property +undefined+)))
        (when (eq +undefined+ value)
          (error "Probe did not return expected property."))
        value))
    
    (defun make-resource (type id &rest initargs &key &allow-other-keys)
      (apply #'make-instance type :id id initargs))