def up_dns(self, dns_servers): resolvconf = '# automatically generated, do not edit\n' for srv in dns_servers: addr = srv.address if addr is not None: # XXX: when? resolvconf += 'nameserver %s\n' % addr.toString() helpers.write_file('/etc/resolv.conf', resolvconf, perms=0644)
def write_config(self): for i in self.configs: mode = 0644 try: mode = i['mode'] except: pass helpers.write_file(i['file'], i['cont'], perms=mode)
def write_status(self, status): try: helpers.write_file(constants.INSTALL_STATUS_FILE, status, append=False, perms=0644) except: self.error('writing installation status failed, ignored.')
def _write_update_timestamps(): try: from codebay.common import datatypes from codebay.l2tpserver import helpers from codebay.l2tpserver import constants helpers.write_file(constants.TIMESYNC_TIMESTAMP_FILE, datatypes.encode_datetime_to_iso8601_subset(datetime.datetime.utcnow())) helpers.write_file(constants.TIMESYNC_PERSISTENT_TIMESTAMP_FILE, datatypes.encode_datetime_to_iso8601_subset(datetime.datetime.utcnow())) except: if _log is not None: _log.exception('writing system time update timestamp failed.')
def get_apt_info(self, apt_sources_list): """Get apt information for an apt sources.list. Retrieves relevant information (with caching), and returns a tuple consisting of: latest version number, changelog. FIXME: has some trouble now with downgrade, maybe need to nuke /var/lib/apt/lists/vpnease* or something. Also, could we download lists to some temporary file instead? """ try: now = datetime.datetime.utcnow() # serve from cache? if self.cache.has_key(apt_sources_list): cinfo = self.cache[apt_sources_list] diff = now - cinfo.cachetime if (diff >= datetime.timedelta(0, 0, 0)) and (diff <= datetime.timedelta(0, self.interval, 0)): _log.info('serving cached apt info for version %s' % cinfo.version) return cinfo.version, cinfo.changelog else: pass # no, fetch using apt and cache _log.info('fetching apt info, apt source:') _log.info(apt_sources_list) helpers.write_file(self.tmpsource, apt_sources_list, perms=0644) run_command(['aptitude'] + self.aptitude_options + ['update'], retval=runcommand.FAIL) run_command(['aptitude'] + self.aptitude_options + ['download', 'vpnease'], cwd='/tmp', retval=runcommand.FAIL) version = None for i in os.listdir('/tmp'): if self.package_name_re.match(i): version = versioninfo.get_package_version_info(os.path.join('/tmp', i)) changelog = versioninfo.get_package_changelog(os.path.join('/tmp', i)) run_command(['/bin/rm', '-f', os.path.join('/tmp', i)], retval=runcommand.FAIL) if version is None: raise Exception('vpnease package file not found') if changelog is None: raise Exception('vpnease package changelog not found') # create a new cache object cinfo = AptCacheInfo(now, version, changelog) self.cache[apt_sources_list] = cinfo # return fresh data _log.info('serving fresh apt info for version %s' % cinfo.version) return cinfo.version, cinfo.changelog except: _log.exception('failed to get package version info') raise raise Exception('should not be here')
def _update_etc_issue(_log, is_livecd): from codebay.l2tpserver import versioninfo [version_string, cached] = versioninfo.get_version_info() if is_livecd: name = 'VPNease' else: name = 'VPNease' issue = textwrap.dedent("""\ VPNease (version %s) """ % version_string) helpers.write_file('/etc/issue', issue, append=False, perms=0644) helpers.write_file('/etc/issue.net', issue, append=False, perms=0644)
def _write_update_timestamps(): try: from codebay.common import datatypes from codebay.l2tpserver import helpers from codebay.l2tpserver import constants helpers.write_file( constants.TIMESYNC_TIMESTAMP_FILE, datatypes.encode_datetime_to_iso8601_subset( datetime.datetime.utcnow())) helpers.write_file( constants.TIMESYNC_PERSISTENT_TIMESTAMP_FILE, datatypes.encode_datetime_to_iso8601_subset( datetime.datetime.utcnow())) except: if _log is not None: _log.exception('writing system time update timestamp failed.')
def _clear_state(self): """Clear portmap state. Remove openl2tpd and ippoold PROGRAM numbers from portmap state file so that their RPC registering will never fail. This is only useful if we want to emulate portmap startup script behaviour so that the portmap state is stored/restored in start/stop. Note: use this only if state-file handling is required, currently it is not done """ progs_to_remove = ['300773', '300774', '300775'] state = '' for line in open(constants.PORTMAP_STATEFILE): num = line.strip().split()[0] if num in progs_to_remove: continue state += line helpers.write_file(constants.PORTMAP_STATEFILE, state, perms=0644)
def enable_forwarding(self): """Enable packet forwarding.""" _log.debug('enable_forwarding') helpers.write_file('/proc/sys/net/ipv4/ip_forward', '1', perms=None)
def enable_forwarding(self): """Enable packet forwarding.""" _log.debug('enable_forwarding') helpers.write_file ('/proc/sys/net/ipv4/ip_forward', '1', perms=None)
def _check_interfaces(_log): from codebay.l2tpserver import interfacehelper _re_iftab_line = re.compile(r'^([^ #]+)\s+\w{3}\s+(\w{2}:\w{2}:\w{2}:\w{2}:\w{2}:\w{2})') infos = interfacehelper.get_all_interfaces() current_ifaces = {} for i in infos.get_interface_list(): _log.info('system interface: %s' % i.toString()) if not i.is_ethernet_device(): continue current_ifaces[i.get_device_name()] = i.get_mac() iftab_ifaces = {} try: iftab = open('/etc/iftab', 'r') for l in iftab.read().split('\n'): m = _re_iftab_line.match(l) if m is None or len(m.groups()) < 2: continue dev = m.groups()[0] mac = m.groups()[1] info = interfacehelper.InterfaceInfo(dev, 0, 0, 0, 0, 0, mac, 'ether') if info.is_ethernet_device(): iftab_ifaces[dev] = mac _log.info('iftab line: %s, %s' % (dev, mac)) iftab.close() except: # Note: file may be missing, do not panic _log.exception('Failed to read /etc/iftab, creating from scratch.') pass for i in set(current_ifaces.keys()) - set(iftab_ifaces.keys()): # Note: GUI detects new interfaces by itself, no action pass for i in set(iftab_ifaces.keys()) - set(current_ifaces.keys()): # Note: missing interfaces are ok.. and cannot tell if the # interface vanished *just* now because mac locks are not removed. pass # Note: this removes old interface bindings if any.. # Note: we do not care of invalid iftab bindinds, they are udev problems new_ifaces = iftab_ifaces new_ifaces.update(current_ifaces) if_lines = '' keys = new_ifaces.keys() keys.sort() for i in keys: # XXX: could also try to resolve the arp type and write if to # config, but propably not worth the effort? if_lines += '%s mac %s\n' % (i, new_ifaces[i]) helpers.write_file('/etc/iftab', textwrap.dedent("""\ # This file assigns persistent names to network interfaces. # See iftab(5) for syntax. # This file is autogenerated on system boot, do not edit. %s""") % if_lines)
def run_install(self, target, hostname, adminuser, adminpassword, recovery_data=None, large_install=False): """Perform normal product install.""" try: # Set umask to be the same as normal ubuntu user umask (0022) that ubuquity install assumes # instead of default root umask (0066) that we are otherwise using. posix.umask(022) self.prepare() self.hostname = hostname self.large_install = large_install self.install_stage_set('Preparing target disk') self.debconf_progress_start(0, 100, '') self.debconf_progress_set(0) self.debconf_progress_region(0, 4) self.create_filesystems(target) self.setup_fat_partition('/target-fat') self.debconf_progress_set(4) # Copy recovery data for the first time, to ensure that the data is available # in case this recovery fails self.copy_recovery_data(recovery_data) self.install_stage_set('Copying files') # Note: resume partition setup is not a problem # because we do not use swap in live-cd => ubiquity # cannot find a swap partition to use for resume.. # Set configuration values to the debconf database so that # Ubiquity can find them. # Grub boot device self.set_debconf_variable('grub-installer/bootdev', target) # Disable os-prober so that grub installer will not find other # devices to boot from. This is not configurable from debconf # and requires this hack (or menu.lst cleanup afterwards). os_prober = '/usr/bin/os-prober' os_prober_orig = '/usr/bin/os-prober.orig' # Note: first move command in unionfs system changes the directory permissions, # avoiding this by doing a copy/delete instead of move. run_command([constants.CMD_CP, '-f', os_prober, os_prober_orig], retval=runcommand.FAIL) run_command([constants.CMD_RM, '-f', os_prober], retval=runcommand.FAIL) helpers.write_file(os_prober, '#!/bin/sh\nexit 0\n', append=False, perms=0755) # Admin user and passwd self.set_debconf_variable('passwd/username', adminuser) self.set_debconf_variable('passwd/user-fullname', 'Administrator') self.set_debconf_variable('passwd/user-uid', '999') # Note: here we could use real password received from UI (currently cleartext) # eg.: self.set_debconf_variable('passwd/user-password', adminpassword) # For now the admin password is disabled. self.set_debconf_variable('passwd/user-password-crypted', '*') # Set root password disabled. self.set_debconf_variable('passwd/root-password-crypted', '*') # Disable unwanted parts of Ubiquity # 1. language_apply (first two) # 2. apt_setup # 3. timezone_apply (zone and clock) # 4. keyboard_chooser for i in [ '/usr/lib/ubiquity/localechooser/post-base-installer', '/usr/lib/ubiquity/localechooser/prebaseconfig', '/usr/share/ubiquity/apt-setup', '/usr/lib/ubiquity/tzsetup/prebaseconfig', '/usr/share/ubiquity/clock-setup', '/usr/lib/ubiquity/kbd-chooser/prebaseconfig' ]: helpers.write_file( i, textwrap.dedent("""\ #!/bin/sh exit 0 """)) # Run Ubiquity to do the main part of installation self.debconf_progress_region(4, 97) if install.Install(self).run_command(): raise Exception('Ubiquity installer failed') # Set back os-prober # Note: first move command in unionfs system changes the directory permissions, # avoiding this by doing a delete/copy/delete instead of move. run_command([constants.CMD_RM, '-f', os_prober], retval=runcommand.FAIL) run_command([constants.CMD_CP, '-f', os_prober_orig, os_prober], retval=runcommand.FAIL) run_command([constants.CMD_RM, '-f', os_prober_orig], retval=runcommand.FAIL) # Clear debconf database for i in [ 'grub-installer/bootdev', 'passwd/user-password', 'passwd/user-password-crypted', 'passwd/root-password-crypted', 'passwd/username', 'passwd/user-fullname', 'passwd/user-uid' ]: self.set_debconf_variable(i, None) # Ensure that the default user has sudo rights because # user-setup-apply fails when username is "admin". helpers.write_file('/target/etc/sudoers', textwrap.dedent("""\ # Ensure that the default user has admin rights always. %s ALL=(ALL) NOPASSWD: ALL """ % adminuser), append=True, perms=None) # Add GRUB options to /boot/grub/menu.lst: # * recover from kernel panic (panic=60) # * force a 16-bit VESA mode for Virtual PC 2007 compatibility # (affects only startup) f = open('/target/boot/grub/menu.lst') grub_menu = f.read() f.close() def_re = re.compile(r'^# defoptions=') alt_re = re.compile(r'^# altoptions=') updated_grub_menu = '' for l in grub_menu.split('\n'): if (def_re.match(l) is not None) or (alt_re.match(l) is not None): updated_grub_menu += '%s panic=60 vga=785' % l else: updated_grub_menu += l updated_grub_menu += '\n' f = open('/target/boot/grub/menu.lst', 'wb') f.write(updated_grub_menu) f.close() run_command(['/usr/sbin/chroot', '/target', '/sbin/update-grub'], retval=runcommand.FAIL) # Fix permissions of all ubiquity-created files on target system. # These permissions are broken because of root umask (0066) is used # when running installer instead of ubuntu-user umask (0022)) # # Files affected include at least: /etc/hosts, /etc/iftab, /etc/kernel-img.conf, /boot/grub, /boot/grub/* # # The following files already have proper permissions (files do exist before write), # but setting anyways: /etc/hostname, /etc/network/interfaces # # Note: this is still in place as a safeguard even when the umask is now set # in script start. for f, p in [['etc/hosts', 0644], ['etc/iftab', 0644], ['etc/hostname', 0644], ['etc/network/interfaces', 0644], ['etc/kernel-img.conf', 0644]]: os.chmod(os.path.join('/target', f), p) for r, d, files in os.walk('/target/boot/grub'): os.chmod(r, 0755) for f in files: os.chmod(os.path.join(r, f), 0644) # Note: Use this if the login stuff gets broken again and # debugging is required. # helpers.write_file('/target/etc/sudoers', textwrap.dedent("""\ # debug ALL=(ALL) NOPASSWD: ALL # """), append=True, perms=None) # run_command(['/usr/sbin/chroot', '/target', 'adduser', '--disabled-password', '--gecos', 'Debug', '--uid', '1020', 'debug']) # run_command(['/usr/sbin/chroot', '/target', 'chpasswd'], stdin='debug:debug\n') self.install_stage_set('Finishing installation') if not os.path.exists(constants.LOWMEM_MARKER_FILE): # XXX: database stuff: (when testusage is implemented) # - stop database # - stop cron scripts from accessing database # - copy database to installed system # - copy marker files to installed system? # - configured marker, other marker files # - whole /var/lib/l2tpgw/ ? # - copy uuid file to installed system? # - copy logfiles to installed system # - remove/nocopy: fastboot marker pass self.debconf_progress_region(97, 99) # Recreate ssh keys for f, t in [['/etc/ssh/ssh_host_rsa_key', 'rsa'], ['/etc/ssh/ssh_host_dsa_key', 'dsa']]: run_command( ['/usr/sbin/chroot', '/target', '/bin/rm', '-f', f], retval=runcommand.FAIL) run_command([ '/usr/sbin/chroot', '/target', '/usr/bin/ssh-keygen', '-q', '-N', '', '-f', f, '-t', t, '-C', 'root@%s' % self.hostname ], retval=runcommand.FAIL) # Copy recovery data (again) self.copy_recovery_data(recovery_data) self.copy_debconf() self.cleanup() self.debconf_progress_set(100) self.write_status('success\nInstall completed') self.info('installation success') except: self.cleanup() self.write_status('failure\nInstall failed') self.error('installation failed') raise
def create_filesystems(self, device): """Repartition the device, create filesystems and /etc/fstab.""" self.debconf_progress_start(0, 100, '') self.debconf_progress_set(0) self.debconf_progress_info('Finding target device: %s' % device) m = mediahelper.get_media() target = m.get_medium_by_device_name(device) if target is None: self.error('failed to find installation target device: %s' % device) raise Exception('failed to find installation target device: %s' % device) # determine various parameters for later partitioning and fs setup create_swap = True part_fat = None part_boot = None part_swap = None part_root = None if create_swap: if self.large_install: part_fat = target.get_partition_devicename(1) part_boot = target.get_partition_devicename(2) part_swap = target.get_partition_devicename(3) part_root = target.get_partition_devicename(4) partitions = [part_fat, part_boot, part_swap, part_root] else: part_fat = target.get_partition_devicename(1) part_swap = target.get_partition_devicename(2) part_root = target.get_partition_devicename(3) partitions = [part_fat, part_swap, part_root] else: if self.large_install: part_fat = target.get_partition_devicename(1) part_boot = target.get_partition_devicename(2) part_root = target.get_partition_devicename(3) partitions = [part_fat, part_boot, part_root] else: part_fat = target.get_partition_devicename(1) part_root = target.get_partition_devicename(2) partitions = [part_fat, part_root] disk_min_size = (constants.DISK_SIZE_MINIMUM - constants.DISK_SIZE_SAFETY_MARGIN) / 512 * 512 # disk size with safety margin disk_size = (target.get_size() - constants.DISK_SIZE_SAFETY_MARGIN) / 512 * 512 # partition "end points" fat_end = '1MB' disk_min_end = '%ss' % (disk_min_size / 512) # sectors disk_end = '%ss' % (disk_size / 512) # sectors try: self.debconf_progress_info('Wiping target device: %s' % device) self.nuke_and_create_disklabel(device) # Just in case time.sleep(1) self.debconf_progress_set(5) self.wait_for_partition_devices_to_disappear(partitions) self.debconf_progress_set(10) # MBR is at sector 0, and the first partition should begin at sector 63. # This leaves sectors 1...62 as "no man's land" (31kiB) which is used # for Grub stage 1.5 # # See: http://en.wikipedia.org/wiki/GNU_GRUB # # XXX: currently partitions don't end at cylinder # boundaries. It would probably be best to round the # partitions up to cylinder boundaries, as a # "conservative" partitioning is typically done so. # However, this should not matter as long as we only run # Grub and Linux itself. # # NOTE: Even Windows Vista partitions USB sticks without # caring about cylinders. Probably not an issue anymore. # Just in case time.sleep(1) self.debconf_progress_set(15) # XXX: endpoint is exclusive in parted self.debconf_progress_info('Creating partitions') if create_swap: if self.large_install: boot_end = '256MB' swap_end = '768MB' parted_cmds = [ ['mkpart', 'primary', 'fat32', '63s', fat_end], ['mkpart', 'primary', 'ext2', fat_end, boot_end], ['mkpart', 'primary', 'linux-swap', boot_end, swap_end], ['mkpart', 'primary', 'ext2', swap_end, disk_end], ['set', '2', 'boot', 'on'] ] else: swap_end = '512MB' parted_cmds = [ ['mkpart', 'primary', 'fat32', '63s', fat_end], ['mkpart', 'primary', 'linux-swap', fat_end, swap_end], ['mkpart', 'primary', 'ext2', swap_end, disk_min_end], ['set', '3', 'boot', 'on'] ] else: if self.large_install: boot_end = '256MB' parted_cmds = [ ['mkpart', 'primary', 'fat32', '63s', fat_end], ['mkpart', 'primary', 'ext2', fat_end, boot_end], ['mkpart', 'primary', 'ext2', boot_end, disk_end], ['set', '2', 'boot', 'on'] ] else: parted_cmds = [ ['mkpart', 'primary', 'fat32', '63s', fat_end], ['mkpart', 'primary', 'ext2', fat_end, disk_min_end], ['set', '2', 'boot', 'on'] ] for i in parted_cmds: run_command(['/sbin/parted', '-s', device] + i, retval=runcommand.FAIL) self.debconf_progress_set(20) # Just in case time.sleep(1) self.debconf_progress_set(25) self.wait_for_partition_devices_to_appear(partitions) self.debconf_progress_set(30) # Sleep for a while to ensure that there is no "flicker" of device nodes. # For some reason this happens with at least native hardware and USB sticks. # See #435. time.sleep(5) self.debconf_progress_set(55) self.debconf_progress_info('Creating partitions and filesystems') run_command(['/sbin/mkfs.vfat', '-n', constants.PRODUCT_NAME.upper(), part_fat], retval=runcommand.FAIL) self.debconf_progress_set(60) if part_swap is not None: run_command(['/sbin/mkswap', '-L', 'SWAP', part_swap], retval=runcommand.FAIL) if part_boot is not None: # XXX: opts here? run_command(['/sbin/mkfs.ext3', '-L', 'ROOT', '-b', str(1024), '-i', str(4096), '-m', str(0), '-O', 'sparse_super,filetype,resize_inode,dir_index', '-v', part_boot], retval=runcommand.FAIL) [rc, stdout, stderr] = run_command(['/sbin/dumpe2fs', part_boot]) self._log.info('dumpe2fs dump of boot filesystem:\n%s' % stdout) else: self._log.info('no boot partition, skipping mkfs') run_command(['/sbin/mkfs.ext3', '-L', 'ROOT', '-b', str(1024), '-i', str(4096), '-m', str(0), '-O', 'sparse_super,filetype,resize_inode,dir_index', '-v', part_root], retval=runcommand.FAIL) [rc, stdout, stderr] = run_command(['/sbin/dumpe2fs', part_root]) self._log.info('dumpe2fs dump of root filesystem:\n%s' % stdout) self.debconf_progress_set(95) except: self.error('failed to create partitions, exiting.') raise # Create targets and fstab. self.debconf_progress_info('Mounting target and creating fstab') # Note: not using -p because the target should not exist at this point (cleanup done) run_command([constants.CMD_MKDIR, '/target'], retval=runcommand.FAIL) run_command([constants.CMD_MKDIR, '/target-fat'], retval=runcommand.FAIL) run_command([constants.CMD_MOUNT, part_root, '/target'], retval=runcommand.FAIL) run_command([constants.CMD_MOUNT, part_fat, '/target-fat'], retval=runcommand.FAIL) if part_boot is not None: run_command([constants.CMD_MKDIR, '/target/boot'], retval=runcommand.FAIL) run_command([constants.CMD_MOUNT, part_boot, '/target/boot'], retval=runcommand.FAIL) # Write fstab now (it will not be overwritten by copy process) run_command([constants.CMD_MKDIR, '/target/etc/'], retval=runcommand.FAIL) fstab = textwrap.dedent("""\ # /etc/fstab: static file system information. # # <file system> <mount point> <type> <options> <dump> <pass> proc /proc proc defaults 0 0 %(part_root)s / ext3 defaults,errors=remount-ro,noatime 0 1 """) if part_swap is not None: fstab += textwrap.dedent("""\ %(part_swap)s none swap sw 0 0 """) if part_boot is not None: fstab += textwrap.dedent("""\ %(part_boot)s /boot ext3 defaults 0 2 """) fstab = fstab % {'part_swap':part_swap, 'part_root':part_root, 'part_boot':part_boot, 'part_fat':part_fat} helpers.write_file('/target/etc/fstab', fstab) self.debconf_progress_set(100) # XXX: cdrom to fstab? nope for now # /dev/hdc /media/cdrom0 iso9660 ro,user,noauto 0 0 self.debconf_progress_stop()
def get_apt_info(self, apt_sources_list): """Get apt information for an apt sources.list. Retrieves relevant information (with caching), and returns a tuple consisting of: latest version number, changelog. FIXME: has some trouble now with downgrade, maybe need to nuke /var/lib/apt/lists/vpnease* or something. Also, could we download lists to some temporary file instead? """ try: now = datetime.datetime.utcnow() # serve from cache? if self.cache.has_key(apt_sources_list): cinfo = self.cache[apt_sources_list] diff = now - cinfo.cachetime if (diff >= datetime.timedelta(0, 0, 0)) and ( diff <= datetime.timedelta(0, self.interval, 0)): _log.info('serving cached apt info for version %s' % cinfo.version) return cinfo.version, cinfo.changelog else: pass # no, fetch using apt and cache _log.info('fetching apt info, apt source:') _log.info(apt_sources_list) helpers.write_file(self.tmpsource, apt_sources_list, perms=0644) run_command(['aptitude'] + self.aptitude_options + ['update'], retval=runcommand.FAIL) run_command(['aptitude'] + self.aptitude_options + ['download', 'vpnease'], cwd='/tmp', retval=runcommand.FAIL) version = None for i in os.listdir('/tmp'): if self.package_name_re.match(i): version = versioninfo.get_package_version_info( os.path.join('/tmp', i)) changelog = versioninfo.get_package_changelog( os.path.join('/tmp', i)) run_command(['/bin/rm', '-f', os.path.join('/tmp', i)], retval=runcommand.FAIL) if version is None: raise Exception('vpnease package file not found') if changelog is None: raise Exception('vpnease package changelog not found') # create a new cache object cinfo = AptCacheInfo(now, version, changelog) self.cache[apt_sources_list] = cinfo # return fresh data _log.info('serving fresh apt info for version %s' % cinfo.version) return cinfo.version, cinfo.changelog except: _log.exception('failed to get package version info') raise raise Exception('should not be here')
def run_update(self): """Run update script. Returns a Deferred, which either raises an update-related exception (ending up in caller's errback), or returns None if the update completes normally. """ def _script_timeout(): _log.warning('_script_timeout()') self.script_timeout_call = None # this is not nice, but what else to do here? if self._update_process_protocol is not None: self._update_process_protocol.sendTerm() self.update_failed = True self.update_exit_code = 3 # XXX: fake update exit code to cause UpdateFailedError self.stop_twisted() def _update_completed(res): _log.debug('_update_completed()') if self.script_timeout_call is not None: self.script_timeout_call.cancel() self.script_timeout_call = None # XXX: In a script timeout case, we get here with 'res' not being # an integer. This is not nice, but causes no actual problems. self.update_exit_code = int(res) # store exit code _log.info('update exit code: %s' % self.update_exit_code) return None _log.info('update needed, starting update process') # export configuration before update from sqlite so that new code after # update has the option of re-creating the sqlite database or switch to # a new backend format without resorting to ugly sqlite dependencies try: _log.info('exporting rdf/xml for update') self._export_rdfxml_for_update() _log.info('export rdf/xml for update successful') except: _log.exception('_export_rdfxml_for_update() failed, ignoring') try: if os.path.exists(constants.UPDATE_PROCESS_RDFXML_EXPORT_FILE): os.unlink(constants.UPDATE_PROCESS_RDFXML_EXPORT_FILE) except: _log.exception('_export_rdfxml_for_update(), cleanup failed') # set sources.list helpers.write_file('/etc/apt/sources.list', self.sources) # set repository keys helpers.write_file(constants.UPDATE_REPOSITORY_KEYS_FILE, self.repokeys, perms=0600, append=False) # determine parameters for update cmd = constants.CMD_PYTHON if self.scriptspath is not None: pyfile = os.path.join( self.scriptspath, os.path.basename(constants.CMD_L2TPGW_UPDATE_PRODUCT)) else: pyfile = constants.CMD_L2TPGW_UPDATE_PRODUCT _log.info('update command: %s, script: %s, arguments: %s' % (cmd, pyfile, self.importpath)) # failure timer for running script self.script_timeout_call = reactor.callLater( constants.UPDATE_SCRIPT_TIMEOUT, _script_timeout) # start update process u = UpdateProcessProtocol() self._update_process_protocol = u reactor.spawnProcess( u, executable=cmd, args=[cmd, pyfile, '--import-path', self.importpath], env=None, # Uses os.environ if set to None, default is empty usePTY=1) d = u.waitCompleted() d.addCallback(_update_completed) self.run_update_deferred = d return d
def run_install(self, target, hostname, adminuser, adminpassword, recovery_data=None, large_install=False): """Perform normal product install.""" try: # Set umask to be the same as normal ubuntu user umask (0022) that ubuquity install assumes # instead of default root umask (0066) that we are otherwise using. posix.umask(022) self.prepare() self.hostname = hostname self.large_install = large_install self.install_stage_set('Preparing target disk') self.debconf_progress_start(0, 100, '') self.debconf_progress_set(0) self.debconf_progress_region(0, 4) self.create_filesystems(target) self.setup_fat_partition('/target-fat') self.debconf_progress_set(4) # Copy recovery data for the first time, to ensure that the data is available # in case this recovery fails self.copy_recovery_data(recovery_data) self.install_stage_set('Copying files') # Note: resume partition setup is not a problem # because we do not use swap in live-cd => ubiquity # cannot find a swap partition to use for resume.. # Set configuration values to the debconf database so that # Ubiquity can find them. # Grub boot device self.set_debconf_variable('grub-installer/bootdev', target) # Disable os-prober so that grub installer will not find other # devices to boot from. This is not configurable from debconf # and requires this hack (or menu.lst cleanup afterwards). os_prober = '/usr/bin/os-prober' os_prober_orig = '/usr/bin/os-prober.orig' # Note: first move command in unionfs system changes the directory permissions, # avoiding this by doing a copy/delete instead of move. run_command([constants.CMD_CP, '-f', os_prober, os_prober_orig], retval=runcommand.FAIL) run_command([constants.CMD_RM, '-f', os_prober], retval=runcommand.FAIL) helpers.write_file(os_prober, '#!/bin/sh\nexit 0\n', append=False, perms=0755) # Admin user and passwd self.set_debconf_variable('passwd/username', adminuser) self.set_debconf_variable('passwd/user-fullname', 'Administrator') self.set_debconf_variable('passwd/user-uid', '999') # Note: here we could use real password received from UI (currently cleartext) # eg.: self.set_debconf_variable('passwd/user-password', adminpassword) # For now the admin password is disabled. self.set_debconf_variable('passwd/user-password-crypted', '*') # Set root password disabled. self.set_debconf_variable('passwd/root-password-crypted', '*') # Disable unwanted parts of Ubiquity # 1. language_apply (first two) # 2. apt_setup # 3. timezone_apply (zone and clock) # 4. keyboard_chooser for i in ['/usr/lib/ubiquity/localechooser/post-base-installer', '/usr/lib/ubiquity/localechooser/prebaseconfig', '/usr/share/ubiquity/apt-setup', '/usr/lib/ubiquity/tzsetup/prebaseconfig', '/usr/share/ubiquity/clock-setup', '/usr/lib/ubiquity/kbd-chooser/prebaseconfig']: helpers.write_file(i, textwrap.dedent("""\ #!/bin/sh exit 0 """)) # Run Ubiquity to do the main part of installation self.debconf_progress_region(4, 97) if install.Install(self).run_command(): raise Exception('Ubiquity installer failed') # Set back os-prober # Note: first move command in unionfs system changes the directory permissions, # avoiding this by doing a delete/copy/delete instead of move. run_command([constants.CMD_RM, '-f', os_prober], retval=runcommand.FAIL) run_command([constants.CMD_CP, '-f', os_prober_orig, os_prober], retval=runcommand.FAIL) run_command([constants.CMD_RM, '-f', os_prober_orig], retval=runcommand.FAIL) # Clear debconf database for i in ['grub-installer/bootdev', 'passwd/user-password', 'passwd/user-password-crypted', 'passwd/root-password-crypted', 'passwd/username', 'passwd/user-fullname', 'passwd/user-uid']: self.set_debconf_variable(i, None) # Ensure that the default user has sudo rights because # user-setup-apply fails when username is "admin". helpers.write_file('/target/etc/sudoers', textwrap.dedent("""\ # Ensure that the default user has admin rights always. %s ALL=(ALL) NOPASSWD: ALL """ % adminuser), append=True, perms=None) # Add GRUB options to /boot/grub/menu.lst: # * recover from kernel panic (panic=60) # * force a 16-bit VESA mode for Virtual PC 2007 compatibility # (affects only startup) f = open('/target/boot/grub/menu.lst') grub_menu = f.read() f.close() def_re = re.compile(r'^# defoptions=') alt_re = re.compile(r'^# altoptions=') updated_grub_menu = '' for l in grub_menu.split('\n'): if (def_re.match(l) is not None) or (alt_re.match(l) is not None): updated_grub_menu += '%s panic=60 vga=785' % l else: updated_grub_menu += l updated_grub_menu += '\n' f = open('/target/boot/grub/menu.lst', 'wb') f.write(updated_grub_menu) f.close() run_command(['/usr/sbin/chroot', '/target', '/sbin/update-grub'], retval=runcommand.FAIL) # Fix permissions of all ubiquity-created files on target system. # These permissions are broken because of root umask (0066) is used # when running installer instead of ubuntu-user umask (0022)) # # Files affected include at least: /etc/hosts, /etc/iftab, /etc/kernel-img.conf, /boot/grub, /boot/grub/* # # The following files already have proper permissions (files do exist before write), # but setting anyways: /etc/hostname, /etc/network/interfaces # # Note: this is still in place as a safeguard even when the umask is now set # in script start. for f, p in [['etc/hosts', 0644], ['etc/iftab', 0644], ['etc/hostname', 0644], ['etc/network/interfaces', 0644], ['etc/kernel-img.conf', 0644]]: os.chmod(os.path.join('/target', f), p) for r, d, files in os.walk('/target/boot/grub'): os.chmod(r, 0755) for f in files: os.chmod(os.path.join(r, f), 0644) # Note: Use this if the login stuff gets broken again and # debugging is required. # helpers.write_file('/target/etc/sudoers', textwrap.dedent("""\ # debug ALL=(ALL) NOPASSWD: ALL # """), append=True, perms=None) # run_command(['/usr/sbin/chroot', '/target', 'adduser', '--disabled-password', '--gecos', 'Debug', '--uid', '1020', 'debug']) # run_command(['/usr/sbin/chroot', '/target', 'chpasswd'], stdin='debug:debug\n') self.install_stage_set('Finishing installation') if not os.path.exists(constants.LOWMEM_MARKER_FILE): # XXX: database stuff: (when testusage is implemented) # - stop database # - stop cron scripts from accessing database # - copy database to installed system # - copy marker files to installed system? # - configured marker, other marker files # - whole /var/lib/l2tpgw/ ? # - copy uuid file to installed system? # - copy logfiles to installed system # - remove/nocopy: fastboot marker pass self.debconf_progress_region(97, 99) # Recreate ssh keys for f, t in [['/etc/ssh/ssh_host_rsa_key', 'rsa'], ['/etc/ssh/ssh_host_dsa_key', 'dsa']]: run_command(['/usr/sbin/chroot', '/target', '/bin/rm', '-f', f], retval=runcommand.FAIL) run_command(['/usr/sbin/chroot', '/target', '/usr/bin/ssh-keygen', '-q', '-N', '', '-f',f, '-t', t, '-C', 'root@%s' % self.hostname], retval=runcommand.FAIL) # Copy recovery data (again) self.copy_recovery_data(recovery_data) self.copy_debconf() self.cleanup() self.debconf_progress_set(100) self.write_status('success\nInstall completed') self.info('installation success') except: self.cleanup() self.write_status('failure\nInstall failed') self.error('installation failed') raise
def write_status(self, status): try: helpers.write_file(constants.INSTALL_STATUS_FILE, status, append=False, perms=0644) except: self.error ('writing installation status failed, ignored.')
def create_filesystems(self, device): """Repartition the device, create filesystems and /etc/fstab.""" self.debconf_progress_start(0, 100, '') self.debconf_progress_set(0) self.debconf_progress_info('Finding target device: %s' % device) m = mediahelper.get_media() target = m.get_medium_by_device_name(device) if target is None: self.error('failed to find installation target device: %s' % device) raise Exception('failed to find installation target device: %s' % device) # determine various parameters for later partitioning and fs setup create_swap = True part_fat = None part_boot = None part_swap = None part_root = None if create_swap: if self.large_install: part_fat = target.get_partition_devicename(1) part_boot = target.get_partition_devicename(2) part_swap = target.get_partition_devicename(3) part_root = target.get_partition_devicename(4) partitions = [part_fat, part_boot, part_swap, part_root] else: part_fat = target.get_partition_devicename(1) part_swap = target.get_partition_devicename(2) part_root = target.get_partition_devicename(3) partitions = [part_fat, part_swap, part_root] else: if self.large_install: part_fat = target.get_partition_devicename(1) part_boot = target.get_partition_devicename(2) part_root = target.get_partition_devicename(3) partitions = [part_fat, part_boot, part_root] else: part_fat = target.get_partition_devicename(1) part_root = target.get_partition_devicename(2) partitions = [part_fat, part_root] disk_min_size = (constants.DISK_SIZE_MINIMUM - constants.DISK_SIZE_SAFETY_MARGIN ) / 512 * 512 # disk size with safety margin disk_size = (target.get_size() - constants.DISK_SIZE_SAFETY_MARGIN) / 512 * 512 # partition "end points" fat_end = '1MB' disk_min_end = '%ss' % (disk_min_size / 512) # sectors disk_end = '%ss' % (disk_size / 512) # sectors try: self.debconf_progress_info('Wiping target device: %s' % device) self.nuke_and_create_disklabel(device) # Just in case time.sleep(1) self.debconf_progress_set(5) self.wait_for_partition_devices_to_disappear(partitions) self.debconf_progress_set(10) # MBR is at sector 0, and the first partition should begin at sector 63. # This leaves sectors 1...62 as "no man's land" (31kiB) which is used # for Grub stage 1.5 # # See: http://en.wikipedia.org/wiki/GNU_GRUB # # XXX: currently partitions don't end at cylinder # boundaries. It would probably be best to round the # partitions up to cylinder boundaries, as a # "conservative" partitioning is typically done so. # However, this should not matter as long as we only run # Grub and Linux itself. # # NOTE: Even Windows Vista partitions USB sticks without # caring about cylinders. Probably not an issue anymore. # Just in case time.sleep(1) self.debconf_progress_set(15) # XXX: endpoint is exclusive in parted self.debconf_progress_info('Creating partitions') if create_swap: if self.large_install: boot_end = '256MB' swap_end = '768MB' parted_cmds = [ ['mkpart', 'primary', 'fat32', '63s', fat_end], ['mkpart', 'primary', 'ext2', fat_end, boot_end], [ 'mkpart', 'primary', 'linux-swap', boot_end, swap_end ], ['mkpart', 'primary', 'ext2', swap_end, disk_end], ['set', '2', 'boot', 'on'] ] else: swap_end = '512MB' parted_cmds = [ ['mkpart', 'primary', 'fat32', '63s', fat_end], ['mkpart', 'primary', 'linux-swap', fat_end, swap_end], ['mkpart', 'primary', 'ext2', swap_end, disk_min_end], ['set', '3', 'boot', 'on'] ] else: if self.large_install: boot_end = '256MB' parted_cmds = [ ['mkpart', 'primary', 'fat32', '63s', fat_end], ['mkpart', 'primary', 'ext2', fat_end, boot_end], ['mkpart', 'primary', 'ext2', boot_end, disk_end], ['set', '2', 'boot', 'on'] ] else: parted_cmds = [[ 'mkpart', 'primary', 'fat32', '63s', fat_end ], ['mkpart', 'primary', 'ext2', fat_end, disk_min_end], ['set', '2', 'boot', 'on']] for i in parted_cmds: run_command(['/sbin/parted', '-s', device] + i, retval=runcommand.FAIL) self.debconf_progress_set(20) # Just in case time.sleep(1) self.debconf_progress_set(25) self.wait_for_partition_devices_to_appear(partitions) self.debconf_progress_set(30) # Sleep for a while to ensure that there is no "flicker" of device nodes. # For some reason this happens with at least native hardware and USB sticks. # See #435. time.sleep(5) self.debconf_progress_set(55) self.debconf_progress_info('Creating partitions and filesystems') run_command([ '/sbin/mkfs.vfat', '-n', constants.PRODUCT_NAME.upper(), part_fat ], retval=runcommand.FAIL) self.debconf_progress_set(60) if part_swap is not None: run_command(['/sbin/mkswap', '-L', 'SWAP', part_swap], retval=runcommand.FAIL) if part_boot is not None: # XXX: opts here? run_command([ '/sbin/mkfs.ext3', '-L', 'ROOT', '-b', str(1024), '-i', str(4096), '-m', str(0), '-O', 'sparse_super,filetype,resize_inode,dir_index', '-v', part_boot ], retval=runcommand.FAIL) [rc, stdout, stderr] = run_command(['/sbin/dumpe2fs', part_boot]) self._log.info('dumpe2fs dump of boot filesystem:\n%s' % stdout) else: self._log.info('no boot partition, skipping mkfs') run_command([ '/sbin/mkfs.ext3', '-L', 'ROOT', '-b', str(1024), '-i', str(4096), '-m', str(0), '-O', 'sparse_super,filetype,resize_inode,dir_index', '-v', part_root ], retval=runcommand.FAIL) [rc, stdout, stderr] = run_command(['/sbin/dumpe2fs', part_root]) self._log.info('dumpe2fs dump of root filesystem:\n%s' % stdout) self.debconf_progress_set(95) except: self.error('failed to create partitions, exiting.') raise # Create targets and fstab. self.debconf_progress_info('Mounting target and creating fstab') # Note: not using -p because the target should not exist at this point (cleanup done) run_command([constants.CMD_MKDIR, '/target'], retval=runcommand.FAIL) run_command([constants.CMD_MKDIR, '/target-fat'], retval=runcommand.FAIL) run_command([constants.CMD_MOUNT, part_root, '/target'], retval=runcommand.FAIL) run_command([constants.CMD_MOUNT, part_fat, '/target-fat'], retval=runcommand.FAIL) if part_boot is not None: run_command([constants.CMD_MKDIR, '/target/boot'], retval=runcommand.FAIL) run_command([constants.CMD_MOUNT, part_boot, '/target/boot'], retval=runcommand.FAIL) # Write fstab now (it will not be overwritten by copy process) run_command([constants.CMD_MKDIR, '/target/etc/'], retval=runcommand.FAIL) fstab = textwrap.dedent("""\ # /etc/fstab: static file system information. # # <file system> <mount point> <type> <options> <dump> <pass> proc /proc proc defaults 0 0 %(part_root)s / ext3 defaults,errors=remount-ro,noatime 0 1 """) if part_swap is not None: fstab += textwrap.dedent("""\ %(part_swap)s none swap sw 0 0 """) if part_boot is not None: fstab += textwrap.dedent("""\ %(part_boot)s /boot ext3 defaults 0 2 """) fstab = fstab % { 'part_swap': part_swap, 'part_root': part_root, 'part_boot': part_boot, 'part_fat': part_fat } helpers.write_file('/target/etc/fstab', fstab) self.debconf_progress_set(100) # XXX: cdrom to fstab? nope for now # /dev/hdc /media/cdrom0 iso9660 ro,user,noauto 0 0 self.debconf_progress_stop()
def run_update(self): """Run update script. Returns a Deferred, which either raises an update-related exception (ending up in caller's errback), or returns None if the update completes normally. """ def _script_timeout(): _log.warning('_script_timeout()') self.script_timeout_call = None # this is not nice, but what else to do here? if self._update_process_protocol is not None: self._update_process_protocol.sendTerm() self.update_failed = True self.update_exit_code = 3 # XXX: fake update exit code to cause UpdateFailedError self.stop_twisted() def _update_completed(res): _log.debug('_update_completed()') if self.script_timeout_call is not None: self.script_timeout_call.cancel() self.script_timeout_call = None # XXX: In a script timeout case, we get here with 'res' not being # an integer. This is not nice, but causes no actual problems. self.update_exit_code = int(res) # store exit code _log.info('update exit code: %s' % self.update_exit_code) return None _log.info('update needed, starting update process') # export configuration before update from sqlite so that new code after # update has the option of re-creating the sqlite database or switch to # a new backend format without resorting to ugly sqlite dependencies try: _log.info('exporting rdf/xml for update') self._export_rdfxml_for_update() _log.info('export rdf/xml for update successful') except: _log.exception('_export_rdfxml_for_update() failed, ignoring') try: if os.path.exists(constants.UPDATE_PROCESS_RDFXML_EXPORT_FILE): os.unlink(constants.UPDATE_PROCESS_RDFXML_EXPORT_FILE) except: _log.exception('_export_rdfxml_for_update(), cleanup failed') # set sources.list helpers.write_file('/etc/apt/sources.list', self.sources) # set repository keys helpers.write_file(constants.UPDATE_REPOSITORY_KEYS_FILE, self.repokeys, perms=0600, append=False) # determine parameters for update cmd = constants.CMD_PYTHON if self.scriptspath is not None: pyfile = os.path.join(self.scriptspath, os.path.basename(constants.CMD_L2TPGW_UPDATE_PRODUCT)) else: pyfile = constants.CMD_L2TPGW_UPDATE_PRODUCT _log.info('update command: %s, script: %s, arguments: %s' % (cmd, pyfile, self.importpath)) # failure timer for running script self.script_timeout_call = reactor.callLater(constants.UPDATE_SCRIPT_TIMEOUT, _script_timeout) # start update process u = UpdateProcessProtocol() self._update_process_protocol = u reactor.spawnProcess(u, executable=cmd, args=[cmd, pyfile, '--import-path', self.importpath], env=None, # Uses os.environ if set to None, default is empty usePTY=1) d = u.waitCompleted() d.addCallback(_update_completed) self.run_update_deferred = d return d