runpromise.py 6.59 KB
Newer Older
1 2 3 4 5 6 7 8 9
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import os
import subprocess
import json
import psutil
import time
10
from datetime import datetime
11 12 13 14
from shutil import copyfile
import glob
import argparse
import traceback
15
import logging
16
from six.moves import configparser
17 18 19
from slapos.grid.promise import PromiseLauncher, PromiseQueueResult, PromiseError
from slapos.grid.promise.generic import PROMISE_LOG_FOLDER_NAME
from slapos.util import mkdir_p
20

21 22
# Promise timeout after 20 seconds by default
promise_timeout = 20
23

24
def getArgumentParser():
25 26 27 28
  """
  Parse arguments for monitor collector instance.
  """
  parser = argparse.ArgumentParser()
29 30
  parser.add_argument('-c', '--config', dest='config_file',
                      help='The Path of configuration file to load.')
31
  parser.add_argument('-p', '--partition-folder',
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
                      help='Base path of the partition.')
  parser.add_argument('-L', '--log-folder',
                      help='Folder where promises will write logs.')
  parser.add_argument('--console',
                      help='Log to console too',
                      default=False, action='store_true')
  parser.add_argument('-a', '--check-anomaly',
                      help='Enable check of anomaly on promises.',
                      default=False, action='store_true')
  parser.add_argument('-D', '--debug',
                      help='Configure loggin in debug mode.',
                      default=False, action='store_true')
  parser.add_argument('--master-url',
                      help='SlapOS Master Service URL.')
  parser.add_argument('--partition-cert',
                      help='Computer partition certificate file path.')
  parser.add_argument('--partition-key',
                      help='Computer partition key file path.')
  parser.add_argument('--partition-id',
                      help='Computer partition ID.')
  parser.add_argument('--computer-id',
                      help='Computer ID.')
  parser.add_argument('--pid-path',
55
                      help='Path where the pid of this process will be writen.')
56 57 58 59 60 61 62 63 64 65 66
  parser.add_argument('--run-only',
                      help='List of python promises to run separates by space.',
                      default="")
  parser.add_argument('-f', '--force',
                      help='Force to run promise without consider the periodicity.',
                      default=False, action='store_true')
  parser.add_argument('--dry-run',
                      help='Only run promise without save result.',
                      default=False, action='store_true')
  parser.add_argument('--promise-timeout', default=20, type=int,
                      help='Maximum promise execution time.')
67 68 69

  return parser

70

71
class MonitorPromiseLauncher(object):
72 73 74

  def __init__(self, config_parser):
    self.config = config_parser
75 76 77 78
    if self.config.config_file:
      self._loadConfigFromFile(self.config.config_file)
    self.logger = logging.getLogger("Monitor")
    self.logger.setLevel(logging.DEBUG if self.config.debug else logging.INFO)
79

80
    if not self.config.log_folder or self.config.console:
81 82
      if len(self.logger.handlers) == 0 or \
          not isinstance(self.logger.handlers[0], logging.StreamHandler):
83 84 85 86 87 88 89
        handler = logging.StreamHandler()
        handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
        self.logger.addHandler(handler)
    if self.config.log_folder:
      log_file = os.path.join(self.config.log_folder,
        '%s.log' % self.config.title.replace(' ', '_'))
      file_handler = logging.FileHandler(log_file)
90 91 92
      file_handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
      self.logger.addHandler(file_handler)

93

94
  def _loadConfigFromFile(self, config_file):
95
    config = configparser.ConfigParser()
96 97 98 99 100 101 102 103 104 105 106 107 108
    config.read([config_file])
    known_key_list = ['partition-cert', 'partition-key', 'partition-id',
                      'pid-path', 'computer-id', 'check-anomaly',
                      'master-url', 'partition-folder', 'promise-timeout']
 
    if config.has_section('promises'):
      for key, value in config.items('promises'):
        key_add = key.replace('-', '_')
        if key in known_key_list and \
            getattr(self.config, key_add, None) is None:
          setattr(self.config, key_add, value)

  def start(self):
109 110 111
    """
      run all promises in sequential ways
    """
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
    if self.config.pid_path:
      if os.path.exists(self.config.pid_path):
        # Check if another run promise is running
        with open(self.config.pid_path) as fpid:
          try:
            pid = int(fpid.read(6))
          except ValueError:
            pid = None
          if pid and os.path.exists("/proc/" + str(pid)):
            self.logger.warning("A process is already running with pid " + str(pid))
            return []

      with open(self.config.pid_path, 'w') as fpid:
        fpid.write(str(os.getpid()))

127 128 129
    if not self.config.partition_folder:
      raise ValueError("Partition folder is not specified")

130 131 132 133 134 135 136 137 138 139 140 141 142 143
    parameter_dict = {
      'promise-timeout': self.config.promise_timeout or promise_timeout,
      'promise-folder': os.path.join(self.config.partition_folder, 'etc', 'plugin'),
      'legacy-promise-folder': os.path.join(self.config.partition_folder, 'etc', 'promise'),
      'partition-folder': self.config.partition_folder,
      'master-url': self.config.master_url,
      'partition-cert': self.config.partition_cert,
      'partition-key': self.config.partition_key,
      'partition-id': self.config.partition_id,
      'computer-id': self.config.computer_id,
      'debug': self.config.debug,
      'check-anomaly': self.config.check_anomaly,
      'force': self.config.force,
      'run-only-promise-list': [x for x in self.config.run_only.split(' ') if x]
144
    }
145 146
    if self.config.log_folder:
      parameter_dict['log-folder'] = self.config.log_folder
147
    else:
148 149 150 151 152 153 154 155
      parameter_dict['log-folder'] = os.path.join(self.config.partition_folder,
                                                  PROMISE_LOG_FOLDER_NAME)
      mkdir_p(parameter_dict['log-folder'])

    promise_launcher = PromiseLauncher(
      config=parameter_dict,
      logger=self.logger,
      dry_run=self.config.dry_run
156 157
    )

158
    self.logger.info("Checking promises...")
159
    exit_code = 0
160 161
    try:
      promise_launcher.run()
162
    except PromiseError as e:
163
      # error was already logged
164
      exit_code = 1
165 166
    if self.config.pid_path:
      os.remove(self.config.pid_path)
167
    self.logger.info("Finished promises.")
168
    return exit_code
169 170

def main():
171 172 173
  arg_parser = getArgumentParser()
  promise_runner = MonitorPromiseLauncher(arg_parser.parse_args())
  sys.exit(promise_runner.start())