def mount_target(self, chroot, treat_mtab=True, pseudo=True): """Mount a set of file systems into a chroot :param chroot: Directory where to mount file systems :param treat_mtab: If mtab needs to be actualized (Default: True) :param pseudo: If pseudo file systems need to be mounted (Default: True) """ LOG.debug('Mounting target file systems: %s', chroot) # Here we are going to mount all file systems in partition scheme. for fs in self.driver.partition_scheme.fs_sorted_by_depth(): if fs.mount == 'swap': continue mount = chroot + fs.mount utils.makedirs_if_not_exists(mount) fu.mount_fs(fs.type, str(fs.device), mount) if pseudo: for path in ('/sys', '/dev', '/proc'): utils.makedirs_if_not_exists(chroot + path) fu.mount_bind(chroot, path) if treat_mtab: mtab = utils.execute( 'chroot', chroot, 'grep', '-v', 'rootfs', '/proc/mounts')[0] mtab_path = chroot + '/etc/mtab' if os.path.islink(mtab_path): os.remove(mtab_path) with open(mtab_path, 'wb') as f: f.write(mtab)
def mount_target(self, chroot, treat_mtab=True, pseudo=True): """Mount a set of file systems into a chroot :param chroot: Directory where to mount file systems :param treat_mtab: If mtab needs to be actualized (Default: True) :param pseudo: If pseudo file systems need to be mounted (Default: True) """ LOG.debug('Mounting target file systems') # Here we are going to mount all file systems in partition scheme. for fs in self.driver.partition_scheme.fs_sorted_by_depth(): if fs.mount == 'swap': continue mount = chroot + fs.mount utils.makedirs_if_not_exists(mount) fu.mount_fs(fs.type, str(fs.device), mount) if pseudo: for path in ('/sys', '/dev', '/proc'): utils.makedirs_if_not_exists(chroot + path) fu.mount_bind(chroot, path) if treat_mtab: mtab = utils.execute('chroot', chroot, 'grep', '-v', 'rootfs', '/proc/mounts')[0] mtab_path = chroot + '/etc/mtab' if os.path.islink(mtab_path): os.remove(mtab_path) with open(mtab_path, 'wb') as f: f.write(mtab)
def test_mount_bind_path2(self, mock_exec): fu.mount_bind('/target', '/fake', '/fake2') mock_exec.assert_called_once_with('mount', '--bind', '/fake', '/target/fake2', check_exit_code=[0])
def mount_target(self, chroot): # Here we are going to mount all file systems in partition scheme. # Shorter paths earlier. We sort all mount points by their depth. # ['/', '/boot', '/var', '/var/lib/mysql'] key = lambda x: len(x.mount.rstrip('/').split('/')) for fs in sorted(self.partition_scheme.fss, key=key): if fs.mount == 'swap': continue mount = chroot + fs.mount if not os.path.isdir(mount): os.makedirs(mount, mode=0o755) fu.mount_fs(fs.type, fs.device, mount) fu.mount_bind(chroot, '/sys') fu.mount_bind(chroot, '/dev') fu.mount_bind(chroot, '/proc')
def mount_target(self, chroot): LOG.debug('Mounting target file systems') # Here we are going to mount all file systems in partition scheme. # Shorter paths earlier. We sort all mount points by their depth. # ['/', '/boot', '/var', '/var/lib/mysql'] key = lambda x: len(x.mount.rstrip('/').split('/')) for fs in sorted(self.partition_scheme.fss, key=key): if fs.mount == 'swap': continue mount = chroot + fs.mount if not os.path.isdir(mount): os.makedirs(mount, mode=0o755) fu.mount_fs(fs.type, fs.device, mount) fu.mount_bind(chroot, '/sys') fu.mount_bind(chroot, '/dev') fu.mount_bind(chroot, '/proc') mtab = utils.execute('chroot', chroot, 'grep', '-v', 'rootfs', '/proc/mounts')[0] mtab_path = chroot + '/etc/mtab' if os.path.islink(mtab_path): os.remove(mtab_path) with open(mtab_path, 'wb') as f: f.write(mtab)
def mount_target(self, chroot): LOG.debug('Mounting target file systems') # Here we are going to mount all file systems in partition scheme. # Shorter paths earlier. We sort all mount points by their depth. # ['/', '/boot', '/var', '/var/lib/mysql'] key = lambda x: len(x.mount.rstrip('/').split('/')) for fs in sorted(self.partition_scheme.fss, key=key): if fs.mount == 'swap': continue mount = chroot + fs.mount if not os.path.isdir(mount): os.makedirs(mount, mode=0o755) fu.mount_fs(fs.type, fs.device, mount) fu.mount_bind(chroot, '/sys') fu.mount_bind(chroot, '/dev') fu.mount_bind(chroot, '/proc') mtab = utils.execute( 'chroot', chroot, 'grep', '-v', 'rootfs', '/proc/mounts')[0] mtab_path = chroot + '/etc/mtab' if os.path.islink(mtab_path): os.remove(mtab_path) with open(mtab_path, 'wb') as f: f.write(mtab)
def test_mount_bind_path2(self, mock_exec): fu.mount_bind('/target', '/fake', '/fake2') mock_exec.assert_called_once_with( 'mount', '--bind', '/fake', '/target/fake2', check_exit_code=[0])
def do_build_image(self): """Building OS images includes the following steps 1) create temporary sparse files for all images (truncate) 2) attach temporary files to loop devices (losetup) 3) create file systems on these loop devices 4) create temporary chroot directory 5) mount loop devices into chroot directory 6) install operating system (debootstrap and apt-get) 7) configure OS (clean sources.list and preferences, etc.) 8) umount loop devices 9) resize file systems on loop devices 10) shrink temporary sparse files (images) 11) containerize (gzip) temporary sparse files 12) move temporary gzipped files to their final location """ LOG.info('--- Building image (do_build_image) ---') # TODO(kozhukalov): Implement metadata # as a pluggable data driver to avoid any fixed format. metadata = {} # TODO(kozhukalov): implement this using image metadata # we need to compare list of packages and repos LOG.info('*** Checking if image exists ***') if all([os.path.exists(img.uri.split('file://', 1)[1]) for img in self.driver.image_scheme.images]): LOG.debug('All necessary images are available. ' 'Nothing needs to be done.') return LOG.debug('At least one of the necessary images is unavailable. ' 'Starting build process.') try: LOG.debug('Creating temporary chroot directory') chroot = tempfile.mkdtemp( dir=CONF.image_build_dir, suffix=CONF.image_build_suffix) LOG.debug('Temporary chroot: %s', chroot) proc_path = os.path.join(chroot, 'proc') LOG.info('*** Preparing image space ***') for image in self.driver.image_scheme.images: LOG.debug('Creating temporary sparsed file for the ' 'image: %s', image.uri) img_tmp_file = bu.create_sparse_tmp_file( dir=CONF.image_build_dir, suffix=CONF.image_build_suffix) LOG.debug('Temporary file: %s', img_tmp_file) # we need to remember those files # to be able to shrink them and move in the end image.img_tmp_file = img_tmp_file LOG.debug('Looking for a free loop device') image.target_device.name = bu.get_free_loop_device() LOG.debug('Attaching temporary image file to free loop device') bu.attach_file_to_loop(img_tmp_file, str(image.target_device)) # find fs with the same loop device object # as image.target_device fs = self.driver.partition_scheme.fs_by_device( image.target_device) LOG.debug('Creating file system on the image') fu.make_fs( fs_type=fs.type, fs_options=fs.options, fs_label=fs.label, dev=str(fs.device)) # mounting all images into chroot tree self.mount_target(chroot, treat_mtab=False, pseudo=False) LOG.info('*** Shipping image content ***') LOG.debug('Installing operating system into image') # FIXME(kozhukalov): !!! we need this part to be OS agnostic # DEBOOTSTRAP # we use first repo as the main mirror uri = self.driver.operating_system.repos[0].uri suite = self.driver.operating_system.repos[0].suite LOG.debug('Preventing services from being get started') bu.suppress_services_start(chroot) LOG.debug('Installing base operating system using debootstrap') bu.run_debootstrap(uri=uri, suite=suite, chroot=chroot) # APT-GET LOG.debug('Configuring apt inside chroot') LOG.debug('Setting environment variables') bu.set_apt_get_env() LOG.debug('Allowing unauthenticated repos') bu.pre_apt_get(chroot) for repo in self.driver.operating_system.repos: LOG.debug('Adding repository source: name={name}, uri={uri},' 'suite={suite}, section={section}'.format( name=repo.name, uri=repo.uri, suite=repo.suite, section=repo.section)) bu.add_apt_source( name=repo.name, uri=repo.uri, suite=repo.suite, section=repo.section, chroot=chroot) LOG.debug('Adding repository preference: ' 'name={name}, priority={priority}'.format( name=repo.name, priority=repo.priority)) if repo.priority is not None: bu.add_apt_preference( name=repo.name, priority=repo.priority, suite=repo.suite, section=repo.section, chroot=chroot, uri=repo.uri) metadata.setdefault('repos', []).append({ 'type': 'deb', 'name': repo.name, 'uri': repo.uri, 'suite': repo.suite, 'section': repo.section, 'priority': repo.priority, 'meta': repo.meta}) LOG.debug('Preventing services from being get started') bu.suppress_services_start(chroot) packages = self.driver.operating_system.packages metadata['packages'] = packages # we need /proc to be mounted for apt-get success utils.makedirs_if_not_exists(proc_path) fu.mount_bind(chroot, '/proc') LOG.debug('Installing packages using apt-get: %s', ' '.join(packages)) bu.run_apt_get(chroot, packages=packages) LOG.debug('Post-install OS configuration') bu.do_post_inst(chroot) LOG.debug('Making sure there are no running processes ' 'inside chroot before trying to umount chroot') if not bu.stop_chrooted_processes(chroot, signal=signal.SIGTERM): if not bu.stop_chrooted_processes( chroot, signal=signal.SIGKILL): raise errors.UnexpectedProcessError( 'Stopping chrooted processes failed. ' 'There are some processes running in chroot %s', chroot) LOG.info('*** Finalizing image space ***') fu.umount_fs(proc_path) # umounting all loop devices self.umount_target(chroot, pseudo=False, try_lazy_umount=False) for image in self.driver.image_scheme.images: LOG.debug('Deattaching loop device from file: %s', image.img_tmp_file) bu.deattach_loop(str(image.target_device)) LOG.debug('Shrinking temporary image file: %s', image.img_tmp_file) bu.shrink_sparse_file(image.img_tmp_file) raw_size = os.path.getsize(image.img_tmp_file) raw_md5 = utils.calculate_md5(image.img_tmp_file, raw_size) LOG.debug('Containerizing temporary image file: %s', image.img_tmp_file) img_tmp_containerized = bu.containerize( image.img_tmp_file, image.container) img_containerized = image.uri.split('file://', 1)[1] # NOTE(kozhukalov): implement abstract publisher LOG.debug('Moving image file to the final location: %s', img_containerized) shutil.move(img_tmp_containerized, img_containerized) container_size = os.path.getsize(img_containerized) container_md5 = utils.calculate_md5( img_containerized, container_size) metadata.setdefault('images', []).append({ 'raw_md5': raw_md5, 'raw_size': raw_size, 'raw_name': None, 'container_name': os.path.basename(img_containerized), 'container_md5': container_md5, 'container_size': container_size, 'container': image.container, 'format': image.format}) # NOTE(kozhukalov): implement abstract publisher LOG.debug('Image metadata: %s', metadata) with open(self.driver.metadata_uri.split('file://', 1)[1], 'w') as f: yaml.safe_dump(metadata, stream=f) LOG.info('--- Building image END (do_build_image) ---') except Exception as exc: LOG.error('Failed to build image: %s', exc) raise finally: LOG.debug('Finally: stopping processes inside chroot: %s', chroot) if not bu.stop_chrooted_processes(chroot, signal=signal.SIGTERM): bu.stop_chrooted_processes(chroot, signal=signal.SIGKILL) LOG.debug('Finally: umounting procfs %s', proc_path) fu.umount_fs(proc_path) LOG.debug('Finally: umounting chroot tree %s', chroot) self.umount_target(chroot, pseudo=False, try_lazy_umount=False) for image in self.driver.image_scheme.images: LOG.debug('Finally: detaching loop device: %s', str(image.target_device)) try: bu.deattach_loop(str(image.target_device)) except errors.ProcessExecutionError as e: LOG.warning('Error occured while trying to detach ' 'loop device %s. Error message: %s', str(image.target_device), e) LOG.debug('Finally: removing temporary file: %s', image.img_tmp_file) try: os.unlink(image.img_tmp_file) except OSError: LOG.debug('Finally: file %s seems does not exist ' 'or can not be removed', image.img_tmp_file) LOG.debug('Finally: removing chroot directory: %s', chroot) try: os.rmdir(chroot) except OSError: LOG.debug('Finally: directory %s seems does not exist ' 'or can not be removed', chroot)
def do_build_image(self): """Building OS images includes the following steps 1) create temporary sparse files for all images (truncate) 2) attach temporary files to loop devices (losetup) 3) create file systems on these loop devices 4) create temporary chroot directory 5) mount loop devices into chroot directory 6) install operating system (debootstrap and apt-get) 7) configure OS (clean sources.list and preferences, etc.) 8) umount loop devices 9) resize file systems on loop devices 10) shrink temporary sparse files (images) 11) containerize (gzip) temporary sparse files 12) move temporary gzipped files to their final location """ LOG.info('--- Building image (do_build_image) ---') # TODO(kozhukalov): Implement metadata # as a pluggable data driver to avoid any fixed format. metadata = {} # TODO(kozhukalov): implement this using image metadata # we need to compare list of packages and repos LOG.info('*** Checking if image exists ***') if all([ os.path.exists(img.uri.split('file://', 1)[1]) for img in self.driver.image_scheme.images ]): LOG.debug('All necessary images are available. ' 'Nothing needs to be done.') return LOG.debug('At least one of the necessary images is unavailable. ' 'Starting build process.') LOG.info('*** Preparing image space ***') for image in self.driver.image_scheme.images: LOG.debug('Creating temporary sparsed file for the ' 'image: %s', image.uri) img_tmp_file = bu.create_sparse_tmp_file( dir=CONF.image_build_dir, suffix=CONF.image_build_suffix) LOG.debug('Temporary file: %s', img_tmp_file) # we need to remember those files # to be able to shrink them and move in the end image.img_tmp_file = img_tmp_file LOG.debug('Looking for a free loop device') image.target_device.name = bu.get_free_loop_device() LOG.debug('Attaching temporary image file to free loop device') bu.attach_file_to_loop(img_tmp_file, str(image.target_device)) # find fs with the same loop device object # as image.target_device fs = self.driver.partition_scheme.fs_by_device(image.target_device) LOG.debug('Creating file system on the image') fu.make_fs(fs_type=fs.type, fs_options=fs.options, fs_label=fs.label, dev=str(fs.device)) LOG.debug('Creating temporary chroot directory') chroot = tempfile.mkdtemp(dir=CONF.image_build_dir, suffix=CONF.image_build_suffix) LOG.debug('Temporary chroot: %s', chroot) # mounting all images into chroot tree self.mount_target(chroot, treat_mtab=False, pseudo=False) LOG.info('*** Shipping image content ***') LOG.debug('Installing operating system into image') # FIXME(kozhukalov): !!! we need this part to be OS agnostic # DEBOOTSTRAP # we use first repo as the main mirror uri = self.driver.operating_system.repos[0].uri suite = self.driver.operating_system.repos[0].suite LOG.debug('Preventing services from being get started') bu.suppress_services_start(chroot) LOG.debug('Installing base operating system using debootstrap') bu.run_debootstrap(uri=uri, suite=suite, chroot=chroot) # APT-GET LOG.debug('Configuring apt inside chroot') LOG.debug('Setting environment variables') bu.set_apt_get_env() LOG.debug('Allowing unauthenticated repos') bu.pre_apt_get(chroot) for repo in self.driver.operating_system.repos: LOG.debug('Adding repository source: name={name}, uri={uri},' 'suite={suite}, section={section}'.format( name=repo.name, uri=repo.uri, suite=repo.suite, section=repo.section)) bu.add_apt_source(name=repo.name, uri=repo.uri, suite=repo.suite, section=repo.section, chroot=chroot) LOG.debug('Adding repository preference: ' 'name={name}, priority={priority}'.format( name=repo.name, priority=repo.priority)) bu.add_apt_preference(name=repo.name, priority=repo.priority, suite=repo.suite, section=repo.section, chroot=chroot) metadata.setdefault('repos', []).append({ 'type': 'deb', 'name': repo.name, 'uri': repo.uri, 'suite': repo.suite, 'section': repo.section, 'priority': repo.priority, 'meta': repo.meta }) LOG.debug('Preventing services from being get started') bu.suppress_services_start(chroot) packages = self.driver.operating_system.packages metadata['packages'] = packages # we need /proc to be mounted for apt-get success proc_path = os.path.join(chroot, 'proc') utils.makedirs_if_not_exists(proc_path) fu.mount_bind(chroot, '/proc') LOG.debug('Installing packages using apt-get: %s', ' '.join(packages)) bu.run_apt_get(chroot, packages=packages) LOG.debug('Post-install OS configuration') bu.do_post_inst(chroot) LOG.debug('Making sure there are no running processes ' 'inside chroot before trying to umount chroot') bu.send_signal_to_chrooted_processes(chroot, signal.SIGTERM) # We assume there might be some processes which # require some reasonable time to stop before we try # to send them SIGKILL. Waiting for 2 seconds # looks reasonable here. time.sleep(2) bu.send_signal_to_chrooted_processes(chroot, signal.SIGKILL) LOG.info('*** Finalizing image space ***') fu.umount_fs(proc_path) # umounting all loop devices self.umount_target(chroot, pseudo=False) for image in self.driver.image_scheme.images: LOG.debug('Deattaching loop device from file: %s', image.img_tmp_file) bu.deattach_loop(str(image.target_device)) LOG.debug('Shrinking temporary image file: %s', image.img_tmp_file) bu.shrink_sparse_file(image.img_tmp_file) raw_size = os.path.getsize(image.img_tmp_file) raw_md5 = utils.calculate_md5(image.img_tmp_file, raw_size) LOG.debug('Containerizing temporary image file: %s', image.img_tmp_file) img_tmp_containerized = bu.containerize(image.img_tmp_file, image.container) img_containerized = image.uri.split('file://', 1)[1] # NOTE(kozhukalov): implement abstract publisher LOG.debug('Moving image file to the final location: %s', img_containerized) shutil.move(img_tmp_containerized, img_containerized) container_size = os.path.getsize(img_containerized) container_md5 = utils.calculate_md5(img_containerized, container_size) metadata.setdefault('images', []).append({ 'raw_md5': raw_md5, 'raw_size': raw_size, 'raw_name': None, 'container_name': os.path.basename(img_containerized), 'container_md5': container_md5, 'container_size': container_size, 'container': image.container, 'format': image.format }) # NOTE(kozhukalov): implement abstract publisher LOG.debug('Image metadata: %s', metadata) with open(self.driver.metadata_uri.split('file://', 1)[1], 'w') as f: yaml.safe_dump(metadata, stream=f) LOG.info('--- Building image END (do_build_image) ---')