[Erp5-report] r36109 luke - /erp5/trunk/utils/erp5.timmy/src/erp5/timmy/timmy.py

nobody at svn.erp5.org nobody at svn.erp5.org
Tue Jun 8 18:26:25 CEST 2010


Author: luke
Date: Tue Jun  8 18:26:25 2010
New Revision: 36109

URL: http://svn.erp5.org?rev=36109&view=rev
Log:
 - start cleanup of timmy

Modified:
    erp5/trunk/utils/erp5.timmy/src/erp5/timmy/timmy.py

Modified: erp5/trunk/utils/erp5.timmy/src/erp5/timmy/timmy.py
URL: http://svn.erp5.org/erp5/trunk/utils/erp5.timmy/src/erp5/timmy/timmy.py?rev=36109&r1=36108&r2=36109&view=diff
==============================================================================
--- erp5/trunk/utils/erp5.timmy/src/erp5/timmy/timmy.py [utf8] (original)
+++ erp5/trunk/utils/erp5.timmy/src/erp5/timmy/timmy.py [utf8] Tue Jun  8 18:26:25 2010
@@ -36,187 +36,6 @@
   new_uid = generateServerKey()
   file(key_file, 'w').write(new_uid)
   return new_uid
-
-def parseOptions():
-  parser = OptionParser()
-  parser.add_option("-k", "--key-file",
-    help="File with server key")
-
-  parser.add_option("-s", "--server-url",
-    help="URL of provisioning server")
-
-  parser.add_option("-b", "--base-profile",
-    help="Base profile")
-
-  parser.add_option("-t", "--template-directory",
-    help="Directory with templates")
-
-  parser.add_option("-o", "--main-output",
-    help="Main profile output, which will be run by buildout")
-
-  parser.add_option("-d", "--instances-directory",
-    help="Instances profiles output, must be existing directory")
-
-  parser.add_option("-r", "--buildout-binary",
-    help="Buildout binary to run")
-
-  parser.add_option("-p", "--pid-file",
-    help="Lock file with the pid file.")
-
-  parser.add_option("-l", "--log-file",
-    help="Log file.")
-
-  parser.set_defaults(buildout_offline=False)
-  parser.add_option("-N", "--buildout-offline", action="store_true",
-    help="Run buildout in offline mode.")
-
-  (options, args) = parser.parse_args()
-  required_option_list = [
-      'base_profile',
-      'buildout_binary',
-      'instances_directory',
-      'key_file',
-      'main_output',
-      'server_url',
-      'pid_file'
-    ]
-  missing_required_option_list = [o for o in required_option_list \
-      if not getattr(options, o, None)]
-  if len(missing_required_option_list):
-    parser.error('Required options are missing: %s' % ', '.join(
-      missing_required_option_list))
-
-  for filepath in options.base_profile,:
-    if not os.path.exists(filepath):
-      raise ValueError('Cannot find "%s"' % os.path.abspath(filepath))
-  if not os.path.exists(options.instances_directory):
-    os.mkdir(options.instances_directory)
-  elif not os.path.isdir(options.instances_directory):
-    raise ValueError('File %s is not a directory' %
-        os.path.abspath(options.instances_directory))
-  return options, args
-
-def setRunning(value, pid_file):
-  if value:
-    if os.path.exists(pid_file):
-      # Pid file is present
-      logging.warning('Timmy already have the pid file %s' % pid_file)
-      pid = open(pid_file, 'r').readline()
-      # XXX This could use psutil library.
-      if os.path.exists("/proc/%s" % pid):
-        # In case process is present, ignore.
-        logging.critical('A Timmy process is running with pid %s' % pid)
-        sys.exit(1)
-    # Start new process
-    write_pid(pid_file)
-  else:
-    os.remove(pid_file)
-
-def write_pid(pid_file):
-  pid = os.getpid()
-  try:
-    f = open(pid_file, 'w')
-    f.write('%s' % pid)
-    f.close()
-  except (IOError, OSError):
-    logging.critical('Timmy could not write pidfile %s' % pid_file)
-    raise
-
-def findTemplate(template_directory_list, template_name):
-  for template_directory in template_directory_list:
-    path = os.path.join(template_directory, template_name)
-    if os.path.isfile(path):
-      logging.debug('Found template %r' % path)
-      return path
-  return None
-
-def updateBaseProfile(template_directory_list, file_output, base_profile,
-    instances_directory, instance_dict_list):
-  # TODO:
-  #  * cleanup in case of problem
-  #  * use safe update of output file
-  template_data = ''.join(file(findTemplate(template_directory_list,
-    'main-template.cfg')).readlines())
-  template = PercentTemplate(template_data)
-  replacement_dict = {
-    'BASE_PROFILE': base_profile,
-    'INSTANCE_PROFILE_LIST': [],
-    'INSTANCE_PART_LIST': []
-  }
-  for instance in [q for q in instance_dict_list if q['TYPE'] != 'Zope Instance']:
-    profile_path = os.path.join(instances_directory,
-        '%s.cfg' % instance['ID'])
-    if not os.path.exists(profile_path):
-      logging.warning('Profile %r not generated, ignoring' % profile_path)
-      continue
-    replacement_dict['INSTANCE_PROFILE_LIST'].append('%s/%s.cfg' % (
-      instances_directory, instance['ID']))
-    replacement_dict['INSTANCE_PART_LIST'].append(instance['ID'])
-
-  for instance in [q for q in instance_dict_list if q['TYPE'] == 'Zope Instance']:
-    profile_path = os.path.join(instances_directory,
-        '%s.cfg' % instance['ID'])
-    if not os.path.exists(profile_path):
-      logging.warning('Profile %r not generated' % profile_path)
-      continue
-    replacement_dict['INSTANCE_PROFILE_LIST'].append('%s/%s.cfg' % (
-      instances_directory, instance['ID']))
-    replacement_dict['INSTANCE_PART_LIST'].append(instance['ID'])
-
-  replacement_dict['INSTANCE_PROFILE_LIST'] = '\n  '.join(
-      replacement_dict['INSTANCE_PROFILE_LIST'])
-  replacement_dict['INSTANCE_PART_LIST'] = '\n  '.join(
-      replacement_dict['INSTANCE_PART_LIST'])
-  out = file(file_output, 'w')
-  out.write(template.substitute(replacement_dict))
-  out.close()
-
-def updateInstanceProfiles(template_directory_list, output_directory,
-    instance_dict_list):
-  for instance in instance_dict_list:
-    template_name = '%s-template.cfg' % instance['TYPE'].lower()\
-      .replace(' ','-')
-    template_path = findTemplate(template_directory_list, template_name)
-    if template_path is None:
-      logging.warning('Template %r for %s not found, ignoring' % (template_name,
-        instance['TYPE']))
-      continue
-    template_data = ''.join(file(template_path).readlines())
-
-    template = PercentTemplate(template_data)
-    if 'BT5_LIST' in instance:
-      instance['BT5_LIST'] = '\n  '.join(instance['BT5_LIST'])
-    if 'IP_ADDRESS_LIST' in instance:
-      if instance['IP_ADDRESS_LIST']:
-        instance['IP_ADDRESS'] = instance['IP_ADDRESS_LIST'][0]
-    out = file(os.path.join(output_directory, '%s.cfg' % instance['ID']), 'w')
-    out.write(template.substitute(instance))
-    out.close()
-
-def runBuildout(buildout, profile, offline):
-  invoke_list = [buildout]
-  if offline:
-    invoke_list.append('-N')
-  invoke_list.extend(['-c', profile, '-t', '5'])
-  logging.info('invoking %s' % ' '.join(invoke_list))
-  popen = subprocess.Popen([buildout, '-c', profile],
-      stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-  (result_std, result_err) = popen.communicate()
-  # TODO: check that site is accesible in Data.fs, we cannot belive in
-  #       Popen.returncode, as subprocesses of subprocesses are not handled
-  #       properly
-
-  # TODO: parse result_std and result_err
-
-  if popen.returncode == 0:
-    log_method = logging.info
-  else:
-    log_method = logging.error
-    log_method('Buildout finished with bad status code (%s)' % \
-        popen.returncode)
-  log_method('Standard output:\n%s' % result_std)
-  log_method('Error output:\n%s' % result_err)
-  return popen.returncode, result_std, result_err
 
 class Partition(object):
   # generic
@@ -580,199 +399,387 @@
   print 'Server key located at: %s' % os.path.abspath(options.key_file)
   print 'Key: %s' % key
 
-def getTemplateDirectoryList(user_template_directory):
-  """Returns list of directories, in which there might be templates"""
-  template_directory_list = []
-  if pkg_resources.resource_isdir(__name__, 'template'):
-    template_directory = pkg_resources.resource_filename(__name__, "template")
-    template_directory_list.append(template_directory)
-    logging.info('Using %r as fallback directory with templates' %
-        template_directory)
-
-  if user_template_directory is not None:
-    if os.path.isdir(user_template_directory):
-      template_directory = os.path.abspath(user_template_directory)
+class Timmy(object):
+  def __init__(self):
+    socket.setdefaulttimeout(30.0)
+    self.options = self.parseOptions()[0]
+    logging_kw = dict(level=logging.INFO,
+        format='%(asctime)s:%(levelname)s:%(name)s:%(message)s')
+    if self.options.log_file is not None:
+      logging_kw['filename'] = options.log_file
+    logging.basicConfig(**logging_kw)
+    self.time_begin = time.time()
+    logging.info('[%s] Timmy started' % os.getpid())
+    self.options.template_directory_list = self.getTemplateDirectoryList(self.options.template_directory)
+
+  def getTemplateDirectoryList(self, user_template_directory):
+    """Returns list of directories, in which there might be templates"""
+    template_directory_list = []
+    if pkg_resources.resource_isdir(__name__, 'template'):
+      template_directory = pkg_resources.resource_filename(__name__, "template")
       template_directory_list.append(template_directory)
-      logging.info('Using %r as default directory with templates' %
+      logging.info('Using %r as fallback directory with templates' %
           template_directory)
-    else:
-      logging.warning('User template %r is not a directory' %
-          template_directory)
-  if len(template_directory_list) == 0:
-    logging.warning('No templates were found, some functionality will be not available')
-  template_directory_list.reverse()
-  return template_directory_list
-
-def run():
-  socket.setdefaulttimeout(30.0)
-  (options, args) = parseOptions()
-  logging_kw = dict(level=logging.INFO,
-      format='%(asctime)s:%(levelname)s:%(name)s:%(message)s')
-  if options.log_file is not None:
-    logging_kw['filename'] = options.log_file
-  logging.basicConfig(**logging_kw)
-  time_begin = time.time()
-  logging.info('[%s] Timmy started' % os.getpid())
-  options.template_directory_list = getTemplateDirectoryList(options.template_directory)
-  try:
-    setRunning(True, options.pid_file)
-  except:
-    # XXX-Luke: If possible it would be nice to send such issue to
-    #           master server
-    logging.error('Issue during setting pid file %s %s' % sys.exc_info()[:2])
-    raise
-  try:
+
+    if user_template_directory is not None:
+      if os.path.isdir(user_template_directory):
+        template_directory = os.path.abspath(user_template_directory)
+        template_directory_list.append(template_directory)
+        logging.info('Using %r as default directory with templates' %
+            template_directory)
+      else:
+        logging.warning('User template %r is not a directory' %
+            template_directory)
+    if len(template_directory_list) == 0:
+      logging.warning('No templates were found, some functionality will be not available')
+    template_directory_list.reverse()
+    return template_directory_list
+
+  def run(self):
+    try:
+      self.setRunning(True, self.options.pid_file)
+    except:
+      # XXX-Luke: If possible it would be nice to send such issue to
+      #           master server
+      logging.error('Issue during setting pid file %s %s' % sys.exc_info()[:2])
+      raise
     try:
       try:
-        server = XMLRPCServer(options.server_url,
-            getServerKey(options.key_file))
-        partition_dict_list = server.call('getComputerConfigurationDictList')
-      except socket.error, e:
-        logging.error('Unable to connect to remote server, verify if the URL is'
-            ' accessible.')
-        sys.exit(1)
-      except xmlrpclib.Fault, e:
-        logging.error('Error found on server side, unable to continue, failed:'
-            ' %s.' % e.faultString)
-        sys.exit(1)
-
-      computer_id = getServerKey(options.key_file)
-      server.call('updatePartitionState', computer_id, 'reportStarted',
-          'timmy is running on computer')
-      supervisor_id = computer_id
-      supervisor_list = [q for q in partition_dict_list if q['TYPE'] == \
-          'Supervisor Server']
-      if len(supervisor_list) > 0:
-        supervisor_id = supervisor_list[0]['PARTITION_ID']
-
-      # prepare - run supervisor
-      try:
-        supervisord_popen = subprocess.Popen([SUPERVISORD],
+        try:
+          server = XMLRPCServer(self.options.server_url,
+              getServerKey(self.options.key_file))
+          partition_dict_list = server.call('getComputerConfigurationDictList')
+        except socket.error, e:
+          logging.error('Unable to connect to remote server, verify if the URL is'
+              ' accessible.')
+          sys.exit(1)
+        except xmlrpclib.Fault, e:
+          logging.error('Error found on server side, unable to continue, failed:'
+              ' %s.' % e.faultString)
+          sys.exit(1)
+
+        computer_id = getServerKey(self.options.key_file)
+        server.call('updatePartitionState', computer_id, 'reportStarted',
+            'timmy is running on computer')
+        supervisor_id = computer_id
+        supervisor_list = [q for q in partition_dict_list if q['TYPE'] == \
+            'Supervisor Server']
+        if len(supervisor_list) > 0:
+          supervisor_id = supervisor_list[0]['PARTITION_ID']
+
+        # prepare - run supervisor
+        try:
+          supervisord_popen = subprocess.Popen([SUPERVISORD],
+              stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+          (result_std, result_err) = supervisord_popen.communicate()
+        except:
+          server.call('updatePartitionState', supervisor_id, 'reportError',
+              'Unsupported issue while trying to start supervisord: %s:%s' % (
+                str(sys.exc_info()[0]), str(sys.exc_info()[1])))
+          raise
+
+        if supervisord_popen.returncode == 0:
+          log_message = 'Supervisord started with: stdout = %r stderr = %r' % (
+              result_std, result_err)
+          server.call('updatePartitionState', supervisor_id, 'reportStarted',
+              log_message)
+          logging.info(log_message)
+        else:
+          if "Another program is already listening" in result_err:
+            logging.info('Supervisord already running: stdout = %r stderr = '
+                '%r' % (result_std, result_err))
+          else:
+            log_message = 'Supervisord unknown problem: stdout = %r stderr = %r' \
+                % (result_std, result_err)
+            server.call('updatePartitionState', supervisor_id, 'reportError',
+                log_message)
+            logging.info(log_message)
+
+        # 1a pass - instance profiles
+        try:
+          self.updateInstanceProfiles(self.options.template_directory_list,
+            self.options.instances_directory, partition_dict_list)
+        except:
+          server.call('updatePartitionState', computer_id, 'reportError',
+              'Unexpected issue while updating instance profiles: %s:%s' % (
+                str(sys.exc_info()[0]), str(sys.exc_info()[1])))
+          raise
+        # 1b pass - main profile
+        try:
+          self.updateBaseProfile(self.options.template_directory_list, self.options.main_output,
+            self.options.base_profile, self.options.instances_directory,
+            partition_dict_list)
+        except:
+          server.call('updatePartitionState', computer_id, 'reportError',
+              'Unexpected issue while updating base profile: %s:%s' % (
+                str(sys.exc_info()[0]), str(sys.exc_info()[1])))
+          raise
+        # 2 pass - run buildout
+        try:
+          return_code, result_std, result_err = self.runBuildout(
+              self.options.buildout_binary, self.options.main_output,
+              self.options.buildout_offline)
+          if return_code != 0:
+            server.call('updatePartitionState', computer_id, 'reportError',
+                'Buildout finished with bad status code (%s). Stdout = "%s", stde'
+                'rr = "%s"' % (return_code, result_std, result_err))
+        except:
+          server.call('updatePartitionState', computer_id, 'reportError',
+              'Unexpected issue while running buildout: %s:%s' % (
+                str(sys.exc_info()[0]), str(sys.exc_info()[1])))
+          raise
+
+        # force supervisor to reload its configuration
+        supervisorctl_popen = subprocess.Popen([SUPERVISORCTL, 'update'],
             stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-        (result_std, result_err) = supervisord_popen.communicate()
-      except:
-        server.call('updatePartitionState', supervisor_id, 'reportError',
-            'Unsupported issue while trying to start supervisord: %s:%s' % (
-              str(sys.exc_info()[0]), str(sys.exc_info()[1])))
-        raise
-
-      if supervisord_popen.returncode == 0:
-        log_message = 'Supervisord started with: stdout = %r stderr = %r' % (
-            result_std, result_err)
-        server.call('updatePartitionState', supervisor_id, 'reportStarted',
-            log_message)
-        logging.info(log_message)
-      else:
-        if "Another program is already listening" in result_err:
-          logging.info('Supervisord already running: stdout = %r stderr = '
-              '%r' % (result_std, result_err))
+        (result_std, result_err) = supervisorctl_popen.communicate()
+        if supervisorctl_popen.returncode == 0:
+          log_message = 'Supervisorctl updated with: stdout = %r stderr = %r' % (
+              result_std, result_err)
+          server.call('updatePartitionState', supervisor_id, 'reportStarted',
+              log_message)
+          logging.info(log_message)
         else:
-          log_message = 'Supervisord unknown problem: stdout = %r stderr = %r' \
-              % (result_std, result_err)
+          log_message = 'Supervisorctl issue during update: stdout = %r stderr ='\
+              ' %r' % (result_std, result_err)
           server.call('updatePartitionState', supervisor_id, 'reportError',
               log_message)
-          logging.info(log_message)
-
-      # 1a pass - instance profiles
-      try:
-        updateInstanceProfiles(options.template_directory_list,
-          options.instances_directory, partition_dict_list)
-      except:
-        server.call('updatePartitionState', computer_id, 'reportError',
-            'Unexpected issue while updating instance profiles: %s:%s' % (
-              str(sys.exc_info()[0]), str(sys.exc_info()[1])))
+          logging.error(log_message)
+
+        # do start all of supervisor controlled programs
+        # it is noop in case if all is running
+        # it forces to start problematic services (FATAL state)
+        supervisorctl_popen = subprocess.Popen([SUPERVISORCTL, 'start', 'all'],
+            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        (result_std, result_err) = supervisorctl_popen.communicate()
+        if supervisorctl_popen.returncode != 0:
+          log_message = 'Supervisorctl issue during start all: stdout = %r std'\
+              'err = %r' % (result_std, result_err)
+          server.call('updatePartitionState', supervisor_id, 'reportError',
+              log_message)
+          logging.error(log_message)
+        elif len(result_std) > 0 or len(result_err) > 0:
+          # emit log only in case if something important was done by supervisor
+          # no need to pollute master server, as this is only local server hack
+          logging.info('Supervisorctl start all with: stdout = %r stderr = %r' % (
+              result_std, result_err))
+
+
+        # 3 pass - manage instances
+        # XXX-Luke: To be moved to proper recipe
+        updated_partition_list = []
+        helper = Helper()
+        for partition_dict in partition_dict_list:
+          # install
+          # stop
+          # switch to special management
+          # start
+          partition = Partition(partition_dict)
+          switcher_dict = dict(
+              start = 'manageStart',
+              stop = 'manageStop',
+              nothing = 'manageNothing',
+              install = 'manageInstall',
+          )
+          action = getattr(helper, switcher_dict[partition_dict['ACTION']])
+          action(partition, server)
+      except xmlrpclib.Fault, e:
+        logging.error('Unexpected error in xml-rpc communication, unable to co'
+            'ntinue, failed: %s.' % e.faultString)
         raise
-      # 1b pass - main profile
-      try:
-        updateBaseProfile(options.template_directory_list, options.main_output,
-          options.base_profile, options.instances_directory,
-          partition_dict_list)
-      except:
-        server.call('updatePartitionState', computer_id, 'reportError',
-            'Unexpected issue while updating base profile: %s:%s' % (
-              str(sys.exc_info()[0]), str(sys.exc_info()[1])))
+      except socket.sslerror, e:
+        logging.error('Unexpected error in socket communication, unable to co'
+            'ntinue, failed: %s.' % e)
         raise
-      # 2 pass - run buildout
-      try:
-        return_code, result_std, result_err = runBuildout(
-            options.buildout_binary, options.main_output,
-            options.buildout_offline)
-        if return_code != 0:
-          server.call('updatePartitionState', computer_id, 'reportError',
-              'Buildout finished with bad status code (%s). Stdout = "%s", stde'
-              'rr = "%s"' % (return_code, result_std, result_err))
-      except:
-        server.call('updatePartitionState', computer_id, 'reportError',
-            'Unexpected issue while running buildout: %s:%s' % (
-              str(sys.exc_info()[0]), str(sys.exc_info()[1])))
-        raise
-
-      # force supervisor to reload its configuration
-      supervisorctl_popen = subprocess.Popen([SUPERVISORCTL, 'update'],
-          stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-      (result_std, result_err) = supervisorctl_popen.communicate()
-      if supervisorctl_popen.returncode == 0:
-        log_message = 'Supervisorctl updated with: stdout = %r stderr = %r' % (
-            result_std, result_err)
-        server.call('updatePartitionState', supervisor_id, 'reportStarted',
-            log_message)
-        logging.info(log_message)
-      else:
-        log_message = 'Supervisorctl issue during update: stdout = %r stderr ='\
-            ' %r' % (result_std, result_err)
-        server.call('updatePartitionState', supervisor_id, 'reportError',
-            log_message)
-        logging.error(log_message)
-
-      # do start all of supervisor controlled programs
-      # it is noop in case if all is running
-      # it forces to start problematic services (FATAL state)
-      supervisorctl_popen = subprocess.Popen([SUPERVISORCTL, 'start', 'all'],
-          stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-      (result_std, result_err) = supervisorctl_popen.communicate()
-      if supervisorctl_popen.returncode != 0:
-        log_message = 'Supervisorctl issue during start all: stdout = %r std'\
-            'err = %r' % (result_std, result_err)
-        server.call('updatePartitionState', supervisor_id, 'reportError',
-            log_message)
-        logging.error(log_message)
-      elif len(result_std) > 0 or len(result_err) > 0:
-        # emit log only in case if something important was done by supervisor
-        # no need to pollute master server, as this is only local server hack
-        logging.info('Supervisorctl start all with: stdout = %r stderr = %r' % (
-            result_std, result_err))
-
-
-      # 3 pass - manage instances
-      # XXX-Luke: To be moved to proper recipe
-      updated_partition_list = []
-      helper = Helper()
-      for partition_dict in partition_dict_list:
-        # install
-        # stop
-        # switch to special management
-        # start
-        partition = Partition(partition_dict)
-        switcher_dict = dict(
-            start = 'manageStart',
-            stop = 'manageStop',
-            nothing = 'manageNothing',
-            install = 'manageInstall',
-        )
-        action = getattr(helper, switcher_dict[partition_dict['ACTION']])
-        action(partition, server)
-    except xmlrpclib.Fault, e:
-      logging.error('Unexpected error in xml-rpc communication, unable to co'
-          'ntinue, failed: %s.' % e.faultString)
+    finally:
+      self.setRunning(False, self.options.pid_file)
+      logging.info('[%s] Timmy finished, invocation time: %.3fs' % (os.getpid(),
+        time.time() - self.time_begin))
+
+  def runBuildout(self, buildout, profile, offline):
+    invoke_list = [buildout]
+    if offline:
+      invoke_list.append('-N')
+    invoke_list.extend(['-c', profile, '-t', '5'])
+    logging.info('invoking %s' % ' '.join(invoke_list))
+    popen = subprocess.Popen([buildout, '-c', profile],
+        stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    (result_std, result_err) = popen.communicate()
+    # TODO: check that site is accesible in Data.fs, we cannot belive in
+    #       Popen.returncode, as subprocesses of subprocesses are not handled
+    #       properly
+
+    # TODO: parse result_std and result_err
+
+    if popen.returncode == 0:
+      log_method = logging.info
+    else:
+      log_method = logging.error
+      log_method('Buildout finished with bad status code (%s)' % \
+          popen.returncode)
+    log_method('Standard output:\n%s' % result_std)
+    log_method('Error output:\n%s' % result_err)
+    return popen.returncode, result_std, result_err
+
+  def updateInstanceProfiles(self, template_directory_list, output_directory,
+      instance_dict_list):
+    for instance in instance_dict_list:
+      template_name = '%s-template.cfg' % instance['TYPE'].lower()\
+        .replace(' ','-')
+      template_path = self.findTemplate(template_directory_list, template_name)
+      if template_path is None:
+        logging.warning('Template %r for %s not found, ignoring' % (template_name,
+          instance['TYPE']))
+        continue
+      template_data = ''.join(file(template_path).readlines())
+
+      template = PercentTemplate(template_data)
+      if 'BT5_LIST' in instance:
+        instance['BT5_LIST'] = '\n  '.join(instance['BT5_LIST'])
+      if 'IP_ADDRESS_LIST' in instance:
+        if instance['IP_ADDRESS_LIST']:
+          instance['IP_ADDRESS'] = instance['IP_ADDRESS_LIST'][0]
+      out = file(os.path.join(output_directory, '%s.cfg' % instance['ID']), 'w')
+      out.write(template.substitute(instance))
+      out.close()
+
+  def findTemplate(self, template_directory_list, template_name):
+    for template_directory in template_directory_list:
+      path = os.path.join(template_directory, template_name)
+      if os.path.isfile(path):
+        logging.debug('Found template %r' % path)
+        return path
+    return None
+
+  def updateBaseProfile(self, template_directory_list, file_output, base_profile,
+      instances_directory, instance_dict_list):
+    # TODO:
+    #  * cleanup in case of problem
+    #  * use safe update of output file
+    template_data = ''.join(file(self.findTemplate(template_directory_list,
+      'main-template.cfg')).readlines())
+    template = PercentTemplate(template_data)
+    replacement_dict = {
+      'BASE_PROFILE': base_profile,
+      'INSTANCE_PROFILE_LIST': [],
+      'INSTANCE_PART_LIST': []
+    }
+    for instance in [q for q in instance_dict_list if q['TYPE'] != 'Zope Instance']:
+      profile_path = os.path.join(instances_directory,
+          '%s.cfg' % instance['ID'])
+      if not os.path.exists(profile_path):
+        logging.warning('Profile %r not generated, ignoring' % profile_path)
+        continue
+      replacement_dict['INSTANCE_PROFILE_LIST'].append('%s/%s.cfg' % (
+        instances_directory, instance['ID']))
+      replacement_dict['INSTANCE_PART_LIST'].append(instance['ID'])
+
+    for instance in [q for q in instance_dict_list if q['TYPE'] == 'Zope Instance']:
+      profile_path = os.path.join(instances_directory,
+          '%s.cfg' % instance['ID'])
+      if not os.path.exists(profile_path):
+        logging.warning('Profile %r not generated' % profile_path)
+        continue
+      replacement_dict['INSTANCE_PROFILE_LIST'].append('%s/%s.cfg' % (
+        instances_directory, instance['ID']))
+      replacement_dict['INSTANCE_PART_LIST'].append(instance['ID'])
+
+    replacement_dict['INSTANCE_PROFILE_LIST'] = '\n  '.join(
+        replacement_dict['INSTANCE_PROFILE_LIST'])
+    replacement_dict['INSTANCE_PART_LIST'] = '\n  '.join(
+        replacement_dict['INSTANCE_PART_LIST'])
+    out = file(file_output, 'w')
+    out.write(template.substitute(replacement_dict))
+    out.close()
+
+  def setRunning(self, value, pid_file):
+    if value:
+      if os.path.exists(pid_file):
+        # Pid file is present
+        logging.warning('Timmy already have the pid file %s' % pid_file)
+        pid = open(pid_file, 'r').readline()
+        # XXX This could use psutil library.
+        if os.path.exists("/proc/%s" % pid):
+          # In case process is present, ignore.
+          logging.critical('A Timmy process is running with pid %s' % pid)
+          sys.exit(1)
+      # Start new process
+      self.write_pid(pid_file)
+    else:
+      os.remove(pid_file)
+
+  def write_pid(self, pid_file):
+    pid = os.getpid()
+    try:
+      f = open(pid_file, 'w')
+      f.write('%s' % pid)
+      f.close()
+    except (IOError, OSError):
+      logging.critical('Timmy could not write pidfile %s' % pid_file)
       raise
-    except socket.sslerror, e:
-      logging.error('Unexpected error in socket communication, unable to co'
-          'ntinue, failed: %s.' % e.faultString)
-      raise
-  finally:
-    setRunning(False, options.pid_file)
-    logging.info('[%s] Timmy finished, invocation time: %.3fs' % (os.getpid(),
-      time.time() - time_begin))
+
+  def parseOptions(self):
+    parser = OptionParser()
+    parser.add_option("-k", "--key-file",
+      help="File with server key")
+
+    parser.add_option("-s", "--server-url",
+      help="URL of provisioning server")
+
+    parser.add_option("-b", "--base-profile",
+      help="Base profile")
+
+    parser.add_option("-t", "--template-directory",
+      help="Directory with templates")
+
+    parser.add_option("-o", "--main-output",
+      help="Main profile output, which will be run by buildout")
+
+    parser.add_option("-d", "--instances-directory",
+      help="Instances profiles output, must be existing directory")
+
+    parser.add_option("-r", "--buildout-binary",
+      help="Buildout binary to run")
+
+    parser.add_option("-p", "--pid-file",
+      help="Lock file with the pid file.")
+
+    parser.add_option("-l", "--log-file",
+      help="Log file.")
+
+    parser.set_defaults(buildout_offline=False)
+    parser.add_option("-N", "--buildout-offline", action="store_true",
+      help="Run buildout in offline mode.")
+
+    (options, args) = parser.parse_args()
+    required_option_list = [
+        'base_profile',
+        'buildout_binary',
+        'instances_directory',
+        'key_file',
+        'main_output',
+        'server_url',
+        'pid_file'
+      ]
+    missing_required_option_list = [o for o in required_option_list \
+        if not getattr(options, o, None)]
+    if len(missing_required_option_list):
+      parser.error('Required options are missing: %s' % ', '.join(
+        missing_required_option_list))
+
+    for filepath in options.base_profile,:
+      if not os.path.exists(filepath):
+        raise ValueError('Cannot find "%s"' % os.path.abspath(filepath))
+    if not os.path.exists(options.instances_directory):
+      os.mkdir(options.instances_directory)
+    elif not os.path.isdir(options.instances_directory):
+      raise ValueError('File %s is not a directory' %
+          os.path.abspath(options.instances_directory))
+    return options, args
+
+
+def run():
+  return Timmy().run()
 
 if __name__ == '__main__':
   run()




More information about the Erp5-report mailing list