/[svn.andrew.net.au]/usbspindownd/usbspindownd.py
ViewVC logotype

Diff of /usbspindownd/usbspindownd.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 29 by apollock, Tue Jan 29 19:48:35 2008 UTC revision 30 by apollock, Thu Jan 31 02:21:39 2008 UTC
# Line 1  Line 1 
1  #!/usr/bin/python  #!/usr/bin/python
2    # vi:set ts=2:et:sw=2
3  #  #
4  # Daemon to monitor /proc/diskstats and spin down USB drives determined to be  # Daemon to monitor /proc/diskstats and spin down USB drives determined to be
5  # idle  # idle
# Line 21  Line 21 
21  # spinning it down every time we think it's idle  # spinning it down every time we think it's idle
22  #  #
23    
24    import diskstats
25    import syslog
26    import optparse
27    import ConfigParser
28    import sys
29    import os
30    import time
31    
32    spindown_cmd = ""
33    spindown_cmd_args = ""
34    
35    #
36    # Disk, how long to wait for, last msio value, spun down
37    #
38    
39    def search_path(filename, search_path):
40      file_found = False
41      paths = search_path.split(os.pathsep)
42      for path in paths:
43        if os.path.exists(os.path.join(path, filename)):
44          file_found = True
45          break
46      if file_found:
47        return os.path.abspath(os.path.join(path, filename))
48      else:
49        return None
50    
51    
52    def debug(options, message):
53      if options.debug:
54        print "%s: %s" % (time.asctime(time.localtime()), message)
55    
56    def load_config(options):
57      global spindown_cmd, spindown_cmd_args
58      cf = ConfigParser.SafeConfigParser()
59      try:
60        debug(options, "Attempting to read %s" % options.config)
61        cf.readfp(open(options.config))
62      except IOError, e:
63        print "Got a '%s' trying to read %s" % (e.strerror, options.config)
64        sys.exit(1)
65      if cf.has_option("DEFAULT", "wait"):
66        defaultwait = cf.get("DEFAULT", "wait")
67      else:
68        defaultwait = 600;
69      config = {}
70      # TODO: Need to deal with a malformed config file here
71      for disk in cf.defaults()['disks'].split(","):
72        if cf.has_option(disk, "wait"):
73          wait = cf.get(disk, "wait")
74        else:
75          wait = defaultwait
76        config[disk] = { 'wait': wait, 'last_msio': 0, 'spun_down': False, 'timestamp': 0 }
77      if cf.has_option("DEFAULT", "spindown_cmd"):
78        spindown_cmd = cf.get("DEFAULT", "spindown_cmd")
79        if cf.has_option("DEFAULT", "spindown_cmd_args"):
80          spindown_cmd_args = cf.get("DEFAULT", "spindown_cmd_args")
81      if not spindown_cmd:
82        # Try searching for sg_start
83        spindown_cmd = search_path("sg_start", os.getenv("PATH"))
84        if spindown_cmd:
85          spindown_cmd_args = "--stop --pc=2"
86        else:
87          # We couldn't find anything to spin down the disks
88          print "No disk spinning down command specified or found in $PATH"
89          sys.exit(1)
90      debug(options, "Configuration loaded:")
91      debug(options, "spindown_cmd: %s" % (spindown_cmd))
92      debug(options, "spindown_cmd_args: %s" % (spindown_cmd_args))
93      if options.debug:
94        for disk in config:
95          debug(options, "%s: %s" % (disk, config[disk]))
96      return config
97          
98    
99    def spin_down(options, disk):
100      if not options.noop:
101        if spindown_cmd_args:
102          return os.system("%s %s %s" % (spindown_cmd, spindown_cmd_args, disk)) >> 8
103        else:
104          return os.system("%s %s" % (spindown_cmd, disk)) >> 8
105      else:
106        debug(options, "Not really spinning down the disk")
107        return 0
108    
109    
110    def monitor_disks(config, options):
111      debug(options, "Monitoring disks")
112      while True:
113        for disk in config:
114          debug(options, "Considering %s" % (disk))
115          ds = diskstats.DiskStats(disk)
116          msio = None
117          try:
118            msio = ds.diskstat("msio")
119          except diskstats.Error, e:
120            # This disk doesn't exist at this time
121            debug(options, "%s is not present" % (disk))
122            continue
123          if config[disk]["last_msio"] == 0:
124            debug(options, "First time we've considered this disk")
125            config[disk]["last_msio"] = msio
126            config[disk]["timestamp"] = int(time.time())
127          else:
128            if msio == config[disk]["last_msio"]:
129              debug(options, "Disk has been idle since last considered")
130              now = int(time.time())
131              if (now - config[disk]["timestamp"]) >= config[disk]["wait"]:
132                debug(options, "Disk eligible for spinning down")
133                # We can spin this disk down
134                if not config[disk]["spun_down"]:
135                  if spin_down(disk):
136                    debug(options, "Disk spun down")
137                    config[disk]["spun_down"] = True
138                  else:
139                    raise Error("Failed to spin down %s" % disk)
140                else:
141                  debug(options, "Disk already spun down")
142              else:
143                # This disk is ineligible for spinning down at this time
144                debug(options, "Disk idle for %s seconds, but not for long enough (%s)" % (config[disk]["timestamp"] - now, config[disk]["wait"]))
145                config[disk]["timestamp"] = int(time.time())
146            else:
147              debug(options, "Disk not idle (old msio: %s, current msio: %s)" % (config[disk]["last_msio"], msio))
148              config[disk]["last_msio"] = msio
149              config[disk]["timestamp"] = int(time.time())
150              config[disk]["spun_down"] = False
151        debug(options, "Sleeping")
152        time.sleep(60)
153    
154  def main():  def main():
155          pass    parser = optparse.OptionParser()
156      parser.add_option("-n", "--dry-run",
157        action="store_true",
158        dest="noop",
159        default=False,
160        help="Don't do anything, just log what would be done")
161      parser.add_option("-c", "--config",
162        action="store",
163        dest="config",
164        default="/etc/usbspindownd.conf",
165        help="Configuration file for usbspindownd")
166      parser.add_option("-d", "--debug",
167        action="store_true",
168        dest="debug",
169        default=False,
170        help="Turn on extra debugging")
171      (options, args) = parser.parse_args()
172    
173      config = load_config(options)
174      monitor_disks(config, options)
175    
176  if __name__ == "__main__":  if __name__ == "__main__":
177          main()    main()

Legend:
Removed from v.29  
changed lines
  Added in v.30

  ViewVC Help
Powered by ViewVC 1.1.22