mirror of
https://github.com/Telecominfraproject/OpenNetworkLinux.git
synced 2025-11-02 19:28:18 +00:00
765 lines
29 KiB
Python
Executable File
765 lines
29 KiB
Python
Executable File
#!/usr/bin/python2
|
|
############################################################
|
|
#
|
|
# ONL Root Filesystem Generator
|
|
#
|
|
############################################################
|
|
import argparse
|
|
import os
|
|
import sys
|
|
import logging
|
|
import yaml
|
|
import tempfile
|
|
import shutil
|
|
import pprint
|
|
import fcntl
|
|
import subprocess
|
|
import glob
|
|
import submodules
|
|
import StringIO
|
|
from collections import Iterable
|
|
import onlyaml
|
|
import onlu
|
|
import fileinput
|
|
import crypt
|
|
import string
|
|
import random
|
|
import re
|
|
import json
|
|
|
|
logger = onlu.init_logging('onlrfs')
|
|
|
|
class OnlRfsError(Exception):
|
|
"""General Error Exception"""
|
|
def __init__(self, value):
|
|
self.value = value
|
|
|
|
def __str__(self):
|
|
return repr(self.value)
|
|
|
|
|
|
|
|
|
|
class OnlRfsSystemAdmin(object):
|
|
|
|
def __init__(self, chroot):
|
|
self.chroot = chroot
|
|
|
|
@staticmethod
|
|
def gen_salt():
|
|
# return an eight character salt value
|
|
salt_map = './' + string.digits + string.ascii_uppercase + \
|
|
string.ascii_lowercase
|
|
rand = random.SystemRandom()
|
|
salt = ''
|
|
for i in range(0, 8):
|
|
salt += salt_map[rand.randint(0, 63)]
|
|
return salt
|
|
|
|
@staticmethod
|
|
def chmod(mode, file_):
|
|
onlu.execute("sudo chmod %s %s" % (mode, file_),
|
|
ex=OnlRfsError("Could not change permissions (%s) on file %s" % (mode, file_)))
|
|
|
|
@staticmethod
|
|
def chown(file_, ownspec):
|
|
onlu.execute("sudo chown %s %s" % (ownspec, file_),
|
|
ex=OnlRfsError("Could not change ownership (%s) on file %s" % (ownspec, file_)))
|
|
|
|
def userdel(self, username):
|
|
pf = os.path.join(self.chroot, 'etc/passwd')
|
|
sf = os.path.join(self.chroot, 'etc/shadow')
|
|
|
|
self.chmod("a+rwx", os.path.dirname(pf))
|
|
self.chmod("a+rw", pf);
|
|
self.chmod("a+rw", sf);
|
|
|
|
# Can't use the userdel command because of potential uid 0 in-user problems while running ourselves
|
|
for line in fileinput.input(pf, inplace=True):
|
|
if not line.startswith('%s:' % username):
|
|
print line,
|
|
for line in fileinput.input(sf, inplace=True):
|
|
if not line.startswith('%s:' % username):
|
|
print line,
|
|
|
|
self.chmod("go-wx", pf);
|
|
self.chmod("go-wx", sf);
|
|
|
|
def groupadd(self, group, gid=None, unique=True, system=False, force=False, password=None):
|
|
args = [ 'groupadd' ]
|
|
if force:
|
|
args.append("--force")
|
|
if system:
|
|
args.append("--system")
|
|
if not unique:
|
|
args.append("--non-unique")
|
|
if password:
|
|
args = args + [ '--password', password ]
|
|
if gid:
|
|
args = args + [ '--gid', str(gid) ]
|
|
|
|
args.append(group)
|
|
|
|
onlu.execute(args,
|
|
chroot=self.chroot,
|
|
ex=OnlRfsError("Adding group '%s' failed." % group))
|
|
|
|
logger.info("added group %s", group)
|
|
|
|
def useradd(self, username, uid=None, gid=None, password=None, shell='/bin/bash', home=None, groups=None, sudo=False, deleteFirst=True):
|
|
args = [ 'useradd', '--create-home' ]
|
|
|
|
if uid is not None:
|
|
args = args + [ '--non-unique', '--uid', str(uid) ]
|
|
|
|
if gid is not None:
|
|
args = args + [ '--gid', str(gid) ]
|
|
|
|
if password:
|
|
epassword=crypt.crypt(password, '$1$%s$' % self.gen_salt());
|
|
args = args + ['-p', epassword ]
|
|
|
|
if shell:
|
|
args = args + [ '--shell', shell ]
|
|
|
|
if home:
|
|
args = args + [ '--home', home ]
|
|
|
|
if groups:
|
|
args = args + [ '--groups', ','.join(groups) ]
|
|
|
|
if deleteFirst:
|
|
self.userdel(username)
|
|
|
|
args.append(username)
|
|
|
|
onlu.execute(args,
|
|
chroot=self.chroot,
|
|
ex=OnlRfsError("Adding user '%s' failed." % username))
|
|
|
|
if password is None:
|
|
onlu.execute("passwd -d %s" % username,
|
|
chroot=self.chroot,
|
|
ex=OnlRfsError("Error deleting password for user '%s'" % username))
|
|
|
|
logger.info("user %s password %s", username, password)
|
|
|
|
if sudo:
|
|
sudoer = os.path.join(self.chroot, 'etc/sudoers.d', username)
|
|
self.chmod("777", os.path.dirname(sudoer))
|
|
with open(sudoer, "w") as f:
|
|
f.write("%s ALL=(ALL:ALL) NOPASSWD:ALL\n" % username);
|
|
self.chmod("0440", sudoer)
|
|
self.chown(sudoer, "root:root")
|
|
self.chmod("755", os.path.dirname(sudoer))
|
|
|
|
def user_password_set(self, username, password):
|
|
logger.info("user %s password now %s", username, password)
|
|
epassword=crypt.crypt(password, '$1$%s$' % self.gen_salt());
|
|
onlu.execute(['usermod', '-p', epassword, username],
|
|
chroot=self.chroot,
|
|
ex=OnlRfsError("Error setting password for user '%s'" % username))
|
|
|
|
def user_shell_set(self, username, shell):
|
|
onlu.execute('chsh --shell %s %s' % (shell, username), chroot=self.chroot,
|
|
ex=OnlRfsError("Error setting shell %s for user %s" % (shell, username)))
|
|
|
|
def user_disable(self, username):
|
|
self.user_shell_set(username, '/bin/false')
|
|
|
|
|
|
|
|
class OnlMultistrapConfig(object):
|
|
def __init__(self, config):
|
|
self.config = config
|
|
self.__validate()
|
|
|
|
def __validate_key(self, d, k, type_, required=False):
|
|
pass
|
|
|
|
def __validate(self):
|
|
"""Validate the contents of our configuration."""
|
|
|
|
# There must be a General section.
|
|
general = self.config.get('General', None)
|
|
|
|
if general is None:
|
|
raise OnlRfsError("Section 'General' is missing from the multistrap configuration.")
|
|
|
|
self.__validate_key(general, 'arch', str, True)
|
|
self.__validate_key(general, 'debootstrap', str, True)
|
|
|
|
for entry in [ 'debootstrap', 'aptsources' ]:
|
|
sectionlist = []
|
|
for e in general[entry].split():
|
|
if e not in self.config:
|
|
raise OnlRfsError("Section '%s' is specified in the %s option but does not exist in the configuration." % (e, entry))
|
|
|
|
if self.config[e].get('arches', None) and general['arch'] not in self.config[e]['arches']:
|
|
del self.config[e]
|
|
else:
|
|
sectionlist.append(e)
|
|
|
|
general[entry] = " ".join(sectionlist)
|
|
|
|
self.localrepos = []
|
|
|
|
def generate_handle(self, handle):
|
|
for (name, fields) in self.config.iteritems():
|
|
handle.write("[%s]\n" % name)
|
|
for (k,v) in fields.iteritems():
|
|
|
|
if type(v) is bool:
|
|
v = 'true' if v == True else 'false'
|
|
if type(v) is list:
|
|
v = " ".join(onlu.sflatten(v))
|
|
|
|
if k == 'source' and os.path.exists(v):
|
|
self.localrepos.append(v)
|
|
v = "copy:%s ./" % v
|
|
|
|
if k == 'packages' and type(v) is list:
|
|
raise OnlRfsError("packages=%s" % v)
|
|
|
|
handle.write("%s=%s\n" % (k, v))
|
|
handle.write("\n")
|
|
|
|
def generate_file(self, fname=None):
|
|
if fname is None:
|
|
h = tempfile.NamedTemporaryFile(delete=False)
|
|
fname = h.name
|
|
elif fname == '-' or fname == 'stdout':
|
|
h = sys.stdout
|
|
else:
|
|
h = open(fname, "w")
|
|
self.generate_handle(h)
|
|
return fname
|
|
|
|
def get_packages(self):
|
|
pkgs = []
|
|
for (name, fields) in self.config.iteritems():
|
|
for (k,v) in fields.iteritems():
|
|
if k == 'packages':
|
|
if type(v) is list:
|
|
pkgs = pkgs + list(onlu.sflatten(v))
|
|
else:
|
|
pkgs = pkgs + v.split()
|
|
return pkgs
|
|
|
|
def __str__(self):
|
|
handle = StringIO.StringIO()
|
|
self.generate_handle(handle)
|
|
return handle.getvalue()
|
|
|
|
|
|
|
|
class OnlRfsContext(object):
|
|
def __init__(self, directory, resolvconf=True):
|
|
self.directory = directory
|
|
self.dev = os.path.join(self.directory, "dev")
|
|
self.proc = os.path.join(self.directory, "proc")
|
|
self.rc = resolvconf
|
|
if self.rc:
|
|
self.resolvconf = os.path.join(self.directory, "etc", "resolv.conf")
|
|
self.resolvconfb = "%s.onlrfs" % (self.resolvconf)
|
|
|
|
def exists(self, fname):
|
|
return os.path.islink(fname) or os.path.exists(fname)
|
|
|
|
def __enter__(self):
|
|
try:
|
|
onlu.execute("sudo mount -t devtmpfs dev %s" % self.dev,
|
|
ex=OnlRfsError("Could not mount dev in rfs."))
|
|
onlu.execute("sudo mount -t proc proc %s" % self.proc,
|
|
ex=OnlRfsError("Could not mount proc in rfs."))
|
|
|
|
if self.rc:
|
|
if self.exists(self.resolvconf):
|
|
onlu.execute("sudo mv %s %s" % (self.resolvconf, self.resolvconfb),
|
|
ex=OnlRfsError("Could not backup resolv.conf"))
|
|
|
|
onlu.execute("sudo cp --remove-destination /etc/resolv.conf %s" % (self.resolvconf),
|
|
ex=OnlRfsError("Could install new resolv.conf"))
|
|
return self
|
|
|
|
except Exception, e:
|
|
logger.error("Exception %s in OnlRfsContext::__enter__" % e)
|
|
self.__exit__(None, None, None)
|
|
raise e
|
|
|
|
def __exit__(self, eType, eValue, eTrace):
|
|
onlu.execute("sudo umount -l %s %s" % (self.dev, self.proc),
|
|
ex=OnlRfsError("Could not unmount dev and proc"))
|
|
|
|
if self.rc:
|
|
onlu.execute("sudo rm %s" % (self.resolvconf),
|
|
ex=OnlRfsError("Could not remove new resolv.conf"))
|
|
if self.exists(self.resolvconfb):
|
|
onlu.execute("sudo mv %s %s" % (self.resolvconfb, self.resolvconf),
|
|
ex=OnlRfsError("Could not restore resolv.conf"))
|
|
|
|
class OnlRfsBuilder(object):
|
|
|
|
DEFAULTS = dict(
|
|
DEBIAN_SUITE='wheezy',
|
|
DEBIAN_MIRROR='mirrors.kernel.org/debian/',
|
|
APT_CACHE='127.0.0.1:3142/'
|
|
)
|
|
|
|
MULTISTRAP='/usr/sbin/multistrap'
|
|
QEMU_PPC='/usr/bin/qemu-ppc-static'
|
|
QEMU_ARM='/usr/bin/qemu-arm-static'
|
|
QEMU_ARM64='/usr/bin/qemu-aarch64-static'
|
|
BINFMT_PPC='/proc/sys/fs/binfmt_misc/qemu-ppc'
|
|
|
|
def __init__(self, config, arch, **kwargs):
|
|
self.kwargs = kwargs
|
|
self.arch = arch
|
|
self.kwargs['ARCH'] = arch
|
|
|
|
# Hack -- we have to pull powerpc from the archive
|
|
# This will need a cleaner fix.
|
|
if arch == 'powerpc':
|
|
self.DEFAULTS['DEBIAN_MIRROR'] = 'archive.debian.org/debian/'
|
|
|
|
self.kwargs.update(self.DEFAULTS)
|
|
self.__load(config)
|
|
self.__validate()
|
|
|
|
def __load(self, config):
|
|
if not os.path.exists(config):
|
|
raise OnlRfsError("Configuration file '%s' does not exist." % config)
|
|
self.config = onlyaml.loadf(config, self.kwargs)
|
|
|
|
def __validate(self):
|
|
|
|
if not os.path.exists(self.MULTISTRAP):
|
|
raise OnlRfsError("%s is missing." % self.MULTISTRAP)
|
|
|
|
if self.arch == 'powerpc':
|
|
if not os.path.exists(self.QEMU_PPC):
|
|
raise OnlRfsError("%s is missing." % self.QEMU_PPC)
|
|
|
|
if self.arch in [ 'armel', 'armhf' ]:
|
|
if not os.path.exists(self.QEMU_ARM):
|
|
raise OnlRfsError("%s is missing." % self.QEMU_ARM)
|
|
|
|
if self.arch == 'arm64':
|
|
if not os.path.exists(self.QEMU_ARM64):
|
|
raise OnlRfsError("%s is missing." % self.QEMU_ARM64)
|
|
|
|
if not 'Multistrap' in self.config:
|
|
raise OnlRfsError("The Multistrap configuration section is missing.")
|
|
|
|
self.ms = OnlMultistrapConfig(self.config['Multistrap'])
|
|
|
|
|
|
def get_packages(self):
|
|
return self.ms.get_packages()
|
|
|
|
def msconfig(self, fname):
|
|
return self.ms.generate_file(fname)
|
|
|
|
def multistrap(self, dir_):
|
|
msconfig = self.ms.generate_file()
|
|
|
|
# Optional local package updates
|
|
if os.getenv("ONLRFS_NO_PACKAGE_SCAN") is None:
|
|
for r in self.ms.localrepos:
|
|
logger.info("Updating %s" % r)
|
|
if os.path.exists(os.path.join(r, 'Makefile')):
|
|
onlu.execute("make -C %s" % r)
|
|
|
|
if os.path.exists(dir_):
|
|
onlu.execute("sudo rm -rf %s" % dir_,
|
|
ex=OnlRfsError("Could not remove target directory."))
|
|
|
|
if onlu.execute("sudo %s -d %s -f %s" % (self.MULTISTRAP, dir_, msconfig)) == 100:
|
|
raise OnlRfsError("Multistrap APT failure.")
|
|
|
|
if os.getenv("MULTISTRAP_DEBUG"):
|
|
raise OnlRfsError("Multistrap debug.")
|
|
|
|
|
|
def dpkg_configure(self, dir_):
|
|
if self.arch == 'powerpc':
|
|
onlu.execute('sudo cp %s %s' % (self.QEMU_PPC, os.path.join(dir_, 'usr/bin')))
|
|
if self.arch in [ 'armel', 'armhf' ]:
|
|
onlu.execute('sudo cp %s %s' % (self.QEMU_ARM, os.path.join(dir_, 'usr/bin')))
|
|
if self.arch == 'arm64':
|
|
onlu.execute('sudo cp %s %s' % (self.QEMU_ARM64, os.path.join(dir_, 'usr/bin')))
|
|
|
|
onlu.execute('sudo cp %s %s' % (os.path.join(os.getenv('ONL'), 'tools', 'scripts', 'base-files.postinst'),
|
|
os.path.join(dir_, 'var', 'lib', 'dpkg', 'info', 'base-files.postinst')));
|
|
|
|
script = os.path.join(dir_, "tmp/configure.sh")
|
|
with open(script, "w") as f:
|
|
os.chmod(script, 0700)
|
|
f.write("""#!/bin/bash -ex
|
|
/bin/echo -e "#!/bin/sh\\nexit 101" >/usr/sbin/policy-rc.d
|
|
chmod +x /usr/sbin/policy-rc.d
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
export DEBCONF_NONINTERACTIVE_SEEN=true
|
|
echo "127.0.0.1 localhost" >/etc/hosts
|
|
touch /etc/fstab
|
|
echo "localhost" >/etc/hostname
|
|
if [ -f /var/lib/dpkg/info/dash.preinst ]; then
|
|
/var/lib/dpkg/info/dash.preinst install
|
|
fi
|
|
if [ -f /usr/sbin/locale-gen ]; then
|
|
echo "en_US.UTF-8 UTF-8" >/etc/locale.gen
|
|
/usr/sbin/locale-gen
|
|
update-locale LANG=en_US.UTF-8
|
|
fi
|
|
|
|
dpkg --configure -a || true
|
|
dpkg --configure -a # configure any packages that failed the first time and abort on failure.
|
|
|
|
rm -f /usr/sbin/policy-rc.d
|
|
""")
|
|
|
|
logger.info("dpkg-configure filesystem...")
|
|
|
|
onlu.execute("sudo chroot %s /tmp/configure.sh" % dir_,
|
|
ex=OnlRfsError("Post Configuration failed."))
|
|
os.unlink(script)
|
|
|
|
|
|
|
|
def configure(self, dir_):
|
|
|
|
if not os.getenv('NO_DPKG_CONFIGURE'):
|
|
with OnlRfsContext(dir_, resolvconf=False):
|
|
self.dpkg_configure(dir_)
|
|
|
|
with OnlRfsContext(dir_):
|
|
os_release = os.path.join(dir_, 'etc', 'os-release')
|
|
os_release_dict = {}
|
|
if os.path.exists(os_release):
|
|
# Convert /etc/os-release to /etc/os-release.json
|
|
import shlex
|
|
contents = open(os_release).read()
|
|
os_release_dict = dict(token.split('=') for token in shlex.split(contents))
|
|
ua = OnlRfsSystemAdmin(dir_)
|
|
ua.chmod('a+rwx', os.path.dirname(os_release))
|
|
with open(os.path.join(os.path.dirname(os_release), 'os-release.json'), "w") as f:
|
|
f.write(json.dumps(os_release_dict))
|
|
ua.chmod('0755', os.path.dirname(os_release))
|
|
|
|
Configure = self.config.get('Configure', None)
|
|
if Configure:
|
|
|
|
for cmd in Configure.get('run', []):
|
|
onlu.execute("sudo chroot %s %s" % (dir_, cmd),
|
|
ex=OnlRfsError("run command '%s' failed" % cmd))
|
|
|
|
for overlay in Configure.get('overlays', []):
|
|
logger.info("Overlay %s..." % overlay)
|
|
onlu.execute('tar -C %s -c --exclude "*~" . | sudo tar -C %s -x -v --no-same-owner' % (overlay, dir_),
|
|
ex=OnlRfsError("Overlay '%s' failed." % overlay))
|
|
|
|
for update in Configure.get('update-rc.d', []):
|
|
onlu.execute("sudo chroot %s /usr/sbin/update-rc.d %s" % (dir_, update),
|
|
ex=OnlRfsError("update-rc.d %s failed." % (update)))
|
|
|
|
for script in Configure.get('scripts', []):
|
|
logger.info("Configuration script %s..." % script)
|
|
onlu.execute("sudo %s %s" % (script, dir_),
|
|
ex=OnlRfsError("script '%s' failed." % script))
|
|
|
|
|
|
for command in Configure.get('commands', []):
|
|
if '__rfs__' in command:
|
|
command = command % dict(__rfs__=dir_)
|
|
logger.info("Configuration command '%s'..." % command)
|
|
onlu.execute(command,
|
|
ex=OnlRfsError("Command '%s' failed." % command))
|
|
|
|
|
|
ua = OnlRfsSystemAdmin(dir_)
|
|
for (group, values) in Configure.get('groups', {}).iteritems():
|
|
ua.groupadd(group=group, **values if values else {})
|
|
|
|
for (user, values) in Configure.get('users', {}).iteritems():
|
|
if user == 'root':
|
|
if 'password' in values:
|
|
ua.user_password_set(user, values['password'])
|
|
else:
|
|
ua.useradd(username=user, **values)
|
|
|
|
|
|
options = Configure.get('options', {})
|
|
if options.get('clean', False):
|
|
logger.info("Cleaning Filesystem...")
|
|
onlu.execute('sudo chroot %s /usr/bin/apt-get clean' % dir_)
|
|
onlu.execute('sudo chroot %s /usr/sbin/localepurge' % dir_ )
|
|
onlu.execute('sudo chroot %s find /usr/share/doc -type f -not -name asr.json -delete' % dir_)
|
|
onlu.execute('sudo chroot %s find /usr/share/man -type f -delete' % dir_)
|
|
|
|
if 'PermitRootLogin' in options:
|
|
config = os.path.join(dir_, 'etc/ssh/sshd_config')
|
|
ua.chmod('a+rw', config)
|
|
lines = open(config).readlines()
|
|
with open(config, "w") as f:
|
|
for line in lines:
|
|
if line.startswith('PermitRootLogin'):
|
|
v = options['PermitRootLogin']
|
|
logger.info("Setting PermitRootLogin to %s" % v)
|
|
f.write('PermitRootLogin %s\n' % v)
|
|
else:
|
|
f.write(line)
|
|
ua.chmod('644', config)
|
|
|
|
if not options.get('securetty', True):
|
|
f = os.path.join(dir_, 'etc/securetty')
|
|
if os.path.exists(f):
|
|
logger.info("Removing %s" % f)
|
|
onlu.execute('sudo rm %s' % f,
|
|
ex=OnlRfsError('Could not remove file %s' % f))
|
|
|
|
if os.path.exists(os.path.join(dir_, 'etc/inittab')):
|
|
if not options.get('ttys', False):
|
|
f = os.path.join(dir_, 'etc/inittab')
|
|
ua.chmod('a+w', f)
|
|
ua.chmod('a+w', os.path.dirname(f))
|
|
|
|
logger.info("Clearing %s ttys..." % f)
|
|
for line in fileinput.input(f, inplace=True):
|
|
if re.match("^[123456]:.*", line):
|
|
line = "#" + line
|
|
print line,
|
|
|
|
ua.chmod('go-w', f)
|
|
ua.chmod('go-w', os.path.dirname(f))
|
|
|
|
if options.get('console', True):
|
|
logger.info('Configuring Console Access in %s' % f)
|
|
f = os.path.join(dir_, 'etc/inittab')
|
|
ua.chmod('a+w', f)
|
|
ua.chmod('a+w', os.path.dirname(f))
|
|
with open(f, 'a') as h:
|
|
h.write("T0:23:respawn:/sbin/pgetty\n")
|
|
ua.chmod('go-w', f)
|
|
ua.chmod('go-w', os.path.dirname(f))
|
|
|
|
if options.get('asr', None):
|
|
asropts = options.get('asr')
|
|
logger.info("Gathering ASR documentation...")
|
|
sys.path.append("%s/sm/infra/tools" % os.getenv('ONL'))
|
|
import asr
|
|
asro = asr.AimSyslogReference()
|
|
asro.merge(dir_)
|
|
asrf = os.path.join(dir_, asropts['file'])
|
|
OnlRfsSystemAdmin.chmod('777', os.path.dirname(asrf))
|
|
asro.format(os.path.join(dir_, asropts['file']), fmt=asropts['format'])
|
|
|
|
for (mf, fields) in Configure.get('manifests', {}).iteritems():
|
|
logger.info("Configuring manifest %s..." % mf)
|
|
if mf.startswith('/'):
|
|
mf = mf[1:]
|
|
mname = os.path.join(dir_, mf)
|
|
onlu.execute("sudo mkdir -p %s" % os.path.dirname(mname))
|
|
onlu.execute("sudo touch %s" % mname)
|
|
onlu.execute("sudo chmod a+w %s" % mname)
|
|
md = {}
|
|
md['version'] = json.load(open(fields['version']))
|
|
md['arch'] = self.arch
|
|
md['os-release'] = os_release_dict
|
|
|
|
if os.path.exists(fields['platforms']):
|
|
md['platforms'] = yaml.load(open(fields['platforms']))
|
|
else:
|
|
md['platforms'] = fields['platforms'].split(',')
|
|
|
|
for (k, v) in fields.get('keys', {}).iteritems():
|
|
if k in md:
|
|
md[k].update(v)
|
|
else:
|
|
md[k] = v
|
|
|
|
with open(mname, "w") as f:
|
|
json.dump(md, f, indent=2)
|
|
onlu.execute("sudo chmod a-w %s" % mname)
|
|
|
|
for (fname, v) in Configure.get('files', {}).get('add', {}).iteritems():
|
|
if fname.startswith('/'):
|
|
fname = fname[1:]
|
|
dst = os.path.join(dir_, fname)
|
|
onlu.execute("sudo mkdir -p %s" % os.path.dirname(dst))
|
|
onlu.execute("sudo touch %s" % dst)
|
|
onlu.execute("sudo chmod a+w %s" % dst)
|
|
if os.path.exists(v):
|
|
shutil.copy(v, dst)
|
|
else:
|
|
with open(dst, "w") as f:
|
|
f.write("%s\n" % v)
|
|
|
|
for fname in Configure.get('files', {}).get('remove', []):
|
|
if fname.startswith('/'):
|
|
fname = fname[1:]
|
|
f = os.path.join(dir_, fname)
|
|
if os.path.exists(f):
|
|
onlu.execute("sudo rm -rf %s" % f)
|
|
|
|
if Configure.get('issue'):
|
|
issue = Configure.get('issue')
|
|
fn = os.path.join(dir_, "etc/issue")
|
|
onlu.execute("sudo chmod a+w %s" % fn)
|
|
with open(fn, "w") as f:
|
|
f.write("%s\n\n" % issue)
|
|
onlu.execute("sudo chmod a-w %s" % fn)
|
|
|
|
fn = os.path.join(dir_, "etc/issue.net")
|
|
onlu.execute("sudo chmod a+w %s" % fn)
|
|
with open(fn, "w") as f:
|
|
f.write("%s\n" % issue)
|
|
onlu.execute("sudo chmod a-w %s" % fn)
|
|
|
|
|
|
def update(self, dir_, packages):
|
|
|
|
ONLPM = "%s/tools/onlpm.py" % os.getenv('ONL')
|
|
|
|
with OnlRfsContext(dir_):
|
|
for pspec in packages:
|
|
for pkg in pspec.split(','):
|
|
logger.info("updating %s into %s", pkg, dir_)
|
|
cmd = (ONLPM, '--verbose',
|
|
'--sudo',
|
|
'--extract-dir', pkg, dir_,)
|
|
onlu.execute(cmd,
|
|
ex=OnlRfsError("update of %s failed" % pkg))
|
|
|
|
def install(self, dir_, packages):
|
|
|
|
ONLPM = "%s/tools/onlpm.py" % os.getenv('ONL')
|
|
|
|
with OnlRfsContext(dir_):
|
|
for pspec in packages:
|
|
for pkg in pspec.split(','):
|
|
|
|
cmd = (ONLPM, '--lookup', pkg,)
|
|
try:
|
|
buf = subprocess.check_output(cmd)
|
|
except subprocess.CalledProcessError as ex:
|
|
logger.error("cannot find %s", pkg)
|
|
raise ValueError("update failed")
|
|
|
|
if not buf.strip():
|
|
raise ValueError("cannot find %s" % pkg)
|
|
src = buf.splitlines(False)[0]
|
|
d, b = os.path.split(src)
|
|
dst = os.path.join(dir_, "tmp", b)
|
|
shutil.copy2(src, dst)
|
|
src2 = os.path.join("/tmp", b)
|
|
|
|
logger.info("installing %s into %s", pkg, dir_)
|
|
cmd = ('/usr/bin/rfs-dpkg', '-i', src2,)
|
|
onlu.execute(cmd,
|
|
chroot=dir_,
|
|
ex=OnlRfsError("install of %s failed" % pkg))
|
|
|
|
name, _, _ = pkg.partition(':')
|
|
logger.info("updating dependencies for %s", pkg)
|
|
cmd = ('/usr/bin/rfs-apt-get', '-f', 'install', name,)
|
|
onlu.execute(cmd,
|
|
chroot=dir_,
|
|
ex=OnlRfsError("install of %s failed" % pkg))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
ap = argparse.ArgumentParser(description="ONL Root Filesystem Generator")
|
|
ap.add_argument("--arch", required=True)
|
|
ap.add_argument("--config", required=True)
|
|
ap.add_argument("--dir")
|
|
ap.add_argument("--show-packages", action='store_true')
|
|
ap.add_argument("--no-build-packages", action='store_true')
|
|
ap.add_argument("--only-build-packages", action='store_true')
|
|
ap.add_argument("--msconfig")
|
|
ap.add_argument("--multistrap-only", action='store_true')
|
|
ap.add_argument("--no-multistrap", action='store_true')
|
|
ap.add_argument("--cpio")
|
|
ap.add_argument("--squash")
|
|
ap.add_argument("--enable-root")
|
|
|
|
ap.add_argument("--no-configure", action='store_true')
|
|
ap.add_argument("--update", action='append')
|
|
ap.add_argument("--install", action='append')
|
|
|
|
ops = ap.parse_args()
|
|
|
|
if ops.enable_root:
|
|
#
|
|
# Fixme -- this should all be rearranged to naturally support
|
|
# arbitrary filesystem modifications.
|
|
#
|
|
sa = OnlRfsSystemAdmin(ops.dir)
|
|
sa.user_password_set('root', ops.enable_root)
|
|
config = os.path.join(ops.dir, 'etc/ssh/sshd_config')
|
|
sa.chmod('a+rw', config)
|
|
lines = open(config).readlines()
|
|
with open(config, "w") as f:
|
|
for line in lines:
|
|
if line.startswith('PermitRootLogin'):
|
|
v = "Yes"
|
|
logger.info("Setting PermitRootLogin to %s" % v)
|
|
f.write('PermitRootLogin %s\n' % v)
|
|
else:
|
|
f.write(line)
|
|
sa.chmod('644', config)
|
|
sys.exit(0)
|
|
|
|
try:
|
|
x = OnlRfsBuilder(ops.config, ops.arch)
|
|
|
|
if ops.msconfig:
|
|
x.msconfig(ops.msconfig)
|
|
sys.exit(0)
|
|
|
|
if ops.show_packages:
|
|
print "\n".join(x.get_packages())
|
|
sys.exit(0)
|
|
|
|
if ops.dir is None:
|
|
raise OnlRfsError("argument --dir is required")
|
|
|
|
if not ops.no_build_packages:
|
|
pkgs = x.get_packages()
|
|
# Invoke onlpm to build all required (local) packages.
|
|
onlu.execute("%s/tools/onlpm.py --try-arches %s all --skip-missing --require %s" % (os.getenv('ONL'), ops.arch, " ".join(pkgs)),
|
|
ex=OnlRfsError("Failed to build all required packages."))
|
|
if ops.only_build_packages:
|
|
sys.exit(0)
|
|
|
|
if ops.multistrap_only:
|
|
x.multistrap(ops.dir)
|
|
sys.exit(0)
|
|
|
|
if not ops.no_multistrap and not os.getenv('NO_MULTISTRAP'):
|
|
x.multistrap(ops.dir)
|
|
|
|
if not ops.no_configure and not os.getenv('NO_DPKG_CONFIGURE'):
|
|
x.configure(ops.dir)
|
|
|
|
if ops.update:
|
|
x.update(ops.dir, ops.update)
|
|
|
|
if ops.install:
|
|
x.install(ops.dir, ops.install)
|
|
|
|
if ops.cpio:
|
|
if onlu.execute("%s/tools/scripts/make-cpio.sh %s %s" % (os.getenv('ONL'), ops.dir, ops.cpio)) != 0:
|
|
raise OnlRfsError("cpio creation failed.")
|
|
|
|
if ops.squash:
|
|
if os.path.exists(ops.squash):
|
|
os.unlink(ops.squash)
|
|
if onlu.execute("sudo mksquashfs %s %s -no-progress -noappend -comp gzip" % (ops.dir, ops.squash)) != 0:
|
|
if os.path.exists(ops.squash):
|
|
os.unlink(ops.squash)
|
|
raise OnlRfsError("Squash creation failed.")
|
|
|
|
except (OnlRfsError, onlyaml.OnlYamlError), e:
|
|
logger.error(e.value)
|