summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Sulfrian <alexander@sulfrian.net>2013-02-04 21:32:13 +0100
committerAlexander Sulfrian <alexander@sulfrian.net>2013-05-02 10:02:02 +0200
commit14893730199bb559bc819571832b40cc6f22ebf7 (patch)
tree129b725d5715f1edf00d5598796dc1b891c070af
parent3ed39a70d21fc25d5f9c1419e4c74e5cad557741 (diff)
downloadbcfg2-14893730199bb559bc819571832b40cc6f22ebf7.tar.gz
bcfg2-14893730199bb559bc819571832b40cc6f22ebf7.tar.bz2
bcfg2-14893730199bb559bc819571832b40cc6f22ebf7.zip
Plugins/Packages/Portage: add Packages Plugin for Portage
-rw-r--r--debian/changelog3
-rw-r--r--schemas/packages.xsd1
-rw-r--r--src/lib/Server/Plugins/Packages/Portage.py290
3 files changed, 293 insertions, 1 deletions
diff --git a/debian/changelog b/debian/changelog
index b2d60915b..44c4a7eb2 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,5 +1,6 @@
bcfg2 (1.2.3-11) UNRELEASED; urgency=low
+ * Plugins/Packages/Portage: add Packages Plugin for Portage
* Plugins/Packages/Collection: add cclass marker for Sources
* Plugins/Packages/Collection: add missing format string parameter
* Client/Tools/APT: save new package version for auto pkgs
@@ -7,7 +8,7 @@ bcfg2 (1.2.3-11) UNRELEASED; urgency=low
* Plugins/Packages: backported packages_form_entry/_to_entry
* Plugins/PkgVars: new plugin to set various vars per package
- -- Alexander Sulfrian <alex@spline.inf.fu-berlin.de> Sun, 10 Mar 2013 22:28:57 +0100
+ -- Alexander Sulfrian <alex@spline.inf.fu-berlin.de> Mon, 04 Feb 2013 21:32:13 +0100
bcfg2 (1.2.3-9) unstable; urgency=low
diff --git a/schemas/packages.xsd b/schemas/packages.xsd
index 88bbaeec2..fe3fca552 100644
--- a/schemas/packages.xsd
+++ b/schemas/packages.xsd
@@ -15,6 +15,7 @@
<xsd:enumeration value="yum"/>
<xsd:enumeration value="apt"/>
<xsd:enumeration value="pac"/>
+ <xsd:enumeration value="portage"/>
</xsd:restriction>
</xsd:simpleType>
diff --git a/src/lib/Server/Plugins/Packages/Portage.py b/src/lib/Server/Plugins/Packages/Portage.py
new file mode 100644
index 000000000..cbf9aa8f8
--- /dev/null
+++ b/src/lib/Server/Plugins/Packages/Portage.py
@@ -0,0 +1,290 @@
+import re
+import gzip
+import sys
+import os
+import lxml.etree
+import Bcfg2.Server.Plugin
+from Bcfg2.Server.Plugins.Packages.Collection import Collection
+
+_portage_python = '/usr/lib/portage/pym/'
+
+def _import_portage(caller):
+ # generate prefix path
+ caller.prefix = os.path.join(caller.basepath, 'cache', 'portage')
+ if not os.path.isdir(caller.prefix):
+ caller.logger.error("Packages: %s is not a dir. "
+ "Portage will not work. Please "
+ "remember to setup a EPREFIX there." %
+ caller.prefix)
+ # TODO: automatic EPREFIX setup
+ raise Exception('Invalid EPREFIX')
+
+ os.environ['PORTAGE_OVERRIDE_EPREFIX'] = caller.prefix
+
+ if not os.path.isdir(_portage_python):
+ self.logger.error("Packages: %s not found. Have you installed "
+ "the portage python modules?" % _portage_python)
+ raise Exception('Portage not found')
+
+ sys.path = sys.path + [_portage_python]
+ portage = __import__('portage', globals(), locals(),
+ ['_sets', 'dbapi.porttree' ])
+ emerge = __import__('_emerge', globals(), locals(),
+ ['RootConfig', 'depgraph', 'Package', 'actions'])
+
+ # setup profile
+ if '_setup_profile' in dir(caller):
+ caller._setup_profile(portage)
+
+ # fix some default settings
+ portage.settings.unlock()
+ portage.settings['PORTAGE_RSYNC_INITIAL_TIMEOUT'] = '0'
+ portage.settings.lock()
+
+ porttree = portage.db[portage.root]['porttree']
+ caller._import_portage(portage, emerge, porttree)
+
+
+class PortageCollection(Collection):
+ def __init__(self, metadata, sources, basepath, debug=False):
+ Collection.__init__(self, metadata, sources, basepath, debug)
+
+ self.portage = None
+ self.emerge = None
+ self.porttree = None
+
+ @property
+ def cachefiles(self):
+ return []
+
+ def complete(self, packagelist, pinnings=None, recommended=None):
+ if not self.portage:
+ _import_portage(self)
+
+ # get global use flags
+ self.portage.settings.unlock()
+ self.portage.settings['USE'] = ''
+ if 'gentoo-use-flags' in self.metadata.Probes:
+ self.portage.settings['USE'] = \
+ self.metadata.Probes['gentoo-use-flags']
+ self.portage.settings.lock()
+
+
+ # calculate deps
+ setconfig = self.portage._sets.load_default_config(
+ self.portage.settings,
+ self.portage.db[self.portage.root])
+ rconfig = self.emerge.RootConfig.RootConfig(
+ self.portage.settings,
+ self.portage.db[self.portage.root],
+ setconfig)
+ self.portage.db[self.portage.root]['root_config'] = rconfig
+ tree = self.portage.db[self.portage.root]['porttree']
+
+ pkgs = ["=" + j.cpv for (i, j) in packagelist if i == 'ok']
+ fail = [j for (i, j) in packagelist if i == 'fail']
+
+ x = self.emerge.depgraph.backtrack_depgraph(
+ self.portage.settings,
+ self.portage.db,
+ {'--pretend': True},
+ {'recurse': True},
+ 'merge',
+ pkgs,
+ None)
+
+ g = x[1]._dynamic_config.digraph
+ packages = [i for i in g.all_nodes() \
+ if isinstance(i, self.emerge.Package.Package)]
+
+ return (set(packages), set(fail))
+
+ def get_additional_data(self):
+ return []
+
+ def get_group(self, group):
+ self.logger.warning("Packages: Package sets are currently not supported")
+ return []
+
+ def packages_from_entry(self, entry):
+ if not self.portage:
+ _import_portage(self)
+
+ try:
+ name = entry.get('name')
+ # TODO: handle package specific accept keywords
+ pkgs = self.porttree.dep_bestmatch(name)
+ except self.portage.exception.AmbiguousPackageName as e:
+ self.logger.error("Packages: AmbiguousPackageName: %s" % e)
+ pkgs = ''
+
+ if pkgs == '':
+ return [('fail', name)]
+
+ return [('ok', pkgs)]
+
+ def packages_to_entry(self, pkgs, entry, config):
+ for pkg in pkgs:
+ if pkg.slot != '0':
+ name = "%s:%s" % (pkg.cp, pkg.slot)
+ else:
+ name = pkg.cp
+
+ lxml.etree.SubElement(entry, 'BoundPackage', name=name,
+ version=config.get("packages", "version",
+ default="auto"),
+ type=self.ptype, origin='Packages')
+
+ def get_new_packages(self, initial, complete):
+ new = []
+ init = [pkg.cp for status, pkg in initial if status == 'ok']
+
+ for pkg in complete:
+ if pkg.cp not in init:
+ new.append(pkg)
+
+ return new
+
+ def setup_data(self, force_update=False):
+ pass
+
+ def _setup_profile(self, portage):
+ if 'gentoo-profile' not in self.metadata.Probes:
+ raise Exception('Unknown profile.')
+
+ profile = os.path.join(self.prefix, 'usr/portage/profiles/',
+ self.metadata.Probes['gentoo-profile'])
+
+ env = portage.settings.configdict['backupenv']
+
+ portage.settings = portage.package.ebuild.config.config(
+ config_root=portage.settings['PORTAGE_CONFIGROOT'],
+ target_root=portage.settings['ROOT'],
+ env=env,
+ eprefix=portage.settings['EPREFIX'],
+ config_profile_path=profile)
+
+ portage.db[portage.root]['porttree'].settings = portage.settings
+ newdbapi = portage.dbapi.porttree.portdbapi(mysettings=portage.settings)
+ portage.db[portage.root]['porttree'].dbapi = newdbapi
+
+ portage.db[portage.root]['vartree'].settings = portage.settings
+ portage.db[portage.root]['vartree'].dbapi.settings = portage.settings
+
+ def _import_portage(self, portage, emerge, porttree):
+ self.portage = portage
+ self.emerge = emerge
+ self.porttree = porttree
+
+ for s in self.sources:
+ if isinstance(s, PortageSource):
+ s._import_portage(portage, emerge, porttree)
+
+
+class PortageSource(Bcfg2.Server.Plugin.Debuggable):
+ basegroups = ['portage', 'gentoo', 'emerge']
+ ptype = 'ebuild'
+
+ def __init__(self, basepath, xsource, config):
+ Bcfg2.Server.Plugin.Debuggable.__init__(self)
+ self.basepath = basepath
+ self.xsource = xsource
+ self.config = config
+
+ self.url = xsource.get('url', '')
+ self.priority = xsource.get('priority', 0)
+ self.cachefile = None
+ self.gpgkeys = []
+ self.recommended = False
+
+ self.portage = None
+ self.emerge = None
+ self.porttree = None
+
+ # build the set of conditions to see if this source applies to
+ # a given set of metadata
+ self.conditions = []
+ self.groups = [] # provided for some limited backwards compat
+ for el in xsource.iterancestors():
+ if el.tag == "Group":
+ if el.get("negate", "false").lower() == "true":
+ self.conditions.append(lambda m, el=el:
+ el.get("name") not in m.groups)
+ else:
+ self.groups.append(el.get("name"))
+ self.conditions.append(lambda m, el=el:
+ el.get("name") in m.groups)
+ elif el.tag == "Client":
+ if el.get("negate", "false").lower() == "true":
+ self.conditions.append(lambda m, el=el:
+ el.get("name") != m.hostname)
+ else:
+ self.conditions.append(lambda m, el=el:
+ el.get("name") == m.hostname)
+
+ def _import_portage(self, portage, emerge, porttree):
+ self.portage = portage
+ self.emerge = emerge
+ self.porttree = porttree
+
+ def save_state(self):
+ pass
+
+ def load_state(self):
+ pass
+
+ def filter_unknown(self, unknown):
+ filtered = set([u for u in unknown if u.startswith('choice')])
+ unknown.difference_update(filtered)
+
+ def get_urls(self):
+ return self.url
+ urls = property(get_urls)
+
+ def magic_groups_match(self, metadata):
+ if self.config.getboolean("global", "magic_groups",
+ default=True) == False:
+ return True
+ else:
+ for group in self.basegroups:
+ if group in metadata.groups:
+ return True
+ return False
+
+ def setup_data(self, force_update=False):
+ if not self.porttree:
+ _import_portage(self)
+
+ timestamp = os.path.join(self.porttree.portroot, 'metadata/timestamp.chk')
+ if not os.path.isfile(timestamp):
+ self.logger.error("Packages: Timestamp not found; "
+ "falling back to sync")
+ force_update = True
+
+ if force_update:
+ # update sync url
+ self.portage.settings.unlock()
+ self.portage.settings['SYNC'] = self.url
+ self.portage.settings.lock()
+
+ # realy force the sync
+ if os.path.isfile(timestamp):
+ os.unlink(timestamp)
+
+ # sync
+ self.logger.info("Packages: Syncing with %s" % self.url)
+ self.emerge.actions.action_sync(self.portage.settings,
+ self.portage.db, None,
+ {'--quiet': True}, 'sync')
+
+ def applies(self, metadata):
+ # check base groups
+ if not self.magic_groups_match(metadata):
+ return False
+
+ # check Group/Client tags from sources.xml
+ for condition in self.conditions:
+ if not condition(metadata):
+ return False
+
+ return True