Commit b9ac2fef authored by Rafael Monnerat's avatar Rafael Monnerat

[slapos.package] First working version for debian updates

  Still working in progress
parent 18007792
from setuptools import setup from setuptools import setup, find_packages
version = '0.0.1.1' # Still under development
version = '0.0.1.3'
name = 'slapos.package' name = 'slapos.package'
long_description = open("README.txt").read() + "\n" + \ long_description = open("README.txt").read() + "\n" + \
open("CHANGES.txt").read() + "\n" open("CHANGES.txt").read() + "\n"
...@@ -16,7 +17,8 @@ setup(name=name, ...@@ -16,7 +17,8 @@ setup(name=name,
license='GPLv3', license='GPLv3',
url='http://www.slapos.org', url='http://www.slapos.org',
author='VIFIB', author='VIFIB',
packages=['slapos.package'], namespace_packages=['slapos'],
packages=find_packages(),
include_package_data=True, include_package_data=True,
install_requires=[ install_requires=[
'slapos.libnetworkcache', 'slapos.libnetworkcache',
...@@ -25,8 +27,16 @@ setup(name=name, ...@@ -25,8 +27,16 @@ setup(name=name,
zip_safe=False, zip_safe=False,
entry_points={ entry_points={
'console_scripts': [ 'console_scripts': [
'slapos-update = slapos.package.update:main', # Those entry points are development version
] 'slappkg-update = slapos.package.update:main',
'slappkg-discover = slapos.package.distribution:do_discover',
'slappkg-upload-key = slapos.package.upload_key:main'
],
# Not supported yet
#'slapos.cli': [
# 'package upload-key = slapos.package.upload_key:main'
# ]
}, },
test_suite="slapos.package.test", test_suite="slapos.package.test",
) )
...@@ -55,7 +55,7 @@ class BasePromise(PackageManager): ...@@ -55,7 +55,7 @@ class BasePromise(PackageManager):
self.log("Calling: %s" % ' '.join(cmd_args)) self.log("Calling: %s" % ' '.join(cmd_args))
if not dry_run: if not dry_run:
p = sub.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
......
import platform import platform
import glob
import re import re
import os
_distributor_id_file_re = re.compile("(?:DISTRIB_ID\s*=)\s*(.*)", re.I) _distributor_id_file_re = re.compile("(?:DISTRIB_ID\s*=)\s*(.*)", re.I)
_release_file_re = re.compile("(?:DISTRIB_RELEASE\s*=)\s*(.*)", re.I) _release_file_re = re.compile("(?:DISTRIB_RELEASE\s*=)\s*(.*)", re.I)
...@@ -30,6 +32,13 @@ def patched_linux_distribution(distname='', version='', id='', ...@@ -30,6 +32,13 @@ def patched_linux_distribution(distname='', version='', id='',
return platform.linux_distribution(distname, version, id, supported_dists, full_distribution_name) return platform.linux_distribution(distname, version, id, supported_dists, full_distribution_name)
class PackageManager: class PackageManager:
def matchSignatureList(self, signature_list):
return self.getOSSignature() in signature_list
def getOSSignature(self):
return "+++".join(patched_linux_distribution())
def getDistributionName(self): def getDistributionName(self):
return patched_linux_distribution()[0] return patched_linux_distribution()[0]
...@@ -62,36 +71,60 @@ class PackageManager: ...@@ -62,36 +71,60 @@ class PackageManager:
""" Add a repository """ """ Add a repository """
return self._getDistribitionHandler().updateRepository(self._call) return self._getDistribitionHandler().updateRepository(self._call)
def _installSoftware(self, name): def _installSoftwareList(self, name_list):
""" Upgrade softwares """ """ Upgrade softwares """
return self._getDistribitionHandler().installSoftware(self._call, name) return self._getDistribitionHandler().installSoftwareList(self._call, name_list)
def _updateSoftware(self): def _updateSoftware(self):
""" Upgrade softwares """ """ Upgrade softwares """
return self._getDistribitionHandler().updateSoftware(self._call) return self._getDistribitionHandler().updateSoftware(self._call)
def updateSystem(self): def _updateSystem(self):
""" Dist-Upgrade of system """ """ Dist-Upgrade of system """
return self._getDistribitionHandler().updateSystem(self._call) return self._getDistribitionHandler().updateSystem(self._call)
def update(self, repository_list=[], package_list=[]):
""" Perform upgrade """
self._purgeRepository()
for alias, url in repository_list:
self._addRepository(url, alias)
self._updateRepository()
if len(package_list):
self._installSoftwareList(package_list)
# This helper implements API for package handling # This helper implements API for package handling
class AptGet: class AptGet:
source_list_path = "/etc/apt/sources.list"
source_list_d_path = "/etc/apt/sources.list.d"
def purgeRepository(self, caller): def purgeRepository(self, caller):
""" Remove all repositories """ """ Remove all repositories """
raise NotImplemented # Aggressive removal
os.remove(self.source_list_path)
open("/etc/apt/sources.list", "w+").write("# Removed all")
for file_path in glob.glob("%s/*" % self.source_list_d_path):
os.remove(file_path)
def addRepository(self, caller, url, alias): def addRepository(self, caller, url, alias):
""" Add a repository """ """ Add a repository """
raise NotImplemented repos_file = open("%s/%s.list" % (self.source_list_d_path, alias), "w")
prefix = "deb "
if alias.endswith("-src"):
prefix = "deb-src "
repos_file.write(prefix + url)
repos_file.close()
def updateRepository(self, caller): def updateRepository(self, caller):
""" Add a repository """ """ Add a repository """
caller(['apt-get', 'update'], stdout=None) caller(['apt-get', 'update'], stdout=None)
def installSoftware(self, caller, name): def installSoftwareList(self, caller, name_list):
""" Instal Software """ """ Instal Software """
self.updateRepository(caller) self.updateRepository(caller)
caller(["apt-get", "install", "-y", name], stdout=None) command_list = ["apt-get", "install", "-y"]
command_list.extend(name_list)
caller(command_list, stdout=None)
def isUpgradable(self, caller, name): def isUpgradable(self, caller, name):
output, err = caller(["apt-get", "upgrade", "--dry-run"]) output, err = caller(["apt-get", "upgrade", "--dry-run"])
...@@ -132,10 +165,12 @@ class Zypper: ...@@ -132,10 +165,12 @@ class Zypper:
return False return False
return True return True
def installSoftware(self, caller, name): def installSoftwareList(self, caller, name_list):
""" Instal Software """ """ Instal Software """
self.updateRepository(caller) self.updateRepository(caller)
caller(['zypper', '--gpg-auto-import-keys', 'up', '-ly', name], stdout=None) command_list = ['zypper', '--gpg-auto-import-keys', 'up', '-ly']
command_list.extend(name_list)
caller(command_list, stdout=None)
def updateSoftware(self, caller): def updateSoftware(self, caller):
""" Upgrade softwares """ """ Upgrade softwares """
...@@ -145,3 +180,9 @@ class Zypper: ...@@ -145,3 +180,9 @@ class Zypper:
""" Dist-Upgrade of system """ """ Dist-Upgrade of system """
caller(['zypper', '--gpg-auto-import-keys', 'dup', '-ly'], stdout=None) caller(['zypper', '--gpg-auto-import-keys', 'dup', '-ly'], stdout=None)
def do_discover():
package_manager = PackageManager()
print package_manager.getOSSignature()
...@@ -73,6 +73,14 @@ class NetworkCache: ...@@ -73,6 +73,14 @@ class NetworkCache:
else: else:
self.directory_key = "slapos-upgrade-testing-key" self.directory_key = "slapos-upgrade-testing-key"
def get_yes_no(prompt):
while True:
answer = raw_input(prompt + " [y,n]: ")
if answer.upper() in ['Y', 'YES']:
return True
if answer.upper() in ['N', 'NO']:
return False
class Signature: class Signature:
def __init__(self, config, logger=None): def __init__(self, config, logger=None):
...@@ -92,6 +100,7 @@ class Signature: ...@@ -92,6 +100,7 @@ class Signature:
for entry in entry_list: for entry in entry_list:
if entry['timestamp'] > timestamp: if entry['timestamp'] > timestamp:
best_entry = entry best_entry = entry
return best_entry return best_entry
return helper_download_network_cached_to_file( return helper_download_network_cached_to_file(
...@@ -145,7 +154,7 @@ class Signature: ...@@ -145,7 +154,7 @@ class Signature:
except Exception: except Exception:
print 'Unable to upload to cache:\n%s.' % traceback.format_exc() print 'Unable to upload to cache:\n%s.' % traceback.format_exc()
def upload(self, dry_run=0): def upload(self, dry_run=0, verbose=1):
upgrade_info = ConfigParser.RawConfigParser() upgrade_info = ConfigParser.RawConfigParser()
upgrade_info.read(self.config.upgrade_file) upgrade_info.read(self.config.upgrade_file)
...@@ -160,13 +169,17 @@ class Signature: ...@@ -160,13 +169,17 @@ class Signature:
upgrade_info.write(file) upgrade_info.write(file)
file.close() file.close()
if verbose:
print " You will update this :"
print open(self.config.upgrade_file).read()
if dry_run: if dry_run:
return return
self._upload(self.config.upgrade_file) if get_yes_no("Do you want to continue? "):
self._upload(self.config.upgrade_file)
def update(self, reboot=None, upgrade=None): def update(self, reboot=None, upgrade=None):
self.load()
if reboot is None and upgrade is None: if reboot is None and upgrade is None:
return return
if not self.current_state.has_section('system'): if not self.current_state.has_section('system'):
...@@ -182,6 +195,20 @@ class Signature: ...@@ -182,6 +195,20 @@ class Signature:
self.current_state.write(current_state_file) self.current_state.write(current_state_file)
current_state_file.close() current_state_file.close()
def get_signature_dict(self):
""" Convert Next state info into a dict """
map_dict = {}
for key in self.next_state.sections():
if key == "system":
continue
def clean_list(l):
return [x.strip() for x in l.split('\n') if x.strip() != '']
map_dict[key] = {}
for entry in self.next_state.options(key):
map_dict[key][entry] = clean_list(self.next_state.get(key, entry))
return map_dict
def _read_state(self, state, name): def _read_state(self, state, name):
""" Extract information from config file """ """ Extract information from config file """
if not state.has_section('system'): if not state.has_section('system'):
...@@ -196,10 +223,10 @@ class Signature: ...@@ -196,10 +223,10 @@ class Signature:
self.current_state = ConfigParser.RawConfigParser() self.current_state = ConfigParser.RawConfigParser()
self.current_state.read(self.config.srv_file) self.current_state.read(self.config.srv_file)
self.next_state = ConfigParser.RawConfigParser() self.next_state = ConfigParser.ConfigParser()
self.next_state.read(self.download()) self.next_state.read(self.download())
self.reboot = self._read_state(self.next_state, "upgrade") self.reboot = self._read_state(self.next_state, "reboot")
self.upgrade = self._read_state(self.next_state, "upgrade") self.upgrade = self._read_state(self.next_state, "upgrade")
self.last_reboot = self._read_state(self.current_state, "reboot") self.last_reboot = self._read_state(self.current_state, "reboot")
self.last_upgrade = self._read_state(self.current_state, "upgrade") self.last_upgrade = self._read_state(self.current_state, "upgrade")
...@@ -37,7 +37,7 @@ import subprocess as sub ...@@ -37,7 +37,7 @@ import subprocess as sub
import sys import sys
import tempfile import tempfile
from signature import Signature from signature import Signature
from base_promise import BasePromise
# create console handler and set level to warning # create console handler and set level to warning
ch = logging.StreamHandler() ch = logging.StreamHandler()
...@@ -97,48 +97,78 @@ class Upgrader: ...@@ -97,48 +97,78 @@ class Upgrader:
# add ch to logger # add ch to logger
self.logger.addHandler(ch) self.logger.addHandler(ch)
def checkConsistency(self, *args, **kw): def fixConsistency(self, signature, upgrade=0, reboot=0, boot=0, **kw):
print "CHECK CONSISTENCY %s" % ((args, kw),) print upgrade, reboot, boot
def run(self):
"""
Will fetch information from web and update and/or reboot
machine if needed
"""
today = datetime.date.today().isoformat() today = datetime.date.today().isoformat()
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!")
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'])
def checkConsistency(self, fixit=0, **kw):
# Get configuration # Get configuration
signature = Signature(self.config) signature = Signature(self.config)
signature.load() signature.load()
self.logger.debug("Expected Reboot early them %s" % signature.reboot) self.logger.debug("Expected Reboot early them %s" % signature.reboot)
self.logger.debug("Expected Upgrade early them %s" % signature.upgrade) self.logger.debug("Expected Upgrade early them %s" % signature.upgrade)
self.logger.debug("Last reboot : %s" % signature.last_reboot) self.logger.debug("Last reboot : %s" % signature.last_reboot)
self.logger.debug("Last upgrade : %s" % signature.last_upgrade) 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 # Check if run for first time
if signature.last_reboot is None: if signature.last_reboot is None:
if not self.config.dry_run: if fixit:
signature.update(reboot=today, upgrade=today) # Purge repositories list and add new ones
self.fixConsistency(signature, upgrade=1, boot=1)
# Purge repositories list and add new ones
self.checkConsistency(fixit=not self.config.dry_run)
else: else:
if signature.last_upgrade < signature.upgrade: if signature.last_upgrade < signature.upgrade:
# Purge repositories list and add new ones # Purge repositories list and add new ones
if not self.config.dry_run: if fixit:
signature.update(upgrade=today) self.fixConsistency(signature, upgrade=1)
self.checkConsistency(fixit=not self.config.dry_run)
else: else:
logger.info("Your system is up to date") logger.info("Your system is up to date")
if signature.last_reboot < signature.reboot: if signature.last_reboot < signature.reboot:
if not self.config.dry_run: if not self.config.dry_run:
signature.update(reboot=today) self.fixConsistency(signature, reboot=1)
else: else:
self.logger.debug("Dry run: Rebooting required.") 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 main(): def main():
"""Update computer and slapos""" """Update computer and slapos"""
usage = "usage: %s [options] " % sys.argv[0] usage = "usage: %s [options] " % sys.argv[0]
...@@ -147,6 +177,5 @@ def main(): ...@@ -147,6 +177,5 @@ def main():
upgrader.run() upgrader.run()
sys.exit() sys.exit()
if __name__ == '__main__': if __name__ == '__main__':
main() main()
...@@ -36,6 +36,10 @@ import sys ...@@ -36,6 +36,10 @@ import sys
from update import Config from update import Config
from signature import Signature from signature import Signature
def do_upgrade(config):
signature = Signature(config)
signature.upload(dry_run=config.dry_run)
class Parser(OptionParser): class Parser(OptionParser):
""" """
...@@ -84,28 +88,10 @@ def get_yes_no(prompt): ...@@ -84,28 +88,10 @@ def get_yes_no(prompt):
if answer.upper() in ['N', 'NO']: if answer.upper() in ['N', 'NO']:
return False return False
def new_upgrade(config):
signature = Signature(config)
signature.upload(dry_run=1)
print " You will update this :"
print open(config.upgrade_file).read()
if not get_yes_no("Do you want to continue? "):
sys.exit(0)
if not config.dry_run:
print "Uploading..."
signature.upload()
def main(): def main():
"""Upload file to update computer and slapos""" """Upload file to update computer and slapos"""
usage = "usage: [options] " usage = "usage: [options] "
# Parse arguments # Parse arguments
config = Config(Parser(usage=usage).check_args()) config = Config(Parser(usage=usage).check_args())
config.srv_file = "/srv/slapupdate" do_upgrade(config)
new_upgrade(config)
sys.exit() sys.exit()
if __name__ == '__main__':
main()
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