Commit a931ac29 authored by Rafael Monnerat's avatar Rafael Monnerat

slapos.package: Re-Implement as plugable Promise-Based tool

  Upgrade of repository/packages are moved to Promise
  Introduce hostname promise, for keep hostname == computer_id
  Move messages to logger
parent 8a8438ea
...@@ -30,7 +30,12 @@ ...@@ -30,7 +30,12 @@
import os import os
import subprocess import subprocess
import logging
import ConfigParser
# slapos.package imports
from distribution import PackageManager from distribution import PackageManager
from signature import Signature
class SlapError(Exception): class SlapError(Exception):
""" """
...@@ -47,14 +52,71 @@ class UsageError(SlapError): ...@@ -47,14 +52,71 @@ class UsageError(SlapError):
class ExecError(SlapError): class ExecError(SlapError):
pass pass
# Class containing all parameters needed for configuration
class Config:
def __init__(self, option_dict):
# Set options parameters
for option, value in option_dict.__dict__.items():
setattr(self, option, value)
class BasePromise(PackageManager): class BasePromise(PackageManager):
systemctl_path_list = ["/bin/systemctl", systemctl_path_list = ["/bin/systemctl",
"/usr/bin/systemctl"] "/usr/bin/systemctl"]
def __init__(self, config_dict):
self.config = Config(config_dict)
self.logger = logging.getLogger('')
self.logger.setLevel(logging.DEBUG)
# add ch to logger
#self.logger.addHandler(ch)
self.signature = None
def getSignature(self):
""" Return signature loaded from signature file """
# Get configuration
if self.signature is None:
self.signature = Signature(self.config)
self.signature.load()
return self.signature
def getSlapOSConfigurationDict(self, section="slapos"):
""" Return a dictionary with the slapos.cfg configuration """
configuration_info = ConfigParser.RawConfigParser()
configuration_info.read(self.config.slapos_configuration)
return dict(configuration_info.items(section))
def isApplicable(self):
""" Define if the promise is applicable checking the promise list """
upgrade_goal = self.getPromiseSectionDict()
if upgrade_goal is None:
return False
if upgrade_goal.get("filter-promise-list") is None:
# Run all if no filter is provided
return True
module = self.__module__.split(".")[-1]
return module in upgrade_goal.get("filter-promise-list")
def getPromiseSectionDict(self):
""" Get the section which matches with the system """
signature = self.getSignature()
configuration_dict = signature.get_signature_dict()
for entry in configuration_dict:
signature_list = configuration_dict[entry].get("signature-list")
if self.matchSignatureList(signature_list):
return configuration_dict[entry]
def log(self, message): def log(self, message):
""" For now only prints, but it is usefull for test purpose """ """ For now only prints, but it is usefull for test purpose """
print message self.logger.info(message)
def _isSystemd(self): def _isSystemd(self):
""" Dectect if Systemd is used """ """ Dectect if Systemd is used """
...@@ -87,26 +149,3 @@ class BasePromise(PackageManager): ...@@ -87,26 +149,3 @@ class BasePromise(PackageManager):
p = subprocess.Popen(cmd_args, stdout=stdout, stderr=stderr) p = subprocess.Popen(cmd_args, stdout=stdout, stderr=stderr)
output, err = p.communicate() output, err = p.communicate()
return output, err return output, err
class BasePackagePromise(BasePromise):
package_name = None
binary_path = None
def checkConsistency(self, fixit=0, **kw):
is_ok = True
if self.binary is not None and \
not os.path.exists(self.binary_path):
is_ok = False
if self._isUpgradable(self.package_name):
is_ok = False
if not is_ok and fixit:
return self.fixConsistency(**kw)
return is_ok
def fixConsistency(self, **kw):
self._installSoftware(self.package_name)
return self.checkConsistency(fixit=0, **kw)
#!/usr/bin/python
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2012-2014 Vifib SARL and Contributors.
# 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 advised 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 General Public License
# as published by the Free Software Foundation; either version 3
# 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 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.
#
##############################################################################
import core
import hostname
promise_list = (
core.Promise,
hostname.Promise,
)
#!/usr/bin/python
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2012-2014 Vifib SARL and Contributors.
# 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 advised 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 General Public License
# as published by the Free Software Foundation; either version 3
# 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 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.
#
##############################################################################
import datetime
import logging
from optparse import OptionParser, Option
import sys
from slapos.package.base_promise import BasePromise
class Promise(BasePromise):
def fixConsistency(self, upgrade=0, reboot=0, boot=0, **kw):
signature = self.getSignature()
today = datetime.date.today().isoformat()
if upgrade:
upgrade_goal = self.getPromiseSectionDict()
if upgrade_goal is None:
raise ValueError("None of the sections are compatible for upgrade!")
repository_tuple_list = []
for repository in upgrade_goal['repository-list']:
alias, url = repository.split("=")
repository_tuple_list.append((alias.strip(), url.strip()))
self.update(repository_tuple_list, upgrade_goal['filter-package-list'])
if upgrade and boot:
signature.update(reboot=today, upgrade=today)
if upgrade:
signature.update(upgrade=today)
elif reboot:
signature.update(reboot=today)
else:
raise ValueError(
"You need upgrade and/or reboot when invoke fixConsistency!")
def checkConsistency(self, fixit=0, **kw):
# Get configuration
signature = self.getSignature()
self.log("Expected Reboot early them %s" % signature.reboot)
self.log("Expected Upgrade early them %s" % signature.upgrade)
self.log("Last reboot : %s" % signature.last_reboot)
self.log("Last upgrade : %s" % signature.last_upgrade)
if signature.upgrade > datetime.date.today():
self.log("Upgrade will happens on %s" % signature.upgrade)
return
# Check if run for first time
if signature.last_reboot is None:
if fixit:
# Purge repositories list and add new ones
self.fixConsistency(upgrade=1, boot=1)
else:
if signature.last_upgrade < signature.upgrade:
# Purge repositories list and add new ones
if fixit:
self.fixConsistency(upgrade=1)
else:
self.log("Your system is up to date")
if signature.last_reboot < signature.reboot:
if not self.config.dry_run:
self.fixConsistency(reboot=1)
else:
self.log("Dry run: Rebooting required.")
#!/usr/bin/python
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2012-2014 Vifib SARL and Contributors.
# 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 advised 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 General Public License
# as published by the Free Software Foundation; either version 3
# 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 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.
#
##############################################################################
from slapos.package.base_promise import BasePromise
import os
class Promise(BasePromise):
configuration_file_path = '/etc/HOSTNAME'
def _getComputerId(self, **kw):
if kw.get("computer_id"):
return kw["computer_id"]
return self.getSlapOSConfigurationDict("slapos").get("computer_id")
def checkConsistency(self, fixit=0, **kw):
is_ok = False
computer_id = self._getComputerId(**kw)
if computer_id is None:
self.log("Unable to detect computer_id from configuration.")
return is_ok
if os.path.exists(self.configuration_file_path):
is_ok = computer_id in open(self.configuration_file_path, 'r').read()
if not is_ok and fixit:
return self.fixConsistency(**kw)
return is_ok
def fixConsistency(self, **kw):
"""Configures hostname daemon"""
computer_id = self._getComputerId(**kw)
if computer_id is None:
return self.checkConsistency(fixit=0, computer_id=computer_id, **kw)
self.log("Setting hostname in : %s" % self.configuration_file_path)
open(self.configuration_file_path, 'w').write("%s\n" % computer_id)
self._call(['hostname', '-F', self.configuration_file_path])
return self.checkConsistency(fixit=0, **kw)
...@@ -87,12 +87,21 @@ class Signature: ...@@ -87,12 +87,21 @@ class Signature:
self.config = config self.config = config
self.logger = logger self.logger = logger
def log(self, message):
if self.logger is not None:
self.logger.debug(message)
else:
print message
def _download(self, path): def _download(self, path):
""" """
Download a tar of the repository from cache, and untar it. Download a tar of the repository from cache, and untar it.
""" """
shacache = NetworkCache(self.config.slapos_configuration) shacache = NetworkCache(self.config.slapos_configuration)
if shacache.signature_certificate_list is None:
raise ValueError("You need at least one valid signature for download")
def strategy(entry_list): def strategy(entry_list):
"""Get the latest entry. """ """Get the latest entry. """
timestamp = 0 timestamp = 0
...@@ -150,9 +159,9 @@ class Signature: ...@@ -150,9 +159,9 @@ class Signature:
shadir_cert_file=shacache.shadir_cert_file, shadir_cert_file=shacache.shadir_cert_file,
shadir_key_file=shacache.shadir_key_file, shadir_key_file=shacache.shadir_key_file,
): ):
print 'Uploaded update file to cache.' self.log('Uploaded update file to cache.')
except Exception: except Exception:
print 'Unable to upload to cache:\n%s.' % traceback.format_exc() self.log('Unable to upload to cache:\n%s.' % traceback.format_exc())
def upload(self, dry_run=0, verbose=1): def upload(self, dry_run=0, verbose=1):
upgrade_info = ConfigParser.RawConfigParser() upgrade_info = ConfigParser.RawConfigParser()
...@@ -170,8 +179,8 @@ class Signature: ...@@ -170,8 +179,8 @@ class Signature:
file.close() file.close()
if verbose: if verbose:
print " You will update this :" self.log(" You will update this :")
print open(self.config.upgrade_file).read() self.log(open(self.config.upgrade_file).read())
if dry_run: if dry_run:
return return
......
...@@ -33,15 +33,7 @@ import logging ...@@ -33,15 +33,7 @@ import logging
from optparse import OptionParser, Option from optparse import OptionParser, Option
import sys import sys
from signature import Signature from signature import Signature
from base_promise import BasePromise from promise import promise_list
# create console handler and set level to warning
ch = logging.StreamHandler()
ch.setLevel(logging.WARNING)
# create formatter
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s")
# add formatter to ch
ch.setFormatter(formatter)
class Parser(OptionParser): class Parser(OptionParser):
""" """
...@@ -76,99 +68,16 @@ class Parser(OptionParser): ...@@ -76,99 +68,16 @@ class Parser(OptionParser):
(options, args) = self.parse_args() (options, args) = self.parse_args()
return options return options
# Class containing all parameters needed for configuration
class Config:
def __init__(self, option_dict):
# Set options parameters
for option, value in option_dict.__dict__.items():
setattr(self, option, value)
class Upgrader:
def __init__(self, config_dict):
# Set options parameters
self.config = Config(config_dict)
self.logger = logging.getLogger('Updating your machine')
self.logger.setLevel(logging.DEBUG)
# add ch to logger
self.logger.addHandler(ch)
def fixConsistency(self, signature, upgrade=0, reboot=0, boot=0, **kw):
today = datetime.date.today().isoformat()
if upgrade:
pkgmanager = BasePromise()
configuration_dict = signature.get_signature_dict()
for entry in configuration_dict:
signature_list = configuration_dict[entry].get("signature-list")
if pkgmanager.matchSignatureList(signature_list):
print "Upgrade FOUND!!!! %s " % entry
upgrade_goal = configuration_dict[entry]
break
repository_tuple_list = []
for repository in upgrade_goal['repository-list']:
alias, url = repository.split("=")
repository_tuple_list.append((alias.strip(), url.strip()))
pkgmanager.update(repository_tuple_list, upgrade_goal['filter-package-list'])
if upgrade and boot:
signature.update(reboot=today, upgrade=today)
if upgrade:
signature.update(upgrade=today)
elif reboot:
signature.update(reboot=today)
else:
raise ValueError(
"You need upgrade and/or reboot when invoke fixConsistency!")
def checkConsistency(self, fixit=0, **kw):
# Get configuration
signature = Signature(self.config)
signature.load()
self.logger.debug("Expected Reboot early them %s" % signature.reboot)
self.logger.debug("Expected Upgrade early them %s" % signature.upgrade)
self.logger.debug("Last reboot : %s" % signature.last_reboot)
self.logger.debug("Last upgrade : %s" % signature.last_upgrade)
if signature.upgrade > datetime.date.today():
self.logger.debug("Upgrade will happens on %s" % signature.upgrade)
return
# Check if run for first time
if signature.last_reboot is None:
if fixit:
# Purge repositories list and add new ones
self.fixConsistency(signature, upgrade=1, boot=1)
else:
if signature.last_upgrade < signature.upgrade:
# Purge repositories list and add new ones
if fixit:
self.fixConsistency(signature, upgrade=1)
else:
self.logger.info("Your system is up to date")
if signature.last_reboot < signature.reboot:
if not self.config.dry_run:
self.fixConsistency(signature, reboot=1)
else:
self.logger.debug("Dry run: Rebooting required.")
def run(self):
"""
Will fetch information from web and update and/or reboot
machine if needed
"""
self.checkConsistency(fixit=not self.config.dry_run)
def do_update(): def do_update():
"""Update computer and slapos""" """Update computer and slapos"""
usage = "usage: %s [options] " % sys.argv[0] usage = "usage: %s [options] " % sys.argv[0]
config_dict = Parser(usage=usage).check_args()
for promise_klass in promise_list:
# Parse arguments # Parse arguments
upgrader = Upgrader(Parser(usage=usage).check_args()) upgrader = promise_klass(config_dict)
upgrader.run() if upgrader.isApplicable():
upgrader.checkConsistency(fixit=not upgrader.config.dry_run)
sys.exit() sys.exit()
...@@ -33,7 +33,7 @@ import datetime ...@@ -33,7 +33,7 @@ import datetime
from optparse import OptionParser, Option from optparse import OptionParser, Option
import sys import sys
from update import Config from base_promise import Config
from signature import Signature from signature import Signature
def do_upgrade(config): def do_upgrade(config):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment