snapshot.py 8.63 KB
Newer Older
1 2 3
# -*- coding: utf-8 -*-
##############################################################################
#
4
# Copyright (c) 2010-2014 Vifib SARL and Contributors.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
# All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
##############################################################################

Bryton Lacquement's avatar
Bryton Lacquement committed
30
from __future__ import print_function
31 32
import psutil
import os
33
import subprocess
Bryton Lacquement's avatar
Bryton Lacquement committed
34
from .temperature import collectComputerTemperature, launchTemperatureTest
35

Bryton Lacquement's avatar
Bryton Lacquement committed
36 37 38
from .temperature.heating import get_contribution_ratio

import six
39 40

MEASURE_INTERVAL = 5
41 42 43 44 45 46 47 48 49 50

class _Snapshot(object):
  def get(self, property, default=None):
    return getattr(self, property, default)

class ProcessSnapshot(_Snapshot):
  """ Take a snapshot from the running process
  """
  def __init__(self, process=None):
    assert type(process) is psutil.Process
51
    ui_counter_list = process.io_counters()
52
    self.username = process.username()
53
    self.process_object = process
54 55
    self.pid = process.pid 
    # Save full command line from the process.
56
    self.process = "%s-%s" % (process.pid, process.create_time())
57
    # CPU percentage, we will have to get actual absolute value
58
    self.cpu_percent = self.process_object.cpu_percent(None)
59
    # CPU Time
60
    self.cpu_time = sum(process.cpu_times())
61
    # Thread number, might not be really relevant
62
    self.cpu_num_threads = process.num_threads()
63
    # Memory percentage
64
    self.memory_percent = process.memory_percent()
65
    # Resident Set Size, virtual memory size is not accouned for
66
    self.memory_rss = process.memory_info()[0]
67 68 69 70 71
    # Byte count, Read and write. OSX NOT SUPPORTED
    self.io_rw_counter = ui_counter_list[2] + ui_counter_list[3]
    # Read + write IO cycles
    self.io_cycles_counter  = ui_counter_list[0] + ui_counter_list[1]

72
  def update_cpu_percent(self):
73 74
    if self.process_object.is_running():
      # CPU percentage, we will have to get actual absolute value
75
      self.cpu_percent = self.process_object.cpu_percent()
76

77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
class FolderSizeSnapshot(_Snapshot):
  """Calculate partition folder size. 
  """
  def __init__(self, folder_path, pid_file=None, use_quota=False):
    # slapos computer partition size
    self.folder_path = folder_path
    self.pid_file = pid_file
    self.disk_usage = 0
    self.use_quota = use_quota

  def update_folder_size(self):
    """Return 0 if the process du is still running
    """
    if self.pid_file and os.path.exists(self.pid_file):
      with open(self.pid_file, 'r') as fpid:
        pid_str = fpid.read()
        if pid_str:
          pid = int(pid_str)
          try:
            os.kill(pid, 0)
          except OSError:
            pass
          else:
            return

    self.disk_usage = self._getSize(self.folder_path)
    # If extra disk added to partition
    data_dir = os.path.join(self.folder_path, 'DATA')
    if os.path.exists(data_dir):
      for filename in os.listdir(data_dir):
        extra_path = os.path.join(data_dir, filename)
        if os.path.islink(extra_path) and os.path.isdir('%s/' % extra_path):
          self.disk_usage += self._getSize('%s/' % extra_path)

  def _getSize(self, file_path):
    size = 0
113
    command = 'du -s %s' % file_path
114 115 116 117 118 119 120 121 122 123
    process = subprocess.Popen(command, stdout=subprocess.PIPE, 
                              stderr=subprocess.PIPE, shell=True)
    if self.pid_file:
      with open(self.pid_file, 'w') as fpid:
        pid = fpid.write(str(process.pid))
    result = process.communicate()[0]
    if process.returncode == 0:
      size, _  = result.strip().split()
    return float(size)

124 125 126
class SystemSnapshot(_Snapshot):
  """ Take a snapshot from current system usage
  """
127 128 129 130 131
  def __init__(self, interval=MEASURE_INTERVAL):

    cpu_idle_percentage = psutil.cpu_times_percent(interval=interval).idle
    load_percent = 100 - cpu_idle_percentage

132
    memory = psutil.virtual_memory()
133
    net_io = psutil.net_io_counters()
134 135 136

    self.memory_free = available = memory.available
    self.memory_used = memory.total - available
137
    self.memory_percent = memory.percent
138 139
    #self.cpu_percent = psutil.cpu_percent()
    self.cpu_percent = load_percent
140 141 142 143 144 145 146 147
    self.load = os.getloadavg()[0]
    self.net_in_bytes = net_io.bytes_recv
    self.net_in_errors = net_io.errin
    self.net_in_dropped = net_io.dropin
    self.net_out_bytes = net_io.bytes_sent
    self.net_out_errors = net_io.errout
    self.net_out_dropped = net_io.dropout

148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
class TemperatureSnapshot(_Snapshot):
  """ Take a snapshot from the current temperature on 
      all available sensors
  """
  def __init__(self, sensor_id, temperature, alarm):
    self.sensor_id = sensor_id
    self.temperature = temperature
    self.alarm = alarm

class HeatingContributionSnapshot(_Snapshot):

  def __init__(self, sensor_id, model_id):
    self.initial_temperature = None
    
    result = launchTemperatureTest(sensor_id)
    if result is None:
Bryton Lacquement's avatar
Bryton Lacquement committed
164
      print("Impossible to test sensor: %s " % sensor_id)
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
      

    initial_temperature, final_temperature, duration = result 
    
    self.initial_temperature = initial_temperature
    self.final_temperature = final_temperature
    self.delta_time = duration
    self.model_id = model_id
    self.sensor_id = sensor_id
    self.zero_emission_ratio = self._get_contribution_ratio()

  def _get_contribution_ratio(self):
    delta_temperature = (self.final_temperature-self.initial_temperature)
    contribution_value = delta_temperature/self.delta_time
    return get_contribution_ratio(self.model_id, contribution_value)

  def _get_uptime(self):
    # Linux only
    if os.path.exists('/proc/uptime'):
      with open('/proc/uptime', 'r') as f:
        return float(f.readline().split()[0])

    return -1


190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
class DiskPartitionSnapshot(_Snapshot):
  """ Take Snapshot from general disk partitions 
      usage 
  """
  def __init__(self, partition, mountpoint):
    self.partition = partition
    self.mountpoint_list = [ mountpoint ]
    disk = psutil.disk_usage(mountpoint)
    disk_io = psutil.disk_io_counters()

    self.disk_size_used = disk.used
    self.disk_size_free = disk.free
    self.disk_size_percent = disk.percent

class ComputerSnapshot(_Snapshot):
  """ Take a snapshot from computer informations
  """
207
  def __init__(self, model_id=None, sensor_id=None, test_heating=False):
208
    self.cpu_num_core = psutil.cpu_count()
209 210
    self.cpu_frequency = 0
    self.cpu_type = 0
211
    self.memory_size = psutil.virtual_memory().total
212 213 214 215 216 217 218
    self.memory_type = 0

    #
    # Include a SystemSnapshot and a list DiskPartitionSnapshot
    # on a Computer Snapshot
    #
    self.system_snapshot = SystemSnapshot()
219
    self.temperature_snapshot_list = self._get_temperature_snapshot_list()
Bryton Lacquement's avatar
Bryton Lacquement committed
220
    self._get_physical_disk_info()
221

222 223 224 225 226 227 228 229 230 231 232 233
    if test_heating and model_id is not None \
                    and sensor_id is not None:
      self.heating_contribution_snapshot = HeatingContributionSnapshot(sensor_id, model_id)

  def _get_temperature_snapshot_list(self):
    temperature_snapshot_list = []
    for sensor_entry in collectComputerTemperature():
      sensor_id, temperature, maximal, critical, alarm = sensor_entry
      temperature_snapshot_list.append(
          TemperatureSnapshot(sensor_id, temperature, alarm))
    return temperature_snapshot_list

234
  def _get_physical_disk_info(self):
Bryton Lacquement's avatar
Bryton Lacquement committed
235 236 237 238
    # XXX: merge the following 2 to avoid calling disk_usage() twice
    self.disk_snapshot_list = []
    self.partition_list = []
    partition_set = set()
239 240

    for partition in psutil.disk_partitions():
Bryton Lacquement's avatar
Bryton Lacquement committed
241 242 243
      dev = partition.device
      if dev not in partition_set: # XXX: useful ?
        partition_set.add(dev)
244
        usage = psutil.disk_usage(partition.mountpoint)
Bryton Lacquement's avatar
Bryton Lacquement committed
245
        self.partition_list.append((dev, usage.total))
246
        self.disk_snapshot_list.append(
Bryton Lacquement's avatar
Bryton Lacquement committed
247
          DiskPartitionSnapshot(dev, partition.mountpoint))