def destroy(self):
   """Destroys the partition and makes it available for subsequent use."
   """
   self.logger.info("Destroying Computer Partition %s..." \
       % self.computer_partition.getId())
   # Gets actual buildout binary
   buildout_binary = os.path.join(self.instance_path, 'sbin', 'buildout')
   if not os.path.exists(buildout_binary):
     buildout_binary = os.path.join(self.software_path, 'bin', 'buildout')
   # Launches "destroy" binary if exists
   destroy_executable_location = os.path.join(self.instance_path, 'sbin',
       'destroy')
   if os.path.exists(destroy_executable_location):
     # XXX: we should factorize this code
     uid, gid = None, None
     stat_info = os.stat(self.instance_path)
     uid = stat_info.st_uid
     gid = stat_info.st_gid
     self.logger.debug('Invoking %r' % destroy_executable_location)
     kw = dict()
     if not self.console:
       kw.update(stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
     process_handler = utils.SlapPopen([destroy_executable_location],
       preexec_fn=lambda: utils.dropPrivileges(uid, gid), cwd=self.instance_path,
       env=utils.getCleanEnvironment(pwd.getpwuid(uid).pw_dir), **kw)
     result_std = process_handler.communicate()[0]
     if self.console:
       result_std = 'Please consult messages above'
     if process_handler.returncode is None or process_handler.returncode != 0:
       message = 'Failed to destroy Computer Partition in %r:\n%s\n' % (
           self.instance_path, result_std)
       raise subprocess.CalledProcessError(message)
   # Manually cleans what remains
   try:
     for f in [self.key_file, self.cert_file]:
       if f:
         if os.path.exists(f):
           os.unlink(f)
     for root, dirs, file_list in os.walk(self.instance_path):
       for directory in dirs:
         shutil.rmtree(os.path.join(self.instance_path, directory))
       for file in file_list:
         os.remove(os.path.join(self.instance_path, file))
       if os.path.exists(self.supervisord_partition_configuration_path):
         os.remove(self.supervisord_partition_configuration_path)
       self.updateSupervisor()
   except IOError as error:
     error_string = "I/O error while freeing partition (%s): %s" \
                    % (self.instance_path, error)
     raise IOError(error_string)
Example #2
0
    def _checkPromises(self, computer_partition):
        self.logger.info("Checking promises...")
        instance_path = os.path.join(self.instance_root, computer_partition.getId())

        uid, gid = None, None
        stat_info = os.stat(instance_path)

        # stat sys call to get statistics informations
        uid = stat_info.st_uid
        gid = stat_info.st_gid

        promise_present = False
        # Get the list of promises
        promise_dir = os.path.join(instance_path, "etc", "promise")
        if os.path.exists(promise_dir) and os.path.isdir(promise_dir):
            cwd = instance_path
            promises_list = os.listdir(promise_dir)

            # Check whether every promise is kept
            for promise in promises_list:
                promise_present = True

                command = [os.path.join(promise_dir, promise)]

                promise = os.path.basename(command[0])
                self.logger.info("Checking promise %r.", promise)

                kw = dict(stdout=subprocess.PIPE, stderr=subprocess.PIPE)

                process_handler = SlapPopen(command, preexec_fn=lambda: dropPrivileges(uid, gid), cwd=cwd, env={}, **kw)

                time.sleep(self.promise_timeout)

                if process_handler.poll() is None:
                    process_handler.terminate()
                    raise Slapgrid.PromiseError("The promise %r timed out" % promise)
                elif process_handler.poll() != 0:
                    stderr = process_handler.communicate()[1]
                    if stderr is None:
                        stderr = "No error output from %r." % promise
                    else:
                        stderr = "Promise %r:" % promise + stderr
                    raise Slapgrid.PromiseError(stderr)

        if not promise_present:
            self.logger.info("No promise.")
Example #3
0
  def agregateAndSendUsage(self):
    """Will agregate usage from each Computer Partition.
    """
    slap_computer_usage = self.slap.registerComputer(self.computer_id)
    computer_partition_usage_list = []
    logger = logging.getLogger('UsageReporting')
    logger.info("Aggregating and sending usage reports...")

    #We retrieve XSD models
    try:
      computer_consumption_model = \
        pkg_resources.resource_string(
          'slapos.slap',
          'doc/computer_consumption.xsd')
    except IOError:
      computer_consumption_model = \
        pkg_resources.resource_string(
          __name__, 
          '../../../../slapos/slap/doc/computer_consumption.xsd')

    try:
      partition_consumption_model = \
        pkg_resources.resource_string(
          'slapos.slap',
          'doc/partition_consumption.xsd')
    except IOError:
      partition_consumption_model = \
        pkg_resources.resource_string(
          __name__, 
          '../../../../slapos/slap/doc/partition_consumption.xsd')

    clean_run = True
    #We loop on the different computer partitions
    for computer_partition in slap_computer_usage.getComputerPartitionList():
      computer_partition_id = computer_partition.getId()

      #We want execute all the script in the report folder
      instance_path = os.path.join(self.instance_root,
          computer_partition.getId())
      report_path = os.path.join(instance_path, 'etc', 'report')
      if os.path.isdir(report_path):
        script_list_to_run = os.listdir(report_path)
      else:
        script_list_to_run = []

      #We now generate the pseudorandom name for the xml file
      # and we add it in the invocation_list
      f = tempfile.NamedTemporaryFile()
      name_xml = '%s.%s' % ('slapreport', os.path.basename(f.name))
      path_to_slapreport = os.path.join(instance_path, 'var', 'xml_report',
          name_xml)

      failed_script_list = []
      for script in script_list_to_run:

        invocation_list = []
        invocation_list.append(os.path.join(instance_path, 'etc', 'report',
          script))
        #We add the xml_file name in the invocation_list
        #f = tempfile.NamedTemporaryFile()
        #name_xml = '%s.%s' % ('slapreport', os.path.basename(f.name))
        #path_to_slapreport = os.path.join(instance_path, 'var', name_xml)

        invocation_list.append(path_to_slapreport)
        #Dropping privileges
        uid, gid = None, None
        stat_info = os.stat(instance_path)
        #stat sys call to get statistics informations
        uid = stat_info.st_uid
        gid = stat_info.st_gid
        kw = dict()
        if not self.console:
          kw.update(stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        process_handler = SlapPopen(invocation_list,
          preexec_fn=lambda: dropPrivileges(uid, gid),
          cwd=os.path.join(instance_path, 'etc', 'report'),
          env=None, **kw)
        result = process_handler.communicate()[0]
        if self.console:
          result = 'Please consult messages above'
        if process_handler.returncode is None:
          process_handler.kill()
        if process_handler.returncode != 0:
          clean_run = False
          failed_script_list.append("Script %r failed with %s." % (script, result))
          logger.warning("Failed to run %r, the result was. \n%s" %
            (invocation_list, result))
        if len(failed_script_list):
          computer_partition.error('\n'.join(failed_script_list))

    #Now we loop through the different computer partitions to ggetId()et reports
    report_usage_issue_cp_list = []
    for computer_partition in slap_computer_usage.getComputerPartitionList():
      filename_delete_list = []
      computer_partition_id = computer_partition.getId()
      instance_path = os.path.join(self.instance_root, computer_partition_id)
      dir_reports = os.path.join(instance_path, 'var', 'xml_report')
      #The directory xml_report contain a number of files equal
      #to the number of software instance running inside the same partition
      if os.path.isdir(dir_reports):
        filename_list = os.listdir(dir_reports)
      else:
        filename_list = []
      #logger.debug('name List %s' % filename_list)
      usage = ''

      for filename in filename_list:

        file_path = os.path.join(dir_reports, filename)
        if os.path.exists(file_path):
          usage_file = open(file_path, 'r')
          usage = usage_file.read()
          usage_file.close()

          #We check the validity of xml content of each reports
          if not self.validateXML(usage, partition_consumption_model):
            logger.info('WARNING: The XML file %s generated by slapreport is not valid - ' \
                            'This report is left as is at %s where you can inspect what went wrong ' % (filename, dir_reports))
            #Warn the SlapOS Master that a partition generates corrupted xml report
          else:
            computer_partition_usage = self.slap.registerComputerPartition(
                    self.computer_id, computer_partition_id)
            computer_partition_usage.setUsage(usage)
            computer_partition_usage_list.append(computer_partition_usage)
            filename_delete_list.append(filename)
        else:
          logger.debug("Usage report %r not found, ignored" % file_path)

        #last_push_date = self.computer.getLastUsagePush()
        #periodicity_timedelta = datetime.timedelta(
        #        self.usage_report_periodicity)
        #if periodicity_timedelta + last_push_date < datetime.datetime.today():
        # Pushes informations, if any

      #After sending the aggregated file we remove all the valid xml reports
      for filename in filename_delete_list:
        os.remove(os.path.join(dir_reports, filename))

    for computer_partition_usage in computer_partition_usage_list:
      logger.info('computer_partition_usage_list : %s - %s' % \
        (computer_partition_usage.usage, computer_partition_usage.getId()))

    #If there is, at least, one report
    if computer_partition_usage_list != []:
      try:
        #We generate the final XML report with asXML method
        computer_consumption = self.asXML(computer_partition_usage_list)

        logger.info('Final xml report : %s' % computer_consumption)

        #We test the XML report before sending it
        if self.validateXML(computer_consumption, computer_consumption_model):
          logger.info('XML file generated by asXML is valid')
          slap_computer_usage.reportUsage(computer_consumption)
        else:
          logger.info('XML file generated by asXML is not valid !')
          raise 'XML file generated by asXML is not valid !'
      except Exception:
        computer_partition_id = computer_partition.getId()
        exception = traceback.format_exc()
        issue = "Cannot report usage for %r: %s" % (computer_partition_id,
          exception)
        logger.info(issue)
        computer_partition.error(issue)
        report_usage_issue_cp_list.append(computer_partition_id)

    for computer_partition in slap_computer_usage.getComputerPartitionList():
      computer_partition_id = computer_partition.getId()
      try:
        software_url = computer_partition.getSoftwareRelease().getURI()
      except NotFoundError:
        software_url = None
      software_path = os.path.join(self.software_root,
            getSoftwareUrlHash(software_url))
      local_partition = Partition(
        software_path=software_path,
        instance_path=os.path.join(self.instance_root,
            computer_partition.getId()),
        supervisord_partition_configuration_path=os.path.join(
          self.supervisord_configuration_directory, '%s.conf' %
          computer_partition_id),
        supervisord_socket=self.supervisord_socket,
        computer_partition=computer_partition,
        computer_id=self.computer_id,
        partition_id=computer_partition_id,
        server_url=self.master_url,
        software_release_url=software_url,
        certificate_repository_path=self.certificate_repository_path,
        console=self.console, buildout=self.buildout
        )
      if computer_partition.getState() == "destroyed":
        try:
          local_partition.stop()
          try:
            computer_partition.stopped()
          except (SystemExit, KeyboardInterrupt):
            exception = traceback.format_exc()
            computer_partition.error(exception)
            raise
          except Exception:
            pass
        except (SystemExit, KeyboardInterrupt):
          exception = traceback.format_exc()
          computer_partition.error(exception)
          raise
        except Exception:
          clean_run = False
          exception = traceback.format_exc()
          computer_partition.error(exception)
          logger.error(exception)
        if computer_partition.getId() in report_usage_issue_cp_list:
          logger.info('Ignoring destruction of %r, as not report usage was '
            'sent' % computer_partition.getId())
          continue
        local_partition.destroy()
        try:
          computer_partition.destroyed()
        except slap.NotFoundError:
          logger.debug('Ignored slap error while trying to inform about '
              'destroying not fully configured Computer Partition %r' %
                  computer_partition.getId())

    logger.info("Finished usage reports...")
    return clean_run
Example #4
0
  def processComputerPartitionList(self):
    """Will start supervisord and process each Computer Partition.
    """
    logger = logging.getLogger('ComputerPartitionProcessing')
    logger.info("Processing computer partitions...")
    # Prepares environment
    self.checkEnvironmentAndCreateStructure()
    self._launchSupervisord()
    # Process Computer Partitions
    clean_run = True
    for computer_partition in self.getComputerPartitionList():
      computer_partition_id = computer_partition.getId()
      try:
        software_url = computer_partition.getSoftwareRelease().getURI()
      except NotFoundError:
        software_url = None
      software_path = os.path.join(self.software_root,
            getSoftwareUrlHash(software_url))
      local_partition = Partition(
        software_path=software_path,
        instance_path=os.path.join(self.instance_root,
            computer_partition.getId()),
        supervisord_partition_configuration_path=os.path.join(
          self.supervisord_configuration_directory, '%s.conf' %
          computer_partition_id),
        supervisord_socket=self.supervisord_socket,
        computer_partition=computer_partition,
        computer_id=self.computer_id,
        partition_id=computer_partition_id,
        server_url=self.master_url,
        software_release_url=software_url,
        certificate_repository_path=self.certificate_repository_path,
        console=self.console, buildout=self.buildout)
      # There are no conditions to try to instanciate partition
      try:
        computer_partition_state = computer_partition.getState()
        if computer_partition_state == "started":
          local_partition.install()
          computer_partition.available()
          local_partition.start()
          computer_partition.started()
        elif computer_partition_state == "stopped":
          local_partition.install()
          computer_partition.available()
          local_partition.stop()
          computer_partition.stopped()
        elif computer_partition_state == "destroyed":
          # Stop, but safely
          try:
            local_partition.stop()
            try:
              computer_partition.stopped()
            except (SystemExit, KeyboardInterrupt):
              exception = traceback.format_exc()
              computer_partition.error(exception)
              raise
            except Exception:
              pass
          except (SystemExit, KeyboardInterrupt):
            exception = traceback.format_exc()
            computer_partition.error(exception)
            raise
          except Exception:
            clean_run = False
            exception = traceback.format_exc()
            logger.error(exception)
            computer_partition.error(exception)
        else:
          error_string = "Computer Partition %r has unsupported state: %s" % \
            (computer_partition_id, computer_partition_state)
          computer_partition.error(error_string)
          raise NotImplementedError(error_string)
      except (SystemExit, KeyboardInterrupt):
        exception = traceback.format_exc()
        computer_partition.error(exception)
        raise
      except Exception:
        clean_run = False
        exception = traceback.format_exc()
        logger.error(exception)
        computer_partition.error(exception)

      # Promises

      instance_path = os.path.join(self.instance_root,
          computer_partition.getId())

      uid, gid = None, None
      stat_info = os.stat(instance_path)

      #stat sys call to get statistics informations
      uid = stat_info.st_uid
      gid = stat_info.st_gid

      # Get the list of promises
      promise_dir = os.path.join(instance_path, 'etc', 'promise')
      if os.path.exists(promise_dir) and os.path.isdir(promise_dir):
        cwd = instance_path
        promises_list = os.listdir(promise_dir)

        # Check whether every promise is kept
        for promise in promises_list:

          command = os.path.join(promise_dir, promise)

          kw = dict()
          if not self.console:
            kw.update(stdout=subprocess.PIPE, stderr=subprocess.PIPE)

          process_handler = SlapPopen(command,
            preexec_fn=lambda: dropPrivileges(uid, gid),
            cwd=cwd,
            env=None, **kw)

          time.sleep(self.promise_timeout)

          promise = os.path.basename(command)

          if process_handler.poll() is None:
            process_handler.kill()
            computer_partition.error("The promise %r timed out" % promise)
            clean_run = False
          elif process_handler.poll() != 0:
            stderr = process_handler.communicate()[1]
            if stderr is None:
              stderr = 'No error output from %r.' % promise
            computer_partition.error(stderr)
            clean_run = False


    logger.info("Finished computer partitions...")
    return clean_run
  def install(self):
    """ Creates configuration file from template in software_path, then
    installs the software partition with the help of buildout
    """
    # XXX: Shall be no op in case if revision had not changed
    #      It requires implementation of revision on server
    self.logger.info("Installing Computer Partition %s..." \
        % self.computer_partition.getId())
    # Checks existence and permissions of Partition directory
    # Note : Partitions have to be created and configured before running slapgrid
    if not os.path.isdir(self.instance_path):
      raise PathDoesNotExistError('Please create partition directory %s'
                                           % self.instance_path)
    instance_stat_info = os.stat(self.instance_path)
    permission = oct(stat.S_IMODE(instance_stat_info.st_mode))
    if permission != REQUIRED_COMPUTER_PARTITION_PERMISSION:
      raise WrongPermissionError('Wrong permissions in %s : actual ' \
                                          'permissions are : %s, wanted ' \
                                          'are %s' %
                                          (self.instance_path, permission,
                                            REQUIRED_COMPUTER_PARTITION_PERMISSION))
    os.environ = utils.getCleanEnvironment(pwd.getpwuid(
      instance_stat_info.st_uid).pw_dir)
    # Generates buildout part from template
    # TODO how to fetch the good template? Naming conventions?
    template_location = os.path.join(self.software_path, 'template.cfg')
    config_location = os.path.join(self.instance_path, 'buildout.cfg')
    self.logger.debug("Coping %r to %r" % (template_location, config_location))
    shutil.copy(template_location, config_location)
    # fill generated buildout with additional information
    buildout_text = open(config_location).read()
    buildout_text += '\n\n' + pkg_resources.resource_string(__name__,
        'templates/buildout-tail.cfg.in') % dict(
      computer_id=self.computer_id,
      partition_id=self.partition_id,
      server_url=self.server_url,
      software_release_url=self.software_release_url,
      key_file=self.key_file,
      cert_file=self.cert_file
    )
    open(config_location, 'w').write(buildout_text)
    os.chmod(config_location, 0640)
    # Try to find the best possible buildout:
    #  *) if software_root/bin/bootstrap exists use this one to bootstrap
    #     locally
    #  *) as last resort fallback to buildout binary from software_path
    bootstrap_candidate_dir = os.path.abspath(os.path.join(self.software_path,
      'bin'))
    if os.path.isdir(bootstrap_candidate_dir):
      bootstrap_candidate_list = [q for q in os.listdir(bootstrap_candidate_dir)
        if q.startswith('bootstrap')]
    else:
      bootstrap_candidate_list = []
    uid, gid = self.getUserGroupId()
    os.chown(config_location, -1, int(gid))
    if len(bootstrap_candidate_list) == 0:
      buildout_binary = os.path.join(self.software_path, 'bin', 'buildout')
      self.logger.warning("Falling back to default buildout %r" %
        buildout_binary)
    else:
      if len(bootstrap_candidate_list) != 1:
        raise ValueError('More then one bootstrap candidate found.')
      # Reads uid/gid of path, launches buildout with thoses privileges
      bootstrap_file = os.path.abspath(os.path.join(bootstrap_candidate_dir,
        bootstrap_candidate_list[0]))

      file = open(bootstrap_file, 'r')
      line = file.readline()
      file.close()
      invocation_list = []
      if line.startswith('#!'):
        invocation_list = line[2:].split()
      invocation_list.append(bootstrap_file)
      self.logger.debug('Invoking %r in %r' % (' '.join(invocation_list),
        self.instance_path))
      kw = dict()
      if not self.console:
        kw.update(stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
      process_handler = utils.SlapPopen(invocation_list,
        preexec_fn=lambda: utils.dropPrivileges(uid, gid), cwd=self.instance_path,
        env=utils.getCleanEnvironment(pwd.getpwuid(uid).pw_dir), **kw)
      result_std = process_handler.communicate()[0]
      if self.console:
        result_std = 'Please consult messages above.'
      if process_handler.returncode is None or process_handler.returncode != 0:
        message = 'Failed to bootstrap buildout in %r:\n%s\n' % (
            self.instance_path, result_std)
        raise BuildoutFailedError(message)
      buildout_binary = os.path.join(self.instance_path, 'sbin', 'buildout')

    if not os.path.exists(buildout_binary):
      # use own buildout generation
      utils.bootstrapBuildout(self.instance_path, self.buildout,
        ['buildout:bin-directory=%s'% os.path.join(self.instance_path,
        'sbin')], console=self.console)
      buildout_binary = os.path.join(self.instance_path, 'sbin', 'buildout')
    # Launches buildout
    utils.launchBuildout(self.instance_path,
                   buildout_binary, console=self.console)
    # Generates supervisord configuration file from template
    self.logger.info("Generating supervisord config file from template...")
    # check if CP/etc/run exists and it is a directory
    # iterate over each file in CP/etc/run
    # if at least one is not 0750 raise -- partition has something funny
    runner_list = []
    if os.path.exists(self.run_path):
      if os.path.isdir(self.run_path):
        runner_list = os.listdir(self.run_path)
    if len(runner_list) == 0:
      self.logger.warning('No runners found for partition %r' %
          self.partition_id)
      if os.path.exists(self.supervisord_partition_configuration_path):
        os.unlink(self.supervisord_partition_configuration_path)
    else:
      partition_id = self.computer_partition.getId()
      program_partition_template = pkg_resources.resource_stream(__name__,
          'templates/program_partition_supervisord.conf.in').read()
      group_partition_template = pkg_resources.resource_stream(__name__,
          'templates/group_partition_supervisord.conf.in').read()
      partition_supervisor_configuration = group_partition_template % dict(
          instance_id=partition_id,
          program_list=','.join(['_'.join([partition_id, runner])
            for runner in runner_list]))
      for runner in runner_list:
        partition_supervisor_configuration += '\n' + \
            program_partition_template % dict(
          program_id='_'.join([partition_id, runner]),
          program_directory=self.instance_path,
          program_command=os.path.join(self.run_path, runner),
          program_name=runner,
          instance_path=self.instance_path,
          user_id=uid,
          group_id=gid,
          # As supervisord has no environment to inherit setup minimalistic one
          HOME=pwd.getpwuid(uid).pw_dir,
          USER=pwd.getpwuid(uid).pw_name,
        )
      utils.updateFile(self.supervisord_partition_configuration_path,
          partition_supervisor_configuration)
    self.updateSupervisor()